Улучшения в Direct3D 9Ex

В этом разделе описывается добавленная поддержка Windows 7 для режима flip Mode Present и связанная с ней текущая статистика в Direct3D 9Ex и Диспетчере окон рабочего стола. К целевым приложениям относятся приложения для презентаций на основе видео или частоты кадров. Приложения, использующие Direct3D 9Ex Flip Mode Present, снижают нагрузку на системные ресурсы при включении DWM. Представленные улучшения статистики, связанные с flip Mode Present, позволяют приложениям Direct3D 9Ex лучше управлять скоростью представления, предоставляя механизмы обратной связи и коррекции в режиме реального времени. Подробные объяснения и указатели на примеры ресурсов включены.

В этом разделе содержатся следующие подразделы.

Улучшения Direct3D 9Ex для Windows 7

Презентация Direct3D 9Ex в режиме пролистывания — это улучшенный режим представления изображений в Direct3D 9Ex, который эффективно передает отрисованные изображения в Windows 7 Desktop Window Manager (DWM) для композиции. Начиная с Windows Vista, DWM составляет весь рабочий стол. Если dwm включен, приложения в оконном режиме представляют свое содержимое на рабочем столе с помощью метода Blt Mode Present to DWM (или Blt Model). При использовании модели Blt DWM поддерживает копию поверхности Direct3D 9Ex, отображаемой для композиции Desktop. При обновлении приложения новое содержимое копируется в область DWM через blt. Для приложений, содержащих содержимое Direct3D и GDI, данные GDI также копируются на поверхность DWM.

Доступный в Windows 7 режим flip Present to DWM (или Flip Model) — это новый метод представления, который по сути позволяет передавать дескрипторы поверхностей приложений между приложениями в оконном режиме и DWM. Помимо экономии ресурсов, модель Flip поддерживает расширенную текущую статистику.

Представленная статистика — это сведения о времени выполнения кадров, которые приложения могут использовать для синхронизации видео- и аудиопотоков и восстановления после сбоев воспроизведения видео. Сведения о времени кадра в текущей статистике позволяют приложениям настраивать частоту отображения своих видеокадров для более плавного представления. В Windows Vista, где DWM поддерживает соответствующую копию поверхности кадра для композиции рабочего стола, приложения могут использовать статистику, предоставленную DWM. Этот метод получения текущей статистики будет по-прежнему доступен в Windows 7 для существующих приложений.

В Windows 7 приложения на основе Direct3D 9Ex, которые используют модель Flip, должны использовать API D3D9Ex для получения текущей статистики. Если dwm включен, приложения Direct3D 9Ex в оконном режиме и монопольном режиме полноэкранного режима могут ожидать те же статистические данные, что и при использовании модели Flip. Direct3D 9Ex Flip Model present statistics позволяет приложениям запрашивать текущую статистику в режиме реального времени, а не после отображения кадра на экране; те же статистические данные доступны для приложений с поддержкой оконного режима Flip-Model в полноэкранном режиме; Добавлен флаг в API D3D9Ex позволяет приложениям Flip Model эффективно удалять поздние кадры во время презентации.

Модель Direct3D 9Ex Flip должна использоваться в новых приложениях для презентаций на основе видео или частоты кадров, предназначенных для Windows 7. Из-за синхронизации между DWM и средой выполнения Direct3D 9Ex приложения, использующие Flip Model, должны указывать от 2 до 4 обратных буфферов, чтобы обеспечить беспроблемную презентацию. Тем приложениям, которые используют представленные статистические сведения, будет полезно использовать усовершенствования текущей статистики с включенной моделью flip.

Презентация режима пролистывания Direct3D 9EX

Повышение производительности direct3D 9Ex Flip Mode Present значительно повышено в системе, когда dwm включен и когда приложение находится в оконном режиме, а не в полноэкранном монопольном режиме. В следующей таблице и на рисунке показано упрощенное сравнение использования пропускной способности памяти и операций чтения и записи системных приложений с окнами, которые выбирают модель Flip и модель использования Blt по умолчанию.

