[このエントリの下部にある重要な新しい情報]
私は、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 画像からデフォルトで描画するのが遅いのはなぜですか?