Solved the performance problems by introducing the concept of a single-buffered
authorSam Lantinga <slouken@libsdl.org>
Wed, 12 Jul 2006 06:39:26 +0000
changeset 1897 c2a27da60b18
parent 1896 4a74fa359e7e
child 1898 f89e49e51e89
Solved the performance problems by introducing the concept of a single-buffered display, which is a fast path used for the whole-surface SDL 1.2 API. Solved the flicker problems by implementing a backbuffer in the GDI renderer. Unfortunately, now using the GDI renderer with a backbuffer and HBITMAPs is significantly slower than SDL's surface code. *sigh*
include/SDL_video.h
src/SDL_compat.c
src/video/SDL_renderer_sw.c
src/video/SDL_video.c
src/video/win32/SDL_gdirender.c
--- a/include/SDL_video.h	Mon Jul 10 21:23:51 2006 +0000
+++ b/include/SDL_video.h	Wed Jul 12 06:39:26 2006 +0000
@@ -170,14 +170,14 @@
  */
 typedef enum
 {
-    SDL_Renderer_PresentDiscard = 0x00000001,   /**< Present leaves the contents of the backbuffer undefined */
+    SDL_Renderer_SingleBuffer = 0x00000001,     /**< Render directly to the window, if possible */
     SDL_Renderer_PresentCopy = 0x00000002,      /**< Present uses a copy from back buffer to the front buffer */
     SDL_Renderer_PresentFlip2 = 0x00000004,     /**< Present uses a flip, swapping back buffer and front buffer */
     SDL_Renderer_PresentFlip3 = 0x00000008,     /**< Present uses a flip, rotating between two back buffers and a front buffer */
-    SDL_Renderer_PresentVSync = 0x00000010,     /**< Present is synchronized with the refresh rate */
-    SDL_Renderer_RenderTarget = 0x00000020,     /**< The renderer can create texture render targets */
-    SDL_Renderer_Accelerated = 0x00000040,      /**< The renderer uses hardware acceleration */
-    SDL_Renderer_ = 0x00000080,      /**< The renderer uses hardware acceleration */
+    SDL_Renderer_PresentDiscard = 0x00000010,   /**< Present leaves the contents of the backbuffer undefined */
+    SDL_Renderer_PresentVSync = 0x00000020,     /**< Present is synchronized with the refresh rate */
+    SDL_Renderer_RenderTarget = 0x00000040,     /**< The renderer can create texture render targets */
+    SDL_Renderer_Accelerated = 0x00000080,      /**< The renderer uses hardware acceleration */
     SDL_Renderer_Minimal = 0x00000100,          /**< The renderer only supports the read/write pixel and present functions */
 } SDL_RendererFlags;
 
--- a/src/SDL_compat.c	Mon Jul 10 21:23:51 2006 +0000
+++ b/src/SDL_compat.c	Wed Jul 12 06:39:26 2006 +0000
@@ -442,7 +442,8 @@
     }
 
     /* Create a renderer for the window */
-    if (SDL_CreateRenderer(SDL_VideoWindow, -1, 0) < 0) {
+    if (SDL_CreateRenderer(SDL_VideoWindow, -1, SDL_Renderer_SingleBuffer) <
+        0) {
         return NULL;
     }
 
@@ -517,6 +518,7 @@
 
     /* Clear the surface for display */
     SDL_FillRect(SDL_PublicSurface, NULL, 0);
+    SDL_UpdateRect(SDL_PublicSurface, 0, 0, 0, 0);
 
     /* We're finally done! */
     return SDL_PublicSurface;
@@ -617,21 +619,11 @@
     if (screen) {
         SDL_Rect rect;
 
-        /* Perform some checking */
-        if (w == 0)
-            w = screen->w;
-        if (h == 0)
-            h = screen->h;
-        if ((int) (x + w) > screen->w)
-            return;
-        if ((int) (y + h) > screen->h)
-            return;
-
         /* Fill the rectangle */
-        rect.x = (Sint16) x;
-        rect.y = (Sint16) y;
-        rect.w = (Uint16) w;
-        rect.h = (Uint16) h;
+        rect.x = (int) x;
+        rect.y = (int) y;
+        rect.w = (int) (w ? w : screen->w);
+        rect.h = (int) (h ? h : screen->h);
         SDL_UpdateRects(screen, 1, &rect);
     }
 }