Режим Blt, представленный в DWM D3D9Ex Flip Mode Present to DWM
1. Приложение обновляет свой фрейм (запись)
1. Приложение обновляет свой фрейм (запись)
2. Среда выполнения Direct3D копирует содержимое поверхности в область перенаправления DWM (чтение, запись)
2. Среда выполнения Direct3D передает область приложения в DWM
3. После завершения копирования общей поверхности DWM отображает область приложения на экране (чтение, запись)
3. DWM отображает поверхность приложения на экране (чтение, запись)

иллюстрация сравнения модели blt и модели flip

Отображение режима показа сокращает использование системной памяти за счет уменьшения количества операций чтения и записи со стороны среды выполнения Direct3D для композиции оконного кадра dwm. Это снижает энергопотребление системы и общее использование памяти.

Приложения могут воспользоваться преимуществами direct3D 9Ex Flip Mode, которые предоставляют улучшения статистики при включенном DWM, независимо от того, находится ли приложение в оконном режиме или в полноэкранном монопольном режиме.

Модель программирования и API

Новые приложения для просмотра видео или частоты кадров, использующие API Direct3D 9Ex в Windows 7, могут воспользоваться преимуществами экономии памяти и электроэнергии, а также улучшенной презентации, предлагаемой flip Mode Present при работе в Windows 7. (При запуске в предыдущих версиях Windows среда выполнения Direct3D по умолчанию использует для приложения значение Blt Mode Present.)

Режим пролистывания — это означает, что приложение может использовать механизмы обратной связи и исправления статистики в режиме реального времени, когда dwm включен. Однако приложения, использующие режим flip-present, должны учитывать ограничения при использовании параллельной отрисовки API GDI.

Вы можете изменить существующие приложения, чтобы воспользоваться преимуществами Flip Mode Present с теми же преимуществами и предостережениями, что и новые приложения.

Выбор модели Direct3D 9Ex Flip

Приложения Direct3D 9Ex, предназначенные для Windows 7, могут выбрать модель Flip, создав цепочку буферов со значением перечисления D3DSWAPEFFECT_FLIPEX . Чтобы выбрать модель Flip, приложения указывают структуру D3DPRESENT_PARAMETERS , а затем передают указатель на эту структуру при вызове API IDirect3D9Ex::CreateDeviceEx . В этом разделе описывается, как приложения, предназначенные для Windows 7, используют IDirect3D9Ex::CreateDeviceEx для выбора модели Flip. Дополнительные сведения об API IDirect3D9Ex::CreateDeviceEx см. в разделе IDirect3D9Ex::CreateDeviceEx на сайте MSDN.

Для удобства здесь повторяется синтаксис D3DPRESENT_PARAMETERS и IDirect3D9Ex::CreateDeviceEx .

HRESULT CreateDeviceEx(
  UINT Adapter,
  D3DDEVTYPE DeviceType,
  HWND hFocusWindow,
  DWORD BehaviorFlags,
  D3DPRESENT_PARAMETERS* pPresentationParameters,
  D3DDISPLAYMODEEX *pFullscreenDisplayMode,
  IDirect3DDevice9Ex **ppReturnedDeviceInterface
);
typedef struct D3DPRESENT_PARAMETERS {
    UINT BackBufferWidth, BackBufferHeight;
    D3DFORMAT BackBufferFormat;
    UINT BackBufferCount;
    D3DMULTISAMPLE_TYPE MultiSampleType;
    DWORD MultiSampleQuality;
    D3DSWAPEFFECT SwapEffect;
    HWND hDeviceWindow;
    BOOL Windowed;
    BOOL EnableAutoDepthStencil;
    D3DFORMAT AutoDepthStencilFormat;
    DWORD Flags;
    UINT FullScreen_RefreshRateInHz;
    UINT PresentationInterval;
} D3DPRESENT_PARAMETERS, *LPD3DPRESENT_PARAMETERS;

При изменении приложений Direct3D 9Ex для Windows 7, чтобы они согласились на использование модели Flip, следует учитывать следующие элементы указанных элементов D3DPRESENT_PARAMETERS:

BackBufferCount

(Только Windows 7)

Если для SwapEffect задан новый тип эффекта цепочки буферов D3DSWAPEFFECT_FLIPEX, число буферов обратно должно быть равно или больше 2, чтобы предотвратить снижение производительности приложения в результате ожидания освобождения предыдущего буфера Present dwM.

Если приложение также использует текущую статистику, связанную с D3DSWAPEFFECT_FLIPEX, рекомендуется задать для счетчика задних буферов значение от 2 до 4.

