2010-07-06 61 views
4

[此條目下有效的重要的新信息]爲什麼我的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()的速度如此緩慢?

+1

你有沒有做過任何簡單的分析,看看哪部分循環速度慢?我的直覺告訴我,'BitBlt'可能是時間,這可能是由於你的視頻卡而不是Win7本身。 – 2010-07-06 15:40:58

+0

是的,它可能是一個視頻卡問題,但這發生在三個不同的Windows 7機器上,並且零個其他機器(我測試過)。 – 2010-07-06 16:07:48

+0

關閉Aero以查看是否有所作爲。也就是說,右鍵單擊桌面並選擇「Windows Basic」或「Windows Classic」主題。重新啓動,看看是否有所作爲。當Aero關閉時,我注意到其他DX應用程序正在獲得提升。 – selbie 2010-07-07 03:32:02

回答

1

我不知道這是爲什麼當前的代碼很慢,但更高效的雙緩衝的解決辦法是隻做在WM_PAINT處理程序BitBlt,並在WM_TIMER處理程序調用DrawGameStuff。這樣,在繪製過程中你所做的事情就是將後臺緩衝區複製到屏幕上,並且實際的繪圖邏輯可以在不同的時間發生。

+0

謝謝,但即使我將DrawGameStuff()調用完全註釋掉,速度也是一樣的。你的觀點總的來說很好,但似乎與我遇到的麻煩有關。 – 2010-07-06 16:06:37

+1

graphics-> Clear is slow then?除了我使用str8 GDI,而不是GDI +,我注意到Windows 7上的代碼非常類似,沒有問題。如果你使用VS not-express,那裏有一個內置的profiler,它應該能夠告訴你在哪裏正在花費 - 一旦你工作了魔咒魔術,使其工作。 – 2010-07-06 21:06:29

0

7和Vista/XP之間的默認插值模式可以不同嗎?至少可以解釋你的DrawImage速度問題。嘗試將其設置爲較低的質量值,以查看是否會影響DrawImage的速度。

AlphaBlend和BitBlt幾乎總是比GDI +的DrawImage更快 - 速度更快。我懷疑其中很大一部分是因爲他們沒有做GDI +所做的插值(即,圖像的質量拉伸/縮小)。

+0

GB,感謝您的建議,但我已經將我的代碼轉換爲DirectX,現在它在任何地方都可以快速運行。但是,關於插值模式,即使在圖像沒有被調整大小時(這對我來說),你知道是否適用? – 2010-07-11 02:33:04

+0

即使在這種情況下,GDI +也會變慢,只是速度並不慢。我從未完全理解爲什麼。我懷疑它與座標系有關 - DrawImage()不僅僅是圍繞BitBlt的包裝,而是它自己的獨立於設備(不是基於像素)的繪圖例程。 DirectX可能是一款遊戲的不錯選擇。現在你不會受到限制。 – GrandmasterB 2010-07-12 18:09:08

相關問題