--- a/src/video/SDL_renderer_sw.c	Mon Jul 10 21:23:51 2006 +0000
+++ b/src/video/SDL_renderer_sw.c	Wed Jul 12 06:39:26 2006 +0000
@@ -77,12 +77,11 @@
     SDL_SW_CreateRenderer,
     {
      "software",
-     (SDL_Renderer_PresentDiscard |
-      SDL_Renderer_PresentCopy |
-      SDL_Renderer_PresentFlip2 |
-      SDL_Renderer_PresentFlip3 | SDL_Renderer_RenderTarget),
-     (SDL_TextureBlendMode_None |
-      SDL_TextureBlendMode_Mask | SDL_TextureBlendMode_Blend),
+     (SDL_Renderer_SingleBuffer | SDL_Renderer_PresentCopy |
+      SDL_Renderer_PresentFlip2 | SDL_Renderer_PresentFlip3 |
+      SDL_Renderer_PresentDiscard | SDL_Renderer_RenderTarget),
+     (SDL_TextureBlendMode_None | SDL_TextureBlendMode_Mask |
+      SDL_TextureBlendMode_Blend),
      (SDL_TextureScaleMode_None | SDL_TextureScaleMode_Fast),
      11,
      {
@@ -108,6 +107,7 @@
     SDL_Surface *target;
     SDL_Renderer *renderer;
     SDL_DirtyRectList dirty;
+    SDL_bool makedirty;
 } SDL_SW_RenderData;
 
 SDL_Renderer *
@@ -185,13 +185,16 @@
     }
     data->current_screen = 0;
     data->target = data->screens[0];
+    data->makedirty = SDL_TRUE;
 
     /* Find a render driver that we can use to display data */
     for (i = 0; i < display->num_render_drivers; ++i) {
         SDL_RenderDriver *driver = &display->render_drivers[i];
         if (driver->info.name != SDL_SW_RenderDriver.info.name) {
             data->renderer =
-                driver->CreateRenderer(window, SDL_Renderer_PresentDiscard);
+                driver->CreateRenderer(window,
+                                       (SDL_Renderer_SingleBuffer |
+                                        SDL_Renderer_PresentDiscard));
             if (data->renderer) {
                 break;
             }
@@ -351,8 +354,10 @@
 
     if (texture) {
         data->target = (SDL_Surface *) texture->driverdata;
+        data->makedirty = SDL_FALSE;
     } else {
         data->target = data->screens[data->current_screen];
+        data->makedirty = SDL_TRUE;
     }
 }
 
@@ -364,7 +369,9 @@
     SDL_Rect real_rect = *rect;
     Uint8 r, g, b, a;
 
-    SDL_AddDirtyRect(&data->dirty, rect);
+    if (data->makedirty) {
+        SDL_AddDirtyRect(&data->dirty, rect);
+    }
 
     a = (Uint8) ((color >> 24) & 0xFF);
     r = (Uint8) ((color >> 16) & 0xFF);
@@ -384,7 +391,9 @@
     SDL_Window *window = SDL_GetWindowFromID(renderer->window);
     SDL_VideoDisplay *display = SDL_GetDisplayFromWindow(window);
 
-    SDL_AddDirtyRect(&data->dirty, dstrect);
+    if (data->makedirty) {
+        SDL_AddDirtyRect(&data->dirty, dstrect);
+    }
 
     if (SDL_ISPIXELFORMAT_FOURCC(texture->format)) {
         SDL_Surface *target = data->target;
@@ -450,7 +459,9 @@
     int row;
     size_t length;
 
-    SDL_AddDirtyRect(&data->dirty, rect);
+    if (data->makedirty) {
+        SDL_AddDirtyRect(&data->dirty, rect);
+    }
 
     src = (Uint8 *) pixels;
     dst =
@@ -471,7 +482,6 @@
     SDL_SW_RenderData *data = (SDL_SW_RenderData *) renderer->driverdata;
     SDL_Surface *surface = data->screens[data->current_screen];
     SDL_DirtyRect *dirty;
-    int new_screen;
 
     /* Send the data to the display */
     for (dirty = data->dirty.list; dirty; dirty = dirty->next) {
@@ -485,19 +495,14 @@
     SDL_ClearDirtyRects(&data->dirty);
     data->renderer->RenderPresent(data->renderer);
 
-
     /* Update the flipping chain, if any */
     if (renderer->info.flags & SDL_Renderer_PresentFlip2) {
-        new_screen = (data->current_screen + 1) % 2;
+        data->current_screen = (data->current_screen + 1) % 2;
+        data->target = data->screens[data->current_screen];
     } else if (renderer->info.flags & SDL_Renderer_PresentFlip3) {
-        new_screen = (data->current_screen + 1) % 3;
-    } else {
-        new_screen = 0;
+        data->current_screen = (data->current_screen + 1) % 3;
+        data->target = data->screens[data->current_screen];
     }
-    if (data->target == data->screens[data->current_screen]) {
-        data->target = data->screens[new_screen];
-    }
-    data->current_screen = new_screen;
 }
 
 static void
--- a/src/video/SDL_video.c	Mon Jul 10 21:23:51 2006 +0000
+++ b/src/video/SDL_video.c	Wed Jul 12 06:39:26 2006 +0000
@@ -1769,9 +1769,8 @@
             return 0;
         }
     }