Использование D3DSWAPEFFECT_FLIPEX в Windows Vista или предыдущих версиях операционной системы приведет к сбою из CreateDeviceEx.

SwapEffect

(Только Windows 7)

Новый тип эффекта цепочки буферов D3DSWAPEFFECT_FLIPEX определяет, когда приложение принимает режим flip present to DWM. Это позволяет приложению более эффективно использовать память и мощность, а также позволяет приложению воспользоваться преимуществами полноэкранного представления статистики в оконном режиме. На полноэкранное поведение приложения не влияет. Если для свойства Windowed задано значение TRUE , а для свойства SwapEffect задано значение D3DSWAPEFFECT_FLIPEX, среда выполнения создает дополнительный задний буфер и поворачивает любой дескриптор, принадлежащий буферу, который становится передним буфером во время презентации.

Flags

(Только Windows 7)

Флаг D3DPRESENTFLAG_LOCKABLE_BACKBUFFER нельзя задать, если для swapEffect задан новый тип эффекта цепочки буферов D3DSWAPEFFECT_FLIPEX .

Рекомендации по проектированию приложений модели Direct3D 9Ex Flip

Используйте рекомендации в следующих разделах для разработки приложений Direct3D 9Ex Flip Model.

Использование режима flip present в отдельном HWND от режима Blt Present

Приложения должны использовать direct3D 9Ex Flip Mode Present в HWND, который не предназначен для других API, включая Blt Mode Present Direct3D 9Ex, другие версии Direct3D или GDI. Режим флип-презентации можно использовать для представления дочерним окнам; то есть приложения могут использовать модель Flip, если она не смешивается с моделью Blt в одном HWND, как показано на следующих иллюстрациях.

иллюстрация родительского окна direct3d и дочернего окна gdi, каждое из которых имеет собственный hwnd

иллюстрация родительского окна gdi и дочернего окна direct3d, каждое из которых имеет собственный hwnd

Так как модель Blt поддерживает дополнительную копию поверхности, GDI и другое содержимое Direct3D можно добавить в тот же HWND с помощью отдельных обновлений из Direct3D и GDI. При использовании модели Flip будет отображаться только содержимое Direct3D 9Ex в D3DSWAPEFFECT_FLIPEX цепочках буферов, передаваемых в DWM. Все остальные обновления содержимого Direct3D или GDI модели Blt будут игнорироваться, как показано на следующих иллюстрациях.

иллюстрация текста gdi, который может не отображаться, если используется модель пролистывания и содержимое Direct3d и gdi находятся в одном и том же hwnd

Иллюстрация содержимого direct3d и gdi, в котором включен dwm и приложение находится в оконном режиме

Поэтому модель Flip должна быть включена для буферов цепочки буферов, где модель direct3D 9Ex Flip только отрисовывает весь HWND.

Не используйте модель flip с scrollWindow или ScrollWindowEx GDI

Некоторые приложения Direct3D 9Ex используют функции ScrollWindow или ScrollWindowEx GDI для обновления содержимого окна при активации события прокрутки пользователя. ScrollWindow и ScrollWindowEx выполняют большие двоичные объекты содержимого окна на экране при прокрутке окна. Эти функции также требуют обновления модели Blt для содержимого GDI и Direct3D 9Ex. Приложения, использующие обе функции, не обязательно будут отображать видимое содержимое окна, прокручиваемое на экране, если приложение находится в оконном режиме и включена функция DWM. Рекомендуется не использовать API-интерфейсы ScrollWindow и ScrollWindowEx GDI в приложениях, а вместо этого перерисовывать их содержимое на экране в ответ на прокрутку.

Использование одной D3DSWAPEFFECT_FLIPEX цепочки буферов на HWND

Приложения, использующие модель Flip, не должны использовать несколько цепочек буферов модели Flip, предназначенных для одного И того же HWND.

Синхронизация кадров приложений модели Direct3D 9Ex Flip

Представленная статистика — это сведения о времени кадра, которые мультимедийные приложения используют для синхронизации видео- и аудиопотоков и восстановления после сбоев воспроизведения видео. Чтобы обеспечить доступность текущей статистики, приложение Direct3D 9Ex должно убедиться, что параметр BehaviorFlags , который приложение передает в IDirect3D9Ex::CreateDeviceEx , содержит флаг поведения устройства D3DCREATE_ENABLE_PRESENTSTATS.

