Поделиться через


Инфраструктура графики DirectX (DXGI): рекомендации

Инфраструктура графики Microsoft DirectX (DXGI) — это новая подсистема, представленная в Windows Vista, которая инкапсулирует некоторые задачи низкого уровня, необходимые Direct3D 10, 10.1, 11 и 11.1. С точки зрения программиста Direct3D 9 DXGI охватывает большую часть кода для перечисления, создания цепочки буферов и презентации, которая ранее была упакована в API Direct3D 9. При переносе приложения в DXGI и Direct3D 10.x и Direct3D 11.x необходимо учитывать некоторые рекомендации, чтобы обеспечить плавность выполнения процесса.

В этой статье рассматриваются ключевые проблемы с переносом.

проблемы Full-Screen

При переносе из Direct3D 9 в DXGI и Direct3D 10.x или Direct3D 11.x проблемы, связанные с переходом из окна в полноэкранный режим, часто могут вызвать головную боль для разработчиков. Основные проблемы возникают из-за того, что приложения Direct3D 9, в отличие от приложений DXGI, требуют более практического подхода к отслеживанию стилей окон и состояний окна. При переносе кода для изменения режима на DXGI часто наблюдается непредвиденное поведение.

Часто приложения Direct3D 9 обрабатывали переход в полноэкранный режим, устанавливая разрешение переднего буфера, принуждая устройство переходить в полноэкранный эксклюзивный режим, а затем устанавливая разрешения заднего буфера. Отдельный путь использовался для изменения размера окна, так как они должны управляться из процесса окна всякий раз, когда приложение получило сообщение WM_SIZE.

DXGI пытается упростить этот подход путем объединения двух вариантов. Например, когда пользователь перетаскивает границу окна в оконном режиме, приложение получает сообщение WM_SIZE. DXGI перехватывает это сообщение и автоматически изменяет размер переднего буфера. Все, что нужно сделать приложению, заключается в вызове IDXGISwapChain::ResizeBuffers, чтобы изменить размер буфера назад до размера, переданного в качестве параметров в WM_SIZE. Аналогичным образом, когда приложению необходимо переключаться между полноэкранным и оконным режимом, приложение может просто вызывать IDXGISwapChain::SetFullscreenState. DXGI изменяет размер переднего буфера, чтобы он соответствовал только что выбранному полноэкранному режиму, и отправляет WM_SIZE сообщение приложению. Приложение снова вызывает ResizeBuffersтак же, как и если граница окна перетаскивалась.

Методология предыдущего объяснения следует очень специфическому пути. DXGI по умолчанию задает разрешение полноэкранного экрана для разрешения рабочего стола. Однако многие приложения переключаются на предпочтительное разрешение полноэкранного экрана. В таком случае DXGI предоставляет IDXGISwapChain::ResizeTarget. Это следует вызывать перед вызовом SetFullscreenState. Хотя эти методы можно вызвать в противоположном порядке (SetFullscreenState сначала, а затем ResizeTarget), это приводит к отправке дополнительного WM_SIZE сообщения в приложение. (Это также может вызвать мерцание, так как DXGI может быть вынужден выполнять два изменения в режиме.) После вызова SetFullscreenStateрекомендуется снова вызвать ResizeTarget с обнулённым элементом RefreshRate в структуре DXGI_MODE_DESC. Это равнозначно неосмысленной инструкции в DXGI, но может избежать проблем с частотой обновления, которые обсуждаются далее.

В полноэкранном режиме диспетчер окон рабочего стола (DWM) будет отключен. DXGI может выполнить переключение кадров, чтобы представить содержимое заднего буфера вместо переноса, который выполнялся бы в оконном режиме. Однако это повышение производительности может быть отменено, если определенные требования не выполнены. Чтобы DXGI выполняло переход с переворачиванием, а не выполнением блита, передний и задний буферы должны иметь одинаковый размер. Если приложение правильно обрабатывает свои WM_SIZE сообщения, это не должно быть проблемой. Кроме того, форматы должны быть идентичными.

