double buffering = colossal performance reduction??

Started by
14 comments, last by silverphyre673 18 years, 9 months ago
It sounds to me like it's not double-buffering by means of page-flipping, but instead by copying the image across. It wont be page-fliping if you're not in fullscreen mode I'd guess.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
Advertisement
I figured maybe I should test how fast my example was so I don't post totally ineffective code without knowing it =). I tried to set the maximum frame limit to 120 FPS and drawing the 320 x 240 rectangle and copying the 640 x 480 backbuffer reached 84 FPS. Which I think is OK since it's plain Win32 GDI, windowed, after all.

Here's a frame limiter if you want to test it yourself:

UINT UpdateFrame(UINT requestedFPS){	static DWORD msNextSecond = 0;	//the frame of the next second	static DWORD msNextFrame = 0;	//the frame to delay until	static UINT frameOfSecond = 0;	//the current frame of this second	static UINT framesPerSecond = 0;	//increment the frame of this second	++frameOfSecond;	if (GetTickCount() >= msNextSecond)	{		//update the time of the next second in milliseconds		msNextSecond = GetTickCount() + 1000;		//update the amount of frames and reset the frame counter		framesPerSecond = frameOfSecond;		frameOfSecond = 0;	}	//delay until the time of the next frame in milliseconds	while (GetTickCount() < msNextFrame);	msNextFrame = GetTickCount() + 1000 / requestedFPS;	//update the time of the next frame in milliseconds	return framesPerSecond;}


Preferrably call it after the rectangle and blt calls (I set the maximum limit to 120 FPS)
//...
//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);
framesPerSecond = UpdateFrame(120);
//...
If you're not using OpenGL you shouldn't be playing around with PIXELFORMATDESCRIPTOR. You should be creating your backbuffer via CreateCompatibleDC and CreateCompatibleBitmap.

If the backbuffer format doesn't exactly match the screen format GDI manually converts every pixel as it blt's. This would explain your perf problems.

You also shouldn't be calling SwapBuffers. Just draw to the memory DC (aka backbuffer) and BitBlt to the foreground when you're done. Assuming you have a compatible DC and bitmap the blt will probably be hardware-accellerated and very fast.
-Mike
OK, I tried this, and it doesn't work. Please, please correct it so that double-buffering will work, the contents of memDC are BitBlt'd to hdc, and everybody goes home happy. All it does is display the mouse coordinates (in my program anyway, but you could just specify dummy values for mouse_x and mouse_y). Currently, this displays nothing, although if you use hdc instead of memDC in the TextOut function, it works (flickeringly).

I will send $5,000 US to whoever tells me the solution.

    //Stuff for drawing    PAINTSTRUCT ps;    RECT rc_client;    //Screen DC    HDC hdc = BeginPaint(hwnd, &ps);    //Memory DC    HDC memDC = CreateCompatibleDC(hdc);    GetClientRect(hwnd, &rc_client);        //String we will display    std::string out;    //Buffer for itoa    char * val = new char[50];    //Get Values for mouse position, convert to strings, and add to "out"    out = "Mouse is at { ";    itoa(mouse_x, val, 10);    out += val; out += ", ";    itoa(mouse_y, val, 10);    out += val; out += " }";    //Write out to the memory buffer    TextOut(memDC, rc_client.right / 2 - 100, rc_client.bottom / 2 - 15, out.c_str(), out.length());    //Remember to free the memory!    delete [] val;    //This doesn't work.  Nothing gets displayed on the screen.    BitBlt(hdc, 0, 0, rc_client.right, rc_client.bottom, memDC, 0, 0, SRCCOPY);        //End painting.  I don't need to ReleaseDC(hdc), do I?    EndPaint(hwnd, &ps);    DeleteDC(memDC);
my siteGenius is 1% inspiration and 99% perspiration
I don't know if this has been missed out or if it's just not in the last code you posted but memDC doesn't refer to a backbuffer, it's just a DC compatible with the window. What you need is a bitmap in memory and a handle (HBITMAP) to it, then the memDC will be selected as the current HDC for this bitmap, then you can draw to the bitmap through memDC! HDCs only describes the object they're assigned to.

Maybe you already know this, but BeginPaint/EndPaint should only be called in response to the WM_PAINT message. You can read more about that here: BeginPaint on MSDN (under the Remarks heading). I don't know if that is what you're doing but it's of course possible to call InvalidateRect to tell a redraw is needed and thus resulting in a WM_PAINT message, don't know how fast that is though.

To make sure you get it right this time I put your code snippet in a generic WinMain function, in the message loop. I marked changed lines in your snippet with "//changed: ". I also exchanged your mouse_x and mouse_y with a POINT.

Here's the code:

#include <string>#include <windows.h>//function protosLRESULT CALLBACK MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);//windows main entry functionint APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd){	UINT framesPerSecond;	HWND hwnd;	MSG msg;	//backbuffer bitmap surface	HDC compatibleDC;	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(LTGRAY_BRUSH);	wc.lpszMenuName = NULL;	wc.lpszClassName = "mainwindow";	if (RegisterClass(&wc) == 0)	{		MessageBox(NULL, "Couldn't register window class.", "Double buffering", 0);		return 0;	}	hwnd = CreateWindow("mainwindow",		"Double buffering",		WS_OVERLAPPEDWINDOW | WS_VISIBLE,		0,		0,		640,		480,		0,		0,		hInstance,		NULL);	if (hwnd == NULL)	{		MessageBox(NULL, "Couldn't create window.", "Double buffering", 0);		return 0;	}	//create a backbuffer bitmap	compatibleDC = GetDC(hwnd); //retrieves the window device context	backbuffer = CreateCompatibleBitmap(compatibleDC, 640, 480); //creates a bitmap compatible with it	ReleaseDC(hwnd, compatibleDC); //releases the compatible window device context	//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			//retrieve the cursor position (on the screen)			POINT mouse;			GetCursorPos(&mouse);			//Stuff for drawing			RECT rc_client;			//Screen DC			//changed: HDC hdc = BeginPaint(hwnd, &ps);			HDC hdc = GetDC(hwnd);			//Memory DC			HDC memDC = CreateCompatibleDC(hdc);			SelectObject(memDC, backbuffer);			GetClientRect(hwnd, &rc_client);			//String we will display			std::string out;			//Buffer for itoa			char * val = new char[50];			//Get Values for mouse position, convert to strings, and add to "out"			out = "Mouse is at { ";			itoa(mouse.x, val, 10);			out += val; out += ", ";			itoa(mouse.y, val, 10);			out += val; out += " }";			//Write out to the memory buffer			TextOut(memDC, rc_client.right / 2 - 100, rc_client.bottom / 2 - 15, out.c_str(), out.length());			//Remember to free the memory!			delete [] val;			//This doesn't work.  Nothing gets displayed on the screen.			BitBlt(hdc, 0, 0, rc_client.right, rc_client.bottom, memDC, 0, 0, SRCCOPY);			//End painting.  I don't need to ReleaseDC(hdc), do I?			//not if you used BeginPaint since EndPaint does that automatically			//but if you used GetDC it must be released			//changed: EndPaint(hwnd, &ps);			ReleaseDC(hwnd, hdc);			DeleteDC(memDC);		}	}	//release backbuffer bitmap	DeleteObject(backbuffer);	return msg.wParam;}//window process/eventLRESULT CALLBACK MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){	HDC hdc;	PAINTSTRUCT ps;	switch (msg)	{	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);}


Quote:Original post by silverphyre673
I will send $5,000 US to whoever tells me the solution.

Hahaha thanks for the imaginary reward =)