Для удобства здесь повторяется синтаксис IDirect3D9Ex::CreateDeviceEx .

HRESULT CreateDeviceEx(
  UINT Adapter,
  D3DDEVTYPE DeviceType,
  HWND hFocusWindow,
  DWORD BehaviorFlags,
  D3DPRESENT_PARAMETERS* pPresentationParameters,
  D3DDISPLAYMODEEX *pFullscreenDisplayMode,
  IDirect3DDevice9Ex **ppReturnedDeviceInterface
);

Direct3D 9Ex Flip Model добавляет флаг презентации D3DPRESENT_FORCEIMMEDIATE , который обеспечивает D3DPRESENT_INTERVAL_IMMEDIATE поведение флага презентации. Приложение Direct3D 9Ex указывает эти флаги презентации в параметре dwFlags , который приложение передает в IDirect3Device9Ex::P resentEx, как показано ниже.

HRESULT PresentEx(
  CONST RECT *pSourceRect,
  CONST RECT *pDestRect,
  HWND hDestWindowOverride,
  CONST RGNDATA *pDirtyRegion,
  DWORD dwFlags
);

При изменении приложения Direct3D 9Ex для Windows 7 следует учитывать следующие сведения об указанных флагах презентации D3DPRESENT :

D3DPRESENT_DONOTFLIP

Этот флаг доступен только в полноэкранном режиме или

(Только Windows 7)

когда приложение задает члену SwapEffectD3DPRESENT_PARAMETERSзначение D3DSWAPEFFECT_FLIPEX в вызове CreateDeviceEx.

D3DPRESENT_FORCEIMMEDIATE

(Только Windows 7)

Этот флаг можно указать, только если приложение задает элементу SwapEffectD3DPRESENT_PARAMETERSD3DSWAPEFFECT_FLIPEX в вызове CreateDeviceEx. Приложение может использовать этот флаг для немедленного обновления поверхности с несколькими кадрами позже в очереди dwm Present, по сути пропуская промежуточные кадры.

Оконные приложения с поддержкой FlipEx могут использовать этот флаг для немедленного обновления поверхности кадром, который находится позже в очереди dwm Present, пропуская промежуточные кадры. Это особенно полезно для приложений мультимедиа, которые хотят удалить кадры, обнаруженные как поздние и представленные последующие кадры во время композиции. IDirect3DDevice9Ex::P resentEx возвращает ошибку недопустимого параметра, если этот флаг указан неправильно.

Чтобы получить сведения о текущей статистике, приложение получает структуру D3DPRESENTSTATS путем вызова API IDirect3DSwapChain9Ex::GetPresentStatistics .

Структура D3DPRESENTSTATS содержит статистику о вызовах IDirect3DDevice9Ex::P resentEx . Устройство должно быть создано с помощью вызова IDirect3D9Ex::CreateDeviceEx с флагом D3DCREATE_ENABLE_PRESENTSTATS . В противном случае данные, возвращаемые GetPresentStatistics , являются неопределенными. Цепочка буферов Direct3D 9Ex с поддержкой Flip-Model предоставляет статистические сведения как в оконном, так и в полноэкранном режиме.

Для цепочек буферов Direct3D 9Ex с поддержкой Blt-Model в оконном режиме все значения структуры D3DPRESENTSTATS будут равны нулям.

Для текущей статистики FlipEx GetPresentStatistics возвращает D3DERR_PRESENT_STATISTICS_DISJOINT в следующих ситуациях:

  • Первый вызов GetPresentStatistics , который указывает начало последовательности
  • Переход dwm с "вкл." на "выкл."
  • Изменение режима: переход из оконного режима в полноэкранный или полноэкранный режим в полноэкранный режим

Для удобства здесь повторяется синтаксис GetPresentStatistics .

HRESULT GetPresentStatistics(
  D3DPRESENTSTATS * pPresentationStatistics
);

Метод IDirect3DSwapChain9Ex::GetLastPresentCount возвращает последний PresentCount, то есть идентификатор Present последнего успешного вызова Present, выполненного устройством отображения, связанным с цепочкой буферов. Этот идентификатор present является значением элемента PresentCount структуры D3DPRESENTSTATS . Для цепочек буферов Direct3D 9Ex с поддержкой Blt-Model в режиме окон все значения структуры D3DPRESENTSTATS будут равны нулям.

