double buffering = colossal performance reduction??
Hey all. I've been developing this worms clone (pure win32 API) in c++, and the screen has been flickering. I finally got around to implementing some double buffering. When I started calling SwapBuffers(), though, I got a colossal performance reduction. When I comment it out, it works normally. ANy guesses why? Here is what I have:
- A global HDC to the HWND, "hdc"
- A PIXELFORMATDESCRIPTOR, "pfd", for hdc, which says that it should be double buffered
- When I call my Rendering function, I do all my drawing to hdc, and then call SwapBuffers on it as the last function call. This leads to a big performance reduction.
I know that it does cause a performance hit, since you have to copy a large amount of data every render, but I see it all the time when I'm working with OpenGL, using (I thought) the same procedure, and I've never had this happen before. Any advice appreciated.
Thanks.
You don't copy the screen when swapping buffers; you just specify a different area in memory for the computer to paint to the screen.
Quote:Original post by silverphyre673
I know that it does cause a performance hit, since you have to copy a large amount of data every render, but I see it all the time when I'm working with OpenGL, using (I thought) the same procedure, and I've never had this happen before. Any advice appreciated.
Thanks.
Like the poster before said, swapping buffers is probably the least intensive operation possible. What you really have are two memory buffers, both for storing screens. At any one time, one buffer is used to display on the screen, and the other is being drawn on. Then, instead of copying the entire buffer, the pointers to the two buffers are switched. That way when you start drawing after a swap you are actually drawing on the buffer the buffer that was just on screen.
I know that actually, I just wrote a confused post :)
I still don't know why I'm taking this sudden performance hit, though. Whenever I see double buffering taught, like in my win32 API reference book, it shows two HDCs: one made through CreateDC(HWND), if I remember correctly, and another through CreateCompatibleDC(HDC). You draw everything to the second HDC, then somehow copy it to the first one for drawing all at once. Can anyone give me a coded example of how to do this? I tried, and failed. Thanks.
I still don't know why I'm taking this sudden performance hit, though. Whenever I see double buffering taught, like in my win32 API reference book, it shows two HDCs: one made through CreateDC(HWND), if I remember correctly, and another through CreateCompatibleDC(HDC). You draw everything to the second HDC, then somehow copy it to the first one for drawing all at once. Can anyone give me a coded example of how to do this? I tried, and failed. Thanks.
If you're using OpenGL you want to enable double-buffering in OGL.
It looks like you're double-buffering using device contexts which is very slow.
It looks like you're double-buffering using device contexts which is very slow.
Your post is a bit confusing because OpenGL is not usually considered part of the win32 api. Nor do you typically draw to an HDC when writing OpenGL apps.
SwapBuffers doesn't just flip pointers around. It also has an implied flush and possible a v-sync as well. You might try calling glFlush before SwapBuffers and see if the perf hit moves to the flush. If so I don't know what to tell you. If not try disabling vsync and see if that helps.
SwapBuffers doesn't just flip pointers around. It also has an implied flush and possible a v-sync as well. You might try calling glFlush before SwapBuffers and see if the perf hit moves to the flush. If so I don't know what to tell you. If not try disabling vsync and see if that helps.
I'm not using opengl. I was just using it as an example; I didn't know that it did it differently. I'm just using pure win32 API - drawing ellipses, rectangles and lines.
When working with a WINDOWED application, 'swapping buffers' involves copying the render-buffer to the screen. And using pure Win32 GDI, copy is the only method.
when working with a FULL SCREEN application, 'swapping buffers' is really a pointer-swap.
You get flickering if you render to the visible surface, that's just a fact of life. You get tearing if you swap or copy buffers without waiting for vertical synchronization. I suggest you switch to SDL.
when working with a FULL SCREEN application, 'swapping buffers' is really a pointer-swap.
You get flickering if you render to the visible surface, that's just a fact of life. You get tearing if you swap or copy buffers without waiting for vertical synchronization. I suggest you switch to SDL.
I'm not switching to SDL; the game is almost done. There must be some way to avoid the screen flickers. The game is definitely not that big, and the amount of flickering is unacceptable. It is over the whole screen - I'm not just talking about animation flickers when something like a picture of a bouncing ball moves around the screen. Even if you had nothing moving, you can still see some visible screen flickering. It's terrible.
I agree with Wyrframe. It's true that often double buffering means swapping pointers, but not always. At least I've understood it in a way that the advantage of double buffering is drawing to a secondary buffer which is not visible, then either swapping this buffer (the pointer) or copying it. The advantage of the double buffer in both cases is that the whole visible screen/window surface is painted in a short time thus reducing flickering. Without the double buffer, when drawing shapes and copying pixels to the visible surface it might take longer than it takes to update the surface, so some of the shapes will be invisible during some of the updates which makes them flicker.
Here's how I use to do double buffers:
Notes:
You can keep the backbuffer DC if you want but remember Windows 98 only allows 5 DCs per thread
I just rewrote this from an old game I did, if you see any error tell me and I'll look into that (maybe I mistyped)
Good luck!
[edit]just fixed the code, it was too wide[/edit]
Here's how I use to do double buffers:
Notes:
You can keep the backbuffer DC if you want but remember Windows 98 only allows 5 DCs per thread
I just rewrote this from an old game I did, if you see any error tell me and I'll look into that (maybe I mistyped)
#include <windows.h>//definitions#define WINDOW_WIDTH 640#define WINDOW_HEIGHT 480//function protosHBITMAP CreateSurface(USHORT width, USHORT height, HWND compatibleHWnd);void ReleaseSurface(HBITMAP hBitmap);HRESULT PaintRectangle(SHORT x, SHORT y, USHORT width, USHORT height, COLORREF color, HBITMAP dstBitmap, HWND compatibleWindow);HRESULT Blt(SHORT dstX, SHORT dstY, USHORT srcX, USHORT srcY, USHORT width, USHORT height, HBITMAP srcBitmap, HWND dstWindow);LRESULT CALLBACK MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);//windows main entry functionint APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd){ HWND hWnd; MSG msg; //bitmap surface HBITMAP backbuffer; //window class setup WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = MainWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); //identifier for system icon wc.hCursor = LoadCursor(NULL, IDC_ARROW); //identifier for system cursor wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = "mainwindow"; if (RegisterClass(&wc) == 0) { MessageBox(NULL, "Couldn't register window class.", "doublebuffer", 0); return 0; } hWnd = CreateWindow("mainwindow", "doublebuffer", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0, hInstance, NULL); if (hWnd == NULL) { MessageBox(NULL, "Couldn't create window.", "doublebuffer", 0); return 0; } //create offscreen surfaces backbuffer = CreateSurface(WINDOW_WIDTH, WINDOW_HEIGHT, hWnd); //window message loop while (true) { //peek messages for all windows belonging to this thread if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) //the window has been destroyed break; TranslateMessage(&msg); DispatchMessage(&msg); //send the message to the event handler } else { //idling - do the drawing here //paint a rectangle onto the backbuffer PaintRectangle(0, 0, WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2, RGB(205, 0, 0), backbuffer, hWnd); //blit the whole backbuffer onto the window Blt(0, 0, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, backbuffer, hWnd); } } //release offscreen surfaces ReleaseSurface(backbuffer); return msg.wParam;}//window process/eventLRESULT CALLBACK MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){ switch (msg) { case WM_PAINT: break; case WM_CLOSE: DestroyWindow(hWnd); //post a WM_DESTROY message and dsetroy the current window case WM_DESTROY: PostQuitMessage(0); //post a WM_QUIT message to the message queue and return return 0; } //return a call to DefWndProc so all messages gets handled return DefWindowProc(hWnd, msg, wParam, lParam);}HBITMAP CreateSurface(USHORT width, USHORT height, HWND compatibleHWnd){ HDC compatibleDC; HBITMAP hBitmap; //create a bitmap compatible with the specified window compatibleDC = GetDC(compatibleHWnd); //retrieves the window device context hBitmap = CreateCompatibleBitmap(compatibleDC, width, height); //creates a bitmap compatible with it ReleaseDC(compatibleHWnd, compatibleDC); //releases the compatible window device context return hBitmap; //got a bitmap surface - done!}void ReleaseSurface(HBITMAP hBitmap){ if (hBitmap) DeleteObject(hBitmap);}HRESULT PaintRectangle(SHORT x, SHORT y, USHORT width, USHORT height, COLORREF color, HBITMAP dstBitmap, HWND compatibleWindow){ if (dstBitmap == NULL || compatibleWindow == NULL) return -1; //not good HRESULT hResult; HDC compatibleDC; HDC dstDC; RECT rect = {x, y, x + width, y + height}; HBRUSH hBrush; /* create a device context for the bitmap surface which is compatible with the one of the compatible window */ compatibleDC = GetDC(compatibleWindow); dstDC = CreateCompatibleDC(compatibleDC); SelectObject(dstDC, dstBitmap); hBrush = CreateSolidBrush(color); hResult = FillRect(dstDC, &rect, hBrush); DeleteObject(hBrush); //delete/release the used device contexts DeleteDC(dstDC); //this DC was created ReleaseDC(compatibleWindow, compatibleDC); //this was just retrieved return hResult;}HRESULT Blt(SHORT dstX, SHORT dstY, USHORT srcX, USHORT srcY, USHORT width, USHORT height, HBITMAP srcBitmap, HWND dstWindow){ HDC srcDC; HDC dstDC; //create a device context for the destination window dstDC = GetDC(dstWindow); //create a device context for the source bitmap (compatible with the window) srcDC = CreateCompatibleDC(dstDC); SelectObject(srcDC, srcBitmap); //blt! BitBlt(dstDC, dstX, dstY, width, height, srcDC, srcX, srcY, SRCCOPY); //delete/release the device contexts DeleteDC(srcDC); ReleaseDC(dstWindow, dstDC); return 0;}
Good luck!
[edit]just fixed the code, it was too wide[/edit]
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement