5

[このエントリの下部にある重要な新しい情報]

私は、GDI+ を使用した非常に標準的なゲーム ループであると信じています。Vista と XP では、かなりうまく機能します (複雑なゲームでは約 25 fps、単純なゲームでは 40 fps)。Windows 7 (非常に高速な CPU とより多くのメモリを使用) で実行すると、速度が低下し、ゲームが使用できなくなります (0 fps から 4 fps の範囲になります)。コードの関連部分であると思われるものを以下に含めました。私が言うように、これは GDI+ を使用した最も単純な種類の (メモリ ビットマップ ベースの) ゲーム ループだと思います。速度を上げるために私が行った 2 つの試みを以下に示します。まず、WM_PAINT メッセージが送信されるよりもはるかに頻繁に InvalidateRect() が呼び出されている場合、システムはこれを、私のプログラムが悪い/遅いという手がかりと見なし、タイム スライスを保留しているのではないかと心配していました。だから私はペイントIsPendingフラグを追加して、そうしなかったことを確認しました. ペイントごとに 2 回以上無効にしないでください。これでは改善されませんでした。次に、以下のオプション セクションにコードを追加しました。おそらく、WM_PAINT メッセージが送信されるのを待つのではなく、自分でトリガーした方がよいのではないかと考えました。繰り返しますが、改善はありません。

このような単純な GDI+ ゲーム ループが Windows 7 で機能しなくなるなんて、私には気が狂いそうです。 -機能的。また、私は 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;
    ...
}

あはは!クリス・ベッケの手がかりに基づいて、私は今、より多くの非常に関連性の高い情報を持っています. 確かに遅いのは 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() 呼び出しよりも速い理由はわかりません。

それで、ゲームの描画コードを追加し直したところ、すぐにそれを殺す別の呼び出しが見つかりました。

graphics->DrawImage( myImageThatCameFromAPngFile, destx, desty, srcx, srcy,
    width, height, UnitPixel );

そして、それらの呼び出しも非常に遅いです。実験として、PNG ファイルから、screenDC と互換性のある 2 番目の memoryDC に事前に描画しました。次に、スプ​​ライトを描画するために、このセカンダリ メモリ DC からプライマリ メモリ DC に描画します。したがって、上記の 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

これが現在のコードが遅い理由かどうかはわかりませんが、より効率的なダブルバッファリングの解決策は、ハンドラーでのみ実行しBitBltWM_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 に答える