Для удобства здесь повторяется синтаксис IDirect3DSwapChain9Ex::GetLastPresentCount .

HRESULT GetLastPresentCount(
  UINT * pLastPresentCount
);

При изменении приложения Direct3D 9Ex для Windows 7 следует учитывать следующие сведения о структуре D3DPRESENTSTATS :

  • Значение PresentCount, возвращаемое GetLastPresentCount , не обновляется, когда вызов PresentEx с D3DPRESENT_DONOTWAIT, указанным в параметре dwFlags , возвращает ошибку.
  • При вызове PresentEx с D3DPRESENT_DONOTFLIP вызов GetPresentStatistics завершается успешно, но не возвращает обновленную структуру D3DPRESENTSTATS , когда приложение находится в режиме окна.
  • PresentRefreshCount и SyncRefreshCount в D3DPRESENTSTATS:
    • Параметр PresentRefreshCount равен SyncRefreshCount , когда приложение отображается в каждой виртуальной синхронизации.
    • SyncRefreshCount получается в интервале vsync при отправке настоящего. SyncQPCTime — это приблизительное время, связанное с интервалом виртуальной синхронизации.
typedef struct _D3DPRESENTSTATS {
    UINT PresentCount;
    UINT PresentRefreshCount;
    UINT SyncRefreshCount;
    LARGE_INTEGER SyncQPCTime;
    LARGE_INTEGER SyncGPUTime;
} D3DPRESENTSTATS;

Синхронизация кадров для оконных приложений при отключении DWM

Если DWM отключен, оконные приложения отображаются непосредственно на экране монитора без прохождения цепочки пролистывания. В Windows Vista не поддерживается получение статистики кадров для оконных приложений, когда DWM отключен. Для поддержки API, в котором приложения не должны учитывать DWM, Windows 7 будет возвращать статистику кадра для оконных приложений, когда DWM отключен. Статистика кадра, возвращаемая при отключении DWM, является только оценкой.

Walk-Through модели direct3D 9Ex Flip и примера статистики представления

Чтобы согласиться на презентацию FlipEx для примера Direct3D 9Ex

  1. Убедитесь, что пример приложения работает в Windows 7 или более поздней версии операционной системы.
  2. Задайте для элемента SwapEffectD3DPRESENT_PARAMETERSзначение D3DSWAPEFFECT_FLIPEX в вызове CreateDeviceEx.
    OSVERSIONINFO version;
    ZeroMemory(&version, sizeof(version));
    version.dwOSVersionInfoSize = sizeof(version);
    GetVersionEx(&version);
    
    // Sample would run only on Win7 or higher
    // Flip Model present and its associated present statistics behavior are only available on Windows 7 or higher operating system
    bool bIsWin7 = (version.dwMajorVersion > 6) || 
        ((version.dwMajorVersion == 6) && (version.dwMinorVersion >= 1));

    if (!bIsWin7)
    {
        MessageBox(NULL, L"This sample requires Windows 7 or higher", NULL, MB_OK);
        return 0;
    }

Чтобы также согласиться на выбор примера для Direct3D 9Ex, связанной с FlipEx,

    // Set up the structure used to create the D3DDevice
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof(d3dpp));

    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_FLIPEX;        // Opts into Flip Model present for D3D9Ex swapchain
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
    d3dpp.BackBufferWidth = 256;                
    d3dpp.BackBufferHeight = 256;
    d3dpp.BackBufferCount = QUEUE_SIZE;
    d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;

    g_iWidth = d3dpp.BackBufferWidth;
    g_iHeight = d3dpp.BackBufferHeight;

    // Create the D3DDevice with present statistics enabled - set D3DCREATE_ENABLE_PRESENTSTATS for behaviorFlags parameter
    if(FAILED(g_pD3D->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                      D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_ENABLE_PRESENTSTATS,
                                      &d3dpp, NULL, &g_pd3dDevice)))
    {
        return E_FAIL;
    }