Проблема для большинства приложений — это частота обновления. Частота обновления, указанная в вызове ResizeTarget, должна быть скоростью обновления, перечисленной объектом IDXGIOutput, который используется цепочкой буферов. DXGI может автоматически вычислить это значение, если приложение обнулит член RefreshRate в DXGI_MODE_DESC, передаваемом в ResizeTarget. Важно не предположить, что некоторые частоты обновления всегда будут поддерживаться и просто жестко кодировать значение. Часто разработчики выбирают 60 Гц в качестве скорости обновления, не зная, что перечисленная частота обновления от монитора составляет примерно 60 000 / 1001 Гц от монитора. Если частота обновления не соответствует ожидаемой частоте обновления 60, DXGI вынуждена выполнять перерезку в полноэкранном режиме, а не перевернуть.

Последняя проблема, с которой разработчики часто сталкиваются, заключается в том, как изменить разрешения полноэкранного экрана, оставаясь в полноэкранном режиме. Вызов ResizeTarget и SetFullscreenState иногда завершается успешно, но разрешение полноэкранного экрана остается разрешением рабочего стола. Кроме того, разработчики могут создать полноэкранную цепочку обмена и задать определенное разрешение, только чтобы обнаружить, что DXGI по умолчанию использует разрешение рабочего стола независимо от переданных параметров. Если иное не указано, DXGI по умолчанию использует разрешение экрана рабочего стола для цепочек обмена кадрами в полноэкранном режиме. При создании полноэкранной цепочки буферов член структуры флагиDXGI_SWAP_CHAIN_DESC необходимо задать значение DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH чтобы переопределить поведение DXGI по умолчанию. Этот флаг также можно передать в ResizeTarget, чтобы включить или отключить эту функцию динамически.

Несколько мониторов

При использовании DXGI с несколькими мониторами существует два правила.

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

Второе правило применяется к выходным данным. Обращайте внимание на выходные данные, используемые при создании цепей обмена. С помощью DXGI объект IDXGIOutput определяет, какой монитор будет использоваться цепочкой буферов при переходе в полноэкранный режим. В отличие от DXGI, Direct3D 9 не имел понятия о выходных данных.

Стили окон и DXGI

Приложения Direct3D 9 имели много работы при переключении между полноэкранным и оконным режимами. Большая часть этой работы связана с изменением стилей окон для добавления и удаления границ, добавления полос прокрутки и т. д. Когда приложения переносятся в DXGI и Direct3D 10.x или Direct3D 11.x, этот код часто остается на месте. В зависимости от внесенных изменений переключение между режимами может привести к неожиданному поведению. Например, при переключении в режим окна приложение может больше не иметь рамку окна или границу окна, несмотря на наличие кода, который специально задает эти стили. Это происходит, так как DXGI теперь обрабатывает большую часть изменения стиля самостоятельно. Настройка стилей окна вручную может повлиять на DXGI, и это может привести к неожиданному поведению.

Рекомендуемое поведение — сделать как можно меньше работы и позволить DXGI обрабатывать большую часть взаимодействия с окнами. Однако, если приложению необходимо управлять собственным поведением окон, IDXGIFactory::MakeWindowAssociation можно использовать для того, чтобы сообщить DXGI об отключении некоторых из его автоматических функций обработки окон.

Многопоточность и DXGI

При использовании DXGI в многопоточных приложениях необходимо соблюдать особое внимание, чтобы гарантировать, что взаимоблокировки не происходят. Из-за тесного взаимодействия DXGI с окном он иногда отправляет сообщения окна в связанное окно приложения. DXGI нуждается в изменениях в окне, прежде чем он сможет продолжить работу, поэтому он будет использовать SendMessage, который является синхронным вызовом. Приложение должно обработать сообщение окна перед возвратом SendMessage.

В приложении, где вызовы DXGI и цикл обработки сообщений находятся в одном потоке (или однопоточном приложении), требуется немного действий. Когда вызов DXGI находится в том же потоке, что и цикл обработки сообщений, SendMessage вызывает WindowProcокна. Это обходит цикл обработки сообщений и позволяет продолжить выполнение после вызова SendMessage. Помните, что вызовы IDXGISwapChain, такие как IDXGISwapChain::Present, также считаются вызовами DXGI; DXGI может отложить выполнение от ResizeBuffers или ResizeTarget до тех пор, пока не будет вызван .

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

