double buffering = colossal performance reduction??

Started by
14 comments, last by silverphyre673 18 years, 9 months ago
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.
my siteGenius is 1% inspiration and 99% perspiration
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.
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.
Turring Machines are better than C++ any day ^_~
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.
my siteGenius is 1% inspiration and 99% perspiration
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.
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
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.
-Mike
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.
my siteGenius is 1% inspiration and 99% perspiration
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.
RIP GameDev.net: launched 2 unusably-broken forum engines in as many years, and now has ceased operating as a forum at all, happy to remain naught but an advertising platform with an attached social media presense, headed by a staff who by their own admission have no idea what their userbase wants or expects.Here's to the good times; shame they exist in the past.
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.
my siteGenius is 1% inspiration and 99% perspiration
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 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