Чтобы избежать ошибок, обнаруживайте и восстанавливайте их после сбоев.

  1. Вызовы с представлением очереди: рекомендуемое количество обратных буферов — от 2 до 4.

  2. В примере Direct3D 9Ex добавляется неявный backbuffer. Фактическая длина очереди present — это счетчик backbuffer + 1.

  3. Создайте структуру вспомогательной очереди Present для хранения всех успешно отправленных идентификаторов Present (PresentCount) и связанных, вычисляемых или ожидаемых PresentRefreshCount.

  4. Чтобы обнаружить сбой, выполните приведенные далее действия.

    • Вызовите GetPresentStatistics.
    • Получите идентификатор (PresentCount) и число vsync, где отображается кадр (PresentRefreshCount) кадра, для которого получена текущая статистика.
    • Получение ожидаемого значения PresentRefreshCount (TargetRefresh в примере кода), связанного с идентификатором present.
    • Если фактическое значение PresentRefreshCount позже, чем ожидалось, произошел сбой.
  5. Чтобы восстановиться после сбоя, выполните приведенные далее действия.

    • Вычислите количество кадров, которые нужно пропустить (g_ переменной iImmediates в примере кода).
    • Представление пропущенных кадров с интервалом D3DPRESENT_FORCEIMMEDIATE.

Рекомендации по обнаружению и восстановлению сбоев

  1. Восстановление сбоем занимает N (g_iQueueDelay переменная в примере кода) числа вызовов Present, где N (g_iQueueDelay) равно g_iImmediates плюс длина очереди Present, то есть:

    • Пропуск кадров с D3DPRESENT_FORCEIMMEDIATE интервала "Представление" плюс
    • Постановка в очередь представлений, которые необходимо обработать
  2. Установите ограничение длины сбоя (GLITCH_RECOVERY_LIMIT в примере). Если пример приложения не может восстановиться после слишком длительного сбоя (т. е. 1 секунды или 60 виртуальных синхронизаций на мониторе с частотой 60 Гц), перейдите через прерывистую анимацию и сбросьте вспомогающую очередь Present.