-    rect = &real_rect;
 
-    return renderer->RenderFill(renderer, rect, color);
+    return renderer->RenderFill(renderer, &real_rect, color);
 }
 
 int
@@ -1793,25 +1792,26 @@
         return -1;
     }
 
-    /* FIXME: implement clipping */
     window = SDL_GetWindowFromID(renderer->window);
-    real_srcrect.x = 0;
-    real_srcrect.y = 0;
-    real_srcrect.w = texture->w;
-    real_srcrect.h = texture->h;
-    real_dstrect.x = 0;
-    real_dstrect.y = 0;
-    real_dstrect.w = window->w;
-    real_dstrect.h = window->h;
-    if (!srcrect) {
-        srcrect = &real_srcrect;
+    if (srcrect) {
+        real_srcrect = *srcrect;
+    } else {
+        real_srcrect.x = 0;
+        real_srcrect.y = 0;
+        real_srcrect.w = texture->w;
+        real_srcrect.h = texture->h;
     }
-    if (!dstrect) {
-        dstrect = &real_dstrect;
+    if (dstrect) {
+        real_dstrect = *dstrect;
+    } else {
+        real_dstrect.x = 0;
+        real_dstrect.y = 0;
+        real_dstrect.w = window->w;
+        real_dstrect.h = window->h;
     }
 
-    return renderer->RenderCopy(renderer, texture, srcrect, dstrect,
-                                blendMode, scaleMode);
+    return renderer->RenderCopy(renderer, texture, &real_srcrect,
+                                &real_dstrect, blendMode, scaleMode);
 }
 
 int
@@ -1882,6 +1882,9 @@
         return;
     }
 
+    if (renderer->SelectRenderTexture) {
+        renderer->SelectRenderTexture(renderer, NULL);
+    }
     renderer->RenderPresent(renderer);
 }
 
--- a/src/video/win32/SDL_gdirender.c	Mon Jul 10 21:23:51 2006 +0000
+++ b/src/video/win32/SDL_gdirender.c	Wed Jul 12 06:39:26 2006 +0000
@@ -24,6 +24,7 @@
 #if SDL_VIDEO_RENDER_GDI
 
 #include "SDL_win32video.h"
+#include "../SDL_rect_c.h"
 #include "../SDL_yuv_sw_c.h"
 
 /* GDI renderer implementation */