Например, если у приложения цикл обработки сообщений находится на одном потоке, а визуализация на другом, ему может понадобиться изменить режим работы. Поток обработки сообщений сообщает потоку отрисовки изменить режим и ожидает завершения изменения режима. Однако поток отрисовки вызывает функции DXGI, которые, в свою очередь, вызывают SendMessage, что блокирует выполнение до тех пор, пока цикл обработки сообщений не обработает сообщение. Взаимоблокировка возникает, так как оба потока заблокированы и ожидают друг друга. Чтобы избежать этого, никогда не блокируйте цикл обработки сообщений. Если блок неизбежен, все взаимодействие DXGI должно происходить в том же потоке, что и цикл обработки сообщений.

Гамма и DXGI

Хотя гамма лучше всего обрабатывается в Direct3D 10.x или Direct3D 11.x с помощью текстур SRGB, гамма-коррекция всё ещё может быть полезна разработчикам, которые хотят другого гамма-значения, чем 2.2, или которые используют формат целевого рендеринга, не поддерживающий SRGB. Имейте в виду две проблемы при настройке гамма-кривой через DXGI. Первая проблема заключается в том, что значения пандуса, передаваемые в IDXGIOutput::SetGammaControl являются значениями с плавающей запятой, а не значения WORD. Кроме того, убедитесь, что код, перенесенный из Direct3D 9, не пытается преобразовать в значения WORD перед передачей этих значений в SetGammaControl.

Вторая проблема заключается в том, что после перехода на полноэкранный режим SetGammaControl может не работать, зависимо от используемого объекта IDXGIOutput. При переходе на полноэкранный режим DXGI создает новый выходной объект и использует объект для всех последующих операций с выходными данными. Если делается вызов SetGammaControl на выходных данных, перечисленных перед переключением на полноэкранный режим, вызов не направляется к выходным данным, которые в настоящее время использует DXGI. Чтобы избежать этого, вызовите IDXGISwapChain::GetContainingOutput, чтобы получить текущие выходные данные, а затем вызовите SetGammaControl для получения правильного поведения.

Сведения об использовании гамма-коррекции см. в разделе Использование гамма-коррекции.

DXGI 1.1

Среда выполнения Direct3D 11, включенная в Windows 7 и установленная в Windows Vista, включает версию 1.1 DXGI. Это обновление добавляет определения для ряда новых форматов (особенно BGRA, 10-разрядная предвзятость X2 и сжатие текстуры BC6H и BC7 Direct3D 11, а также новая версия интерфейсов фабрики DXGI и адаптеров (CreateDXGIFactory1, IDXGIFactory1, IDXGIAdapter1) для перечисления подключений к удаленному рабочему столу.

При использовании Direct3D 11 среда выполнения будет использовать DXGI 1.1 по умолчанию при вызове D3D11CreateDevice или D3D11CreateDeviceAndSwapChain с указателем NULL IDXGIAdapter. Использование DXGI 1.0 и DXGI 1.1 в том же процессе не поддерживается. Сочетание экземпляров объектов DXGI из разных фабрик в одном процессе также не поддерживается. Поэтому при использовании DirectX 11 любое явное использование интерфейсов DXGI использует IDXGIFactory1, созданной CreateDXGIFactory1 точки входа в DXGI.DLL, чтобы приложение всегда использовало DXGI 1.1.

DXGI 1.2

Среда выполнения Direct3D 11.1, включенная в Windows 8, также включает версию 1.2 DXGI.

DXGI 1.2 включает следующие функции:

  • стереовизуализация

  • 16-разрядные форматы пикселей

    • DXGI_FORMAT_B5G6R5_UNORM и DXGI_FORMAT_B5G5R5A1_UNORM теперь полностью поддерживаются
    • Добавлен новый формат DXGI_FORMAT_B5G5R5A1_UNORM
  • форматы видео

  • новые интерфейсы DXGI

Дополнительные сведения об особенностях DXGI 1.2 см. в разделе Улучшения DXGI 1.2.