VOID Render()
{
    g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    g_pd3dDevice->BeginScene();

    // Compute new animation parameters for time and frame based animations

    // Time-based is a difference between base and current SyncRefreshCount
    g_aTimeBasedHistory[g_iBlurHistoryCounter] = g_iStartFrame + g_LastSyncRefreshCount - g_SyncRefreshCount;
    // Frame-based is incrementing frame value
    g_aFrameBasedHistory[g_iBlurHistoryCounter] = g_iStartFrame + g_iFrameNumber;

    RenderBlurredMesh(TRUE);    // Time-based
    RenderBlurredMesh(FALSE);   // Frame-based

    g_iBlurHistoryCounter = (g_iBlurHistoryCounter + 1) % BLUR_FRAMES;

    DrawText();

    g_pd3dDevice->EndScene();

    // Performs glitch recovery if glitch was detected
    if (g_bGlitchRecovery && (g_iImmediates > 0))
    {
        // If we have present immediates queued as a result of glitch detected, issue forceimmediate Presents for glitch recovery 
        g_pd3dDevice->PresentEx(NULL, NULL, NULL, NULL, D3DPRESENT_FORCEIMMEDIATE);
        g_iImmediates--;
        g_iShowingGlitchRecovery = MESSAGE_SHOW;
    }
    // Otherwise, Present normally
    else
    {
        g_pd3dDevice->PresentEx(NULL, NULL, NULL, NULL, 0);
    }

    // Add to helper Present queue: PresentID + expected present refresh count of last submitted Present
    UINT PresentCount;
    g_pd3dSwapChain->GetLastPresentCount(&PresentCount);
    g_Queue.QueueFrame(PresentCount, g_TargetRefreshCount);
    
    // QueueDelay specifies # Present calls to be processed before another glitch recovery attempt
    if (g_iQueueDelay > 0)
    {
        g_iQueueDelay--;
    }

    if (g_bGlitchRecovery)
    {
        // Additional DONOTFLIP presents for frame conversions, which basically follows the same logic, but without rendering
        for (DWORD i = 0; i < g_iDoNotFlipNum; i++)
        {
            if (g_TargetRefreshCount != -1)
            {
                g_TargetRefreshCount++;
                g_iFrameNumber++;
                g_aTimeBasedHistory[g_iBlurHistoryCounter] = g_iStartFrame + g_LastSyncRefreshCount - g_SyncRefreshCount;
                g_aFrameBasedHistory[g_iBlurHistoryCounter] = g_iStartFrame + g_iFrameNumber;
                g_iBlurHistoryCounter = (g_iBlurHistoryCounter + 1) % BLUR_FRAMES;
            }
            
            if (g_iImmediates > 0)
            {
                g_pd3dDevice->PresentEx(NULL, NULL, NULL, NULL, D3DPRESENT_FORCEIMMEDIATE | D3DPRESENT_DONOTFLIP);
                g_iImmediates--;
            }
            else
            {
                g_pd3dDevice->PresentEx(NULL, NULL, NULL, NULL, D3DPRESENT_DONOTFLIP);
            }
            UINT PresentCount;
            g_pd3dSwapChain->GetLastPresentCount(&PresentCount);
            g_Queue.QueueFrame(PresentCount, g_TargetRefreshCount);

            if (g_iQueueDelay > 0)
            {
                g_iQueueDelay--;
            }
        }
    }

    // Check Present Stats info for glitch detection 
    D3DPRESENTSTATS PresentStats;

    // Obtain present statistics information for successfully displayed presents
    HRESULT hr = g_pd3dSwapChain->GetPresentStats(&PresentStats);

    if (SUCCEEDED(hr))
    {
        // Time-based update
        g_LastSyncRefreshCount = PresentStats.SyncRefreshCount;
        if ((g_SyncRefreshCount == -1) && (PresentStats.PresentCount != 0))
        {
            // First time SyncRefreshCount is reported, use it as base
            g_SyncRefreshCount = PresentStats.SyncRefreshCount;
        }

        // Fetch frame from the queue...
        UINT TargetRefresh = g_Queue.DequeueFrame(PresentStats.PresentCount);

        // If PresentStats returned a really old frame that we no longer have in the queue, just don't do any glitch detection
        if (TargetRefresh == FRAME_NOT_FOUND)
            return;

        if (g_TargetRefreshCount == -1)
        {
            // This is first time issued frame is confirmed by present stats, so fill target refresh count for all frames in the queue
            g_TargetRefreshCount = g_Queue.FillRefreshCounts(PresentStats.PresentCount, g_SyncRefreshCount);
        } 
        else
        {
            g_TargetRefreshCount++;
            g_iFrameNumber++;

            // To determine whether we're glitching, see if our estimated refresh count is confirmed
            // if the frame is displayed later than the expected vsync count
            if (TargetRefresh < PresentStats.PresentRefreshCount)
            {
                // then, glitch is detected!

                // If glitch is too big, don't bother recovering from it, just jump animation
                if ((PresentStats.PresentRefreshCount - TargetRefresh) > GLITCH_RECOVERY_LIMIT)
                {
                    g_iStartFrame += PresentStats.SyncRefreshCount - g_SyncRefreshCount;
                    ResetAnimation();
                    if (g_bGlitchRecovery)
                        g_iGlitchesInaRow++;    
                } 
                // Otherwise, compute number of immediate presents to recover from it -- if we?re not still trying to recover from another glitch
                else if (g_iQueueDelay == 0)
                {
                      // skip frames to catch up to expected refresh count
                    g_iImmediates = PresentStats.PresentRefreshCount - TargetRefresh;
                    // QueueDelay specifies # Present calls before another glitch recovery 
                    g_iQueueDelay = g_iImmediates + QUEUE_SIZE;
                    if (g_bGlitchRecovery)
                        g_iGlitchesInaRow++;
                }
            }
            else
            {
                // No glitch, reset glitch count
                g_iGlitchesInaRow = 0;
            }
        }
    }
    else if (hr == D3DERR_PRESENT_STATISTICS_DISJOINT)
    {
        // D3DERR_PRESENT_STATISTICS_DISJOINT means measurements should be started from the scratch (could be caused by mode change or DWM on/off transition)
        ResetAnimation();
    }

    // If we got too many glitches in a row, reduce framerate conversion factor (that is, render less frames)
    if (g_iGlitchesInaRow == FRAMECONVERSION_GLITCH_LIMIT)
    {
        if (g_iDoNotFlipNum < FRAMECONVERSION_LIMIT)
        {
            g_iDoNotFlipNum++;
        }
        g_iGlitchesInaRow = 0;
        g_iShowingDoNotFlipBump = MESSAGE_SHOW;
    }
}