@@ -78,10 +79,11 @@
     SDL_GDI_CreateRenderer,
     {
      "gdi",
-     (SDL_Renderer_PresentDiscard |
-      SDL_Renderer_PresentCopy | SDL_Renderer_RenderTarget),
-     (SDL_TextureBlendMode_None |
-      SDL_TextureBlendMode_Mask | SDL_TextureBlendMode_Blend),
+     (SDL_Renderer_SingleBuffer | SDL_Renderer_PresentCopy |
+      SDL_Renderer_PresentFlip2 | SDL_Renderer_PresentFlip3 |
+      SDL_Renderer_PresentDiscard | SDL_Renderer_RenderTarget),
+     (SDL_TextureBlendMode_None | SDL_TextureBlendMode_Mask |
+      SDL_TextureBlendMode_Blend),
      (SDL_TextureScaleMode_None | SDL_TextureScaleMode_Fast),
      11,
      {
@@ -108,7 +110,11 @@
     HDC memory_hdc;
     HDC current_hdc;
     LPBITMAPINFO bmi;
-    HBITMAP window_bmp;
+    HBITMAP hbm[3];
+    int current_hbm;
+    SDL_DirtyRectList dirty;
+    SDL_bool makedirty;
+    HBITMAP window_dib;
     void *window_pixels;
     int window_pitch;
 } SDL_GDI_RenderData;
@@ -151,6 +157,7 @@
     SDL_GDI_RenderData *data;
     int bmi_size;
     HBITMAP hbm;
+    int i, n;
 
     renderer = (SDL_Renderer *) SDL_malloc(sizeof(*renderer));
     if (!renderer) {
@@ -167,28 +174,6 @@
     }
     SDL_zerop(data);
 
-    data->hwnd = windowdata->hwnd;
-    data->window_hdc = GetDC(data->hwnd);
-    data->render_hdc = CreateCompatibleDC(data->window_hdc);
-    data->memory_hdc = CreateCompatibleDC(data->window_hdc);
-    data->current_hdc = data->window_hdc;
-
-    /* Fill in the compatible bitmap info */
-    bmi_size = sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);
-    data->bmi = (LPBITMAPINFO) SDL_malloc(bmi_size);
-    if (!data->bmi) {
-        SDL_GDI_DestroyRenderer(renderer);
-        SDL_OutOfMemory();
-        return NULL;
-    }
-    SDL_memset(data->bmi, 0, bmi_size);
-    data->bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
-
-    hbm = CreateCompatibleBitmap(data->window_hdc, 1, 1);
-    GetDIBits(data->window_hdc, hbm, 0, 1, NULL, data->bmi, DIB_RGB_COLORS);
-    GetDIBits(data->window_hdc, hbm, 0, 1, NULL, data->bmi, DIB_RGB_COLORS);
-    DeleteObject(hbm);
-
     renderer->CreateTexture = SDL_GDI_CreateTexture;
     renderer->QueryTexturePixels = SDL_GDI_QueryTexturePixels;
     renderer->SetTexturePalette = SDL_GDI_SetTexturePalette;
@@ -211,6 +196,59 @@
 
     renderer->info.flags = SDL_Renderer_RenderTarget;
 
