From e665a7f28dd73ec9d03674a82141a4331910fb87 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 12 Jul 2006 06:39:26 +0000 Subject: [PATCH] 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 | 10 +- src/SDL_compat.c | 22 ++--- src/video/SDL_renderer_sw.c | 45 +++++---- src/video/SDL_video.c | 37 +++---- src/video/win32/SDL_gdirender.c | 166 ++++++++++++++++++++++++-------- 5 files changed, 184 insertions(+), 96 deletions(-) diff --git a/include/SDL_video.h b/include/SDL_video.h index a42187638..78244ec92 100644 --- a/include/SDL_video.h +++ b/include/SDL_video.h @@ -170,14 +170,14 @@ typedef enum */ 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; diff --git a/src/SDL_compat.c b/src/SDL_compat.c index a9b7d7a66..a01f0ff3e 100644 --- a/src/SDL_compat.c +++ b/src/SDL_compat.c @@ -442,7 +442,8 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags) } /* 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 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags) /* 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 @@ SDL_UpdateRect(SDL_Surface * screen, Sint32 x, Sint32 y, Uint32 w, Uint32 h) 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); } } diff --git a/src/video/SDL_renderer_sw.c b/src/video/SDL_renderer_sw.c index 4cbbd4ca2..666adca2b 100644 --- a/src/video/SDL_renderer_sw.c +++ b/src/video/SDL_renderer_sw.c @@ -77,12 +77,11 @@ SDL_RenderDriver SDL_SW_RenderDriver = { 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 @@ typedef struct SDL_Surface *target; SDL_Renderer *renderer; SDL_DirtyRectList dirty; + SDL_bool makedirty; } SDL_SW_RenderData; SDL_Renderer * @@ -185,13 +185,16 @@ SDL_SW_CreateRenderer(SDL_Window * window, Uint32 flags) } 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 @@ SDL_SW_SelectRenderTexture(SDL_Renderer * renderer, SDL_Texture * texture) 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_SW_RenderFill(SDL_Renderer * renderer, const SDL_Rect * rect, 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_SW_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, 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 @@ SDL_SW_RenderWritePixels(SDL_Renderer * renderer, const SDL_Rect * rect, 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_RenderPresent(SDL_Renderer * renderer) 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_SW_RenderPresent(SDL_Renderer * renderer) 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; - } - if (data->target == data->screens[data->current_screen]) { - data->target = data->screens[new_screen]; + data->current_screen = (data->current_screen + 1) % 3; + data->target = data->screens[data->current_screen]; } - data->current_screen = new_screen; } static void diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 9b76c2069..6921185b1 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -1769,9 +1769,8 @@ SDL_RenderFill(const SDL_Rect * rect, Uint32 color) return 0; } } - rect = &real_rect; - return renderer->RenderFill(renderer, rect, color); + return renderer->RenderFill(renderer, &real_rect, color); } int @@ -1793,25 +1792,26 @@ SDL_RenderCopy(SDL_TextureID textureID, const SDL_Rect * srcrect, 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 @@ SDL_RenderPresent(void) return; } + if (renderer->SelectRenderTexture) { + renderer->SelectRenderTexture(renderer, NULL); + } renderer->RenderPresent(renderer); } diff --git a/src/video/win32/SDL_gdirender.c b/src/video/win32/SDL_gdirender.c index c0061f652..690767f7f 100644 --- a/src/video/win32/SDL_gdirender.c +++ b/src/video/win32/SDL_gdirender.c @@ -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_RenderDriver SDL_GDI_RenderDriver = { 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 @@ typedef struct 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_CreateRenderer(SDL_Window * window, Uint32 flags) 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_GDI_CreateRenderer(SDL_Window * window, Uint32 flags) } 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 @@ SDL_GDI_CreateRenderer(SDL_Window * window, Uint32 flags) 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 @@ SDL_GDI_QueryTexturePixels(SDL_Renderer * renderer, SDL_Texture * texture, 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 @@ SDL_GDI_SelectRenderTexture(SDL_Renderer * renderer, SDL_Texture * texture) 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 @@ SDL_GDI_RenderFill(SDL_Renderer * renderer, const SDL_Rect * rect, 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_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, 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 @@ CreateWindowDIB(SDL_GDI_RenderData * data, SDL_Window * window) 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_GDI_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, 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_GDI_RenderWritePixels(SDL_Renderer * renderer, const SDL_Rect * rect, 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 @@ SDL_GDI_RenderWritePixels(SDL_Renderer * renderer, const SDL_Rect * rect, } } - 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 @@ SDL_GDI_RenderWritePixels(SDL_Renderer * renderer, const SDL_Rect * rect, 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 @@ void 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 @@ SDL_GDI_DestroyRenderer(SDL_Renderer * renderer) 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); }