Правильный способ ожидания VBLANK на Windows 10 в оконном режиме

Какой правильный способ ожидания VBLANK для представления на Windows 10 в оконном режиме? В настоящее время я делаю следующее:

D3DKMTWaitForVerticalBlankEvent(&waitData);
swapchain->Present(0, 0);

Однако это приводит к случайному заиканию. Моя текущая теория о заикании такова:
DWM завершает компоновку за некоторое время до события VBLANK, а затем в течение интервала VBLANK DWM пытается выполнить запись в передний буфер как можно быстрее. Поэтому если Present вызывается в интервале VBLANK, это может произойти после того, как DWM записал во фронтальный буфер. Это картинка в моей голове: плохой

Таким образом, более правильным методом будет вызов Present(1, 0) перед VBLANK поставить в очередь задний буфер окна с DWM. Тогда позвони D3DKMTWaitForVerticalBlankEvent ждать следующего VBLANK. То есть:

swapchain->Present(1, 0);
D3DKMTWaitForVerticalBlankEvent(&waitData);

С соответствующей картинкой: хорошо Этот второй метод, кажется, работает отлично, заикания больше нет.

Мой вопрос: как правильно представлять в интервале VBLANK и верна ли моя теория или происходит что-то более сложное / простое? Также есть ли ресурсы, чтобы узнать больше об этом взаимодействии?

Потенциально полезные ссылки:
MSDN Present
DXGI Best Practices
DXGI цепочка свопов Waitable
D3DKMTWaitForVerticalBlankEvent

Редактировать: не уверен, если это будет ответ или редактировать. После некоторого поиска в Google я нашел это объяснение Николаса Стила, которое кажется совпадающим с моей теорией

Не существует точного пожарного способа точно определить, когда произойдет VBlank, потому что Windows не предоставляет прерывание VBlank, и дисплеи /GPU не обязательно должны генерировать его в любом случае (кроме того, информация "текущей строки сканирования", как указано, например, в IDirect3DDevice9)::GetRasterStatus может быть неточным). В результате программы обычно опрашивают VBlank или полагаются на Direct3D/OpenGL, чтобы сделать это для них.

Программы представляют видеокадры во время VBlank, чтобы избежать разрывов, так как монитор с радостью переключится на новый кадр в середине отрисовки. С помощью композитора в Windows Vista и более поздних версиях Windows эти программы будут по-прежнему обнаруживать VBlank и представлять только кадры во время него, так как они думают, что они представляют видеокадры напрямую, когда на самом деле видеокадры сначала подаются в композитор. Кадры, отправленные в композитор (из любых запущенных программ на ПК), будут поставлены в очередь композитором и объединены вместе для замены / копирования на место во время VBlank.

Проблемы, которые могут возникнуть с этой системой:

1) Программа опроса для VBlank может пропустить композицию. Это приведет к тому, что кадр будет поставлен в очередь для следующей композиции, то есть предыдущий кадр будет показан в два раза длиннее.

2) Хуже того, следующий кадр может не пропустить композицию и в итоге перезаписать ранее помещенный в очередь кадр - так что в итоге вы получите дублированный кадр, за которым следует пропущенный кадр.

3) Реализация VSync программы, естественно, может не обнаружить VBlank (который имеет короткую продолжительность), что заставит его ждать следующего VBlank и рискует проблемами 1 и / или 2.

4) Эти проблемы могут даже комбинироваться, чтобы создать "идеальный шторм" из дублированных и / или пропущенных кадров.

Как видите, эта установка опроса не идеальна и намного хуже, когда присутствует композитор. Существует множество проблем, которые могут привести к тому, что новый видеокадр не будет отображаться, в результате чего предыдущий кадр будет отображаться дольше, чем предполагалось, и потенциально может пропустить новый кадр вообще!

Решение состоит в том, чтобы работать с композитором, а не против него. Немедленно и после этого представьте новый видеокадр, вызовите команду, которая будет блокироваться до тех пор, пока композитор не завершит свою задачу (DwmFlush). Это будет гарантировать, что не более 1 нового видеокадра будет представлено композитору между каждым периодом VBlank. Пока композитор активен, вам также больше не придется беспокоиться об опросе VBlank.

Поэтому лучше всего сделать следующее:

if (DWM.isEnabled()) 
{ 
    present(); 
    DwmFlush(); 
} 
else 
{
    waitForVBlank(); 
    present(); 
}

Другое редактирование: для будущих читателей есть два других способа ждать VBLANK, о которых я не знал IDXGIOutput::WaitForVBlank а также IDirectDraw7::WaitForVerticalBlank последний не рекомендуется.

1 ответ

Почему вы пытаетесь дождаться VBLANK в оконном режиме? DWM гарантирует, что композитная поверхность перевернется в области VBLANK.

Когда вы говорите "DWM завершает компоновку за некоторое время до события VBLANK", что заставит DWM инициировать компоновку? Это не происходит после представления любого приложения или после представления приложения в фокусе. Композиция фактически начинается, когда происходит VBLANK. Это может закончить и перевернуть составную поверхность в VBLANK, или это может в конечном итоге фактически перевернуться в следующем VBLANK (так что вы можете получить дополнительную длительность кадра с задержкой). Это не в вашем контроле - это зависит от продолжительности композиции, а не от присутствия вашего приложения.

В оконном приложении, когда вы вызываете подарок, он просто переворачивает цепочку обмена вашего приложения - он не переворачивает отображаемую поверхность. DWM справится с этим. Поэтому "Пока композитор активен, вам также не придется больше беспокоиться о опросе VBlank".

Однако использование DwmFlush() для достижения "не более 1 нового видеокадра, представленного композитору между каждым периодом VBlank", не обязательно желательно. Если вы определенно не хотите показывать 1 кадр в каждом цикле монитора, то вы можете сделать это (хотя я не уверен, что DwmFlush() является наиболее элегантным подходом). Но в большинстве случаев я не считаю желательным применять этот предел.

Другие вопросы по тегам