+    data->hwnd = windowdata->hwnd;
+    data->window_hdc = GetDC(data->hwnd);
+    data->render_hdc = CreateCompatibleDC(data->window_hdc);
+    data->memory_hdc = CreateCompatibleDC(data->window_hdc);
+
+    /* Fill in the compatible bitmap info */
+    bmi_size = sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);
+    data->bmi = (LPBITMAPINFO) SDL_malloc(bmi_size);
+    if (!data->bmi) {
+        SDL_GDI_DestroyRenderer(renderer);
+        SDL_OutOfMemory();
+        return NULL;
+    }
+    SDL_memset(data->bmi, 0, bmi_size);
+    data->bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+
+    hbm = CreateCompatibleBitmap(data->window_hdc, 1, 1);
+    GetDIBits(data->window_hdc, hbm, 0, 1, NULL, data->bmi, DIB_RGB_COLORS);
+    GetDIBits(data->window_hdc, hbm, 0, 1, NULL, data->bmi, DIB_RGB_COLORS);
+    DeleteObject(hbm);
+
+    if (flags & SDL_Renderer_SingleBuffer) {
+        renderer->info.flags |= SDL_Renderer_SingleBuffer;
+        n = 0;
+    } else if (flags & SDL_Renderer_PresentFlip2) {
+        renderer->info.flags |= SDL_Renderer_PresentFlip2;
+        n = 2;
+    } else if (flags & SDL_Renderer_PresentFlip3) {
+        renderer->info.flags |= SDL_Renderer_PresentFlip3;
+        n = 3;
+    } else {
+        renderer->info.flags |= SDL_Renderer_PresentCopy;
+        n = 1;
+    }
+    for (i = 0; i < n; ++i) {
+        data->hbm[i] =
+            CreateCompatibleBitmap(data->window_hdc, window->w, window->h);
+        if (!data->hbm[i]) {
+            SDL_GDI_DestroyRenderer(renderer);
+            WIN_SetError("CreateCompatibleBitmap()");
+            return NULL;
+        }
+    }
+    if (n > 0) {
+        SelectObject(data->render_hdc, data->hbm[0]);
+        data->current_hdc = data->render_hdc;
+        data->makedirty = SDL_TRUE;
+    } else {
+        data->current_hdc = data->window_hdc;
+        data->makedirty = SDL_FALSE;
+    }
+    data->current_hbm = 0;
+
     return renderer;
 }
 
@@ -335,7 +373,7 @@
         return SDL_SW_QueryYUVTexturePixels(data->yuv, pixels, pitch);
     } else {
         *pixels = data->pixels;
-        *pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);
+        *pitch = data->pitch;
         return 0;
     }
 }
@@ -494,8 +532,14 @@
             RealizePalette(data->render_hdc);
         }
         data->current_hdc = data->render_hdc;
+        data->makedirty = SDL_FALSE;
+    } else if (renderer->info.flags & SDL_Renderer_SingleBuffer) {
+        data->current_hdc = data->window_hdc;
+        data->makedirty = SDL_FALSE;
     } else {
-        data->current_hdc = data->current_hdc;
+        SelectObject(data->render_hdc, data->hbm[data->current_hbm]);
+        data->current_hdc = data->render_hdc;
+        data->makedirty = SDL_TRUE;
     }
 }
 
@@ -509,6 +553,10 @@
     static HBRUSH brush;
     int status;
 
+    if (data->makedirty) {
+        SDL_AddDirtyRect(&data->dirty, rect);
+    }
+
     r = (Uint8) ((color >> 16) & 0xFF);
     g = (Uint8) ((color >> 8) & 0xFF);
     b = (Uint8) (color & 0xFF);
@@ -540,6 +588,10 @@
     SDL_GDI_TextureData *texturedata =
         (SDL_GDI_TextureData *) texture->driverdata;
 
