Winapi 为什么我的Win32 gdi+;Windows 7上的游戏速度慢得无法用了?

Winapi 为什么我的Win32 gdi+;Windows 7上的游戏速度慢得无法用了?,winapi,gdi+,game-engine,Winapi,Gdi+,Game Engine,[本条目底部的重要新信息] 我相信这是一个使用GDI+的非常标准的游戏循环。它运行得相当好(对于一个复杂的游戏大约25 fps, 40 fps(一个简单的游戏)在Vista和XP上。当我在Windows7上运行它时(CPU速度明显加快,内存也更多), 它的速度太慢了,以至于游戏无法使用(我到处都能看到) 从0 fps到4 fps)。我在下面列出了我认为是代码的相关部分。正如我所说,我相信这一点 是使用GDI+的最简单的(基于内存位图的)游戏循环。你可以在下面看到我为加快速度所做的两次尝试 向上的

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

我相信这是一个使用GDI+的非常标准的游戏循环。它运行得相当好(对于一个复杂的游戏大约25 fps, 40 fps(一个简单的游戏)在Vista和XP上。当我在Windows7上运行它时(CPU速度明显加快,内存也更多), 它的速度太慢了,以至于游戏无法使用(我到处都能看到) 从0 fps到4 fps)。我在下面列出了我认为是代码的相关部分。正如我所说,我相信这一点 是使用GDI+的最简单的(基于内存位图的)游戏循环。你可以在下面看到我为加快速度所做的两次尝试 向上的首先,我担心如果invalidate()被调用的频率比WM_PAINT消息被调用的频率要高得多 发送时,系统将此视为我的程序坏/慢的线索,并扣留我的时间片。所以我补充说 paintIsPending标志,以确保每次绘制不会超过一次无效。这没有改善。第二,我 在下面的可选部分中添加了代码,认为如果我触发WM_PAINT消息而不是我自己 等待它发送东西会更好。同样,没有改善

在我看来,像这样一个简单的GDI+游戏循环会在Windows7上死掉,这太疯狂了。我知道有些不同 在Windows7如何处理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()时,我突然在Windows7上获得了40 FPS的速度。然后我将graphics->Clear()更改为

你看,它仍然以每分钟40帧的速度运行。我不知道为什么FillRectangle()调用比Clear()调用快

然后我开始添加我的游戏绘图代码,我立即发现另一个调用杀死了它:为了绘制游戏内容,我使用

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 );
你瞧,当我这么做的时候,整个游戏在Windows7上以每秒40帧的速度运行

然而,这并不是一个真正的解决方案,因为在对辅助内存DC进行预渲染时,我丢失了PNG文件中的透明度信息,所以我的精灵现在都是不透明和丑陋的

所以我的问题似乎是memoryDC(为了与screenDC兼容而创建)和PNG源文件之间的不兼容。我不明白为什么只有在Windows7上才存在这种不兼容性(或者至少,为什么它会使速度减慢这么多)。有没有办法保存PNG文件以便从一开始就与屏幕兼容?或者一开始就在内部重新渲染,以获得与屏幕兼容的新PNG文件?嗯

好的因此,我能够通过将PNG文件渲染为32bpp的HBITMAP来正确渲染,如下所示:

    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()渲染它:

现在我的游戏在Windows7上快速运行


但我仍然不明白为什么我要在Windows7上经历这些。为什么在Windows 7上使用DrawImage()从我的PNG图像中默认绘制的速度如此之慢?

我不知道这是否是当前代码速度慢的原因,但更有效的双缓冲解决方案是只在
WM\u PAINT
处理程序中执行
BitBlt
,然后在
WM\u TIMER
处理程序中调用
DrawGameStuff
。这样,在绘制过程中唯一要做的事情就是将后缓冲区复制到屏幕上,实际的绘制逻辑可以在不同的时间发生。

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


AlphaBlend和BitBlt几乎总是要比GDI+的DrawImage快——快得惊人。我怀疑这在很大程度上是因为他们没有像GDI+那样进行插值(即图像的质量拉伸/收缩)

您是否做过任何简单的分析来查看循环的哪一部分比较慢?我的直觉告诉我,
BitBlt
可能是时间消耗,这可能是因为你的视频卡而不是Win7本身。是的,这可能是视频卡的问题,但这发生在三台不同的Windows 7机器上,并且没有其他机器(我已经测试过)。关闭Aero,看看这是否有区别。也就是说,右键单击桌面并选择“Windows Basic”或“Windows Classic”主题。重新启动,看看这是否会有所不同。我注意到,当Aero关闭时,其他DX应用程序得到了提升。谢谢,但是速度是一样的,即使我对DrawGameStuff()调用进行了注释。你的观点总体上是好的,但似乎与我遇到的麻烦无关。那么图形->清除速度慢吗?除了我使用str8gdi,而不是GDI+之外,我没有注意到windows7上非常类似的代码有任何问题。如果您使用的是VS not express,那么有一个内置的探查器,它应该能够告诉您在哪里花费了时间-一旦您使用了mojo magic使其工作。GB,谢谢您的建议,但是我已经将代码转换为DirectX,现在它在任何地方都运行得很快。但是,关于插值模式,你知道它是否适用吗
    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 );
    _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 );