почему моя игра Win32 gdi + необычно медленная на Windows 7? - PullRequest
5 голосов
/ 06 июля 2010

[важная новая информация доступна ниже в нижней части этой записи]

У меня есть то, что я считаю очень стандартным игровым циклом, использующим 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?

Ответы [ 2 ]

1 голос
/ 06 июля 2010

Я не знаю, является ли это , почему ваш текущий код работает медленно, но более эффективное решение с двойной буферизацией состояло бы в том, чтобы сделать только BitBlt в обработчике WM_PAINT и вызвать DrawGameStuff в обработчике WM_TIMER. Таким образом, only , что вы делаете во время рисования, это копирование заднего буфера на экран, и фактическая логика рисования может произойти в другое время.

0 голосов
/ 09 июля 2010

Может ли режим интерполяции по умолчанию отличаться между 7 и Vista / XP?Это может объяснить ваши проблемы со скоростью DrawImage, по крайней мере.Попробуйте установить это специально для более низкого значения качества, чтобы увидеть, влияет ли это на скорость DrawImage.

AlphaBlend и BitBlt почти всегда будут быстрее - значительно быстрее - чем DrawImage GDI +.Я подозреваю, что большая часть этого заключается в том, что они не выполняют интерполяцию (т.е. качественное растяжение / сжатие изображения), как это делает GDI +.

...