[важная новая информация доступна ниже в нижней части этой записи]
У меня есть то, что я считаю очень стандартным игровым циклом, использующим GDI +.Он работает достаточно хорошо (около 25 кадров в секунду для сложной игры, 40 кадров в секунду для простой игры) в Vista и XP.Когда я запускаю его на Windows 7 (со значительно более быстрым ЦП и большим объемом памяти), он замедляется настолько, что игра становится непригодной (я получаю от 0 до 4 кадров в секунду).Я включил ниже, что я думаю, являются соответствующими частями кода.Как я уже сказал, я считаю, что это самый простой вид (основанный на растровом изображении) игрового цикла с использованием GDI +.Ниже вы можете увидеть две попытки, которые я сделал, чтобы ускорить процесс.Во-первых, я боялся, что если InvalidateRect () вызывается гораздо чаще, чем отправляются сообщения WM_PAINT, система воспринимала это как признак того, что моя программа была плохой / медленной и удерживала мои временные интервалы.Поэтому я добавил флаг paintIsPending, чтобы удостовериться, что я не лишал законной силы более одного раза для каждой краски.Это не дало улучшения.Во-вторых, я добавил код в ДОПОЛНИТЕЛЬНОМ РАЗДЕЛЕ ниже, думая, что, возможно, если бы я сам вызвал сообщение WM_PAINT вместо того, чтобы ждать его отправки, было бы лучше.Опять же, никаких улучшений.
Мне кажется сумасшедшим, что такой простой игровой цикл GDI +, как этот, умер бы в Windows 7. Я знаю, что есть некоторые различия в том, как Windows 7 обрабатывает ускорение 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;
...
}
Ага! Теперь у меня есть еще и очень актуальная информация, основанная на подсказке Криса Бекке.Я думал наверняка, что это был BitBlt (), который был медленным, а не графика-> Очистить (), но вот, когда я закомментировал графику-> Очистить (), я внезапно получаю 40 FPS на Windows 7. Итак, яизменил графику-> 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 ().
Итак, я начал добавлять свой код рисования игры обратно и сразу нашел другой вызов, который его убивает: рисоватьсодержимое игры я рисую спрайты в памяти DC, используя
graphics->DrawImage( myImageThatCameFromAPngFile, destx, desty, srcx, srcy,
width, height, UnitPixel );
И эти вызовы тоже очень медленные.В качестве эксперимента я предварительно извлек из своего файла PNG во вторую память DC, совместимую с экраном DC.Затем, чтобы нарисовать мой спрайт, я рисую из этой вторичной памяти DC в свою основную память DC.Поэтому вместо вызова DrawImage, приведенного выше, у меня есть:
BitBlt( memoryDC, destx, desty, width, height, secondaryMemoryDC,
srcx, srcy, SRCCOPY );
И вот, когда я это делаю, вся игра работает на 40 FPS в Windows 7.
Однако это не реальнорешение, потому что при предварительном рендеринге в этот вторичный DC памяти я теряю информацию о прозрачности из файла PNG, поэтому мои спрайты теперь все непрозрачные и уродливые.
Так что, похоже, моя проблема в несовместимости памяти DC (созданный для совместимости с screenDC) и исходный файл PNG.Я не понимаю, почему такая несовместимость существует (или, по крайней мере, почему она сильно тормозит) только в Windows 7. Есть ли способ сохранить файл PNG, чтобы он был совместим с экраном с самого начала?Или повторно выполнить внутреннюю визуализацию, чтобы получить новый файл PNG, совместимый с экраном?Хммм ....
Хорошо , поэтому я смог правильно воспроизвести файл PNG, отобразив его на 32-битном 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 ():
_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. Почему рисование по умолчанию из моего изображения PNG с использованием DrawImage () так медленно в Windows 7?