5

[重要的新信息位于本条目底部]

我有一个我认为是使用 GDI+ 的非常标准的游戏循环。它在 Vista 和 XP 上运行良好(复杂游戏大约 25 fps,简单游戏 40 fps)。当我在 Windows 7 上运行它时(具有明显更快的 CPU 和更多内存),它会减慢很多游戏无法使用(我从 0 fps 到 4 fps)。我在下面包含了我认为是代码的相关部分。正如我所说,我相信这是使用 GDI+ 的最简单的(基于内存位图的)游戏循环。您可以在下面看到我为加快速度所做的两次尝试。首先,我担心如果调用 InvalidateRect() 的频率比发送 WM_PAINT 消息的频率高得多,那么系统会将此作为我的程序坏/慢并且扣留我的时间片的线索。所以我添加了 paintIsPending 标志以确保我没有 t 每次油漆无效一次以上。这没有改善。其次,我在下面的 OPTIONAL SECTION 中添加了代码,认为如果我自己触发 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;
    ...
}

啊哈!根据 Chris Becke 的线索,我现在掌握了更多且非常相关的信息。我确信是 BitBlt() 很慢,而不是 graphics->Clear(),但是当我注释掉 graphics->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 上完成所有这些操作。为什么在 Windows 7 上使用 DrawImage() 从我的 PNG 图像中绘制的默认绘图如此缓慢?

4

2 回答 2

1

我不知道这是否是您当前代码缓慢的原因,但更有效的双缓冲解决方案是只BitBlt在处理程序中执行,并在处理WM_PAINT程序中调用。这样,您在绘制过程中唯一要做的就是将后台缓冲区复制到屏幕上,而实际的绘制逻辑可能发生在不同的时间。DrawGameStuffWM_TIMER

于 2010-07-06T15:48:43.177 回答
0

7 和 Vista/XP 的默认插值模式会不同吗?这至少可以解释您的 DrawImage 速度问题。尝试将其专门设置为较低的质量值,以查看这是否会影响 DrawImage 的速度。

AlphaBlend 和 BitBlt 几乎总是比 GDI+ 的 DrawImage 更快——明显更快。我怀疑其中很大一部分是因为他们没有进行 GDI+ 所做的插值(即图像的质量拉伸/收缩)。

于 2010-07-09T19:09:16.737 回答