我相信使用GDI是一个非常标准的游戏循环.它工作得相当好(复杂游戏大约25 fps,
Vista和XP上的简单游戏40 fps.当我在Windows 7上运行它时(cpu速度更快,内存更多),
它慢慢减速,游戏无法使用(我随处可见
从0 fps到4 fps).我在下面列出了我认为代码的相关部分.正如我所说,我相信这一点
是使用GDI的最简单的(基于内存位图的)游戏循环.你可以在下面看到我为加速事情所做的两次尝试
起来.首先,我担心如果比WM_PAINT消息更频繁地调用InvalidateRect()
发送,系统正在把这作为一个线索,我的程序是坏/慢,并扣留我的时间片.所以我补充道
paintIsPending标志,以确保每个油漆不会多次失效.这没有改善.第二,我
在下面的OPTIONAL SECTION中添加了代码,认为如果我自己触发WM_PAINT消息而不是
等待它发送的东西会更好.再一次,没有改善.
对我来说,像这样的简单GDI游戏循环会在Windows 7上消失似乎很疯狂.我知道存在一些差异
在Windows 7如何处理2D图形加速,但这个代码似乎是如此基本,很难相信它会
没有功能.此外,我知道我可以切换到DirectX,我可能会这样做,但目前有相当数量的投资
在下面的DrawGameStuff(图形)调用代表的代码库中,如果可能的话我宁愿不重写它.
谢谢你的帮助.
#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,255 ) ); DrawGameStuff( graphics ); BitBlt( hdc,CLIENT_HEIGHT,memoryDC,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; ... }
啊哈!根据Chris Becke的线索,我现在有更多非常相关的信息.我确信这是BitBlt()很慢而不是图形 – > Clear(),但是当我注释掉图形时,我看到了> Clear()我在Windows 7上突然得到40 FPS.所以然后我将graphics-> Clear()更改为
// graphics->Clear( Color( 255,255 ) ); SolidBrush brush( Color( 255,255 ) ); graphics->FillRectangle( &brush,CLIENT_HEIGHT );
并且它看起来仍然以40 FPS运行.我不知道为什么FillRectangle()调用比Clear()调用更快.
然后我开始添加我的游戏绘图代码,我立即找到另一个杀死它的调用:绘制游戏内容我使用精灵将精灵绘制到memoryDC中
graphics->DrawImage( myImageThatCameFromAPngFile,destx,desty,srcx,srcy,width,height,UnitPixel );
那些电话也很慢.作为实验,我从PNG文件预先绘制到与screenDC兼容的第二个memoryDC中.然后,为了绘制我的精灵,我从这个辅助memoryDC绘制到我的主memoryDC中.所以我没有上面的DrawImage调用,而是:
BitBlt( memoryDC,secondaryMemoryDC,SRCCOPY );
并且,当我这样做时,整个游戏在Windows 7上以40 FPS运行.
然而,这不是一个真正的解决方案,因为在预渲染到二级存储器DC时,我丢失了PNG文件中的透明度信息,所以我的精灵现在都是不透明和丑陋的.
所以我的问题似乎是memoryDC(创建与screenDC兼容)和PNG源文件之间不兼容.我不明白为什么这种不兼容性存在(或者至少,为什么它会减慢这么多东西)只在Windows 7上.有没有办法保存PNG文件从一开始就与屏幕兼容?或者在一开始就在内部重新渲染它以获得与屏幕兼容的新PNG文件?嗯….
好的,所以我能够通过将其渲染到32bpp HBITMAP来正确渲染我的PNG文件,如下所示:
HDC hdc = CreateCompatibleDC( GetWindowDC( hWnd ) ); Bitmap *bitmap = new Bitmap( image->GetWidth(),image->GetHeight(),PixelFormat32bppARGB ); HBITMAP hbitmap; bitmap->GetHBITMAP( Color( 0,0 ),&hbitmap ); SelectObject( hdc,hbitmap ); Graphics *g = new Graphics( hdc ); g->DrawImage( pngImage,pngImage->GetWidth(),pngImage->GetHeight(),UnitPixel );
然后使用AlphaBlend()渲染它:
_BLENDFUNCTION bf; bf.BlendOp = AC_SRC_OVER; bf.BlendFlags = 0; bf.SourceConstantAlpha = 255; bf.AlphaFormat = AC_SRC_ALPHA; AlphaBlend( memoryDC,hdc,bf );
所以现在我的游戏在Windows 7上运行得很快.
但是我仍然不明白为什么我必须为Windows 7完成所有这些操作.为什么使用DrawImage()的PNG图像的默认绘图在Windows 7上如此之慢?