Пример сценария

  • На следующем рисунке показано приложение с числом backbuffer 4. Таким образом, фактическая длина очереди Present составляет 5.

    иллюстрация приложения для отрисованных кадров и текущей очереди

    Кадр A предназначен для перехода на экран с числом интервалов синхронизации 1, но было обнаружено, что он отображается при счетчике интервалов синхронизации 4. Поэтому произошел сбой. Последующие три кадра отображаются с D3DPRESENT_INTERVAL_FORCEIMMEDIATE. Перед восстановлением сбой должен занять в общей сложности 8 вызовов Present. Следующий кадр будет отображаться согласно целевому количеству интервалов синхронизации.

Сводка рекомендаций по программированию для синхронизации кадров

  • Создайте список резервных копий всех идентификаторов LastPresentCount (полученных с помощью GetLastPresentCount) и связанного предполагаемого значения PresentRefreshCount всех отправленных презентаций.

    Примечание

    Когда приложение вызывает PresentEx с D3DPRESENT_DONOTFLIP, вызов GetPresentStatistics завершается успешно, но не возвращает обновленную структуру D3DPRESENTSTATS , когда приложение находится в режиме окна.

  • Вызовите Метод GetPresentStatistics , чтобы получить фактический Параметр PresentRefreshCount, связанный с каждым отображаемым идентификатором представленных кадров, чтобы убедиться, что приложение обрабатывает ошибки, возвращаемые вызовом.

  • Если фактическое значение PresentRefreshCount позже предполагаемого Значения PresentRefreshCount, обнаруживается сбой. Компенсируйте, отправляя отстающие кадры Present с D3DPRESENT_FORCEIMMEDIATE.

  • Если один кадр отображается с опозданием в очереди Present, все последующие кадры в очереди будут представлены с опозданием. D3DPRESENT_FORCEIMMEDIATE исправит только следующий кадр, который будет отображаться после всех кадров в очереди. Таким образом, очередь present или backbuffer count не должны быть слишком длинными, поэтому есть меньше сбоев кадров, чтобы догнать. Оптимальное число backbuffer от 2 до 4.

  • Если предполагаемое значение PresentRefreshCount позже фактического Значения PresentRefreshCount, возможно, произошло регулирование DWM. Возможны следующие решения:

    • уменьшение длины текущей очереди
    • уменьшение требований к памяти GPU с помощью любых других средств, кроме уменьшения длины очереди (т. е. снижения качества, удаления эффектов и т. д.)
    • указание DwmEnableMMCSS для предотвращения регулирования DWM в целом
  • Проверка функциональности отображения приложения и производительности статистики кадров в следующих сценариях:

    • с включением и выключением DWM
    • полноэкранный монопольный и оконный режимы
    • оборудование с более низкой функциональностью
  • Если приложения не могут восстановиться после большого количества сбойных кадров с D3DPRESENT_FORCEIMMEDIATE Present, они могут выполнять следующие операции:

    • сократите использование ЦП и GPU за счет отрисовки с меньшей рабочей нагрузкой.
    • в случае декодирования видео, декодирования быстрее за счет снижения качества и, следовательно, использования ЦП и GPU.

Вывод об улучшениях Direct3D 9Ex

В Windows 7 приложения, отображающие видео или частоту кадров датчика во время презентации, могут использовать модель flip. Существующие улучшения статистики, связанные с Flip Model Direct3D 9Ex, могут помочь приложениям, которые синхронизируют презентацию на частоту кадров, с обратной связью в режиме реального времени для обнаружения и восстановления сбоев. Разработчикам, которые внедряют модель 9Ex Flip Direct3D, следует учитывать отдельные HWND от синхронизации содержимого GDI и частоты кадров. Дополнительные сведения см. в этой статье и документации MSDN. Дополнительную документацию см . в разделе Центр разработчика DirectX на сайте MSDN.

Призыв к действию

Мы рекомендуем использовать модель Direct3D 9Ex Flip и ее текущую статистику в Windows 7 при создании приложений, которые пытаются синхронизировать частоту кадров презентации или восстановиться после сбоев отображения.

Центр разработчиков DirectX на сайте MSDN