[此条目底部的重要新信息]
我认为使用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, 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;
...
}
啊哈!根据克里斯贝克的线索,我现在拥有更多非常相关的信息。我确信这是BitBlt()很慢而不是图形 - > Clear(),但是当我注释掉图形时,我看到了> Clear()我在Windows 7上突然得到40 FPS。所以然后我将graphics-> Clear()更改为
// graphics->Clear( Color( 255, 200, 200, 255 ) );
SolidBrush brush( Color( 255, 200, 200, 255 ) );
graphics->FillRectangle( &brush, 0, 0, CLIENT_WIDTH, 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, destx, desty, width, height, secondaryMemoryDC,
srcx, srcy, 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, 0, 0 ), &hbitmap );
SelectObject( hdc, hbitmap );
Graphics *g = new Graphics( hdc );
g->DrawImage( pngImage, 0, 0, 0, 0,
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, destx, desty, width, height, hdc,
destx, desty, width, height, bf );
所以现在我的游戏在Windows 7上快速运行。
但是我仍然不明白为什么我必须为Windows 7完成所有这些操作。为什么使用DrawImage()的PNG图像的默认绘图在Windows 7上如此之慢?
答案 0 :(得分:1)
我不知道这是为什么你当前的代码很慢,但更有效的双缓冲解决方案是只执行BitBlt
中的WM_PAINT
处理程序,并在DrawGameStuff
处理程序中调用WM_TIMER
。这样,你在绘画期间只做 的事情是将后台缓冲区复制到屏幕上,而实际的绘图逻辑可以在不同的时间发生。
答案 1 :(得分:0)
7和Vista / XP之间的默认插值模式是否会有所不同?这可以解释你的DrawImage速度问题,至少。尝试将其专门设置为较低质量值,以查看它是否会影响DrawImage的速度。
AlphaBlend和BitBlt几乎总是比GDI +的DrawImage更快 - 速度更快。我怀疑其中很大一部分是因为他们不做GDI +所做的插值(即图像的质量拉伸/收缩)。