Good luck!
\Jimmy H

[edit]Maybe I should also mention the reason why the area around the white colored area where the text is displayed is randomly colored, it's because nothing else was painted onto the backbuffer bitmap so the colors are remains of the memory before backbuffer was allocated.[/edit]
Quote:Original post by Jimmy H
I don't know if this has been missed out or if it's just not in the last code you posted but memDC doesn't refer to a backbuffer, it's just a DC compatible with the window. What you need is a bitmap in memory and a handle (HBITMAP) to it, then the memDC will be selected as the current HDC for this bitmap, then you can draw to the bitmap through memDC! HDCs only describes the object they're assigned to.

Maybe you already know this, but BeginPaint/EndPaint should only be called in response to the WM_PAINT message. You can read more about that here: BeginPaint on MSDN (under the Remarks heading). I don't know if that is what you're doing but it's of course possible to call InvalidateRect to tell a redraw is needed and thus resulting in a WM_PAINT message, don't know how fast that is though.

To make sure you get it right this time I put your code snippet in a generic WinMain function, in the message loop. I marked changed lines in your snippet with "//changed: ". I also exchanged your mouse_x and mouse_y with a POINT.

Here's the code:

*** Source Snippet Removed ***

Quote:Original post by silverphyre673
I will send $5,000 US to whoever tells me the solution.

Hahaha thanks for the imaginary reward =)

Good luck!
\Jimmy H

[edit]Maybe I should also mention the reason why the area around the white colored area where the text is displayed is randomly colored, it's because nothing else was painted onto the backbuffer bitmap so the colors are remains of the memory before backbuffer was allocated.[/edit]



I think... I think I'm falling in love with you :)

I have wired the ++rate to your unnumbered Micronesian bank account. Good luck.

I've understood most of the win32 API, but never been able to fix the screen-flickering as I'd like. Solved! w00t! I'll just encapsulate it so I never have to do it again (that is the project I'm working on now for most of the API).

Thanks again.
my siteGenius is 1% inspiration and 99% perspiration

This topic is closed to new replies.

Advertisement