M K*_*atz 5 winapi gdi+ game-engine
[重要的新信息,请参阅下面此条目底部的内容]
我有一个使用GDI +的非常标准的游戏循环。在Vista和XP上,它工作得相当好(复杂游戏约25 fps,简单游戏约40 fps)。当我在Windows 7(具有明显更快的CPU和更多内存)上运行它时,它会减慢游戏无法使用的速度(我得到的范围从0 fps到4 fps)。我将下面的代码视为相关部分。正如我所说,我相信这是使用GDI +的最简单的(基于内存位图的)游戏循环。您可以在下面看到我为加快速度所做的两次尝试。首先,我担心如果InvalidateRect()的调用频率比发送WM_PAINT消息的调用频率高得多,则系统会以此为线索来表明我的程序运行不良/运行缓慢,并且隐瞒了我的时间片。所以我添加了paintIsPending标志以确保我没有 每种涂料会使失效不止一次。这没有改善。其次,我在下面的可选部分中添加了代码,认为如果我自己触发WM_PAINT消息而不是等待发送消息会更好。同样,没有任何改善。
对我来说,像这样的简单GDI +游戏循环会在Windows 7上死掉似乎让我感到疯狂。我知道Windows 7处理2D图形加速的方式有所不同,但是这段代码似乎太基础了,很难相信它不会功能。另外,我知道我可以切换到DirectX,并且可以这样做,但是目前在下面的DrawGameStuff(graphics)调用表示的代码库中投入了大量资金,如果可能,我宁愿不重写它。
谢谢你的帮助。
#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;
...
}
Run Code Online (Sandbox Code Playgroud)
啊哈!根据克里斯·贝克(Chris Becke)的线索,我现在有更多且非常相关的信息。我以为肯定是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 );
Run Code Online (Sandbox Code Playgroud)
瞧,它仍然以40 FPS的速度运行。我不知道为什么FillRectangle()调用比Clear()调用快。
因此,我开始重新添加游戏绘图代码,然后立即发现另一个调用将其杀死:要绘制游戏内容,请使用以下命令将子画面绘制到memoryDC中
graphics->DrawImage( myImageThatCameFromAPngFile, destx, desty, srcx, srcy,
width, height, UnitPixel );
Run Code Online (Sandbox Code Playgroud)
这些电话也很慢。作为一个实验,我从我的PNG文件中预先提取了另一个与screenDC兼容的memoryDC。然后,为了绘制精灵,我从这个辅助memoryDC绘制到我的主要memoryDC中。因此,我没有上面的DrawImage调用:
BitBlt( memoryDC, destx, desty, width, height, secondaryMemoryDC,
srcx, srcy, SRCCOPY );
Run Code Online (Sandbox Code Playgroud)
当我这样做时,可以看到整个游戏在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 );
Run Code Online (Sandbox Code Playgroud)
然后使用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 );
Run Code Online (Sandbox Code Playgroud)
因此,现在我的游戏可以在Windows 7上快速运行。
但是我仍然不明白为什么必须在Windows 7上完成所有这些工作。为什么在Windows 7上使用DrawImage()从PNG图像中进行默认绘制的速度如此之慢?
我不知道这是否是您当前代码速度慢的原因BitBlt,但更有效的双缓冲解决方案是仅在处理程序中执行操作WM_PAINT,然后DrawGameStuff在WM_TIMER处理程序中调用。这样,您在绘制过程中要做的唯一一件事就是将后台缓冲区复制到屏幕,并且实际的绘制逻辑可以在不同的时间发生。
| 归档时间: |
|
| 查看次数: |
3728 次 |
| 最近记录: |