Why is my Win32 gdi + game ineffective on Windows 7?

[important new information is available below at the bottom of this post]

I have what I consider to be a very standard game loop using GDI +. It works quite well (about 25 frames per second for a complex game, 40 frames per second for a simple game) on Vista and XP. When I run it on Windows 7 (with a significantly faster processor and more memory) it slows down so that the game is unusable (I get anywhere from 0 to 4 frames per second). I have included below what I consider relevant pieces of code. As I said, I believe this is the simplest type of memory-based game cycle (bitmap) using GDI +. Below you can make two attempts to speed things up. Firstly, I was afraid that if InvalidateRect () was called much more often than WM_PAINT messages were sent, the system took it as a key to the fact that my program was bad / slow and withheld my time fragments. So I added the paintIsPending flag to make sure I didn't invalidate more than once per paint. This has not improved. Secondly, I added the code in the ADDITIONAL SECTION below, believing that perhaps if I myself called the WM_PAINT message instead of expecting it to be sent, it would be better. Again, no improvement.

It seems crazy to me that a simple GDI + game loop like this will die on Windows 7. I know there are some differences in how Windows 7 handles 2D graphics acceleration, but again this code seems so basic that it's hard Believe it to be non-functional. In addition, I know that I can switch to DirectX, and I can do it, but currently there is a fair amount of investment in the code base represented by calling DrawGameStuff (graphics) below, and I would prefer not to rewrite it if possible .

Thanks for any help.

#define CLIENT_WIDTH 320 #define CLIENT_HEIGHT 480 Graphics *graphics; HDC memoryDC; HBITMAP memoryBitmap; bool paintIsPending = false; void InitializeEngine( HDC screenDC ) { memoryDC = CreateCompatibleDC( screenDC ); memoryBitmap = CreateCompatibleBitmap( screenDC, CLIENT_WIDTH, CLIENT_HEIGHT ); SelectObject( memoryDC, memoryBitmap ); graphics = new Graphics( memoryDC ); ... } BOOL InitInstance( HINSTANCE hInstance, int nCmdShow ) { ... InitializeEngine( GetWindowDC( hWnd ) ); ... myTimer = SetTimer( hWnd, timerID, 1000 / 60, NULL ); ... } void DrawScreen( HDC hdc ) { graphics->Clear( Color( 255, 200, 200, 255 ) ); DrawGameStuff( graphics ); BitBlt( hdc, 0, 0, CLIENT_WIDTH, CLIENT_HEIGHT, memoryDC, 0, 0, SRCCOPY ); } LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { ... case WM_TIMER: if ( !paintIsPending ) { paintIsPending = true; InvalidateRect( hWnd, NULL, false ); /////// START OPTIONAL SECTION UpdateWindow( hWnd ); ValidateRect( hWnd, NULL ); /////// END OPTIONAL SECTION } break; case WM_PAINT: hdc = BeginPaint( hWnd, &ps ); DrawScreen( hdc ); EndPaint( hWnd, &ps ); paintIsPending = false; break; ... } 

Yeah! Now I have more and very relevant information based on a tip from Chris Beck. I thought for sure that the Blt () bit was slow, not the graphics β†’ Clear (), but when I commented on the graphics β†’ Clear (), I suddenly get 40 FPS in Windows 7. Then I changed the graphics-> Clear () to

 // graphics->Clear( Color( 255, 200, 200, 255 ) ); SolidBrush brush( Color( 255, 200, 200, 255 ) ); graphics->FillRectangle( &brush, 0, 0, CLIENT_WIDTH, CLIENT_HEIGHT ); 

and now, he was still working at 40 FPS. I don't know why calling FillRectangle () is faster than calling Clear ().

So, I started adding my game code again, and I immediately found another call that kills him: to draw the contents of the game I draw sprites in memoryDC using

 graphics->DrawImage( myImageThatCameFromAPngFile, destx, desty, srcx, srcy, width, height, UnitPixel ); 

And these calls are also very slow. As an experiment, I previously extracted from my PNG file into a second memoryDC compatible with screenDC. Then, to paint my sprite, I paint from this secondary memoryDC into my main memoryDC. So instead of calling DrawImage above, I have:

 BitBlt( memoryDC, destx, desty, width, height, secondaryMemoryDC, srcx, srcy, SRCCOPY ); 

And so the whole game runs at 40 FPS on Windows 7 when I do this.

However, this is not a real solution because when I pre-render this secondary DC memory, I lose transparency information from the PNG file, so my sprites are now opaque and ugly.

So my problem is the incompatibility between the DC memory (created for compatibility with screenDC) and the original PNG file. I don’t understand why this incompatibility exists (or at least why it slows down so much) only on Windows 7. Is there a way to save a PNG file for compatibility with the screen from the very beginning? Or repurpose it from the very beginning to get a new PNG file compatible with the screen? Mmm ...

Ok , so I was able to correctly render the rendering of the PNG file by providing it with 32Bpp HBITMAP as follows:

  HDC hdc = CreateCompatibleDC( GetWindowDC( hWnd ) ); Bitmap *bitmap = new Bitmap( image->GetWidth(), image->GetHeight(), PixelFormat32bppARGB ); HBITMAP hbitmap; bitmap->GetHBITMAP( Color( 0, 0, 0, 0 ), &hbitmap ); SelectObject( hdc, hbitmap ); Graphics *g = new Graphics( hdc ); g->DrawImage( pngImage, 0, 0, 0, 0, pngImage->GetWidth(), pngImage->GetHeight(), UnitPixel ); 

and then using AlphaBlend ():

  _BLENDFUNCTION bf; bf.BlendOp = AC_SRC_OVER; bf.BlendFlags = 0; bf.SourceConstantAlpha = 255; bf.AlphaFormat = AC_SRC_ALPHA; AlphaBlend( memoryDC, destx, desty, width, height, hdc, destx, desty, width, height, bf ); 

So now I am quickly launching my game on Windows 7.

But I still don't understand why I had to go through all this for Windows 7. Why is the default drawing from my PNG image using DrawImage () so slow in Windows 7?

+4
source share
2 answers

I do not know if this is the reason that your current code is slow, but a more efficient double-buffered solution should only execute BitBlt in the WM_PAINT handler and call DrawGameStuff in the WM_TIMER handler. Thus, the only thing you do while drawing is copying the back buffer to the screen, and the actual drawing logic may happen at another time.

+1
source

Can the default interpolation mode be different between 7 and Vista / XP? At the very least, this may explain your problems with DrawImage speed. Try adjusting this value to a lower quality to see if this affects the speed of DrawImage.

AlphaBlend and BitBlt will almost always be faster - significantly faster - than GDI + DrawImage. I suspect that most of this is because they do not perform the interpolation (i.e. image stretch / compression quality) that GDI + does.

0
source

Source: https://habr.com/ru/post/1314852/


All Articles