В Direct3D 11 отсутствует GetRasterStatus, как определить период вертикальной пустоты?
Я обновляю приложение, в котором измерение времени представления стимула на экране требует максимальной точности. В настоящее время он написан для DirectDraw, который давно выпущен на пастбище, и необходимо обновить нашу графическую библиотеку.
Способ, которым мы измеряем время представления, использует обнаружение конца периода вертикального бланка. В частности, мне нужно знать, с максимально возможной точностью, когда все, что было перевернуто на первичную поверхность (или представлено в цепочке обмена), фактически рисуется экраном. Обнаружение линии сканирования может повысить достоверность этого измерения, но я смогу работать только с обнаружением, когда период вертикального бланка закончился сразу после вызова функции Flip or Present.
Direct 3D 9 имеет метод IDirect3DDevice9::GetRasterStatus, который возвращает структуру D3DRASTER_STATUS, которая включает логическое значение InVBlank, которое описывает, находится ли устройство в вертикальной пробел, а также текущую строку сканирования. DirectDraw имеет аналогичные функции ( IDirectDraw:: GetVerticalBlankStatus, также IDirectDraw::GetScanLine, который возвращает DDERR_VERTICALBLANKINPROGRESS во время вертикальной пробела, можно использовать для обнаружения VB).
Однако я не смог найти аналогичные функции в Direct3D11. Кто-нибудь знает, была ли эта функциональность перемещена или удалена между Direct3D9 и Direct3D11, и если последний, то почему?
3 ответа
Извините за поздний ответ, но я заметил, что до сих пор нет принятого ответа, так что, возможно, вы никогда не нашли тот, который работал В настоящее время в Windows служба DesktopWindowManager (dwm.exe) координирует все и не может быть обойдена. Начиная с Windows 8, эта служба не может быть отключена.
Таким образом, DWM всегда собирается контролировать частоту кадров, управление очередью рендеринга и окончательную композицию для всех различных объектов IDXGISurface (n) и мониторов IDXGIOutput (n), и нет особого смысла в отслеживании VSync для цели закадрового рендеринга, если я что-то упустил (без сарказма). Что касается вашего вопроса, я не был уверен, что ваша цель была:
- получить чрезвычайно точную информацию о времени, но только для диагностики, профилирования или информационного использования, или
- собиралось ли приложение затем (пытаться) использовать эти результаты (пытаться) планировать свои собственные текущие циклы.
Если это последнее, я полагаю, что вы можете эффективно сделать это, только если приложение D3D работает в полноэкранном монопольном режиме. Это единственный случай, когда DWM- под видом DXGI - будет по- настоящему доверять клиенту, чтобы справиться со своим собственным Present
время.
(Едва ли) хорошая новость заключается в том, что если ваш интерес к VSync носит исключительно информационный характер, то есть вы попадаете в категорию пули (1.) сверху, то вы действительно можете получить все данные о времени, которые вам когда-либо понадобятся, и с разрешением QueryPerformanceFrequency, которое обычно составляет около 320 нс.
Вот как получить эту информацию о синхронизации видео в высоком разрешении. Но, опять же, просто для ясности, несмотря на очевидный успех (согласно традиционному программному наблюдению), любая попытка обусловить детерминистический (и, следовательно, потенциально полезный) результат при чтениях, полученных следующим образом, в действительности будет сорвана посредничеством DWM:
Определяет информацию о времени компоновки Desktop Window Manager (DWM). Используется функцией DwmGetCompositionTimingInfo.
typedef struct _DWM_TIMING_INFO
{
UINT32 cbSize; // size of this DWM_TIMING_INFO structure
URATIO rateRefresh; // monitor refresh rate
QPC_TIME qpcRefreshPeriod; // monitor refresh period
URATIO rateCompose; // composition rate
QPC_TIME qpcVBlank; // query performance counter value before the vertical blank
CFRAMES cRefresh; // DWM refresh counter
UINT cDXRefresh; // DirectX refresh counter
QPC_TIME qpcCompose; // query performance counter value for a frame composition
CFRAMES cFrame; // frame number that was composed at qpcCompose
UINT cDXPresent; // DirectX present number used to identify rendering frames
CFRAMES cRefreshFrame; // refresh count of the frame that was composed at qpcCompose
CFRAMES cFrameSubmitted; // DWM frame number that was last submitted
UINT cDXPresentSubmitted; // DirectX present number that was last submitted
CFRAMES cFrameConfirmed; // DWM frame number that was last confirmed as presented
UINT cDXPresentConfirmed; // DirectX present number that was last confirmed as presented
CFRAMES cRefreshConfirmed; // target refresh count of the last frame confirmed as completed by the GPU
UINT cDXRefreshConfirmed; // DirectX refresh count when the frame was confirmed as presented
CFRAMES cFramesLate; // number of frames the DWM presented late
UINT cFramesOutstanding; // number of composition frames that have been issued but have not been confirmed as completed
CFRAMES cFrameDisplayed; // last frame displayed
QPC_TIME qpcFrameDisplayed; // QPC time of the composition pass when the frame was displayed
CFRAMES cRefreshFrameDisplayed; // vertical refresh count when the frame should have become visible
CFRAMES cFrameComplete; // ID of the last frame marked as completed
QPC_TIME qpcFrameComplete; // QPC time when the last frame was marked as completed
CFRAMES cFramePending; // ID of the last frame marked as pending
QPC_TIME qpcFramePending; // QPC time when the last frame was marked as pending
CFRAMES cFramesDisplayed; // number of unique frames displayed
CFRAMES cFramesComplete; // number of new completed frames that have been received
CFRAMES cFramesPending; // number of new frames submitted to DirectX but not yet completed
CFRAMES cFramesAvailable; // number of frames available but not displayed, used, or dropped
CFRAMES cFramesDropped; // number of rendered frames that were never displayed because composition occurred too late
CFRAMES cFramesMissed; // number of times an old frame was composed when a new frame should have been used but was not available
CFRAMES cRefreshNextDisplayed; // frame count at which the next frame is scheduled to be displayed
CFRAMES cRefreshNextPresented; // frame count at which the next DirectX present is scheduled to be displayed
CFRAMES cRefreshesDisplayed; // total number of refreshes that have been displayed for the application since the DwmSetPresentParameters function was last called
CFRAMES cRefreshesPresented; // total number of refreshes that have been presented by the application since DwmSetPresentParameters was last called
CFRAMES cRefreshStarted; // refresh number when content for this window started to be displayed
ULONGLONG cPixelsReceived; // total number of pixels DirectX redirected to the DWM
ULONGLONG cPixelsDrawn; // number of pixels drawn
CFRAMES cBuffersEmpty; // number of empty buffers in the flip chain
}
DWM_TIMING_INFO;
(Примечание. Чтобы сжать исходный код по горизонтали для отображения на этом веб-сайте по горизонтали, предположите, что добавлены следующие сокращения:)
typedef UNSIGNED_RATIO URATIO;
typedef DWM_FRAME_COUNT DWMFC;
typedef QPC_TIME QPCT;
Теперь для приложений, работающих в оконном режиме, вы можете получать эту подробную информацию так часто, как вам нравится. Если вам нужно только пассивное профилирование, то получение данных из DwmGetCompositionTimingInfo - это современный способ сделать это.
И если говорить о современном, так как вопрос намекал на модернизацию, вы можете рассмотреть возможность использования IDXGISwapChain1, полученного из IDXGIFactory2::CreateSwapChainForComposition, чтобы разрешить использование нового компонента DirectComposition.
DirectComposition обеспечивает богатые и плавные переходы, достигая высокой частоты кадров, используя графическое оборудование и работая независимо от потока пользовательского интерфейса. DirectComposition может принимать растровое содержимое, отображаемое различными библиотеками рендеринга, включая растровые изображения Microsoft DirectX и растровые изображения, отображаемые в окне (растровые изображения HWND). Кроме того, DirectComposition поддерживает различные преобразования, такие как двухмерные аффинные преобразования и трехмерные перспективные преобразования, а также базовые эффекты, такие как отсечение и непрозрачность.
В любом случае, кажется менее вероятным, что подробная информация о времени могла бы с пользой сообщить поведение приложения во время выполнения; возможно, это поможет вам предсказать ваш следующий VSync, но возникает вопрос, какое значение может иметь "острое осознание периода гашения" для какой-то конкретной DWM-подчиненной цепочки сменных экранов.
Поскольку поверхность вашего приложения - лишь одна из многих, с которыми DWM манипулирует, DWM будет выполнять все виды динамической адаптации самостоятельно, исходя из предположения, что каждый клиент ведет себя последовательно. Непредсказуемая адаптация не помогает при таком режиме и, скорее всего, в конечном итоге приведет в замешательство обе стороны.
Заметки:
1. Разрешение QPC на много порядков выше, чем у
DateTime
отметьте, несмотря на то, что последний предполагает использование 100 нс. единица достоинства. Думать о DateTime.Now.Ticks
как переупаковка (в миллисекундах) Environment.TickCount
, но конвертируется в 100 нс единиц. Для максимально возможного разрешения используйте статический метод Stopwatch.GetTimestamp()
вместо DateTime.Now.Ticks
,
Другая альтернатива:
Есть D3DKMTGetScanLine(), который работает с D3D9, D3D10, D3D11, D3D12 и даже OpenGL.
На самом деле это функция GDI32, так что вы можете использовать существующий графический объект hAdaptor в Window для опроса VBlank/Scanline - нет необходимости создавать буфер кадров Direct3D. Вот почему этот API отлично работает с рендерерами OpenGL, Mantle и не Direct3D, несмотря на префикс D3D этого вызова API.
Он также сообщает вам статус VBlank и строку растрового сканирования.
Это полезно для гонок на лучах в критически важных приложениях. Некоторые виртуальные реальности используют гонки на лучах, когда даже 20-миллиметровое отставание может означать разницу между приятным VR и головокружительным / притягательным VR.
Гонки лучей рендерится на лету, следуя сканированию дисплея. В специализированных приложениях, критичных к задержке, вы можете уменьшить задержку от Direct3D Present() до пикселей, попадающих в ваши глазные яблоки, до абсолютного минимума (всего 3 мс).
Чтобы понять, что такое лучевая гонка, https://www.wired.com/2009/03/racing-the-beam/ - это было обычным явлением в те времена, когда графические чипы не имели кадровых буферов - что делало лучевую гонку необходимой для улучшенная графика на Atari 2600, Nintendo, Commodore 64 и т.д...
Для более современной реализации лучевой гонки см. Алгоритм Lagless VSYNC ON для эмуляторов.
"В частности, мне нужно знать с максимально возможной точностью, когда все, что было перевернуто на первичную поверхность (или представленное в цепочке обмена), на самом деле отрисовывается экраном".
Удачи.
На самом деле нет никакой гарантии, что все, что вы помещаете в текущую очередь, когда-либо будет показано на экране (!!); вы можете вручную отбрасывать кадры с флагами присутствия буфера, или NVIDIA может сделать это за вас (... спасибо?)
Буферное секвенирование в DXGI
Очередь переворотов DXGI Swapchain, как правило, является FIFO, но популярные новые драйверы (например, FastSync), которые наверняка будут включены пользователями, обеспокоенными задержкой, будут отдавать предпочтение пропускной способности на стороне процессора таким тривиальным вещам, как отображение любого из нарисованных вами кадров:)
Обычно вы можете рассчитывать на то, что IDXGISwapChain::Present (...) начнет блокировку, когда цепочка подкачки заполнена неотображаемыми изображениями, а драйвер запускает команды на n-много кадров впереди GPU, но с принудительной FastSync, Present никогда не блокируется и render-advance-queue сбрасывает свою работу, перезаписывая все завершенные кадры в Swapchain, которые ожидают на VBLANK.
Состоящие друг от друга представления о том, что завершение выполняется быстрее, чем обновление экрана, не связаны (и не будут) сканировать, поэтому их статус по отношению к VBLANK не имеет смысла.
Если вы не реализуете ограничение скорости самостоятельно, чтобы предотвратить немедленную постановку ЦП следующего кадра после любого вызова Present, вам понадобится совсем другая парадигма для измерения состояния кадра в целом.
D3D9Ex / DXGI поддерживает статистику презентаций только в режиме Flip / Fullscreen:
Фреймы фактически не представляются пользователю, если только следующие API не говорят, что они это делают:
IDXGISwapChain:: GetFrameStatistics (...) и IDXGISwapChain::GetLastPresentCount (...)
Вы можете использовать статистику кадров, чтобы вычислить длину очереди рендеринга / текущую задержку в реальном времени, и ваши временные цели, вероятно, могут быть выполнены путем отслеживания текущего # по учетной информации для успешно синхронизированных кадров.
Вопрос здесь в том, почему? Похоже, вы хотите решить симптом вашей проблемы; может быть, это отвлекает от вашей реальной проблемы. Ожидание vsync было полезной техникой на Amiga или DOS. Это абсолютно неправильно в любой композитной или многопоточной ОС.
Во-первых, чего вы хотите достичь? Рендеринг без разрывов выполняется путем установки интервала подкачки в D3D или OpenGL. Вредно пытаться делать лучше, чем там ОС. Подумайте только о случаях, например, о нескольких мониторах, или о том, что произойдет, если более чем одно приложение попытается синхронизировать.
Если вы являетесь клиентом какого-то другого процесса и хотите запустить синхронизацию на VSync, то, к сожалению, Windows не предлагает никаких объектов для ожидания, насколько я знаю. Лучше всего по-прежнему полагаться на текущий вызов и оценивать происходящее.
Есть два случая: вы делаете или представляете быстрее или медленнее, чем vsync. Если вы быстрее, Present должен заблокировать вас уже. Если подарок никогда не ждет и ваше время между вызовами превышает 1/60 секунды, вы, вероятно, захотите рендериться реже.
Наиболее распространенным случаем, почему люди заботятся о VSync, является видео. Вы можете рендерить намного быстрее, чем vsync, но хотите дождаться нужного времени для презентации. Единственное, что нужно сделать, это запустить несколько кадров так быстро, как вы можете, и исходя из этой оценки, вы рассчитываете время кадра. Используйте некоторое дрожание и обратную связь... или используйте встроенное аппаратное видео, которое достаточно для того, чтобы дружить с ядром с видеодрайвером.