use offscreen blitting when SDL_DOUBLEBUF is not set
authornotaz <notasas@gmail.com>
Sat, 9 Feb 2013 20:55:07 +0000 (22:55 +0200)
committernotaz <notasas@gmail.com>
Sat, 9 Feb 2013 20:58:44 +0000 (22:58 +0200)
otherwise many games suffer flicker.
Provide an option to support old behavior.

README.OMAP
src/video/omapdss/config.c
src/video/omapdss/linux/fbdev.c
src/video/omapdss/linux/fbdev.h
src/video/omapdss/osdl.h
src/video/omapdss/osdl_video.c
src/video/omapdss/sdlif.c

index 1c04162..96af240 100644 (file)
@@ -75,6 +75,15 @@ SDL_OMAP_FORCE_DOUBLEBUF:
   Note that if app isn't updating whole buffer each frame, it will glitch.
   This is the same as specifying SDL_DOUBLEBUF to SDL_SetVideoMode.
 
+SDL_OMAP_FORCE_DIRECTBUF:
+  When double buffering is not used, this option forces all blits to go
+  directly to the framebuffer (SDL_UpdateRect[s]() has no effect), which will
+  give speed but may cause flickering. Otherwise all blits will go to offscreen
+  buffer and SDL_UpdateRect[s]() is needed to update the screen (this is how
+  standard SDL works too).
+  When double buffering is used, this option has no effect (all blits always
+  go to back buffer that's displayed after flip).
+
 SDL_OMAP_NO_TS_TRANSLATE:
   Disable automatic touchscreen screen -> layer coordinate translation,
   return real screen coordinates.
@@ -97,6 +106,9 @@ force_vsync = 1/0
 # same as SDL_OMAP_FORCE_DOUBLEBUF
 force_doublebuf = 1/0
 
+# same as SDL_OMAP_FORCE_DIRECTBUF
+force_directbuf = 1/0
+
 # same as SDL_OMAP_NO_TS_TRANSLATE
 no_ts_translate = 1/0
 
index 0b7146a..10a9036 100644 (file)
@@ -91,6 +91,10 @@ void omapsdl_config(struct SDL_PrivateVideoData *pdata)
                        pdata->cfg_force_doublebuf = !!strtol(p, NULL, 0);
                        continue;
                }
+               else if (check_token_eq(&p, "force_directbuf")) {
+                       pdata->cfg_force_directbuf = !!strtol(p, NULL, 0);
+                       continue;
+               }
                else if (check_token_eq(&p, "no_ts_translate")) {
                        pdata->cfg_no_ts_translate = !!strtol(p, NULL, 0);
                        continue;
@@ -116,6 +120,9 @@ void omapsdl_config_from_env(struct SDL_PrivateVideoData *pdata)
        tmp = getenv("SDL_OMAP_FORCE_DOUBLEBUF");
        if (tmp != NULL)
                pdata->cfg_force_doublebuf = !!strtol(tmp, NULL, 0);
+       tmp = getenv("SDL_OMAP_FORCE_DIRECTBUF");
+       if (tmp != NULL)
+               pdata->cfg_force_directbuf = !!strtol(tmp, NULL, 0);
        tmp = getenv("SDL_OMAP_NO_TS_TRANSLATE");
        if (tmp != NULL)
                pdata->cfg_no_ts_translate = !!strtol(tmp, NULL, 0);
index ec3d5c7..7e8a962 100644 (file)
@@ -175,6 +175,17 @@ int vout_fbdev_get_fd(struct vout_fbdev *fbdev)
        return fbdev->fd;
 }
 
