Графическая инфраструктура DirectX (DXGI): рекомендации

Microsoft DirectX Graphics Infrastructure (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 проблемы, связанные с переходом из окон в полноэкранный режим, часто могут вызвать головную боль у разработчиков. Проблемы main возникают из-за того, что приложениям 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 с элементом RefreshRateDXGI_MODE_DESC обнуляется. Это инструкция без операции в DXGI, но она может избежать проблем с частотой обновления, которые будут обсуждаться далее.

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

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

Последняя проблема, с которой часто сталкиваются разработчики, заключается в том, как изменить разрешение в полноэкранном режиме. Вызовы ResizeTarget и SetFullscreenState иногда удается, но разрешение в полноэкранном режиме остается разрешением рабочего стола. Кроме того, разработчики могут создать полноэкранную цепочку буферов и предоставить определенное разрешение, только чтобы узнать, что DXGI по умолчанию использует разрешение рабочего стола независимо от переданных чисел. Если не указано иное, DXGI по умолчанию использует разрешение рабочего стола для цепочек буферов в полноэкранном режиме. При создании цепочки буферов в полноэкранном режиме элементу Flags структуры 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::P resent, также считаются вызовами DXGI; DXGI может отложить работу из ResizeBuffers или ResizeTarget до вызова Present .

Если вызов 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, включает dxGI версии 1.1. В этом обновлении добавлены определения для ряда новых форматов (в частности, 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 также включает dxGI версии 1.2.

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

  • Стерео отрисовка

  • Форматы 16 бит на пиксель

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

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

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