aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrei Gherzan <agherzan@hanoverdisplays.com>2019-12-17 10:22:36 +0000
committerRichard Purdie <richard.purdie@linuxfoundation.org>2020-01-21 13:17:43 +0000
commit2913c24920a984331685eaa528f6b29c18e285c8 (patch)
treefb24eb50a248d339b9035b9c198edb778579631a
parent8188d68191b8bc2349319e804aa5e5df6e019ec2 (diff)
downloadpsplash-2913c24920a984331685eaa528f6b29c18e285c8.tar.gz
psplash-2913c24920a984331685eaa528f6b29c18e285c8.tar.bz2
psplash-2913c24920a984331685eaa528f6b29c18e285c8.zip
Double buffering support
'psplash' uses only one buffer which can cause tearing artifacts. This change uses the FBIOPAN_DISPLAY ioctl as a way to handle page flipping and also does that after a vsync interrupt to remove any tearing issues. Signed-off-by: Andrei Gherzan <agherzan@hanoverdisplays.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--psplash-fb.c121
-rw-r--r--psplash-fb.h8
-rw-r--r--psplash.c10
3 files changed, 118 insertions, 21 deletions
diff --git a/psplash-fb.c b/psplash-fb.c
index 846c961..6603572 100644
--- a/psplash-fb.c
+++ b/psplash-fb.c
@@ -10,6 +10,46 @@
#include <endian.h>
#include "psplash.h"
+static void
+psplash_wait_for_vsync(PSplashFB *fb)
+{
+ int err = ioctl(fb->fd, FBIO_WAITFORVSYNC, 0);
+ if (err != 0)
+ fprintf(stderr, "Error, FB vsync ioctl [%d]\n", err);
+}
+
+void
+psplash_fb_flip(PSplashFB *fb, int sync)
+{
+ char *tmp;
+
+ if (fb->double_buffering) {
+
+ /* Carry out the flip after a vsync */
+ psplash_wait_for_vsync(fb);
+
+ /* Switch the current activate area in fb */
+ if (fb->fb_var.yoffset == 0 ) {
+ fb->fb_var.yoffset = fb->real_height;
+ } else {
+ fb->fb_var.yoffset = 0;
+ }
+ if (ioctl(fb->fd, FBIOPAN_DISPLAY, &fb->fb_var) == -1 ) {
+ fprintf(stderr, "psplash_fb_flip: FBIOPAN_DISPLAY failed\n");
+ }
+
+ /* Switch the front and back data pointers */
+ tmp = fb->fdata;
+ fb->fdata = fb->bdata;
+ fb->bdata = tmp;
+
+ /* Sync new front to new back when requested */
+ if (sync) {
+ memcpy(fb->bdata, fb->fdata, fb->stride * fb->real_height);
+ }
+ }
+}
+
void
psplash_fb_destroy (PSplashFB *fb)
{
@@ -155,6 +195,29 @@ psplash_fb_new (int angle, int fbdev_id)
goto fail;
}
+ /* Setup double virtual resolution for double buffering */
+ if (ioctl(fb->fd, FBIOPAN_DISPLAY, &fb_var) == -1) {
+ fprintf(stderr, "FBIOPAN_DISPLAY not supported, double buffering disabled");
+ } else {
+ if (fb_var.yres_virtual == fb_var.yres * 2) {
+ DBG("Virtual resolution already double");
+ fb->double_buffering = 1;
+ } else {
+ fb_var.yres_virtual = fb_var.yres * 2;
+ if (ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb_var) == -1) {
+ fprintf(stderr, "FBIOPUT_VSCREENINFO failed, double buffering disabled");
+ } else {
+ if (ioctl(fb->fd, FBIOGET_FSCREENINFO, &fb_fix) == -1) {
+ perror(" Error getting the fixed framebuffer info");
+ goto fail;
+ } else {
+ DBG("Virtual resolution set to double");
+ fb->double_buffering = 1;
+ }
+ }
+ }
+ }
+
fb->real_width = fb->width = fb_var.xres;
fb->real_height = fb->height = fb_var.yres;
fb->bpp = fb_var.bits_per_pixel;
@@ -193,8 +256,7 @@ psplash_fb_new (int angle, int fbdev_id)
fb->width, fb->height, fb->bpp, fb->stride);
fb->base = (char *) mmap ((caddr_t) NULL,
- /*fb_fix.smem_len */
- fb->stride * fb->height,
+ fb_fix.smem_len,
PROT_READ|PROT_WRITE,
MAP_SHARED,
fb->fd, 0);
@@ -209,6 +271,23 @@ psplash_fb_new (int angle, int fbdev_id)
fb->data = fb->base + off;
+ if (fb->double_buffering) {
+ /* fb_var is needed when flipping the buffers */
+ memcpy(&fb->fb_var, &fb_var, sizeof(struct fb_var_screeninfo));
+ if (fb->fb_var.yoffset == 0) {
+ printf("to back\n");
+ fb->fdata = fb->data;
+ fb->bdata = fb->data + fb->stride * fb->height;
+ } else {
+ printf("to front\n");
+ fb->fdata = fb->data + fb->stride * fb->height;
+ fb->bdata = fb->data;
+ }
+ } else {
+ fb->fdata = fb->data;
+ fb->bdata = fb->data;
+ }
+
#if 0
/* FIXME: No support for 8pp as yet */
if (visual == FB_VISUAL_PSEUDOCOLOR
@@ -266,6 +345,8 @@ psplash_fb_plot_pixel (PSplashFB *fb,
uint8 green,
uint8 blue)
{
+ /* Always write to back data (bdata) which points to the right data with or
+ * without double buffering support */
int off;
if (x < 0 || x > fb->width-1 || y < 0 || y > fb->height-1)
@@ -293,22 +374,22 @@ psplash_fb_plot_pixel (PSplashFB *fb,
{
case 24:
#if __BYTE_ORDER == __BIG_ENDIAN
- *(fb->data + off + 0) = red;
- *(fb->data + off + 1) = green;
- *(fb->data + off + 2) = blue;
+ *(fb->bdata + off + 0) = red;
+ *(fb->bdata + off + 1) = green;
+ *(fb->bdata + off + 2) = blue;
#else
- *(fb->data + off + 0) = blue;
- *(fb->data + off + 1) = green;
- *(fb->data + off + 2) = red;
+ *(fb->bdata + off + 0) = blue;
+ *(fb->bdata + off + 1) = green;
+ *(fb->bdata + off + 2) = red;
#endif
break;
case 32:
- *(volatile uint32_t *) (fb->data + off)
+ *(volatile uint32_t *) (fb->bdata + off)
= (red << 16) | (green << 8) | (blue);
break;
case 16:
- *(volatile uint16_t *) (fb->data + off)
+ *(volatile uint16_t *) (fb->bdata + off)
= ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
break;
default:
@@ -320,21 +401,21 @@ psplash_fb_plot_pixel (PSplashFB *fb,
{
case 24:
#if __BYTE_ORDER == __BIG_ENDIAN
- *(fb->data + off + 0) = blue;
- *(fb->data + off + 1) = green;
- *(fb->data + off + 2) = red;
+ *(fb->bdata + off + 0) = blue;
+ *(fb->bdata + off + 1) = green;
+ *(fb->bdata + off + 2) = red;
#else
- *(fb->data + off + 0) = red;
- *(fb->data + off + 1) = green;
- *(fb->data + off + 2) = blue;
+ *(fb->bdata + off + 0) = red;
+ *(fb->bdata + off + 1) = green;
+ *(fb->bdata + off + 2) = blue;
#endif
break;
case 32:
- *(volatile uint32_t *) (fb->data + off)
+ *(volatile uint32_t *) (fb->bdata + off)
= (blue << 16) | (green << 8) | (red);
break;
case 16:
- *(volatile uint16_t *) (fb->data + off)
+ *(volatile uint16_t *) (fb->bdata + off)
= ((blue >> 3) << 11) | ((green >> 2) << 5) | (red >> 3);
break;
default:
@@ -345,13 +426,13 @@ psplash_fb_plot_pixel (PSplashFB *fb,
switch (fb->bpp)
{
case 32:
- *(volatile uint32_t *) (fb->data + off)
+ *(volatile uint32_t *) (fb->bdata + off)
= ((red >> (8 - fb->red_length)) << fb->red_offset)
| ((green >> (8 - fb->green_length)) << fb->green_offset)
| ((blue >> (8 - fb->blue_length)) << fb->blue_offset);
break;
case 16:
- *(volatile uint16_t *) (fb->data + off)
+ *(volatile uint16_t *) (fb->bdata + off)
= ((red >> (8 - fb->red_length)) << fb->red_offset)
| ((green >> (8 - fb->green_length)) << fb->green_offset)
| ((blue >> (8 - fb->blue_length)) << fb->blue_offset);
diff --git a/psplash-fb.h b/psplash-fb.h
index edf1e99..16e2b20 100644
--- a/psplash-fb.h
+++ b/psplash-fb.h
@@ -21,6 +21,7 @@ enum RGBMode {
typedef struct PSplashFB
{
int fd;
+ struct fb_var_screeninfo fb_var;
struct termios save_termios;
int type;
int visual;
@@ -30,6 +31,11 @@ typedef struct PSplashFB
char *data;
char *base;
+ /* Support for double buffering */
+ int double_buffering;
+ char *bdata;
+ char *fdata;
+
int angle, fbdev_id;
int real_width, real_height;
@@ -85,5 +91,7 @@ psplash_fb_draw_text (PSplashFB *fb,
const PSplashFont *font,
const char *text);
+void
+psplash_fb_flip(PSplashFB *fb, int sync);
#endif
diff --git a/psplash.c b/psplash.c
index a81a483..e5dff16 100644
--- a/psplash.c
+++ b/psplash.c
@@ -124,6 +124,7 @@ parse_command (PSplashFB *fb, char *string)
return 1;
}
+ psplash_fb_flip(fb, 0);
return 0;
}
@@ -319,8 +320,15 @@ main (int argc, char** argv)
psplash_draw_msg (fb, PSPLASH_STARTUP_MSG);
#endif
- psplash_main (fb, pipe_fd, 0);
+ /* Scene set so let's flip the buffers. */
+ /* The first time we also synchronize the buffers so we can build on an
+ * existing scene. After the first scene is set in both buffers, only the
+ * text and progress bar change which overwrite the specific areas with every
+ * update.
+ */
+ psplash_fb_flip(fb, 1);
+ psplash_main (fb, pipe_fd, 0);
psplash_fb_destroy (fb);