+void *vout_fbdev_get_active_mem(struct vout_fbdev *fbdev)
+{
+       int i;
+
+       i = fbdev->buffer_write - 1;
+       if (i < 0)
+               i = fbdev->buffer_count - 1;
+
+       return (char *)fbdev->mem + fbdev->fb_size * i;
+}
+
 struct vout_fbdev *vout_fbdev_init(const char *fbdev_name, int *w, int *h, int bpp, int buffer_cnt)
 {
        struct vout_fbdev *fbdev;
index 88cd519..ac2ae15 100644 (file)
@@ -9,6 +9,7 @@ void *vout_fbdev_resize(struct vout_fbdev *fbdev, int w, int h, int bpp,
 void  vout_fbdev_clear(struct vout_fbdev *fbdev);
 void  vout_fbdev_clear_lines(struct vout_fbdev *fbdev, int y, int count);
 int   vout_fbdev_get_fd(struct vout_fbdev *fbdev);
+void *vout_fbdev_get_active_mem(struct vout_fbdev *fbdev);
 int   vout_fbdev_save(struct vout_fbdev *fbdev);
 int   vout_fbdev_restore(struct vout_fbdev *fbdev);
 void  vout_fbdev_finish(struct vout_fbdev *fbdev);
index 3ca1bf5..6b78977 100644 (file)
@@ -27,6 +27,7 @@
 
 struct SDL_PrivateVideoData {
        struct vout_fbdev *fbdev;
+       void *front_buffer;
        void *saved_layer;
        /* physical screen size, should match touchscreen */
        int phys_w, phys_h;
@@ -42,6 +43,7 @@ struct SDL_PrivateVideoData {
        unsigned int app_uses_flip:1;
        unsigned int cfg_force_vsync:1;
        unsigned int cfg_force_doublebuf:1;
+       unsigned int cfg_force_directbuf:1;
        unsigned int cfg_no_ts_translate:1;
        unsigned int cfg_ts_force_tslib:1;
 };
@@ -51,6 +53,7 @@ void *osdl_video_set_mode(struct SDL_PrivateVideoData *pdata,
                          int width, int height, int bpp, int *doublebuf,
                          const char *wm_title);
 void *osdl_video_flip(struct SDL_PrivateVideoData *pdata);
+void *osdl_video_get_active_buffer(struct SDL_PrivateVideoData *pdata);
 int   osdl_video_detect_screen(struct SDL_PrivateVideoData *pdata);
 int   osdl_video_pause(struct SDL_PrivateVideoData *pdata, int is_pause);
 void  osdl_video_finish(struct SDL_PrivateVideoData *pdata);
index fe9a29c..5a291a7 100644 (file)
@@ -454,6 +454,14 @@ void *osdl_video_flip(struct SDL_PrivateVideoData *pdata)
        return ret;
 }
 
+void *osdl_video_get_active_buffer(struct SDL_PrivateVideoData *pdata)
+{
+       if (pdata->fbdev == NULL)
+               return NULL;
+
+       return vout_fbdev_get_active_mem(pdata->fbdev);
+}
+
 int osdl_video_pause(struct SDL_PrivateVideoData *pdata, int is_pause)
 {
        struct omapfb_state *state = pdata->saved_layer;
index 96388f2..27a730f 100644 (file)
@@ -133,7 +133,10 @@ static SDL_Surface *omap_SetVideoMode(SDL_VideoDevice *this, SDL_Surface *curren
                }
        }
 
-       doublebuf = (flags & SDL_DOUBLEBUF) ? 1 : 0;
+       /* always use doublebuf, when SDL_DOUBLEBUF is not set,
+        * we'll have to blit manually on UpdateRects() */
+       doublebuf = 1;
+
        fbmem = osdl_video_set_mode(pdata,
                pdata->border_l, pdata->border_r, pdata->border_t, pdata->border_b,
                width, height, bpp, &doublebuf, this->wm_title);
@@ -143,11 +146,24 @@ static SDL_Surface *omap_SetVideoMode(SDL_VideoDevice *this, SDL_Surface *curren
                    pdata->border_l, pdata->border_r, pdata->border_t, pdata->border_b);
                return NULL;
        }
-       if ((flags & SDL_DOUBLEBUF) && !doublebuf) {
-               log("doublebuffering could not be set\n");
-               flags &= ~SDL_DOUBLEBUF;
+       pdata->front_buffer = osdl_video_get_active_buffer(pdata);
+       if (pdata->front_buffer == NULL) {
+               err("osdl_video_get_active_buffer failed\n");
+               return NULL;
+       }
+
+       if (!doublebuf) {
+               if (flags & SDL_DOUBLEBUF) {
+                       log("doublebuffering could not be set\n");
+                       flags &= ~SDL_DOUBLEBUF;
+               }
+               /* XXX: could just malloc a back buffer here instead */
+               pdata->cfg_force_directbuf = 1;
        }
 
+       if (!(flags & SDL_DOUBLEBUF) && pdata->cfg_force_directbuf)
+               fbmem = pdata->front_buffer;
+
        flags |= SDL_FULLSCREEN | SDL_HWSURFACE;
        unhandled_flags = flags & ~(SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF);
        if (unhandled_flags != 0) {
@@ -187,10 +203,27 @@ static void omap_UnlockHWSurface(SDL_VideoDevice *this, SDL_Surface *surface)
 static int omap_FlipHWSurface(SDL_VideoDevice *this, SDL_Surface *surface)
 {
        struct SDL_PrivateVideoData *pdata = this->hidden;
+       static int warned;
 
        trace("%p", surface);
 
-       surface->pixels = osdl_video_flip(pdata);
+       if (surface != this->screen) {
+               if (!warned) {
+                       err("flip surface %p which is not screen %p?\n",
+                               surface, this->screen);
+                       warned = 1;
+               }
+               return;
+       }
+
+       if (surface->flags & SDL_DOUBLEBUF)
+               surface->pixels = osdl_video_flip(pdata);
+       else {
+               if (surface->pixels != pdata->front_buffer)
+                       memcpy(surface->pixels, pdata->front_buffer,
+                               surface->pitch * surface->h);
+       }
+
        pdata->app_uses_flip = 1;
 
        return 0;
@@ -217,15 +250,47 @@ static int omap_SetColors(SDL_VideoDevice *this, int firstcolor, int ncolors, SD
 static void omap_UpdateRects(SDL_VideoDevice *this, int nrects, SDL_Rect *rects)
 {
        struct SDL_PrivateVideoData *pdata = this->hidden;
+       SDL_Surface *screen = this->screen;
+       Uint16 *src, *dst;
+       int i, x, y, w, h;
 
        trace("%d, %p", nrects, rects);
 
-       /* for doublebuf forcing on apps */
-       if (nrects == 1 && rects->x == 0 && rects->y == 0
-           && !pdata->app_uses_flip && (this->screen->flags & SDL_DOUBLEBUF)
-           && rects->w == this->screen->w && rects->h == this->screen->h)
-       {
-               this->screen->pixels = osdl_video_flip(pdata);
+       if (screen->flags & SDL_DOUBLEBUF) {
+               if (nrects == 1 && rects->x == 0 && rects->y == 0
+                   && (rects->w == screen->w || rects->w == 0)
+                   && (rects->h == screen->h || rects->h == 0)
+                   && !pdata->app_uses_flip)
+               {
+                       screen->pixels = osdl_video_flip(pdata);
+               }
+               return;
+       }
+
+       src = screen->pixels;
+       dst = pdata->front_buffer;
+       if (src == dst)
+               return;
+
+       for (i = 0; i < nrects; i++) {
+               /* this supposedly has no clipping, but we'll do it anyway */
+               x = rects[i].x, y = rects[i].y, w = rects[i].w, h = rects[i].h;
+               if (x < 0)
+                       w += x, x = 0;
+               else if (x + w > screen->w)
+                       w = screen->w - x;
+               if (w <= 0)
+                       continue;
+
+               if (y < 0)
+                       h += y, y = 0;
+               else if (y + h > screen->h)
+                       h = screen->h - y;
+
+               for (; h > 0; y++, h--)
+                       memcpy(dst + y * screen->pitch / 2 + x,
+                              src + y * screen->pitch / 2 + x,
+                              w * 2);
        }
 }