[此條目下有效的重要的新信息]爲什麼我的Win32 gdi +遊戲在Windows 7上運行速度緩慢?
我有我認爲是使用GDI +的非常標準的遊戲循環。它在Vista和XP上運行得相當好(複雜遊戲大約25幀/秒,簡單遊戲中大概是40幀/秒)。當我在Windows 7上運行它(具有顯着更快的CPU和更多內存)時,它會減慢遊戲的運行速度(我從0 fps到4 fps的任何地方都會得到 )。我在下面列出了我認爲是代碼的相關部分。正如我所說,我相信這個 是使用GDI +的最簡單的(基於內存位圖的)遊戲循環。你可以在下面看到我爲加速 而做出的兩次嘗試。首先,我擔心如果InvalidateRect()被調用的次數比發送WM_PAINT消息的次數多得多,系統將此視爲線索,表明我的程序不好/慢,並且扣留了我的時間片。因此,我添加了 paintIsPending標誌,以確保我沒有使每個paint不止一次失效。這沒有改善。其次,我在下面的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;
...
}
啊哈!我現在有更多和非常相關的信息,基於Chris Becke的線索。我確信它是BitBlt()很慢,而不是圖形 - > Clear(),但是當我註釋掉graphics-> Clear()時,我會突然在Windows 7上獲得40 FPS。那麼我將圖形 - >清除()變成
// graphics->Clear(Color(255, 200, 200, 255));
SolidBrush brush(Color(255, 200, 200, 255));
graphics->FillRectangle(&brush, 0, 0, CLIENT_WIDTH, CLIENT_HEIGHT);
並且看它仍然以40FPS運行。我不知道爲什麼FillRectangle()調用比Clear()調用更快。
於是我開始回到加入我的遊戲繪圖代碼,我馬上發現了另一個電話,殺死它:繪製遊戲內容我使用
graphics->DrawImage(myImageThatCameFromAPngFile, destx, desty, srcx, srcy,
width, height, UnitPixel);
繪製精靈進入memoryDC而那些電話是也很慢。作爲一個實驗,我將我的PNG文件預先繪製到與screenDC兼容的第二個memoryDC中。然後,爲了繪製我從這個輔助內存DC繪製我的精靈到我的主內存DC中。所以代替的DrawImage調用上面我有:
BitBlt(memoryDC, destx, desty, width, height, secondaryMemoryDC,
srcx, srcy, SRCCOPY);
你瞧,整場比賽在40 FPS運行於Windows 7,當我做到這一點。
但是,這不是一個真正的解決方案,因爲在預渲染到該輔助存儲器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圖像中使用DrawImage()的速度如此緩慢?
你有沒有做過任何簡單的分析,看看哪部分循環速度慢?我的直覺告訴我,'BitBlt'可能是時間,這可能是由於你的視頻卡而不是Win7本身。 – 2010-07-06 15:40:58
是的,它可能是一個視頻卡問題,但這發生在三個不同的Windows 7機器上,並且零個其他機器(我測試過)。 – 2010-07-06 16:07:48
關閉Aero以查看是否有所作爲。也就是說,右鍵單擊桌面並選擇「Windows Basic」或「Windows Classic」主題。重新啓動,看看是否有所作爲。當Aero關閉時,我注意到其他DX應用程序正在獲得提升。 – selbie 2010-07-07 03:32:02