Плавный deltaTime и GPU

PC разработчики почему то, часто забывают или не интересуются как работает DirectX изнутри. Типичный пример: большинство думает, что Present блокирующий вызов и приводит к синхронизации. На самом же деле Present фактически просто добавляет в PushBuffer команду которая делает flip для отображаемой на экране GPU памяти, но это в том случае, если все хорошо.

Если же CPU заполняет PushBuffer, быстрее чем GPU успевает его исполнять, DX Runtime некоторое время (2 кадра) продолжает класть все команды в PushBuffer надеясь, что GPU успеет или CPU будет поменьше данных давать.

После того, как GPU “отстал” от CPU на пару кадров происходит ожидание GPU при этом CPU простаивает (stall frame). Это очень неприятно, т.к. deltaTime становится крайне неравномерным и брюква становится уже не той (см.лекцию: Баткин Борис. О некоторых особенностях приготовления брюквы.)

В случае проблем график deltaTime типично принимает вид, но может и отличаться (90% случаев на практике, график был именно такой)

Возникает вопрос: как же это забороть ? Забороть совсем нельзя (зоопарк конфигураций, CPU с GPU могут отличаться на порядки по загруженности/производительности), можно уменьшить вред, от этого эффекта, для этого нужно сделать выхов Present синхронизирующим. Что бы получить следующую картину:

При использовании DX9 можно использовать механизм Query, в предыдущих DX Query отсутствуют, но можно использовать Lock/Unlock бэфбуффера.

Код для DirectX9:

...Init...

D3D()->CreateQuery(D3DQUERYTYPE_EVENT, &pEventQuery);

...Frame...

HRESULT issueResult = &pEventQuery->Issue(D3DISSUE_END);
if (issueResult != D3DERR_DEVICELOST)
{
 HRESULT queryResult = &pEventQuery->GetData( NULL, 0, D3DGETDATA_FLUSH );
 if (queryResult == S_FALSE)
 {
   for (;;)
   {
     queryResult = &pEventQuery->GetData( NULL, 0, D3DGETDATA_FLUSH);
     if (queryResult != S_FALSE)
     {
       break;
     }

    //Тут делаем, что хотим - видеокарта не успевает,
    //CPU время можно тратить
    Sleep(0);
   }
 }
}

D3D()->Present();
_Winnie C++ Colorizer

Код для DirectX8:

D3DLOCKED_RECT lockedRect;
d3d8BackBufferSurface->LockRect(&lockedRect, NULL, D3DLOCK_READONLY );
d3d8BackBufferSurface->UnlockRect();

D3D()->Present();
_Winnie C++ Colorizer

Код для DirectX7:

DDSURFACEDESC2 surfaceDesc;
sDesc.dwSize=sizeof(surfaceDesc);
d3d7BackBuffer->Lock(NULL, &surfaceDesc, DDLOCK_WAIT, NULL);
d3d7BackBuffer->Unlock(NULL);

D3D()->Present();
_Winnie C++ Colorizer

про DirectX

Меня периодически спрашивают программисты из различных геймдев компаний всякое про DirectX. Ну почему какие то вызовы DX иногда тормозят, а иногда нет. Что происходит в Present и т.п. (на Xbox ближе к железу и проще ответить что оно там внутри драйвера/DX делает ) Вот нашел неплохой DXFAQ который на 90% этих вопросов отвечает.

Вот еще добавлю от себя. Не все знают, что на некотором железе tex2Dproj подменяется на tex2D и деление. А texCUBE к примеру даже принципиально не может фильтровать пиксели на эджах кубмапа, т.к. все равно сводится все внутри к вызову обычного tex2D (см. сюда глава Mapping Texture Coordinates to Cube Map Faces или сюда страница 5)

Волшебное затухание

В игродеве частенько бывает нужно, что нибудь плавно растворить или сфейдить. Тот-же звук к примеру или объекты какие нибудь по альфе, обычно не задумываясь пишут getDistance() до объекта и потом считают коэфицент фейдинга, но getDistance() не менее класически содержит в себе sqrt, а если объектов много ?

Read the rest of this entry »

Отладочные символы для windows

Оказывается, если скачать Debugging Tools for Windows вот здесь, а потом выполнить в консоли: symchk /r c:\windows\system32 /s SRV*c:\symbols\*http://msdl.microsoft.com/download/symbols то тулза автоматически просканирует всю виндовую директорию и заберет нужные PDB. И можно в отладчике вместо ntdll.dll и компании в колстеке видеть человеческую инфу

   Newer→