Jump to content
  • Advertisement
Sign in to follow this  
silverphyre673

double buffering = colossal performance reduction??

This topic is 4857 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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.

Share this post


Link to post
Share on other sites
Advertisement
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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)


#include <windows.h>

//definitions
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480

//function protos
HBITMAP 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 function
int 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/event
LRESULT 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]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!