+    if (data->makedirty) {
+        SDL_AddDirtyRect(&data->dirty, dstrect);
+    }
+
     SelectObject(data->memory_hdc, texturedata->hbm);
     if (texturedata->hpal) {
         SelectPalette(data->memory_hdc, texturedata->hpal, TRUE);
@@ -590,10 +642,10 @@
     data->bmi->bmiHeader.biHeight = -window->h;
     data->bmi->bmiHeader.biSizeImage =
         window->h * (data->bmi->bmiHeader.biBitCount / 8);
-    data->window_bmp =
+    data->window_dib =
         CreateDIBSection(data->window_hdc, data->bmi, DIB_RGB_COLORS,
                          &data->window_pixels, NULL, 0);
-    if (!data->window_bmp) {
+    if (!data->window_dib) {
         WIN_SetError("CreateDIBSection()");
         return -1;
     }
@@ -607,15 +659,15 @@
     SDL_Window *window = SDL_GetWindowFromID(renderer->window);
     SDL_GDI_RenderData *data = (SDL_GDI_RenderData *) renderer->driverdata;
 
-    if (!data->window_bmp) {
+    if (!data->window_dib) {
         if (CreateWindowDIB(data, window) < 0) {
             return -1;
         }
     }
 
-    SelectObject(data->memory_hdc, data->window_bmp);
+    SelectObject(data->memory_hdc, data->window_dib);
     BitBlt(data->memory_hdc, rect->x, rect->y, rect->w, rect->h,
-           data->window_hdc, rect->x, rect->y, SRCCOPY);
+           data->current_hdc, rect->x, rect->y, SRCCOPY);
 
     {
         int bpp = data->bmi->bmiHeader.biBitCount / 8;
@@ -642,7 +694,11 @@
     SDL_Window *window = SDL_GetWindowFromID(renderer->window);
     SDL_GDI_RenderData *data = (SDL_GDI_RenderData *) renderer->driverdata;
 
-    if (!data->window_bmp) {
+    if (data->makedirty) {
+        SDL_AddDirtyRect(&data->dirty, rect);
+    }
+
+    if (!data->window_dib) {
         if (CreateWindowDIB(data, window) < 0) {
             return -1;
         }
@@ -663,8 +719,8 @@
         }
     }
 
-    SelectObject(data->memory_hdc, data->window_bmp);
-    BitBlt(data->window_hdc, rect->x, rect->y, rect->w, rect->h,
+    SelectObject(data->memory_hdc, data->window_dib);
+    BitBlt(data->current_hdc, rect->x, rect->y, rect->w, rect->h,
            data->memory_hdc, rect->x, rect->y, SRCCOPY);
 
     return 0;
@@ -673,6 +729,31 @@
 static void
 SDL_GDI_RenderPresent(SDL_Renderer * renderer)
 {
+    SDL_GDI_RenderData *data = (SDL_GDI_RenderData *) renderer->driverdata;
+    SDL_DirtyRect *dirty;
+    int new_hbm;
+
+    /* Send the data to the display */
+/*
+    if (!(renderer->info.flags & SDL_Renderer_SingleBuffer)) {
+        for (dirty = data->dirty.list; dirty; dirty = dirty->next) {
+            const SDL_Rect *rect = &dirty->rect;
+            BitBlt(data->window_hdc, rect->x, rect->y, rect->w, rect->h,
+                   data->render_hdc, rect->x, rect->y, SRCCOPY);
+        }
+        SDL_ClearDirtyRects(&data->dirty);
+    }
+*/
+    BitBlt(data->window_hdc, 0, 0, 640, 480, data->render_hdc, 0, 0, SRCCOPY);
+
+    /* Update the flipping chain, if any */
+    if (renderer->info.flags & SDL_Renderer_PresentFlip2) {
+        data->current_hbm = (data->current_hbm + 1) % 2;
+        SelectObject(data->render_hdc, data->hbm[data->current_hbm]);
+    } else if (renderer->info.flags & SDL_Renderer_PresentFlip3) {
+        data->current_hbm = (data->current_hbm + 1) % 3;
+        SelectObject(data->render_hdc, data->hbm[data->current_hbm]);
+    }
 }
 
 static void
@@ -700,6 +781,7 @@
 SDL_GDI_DestroyRenderer(SDL_Renderer * renderer)
 {
     SDL_GDI_RenderData *data = (SDL_GDI_RenderData *) renderer->driverdata;
+    int i;
 
     if (data) {
         ReleaseDC(data->hwnd, data->window_hdc);
@@ -708,8 +790,14 @@
         if (data->bmi) {
             SDL_free(data->bmi);
         }
-        if (data->window_bmp) {
-            DeleteObject(data->window_bmp);
+        for (i = 0; i < SDL_arraysize(data->hbm); ++i) {
+            if (data->hbm[i]) {
+                DeleteObject(data->hbm[i]);
+            }
+        }
+        SDL_FreeDirtyRects(&data->dirty);
+        if (data->window_dib) {
+            DeleteObject(data->window_dib);
         }
         SDL_free(data);
     }