为什么我的Win32 gdi +游戏在Windows 7上速度非常慢?

时间:2010-07-06 15:29:34

标签: winapi gdi+ game-engine

[此条目底部的重要新信息]

我认为使用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上如此之慢?

2 个答案:

答案 0 :(得分:1)

我不知道这是为什么你当前的代码很慢,但更有效的双缓冲解决方案是只执行BitBlt中的WM_PAINT处理程序,并在DrawGameStuff处理程序中调用WM_TIMER。这样,你在绘画期间只做 的事情是将后台缓冲区复制到屏幕上,而实际的绘图逻辑可以在不同的时间发生。

答案 1 :(得分:0)

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

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