Взаимодействие Direct3D 12
D3D12 можно использовать для написания составных приложений.
Обзор взаимодействия
D3D12 может быть очень мощным и позволяет приложениям писать графический код с эффективностью консоли, но не каждому приложению нужно изобретать колесо и писать весь механизм отрисовки с нуля. В некоторых случаях другой компонент или библиотека уже сделали это лучше, или в других случаях производительность части кода не так важна, как ее правильность и удобочитаемость.
В этом разделе рассматриваются следующие методы взаимодействия:
- D3D12 и D3D12 на одном устройстве
- D3D12 и D3D12 на разных устройствах
- D3D12 и любое сочетание D3D11, D3D10 или D2D на одном устройстве
- D3D12 и любое сочетание D3D11, D3D10 или D2D на разных устройствах
- D3D12 и GDI или D3D12 и D3D11 и GDI
Причины использования взаимодействия
Существует несколько причин, по которым приложению требуется взаимодействие D3D12 с другими API. Некоторые примеры.
- Добавочный перенос: требуется перенести все приложение из D3D10 или D3D11 в D3D12, при этом оно будет работать на промежуточных этапах процесса переноса (для включения тестирования и отладки).
- Код черного ящика: требуется оставить определенную часть приложения "как есть" при переносе остальной части кода. Например, может не потребоваться переносить элементы пользовательского интерфейса игры.
- Неизменяемые компоненты: необходимо использовать компоненты, не принадлежащие приложению, которые не записываются в целевой объект D3D12.
- Новый компонент: не требуется перенос всего приложения, но требуется использовать новый компонент, написанный с помощью D3D12.
Существует четыре main метода взаимодействия в D3D12:
- Приложение может предоставить компоненту открытый список команд, который записывает некоторые дополнительные команды отрисовки в уже привязанный целевой объект отрисовки. Это эквивалентно предоставлению подготовленного контекста устройства другому компоненту в D3D11 и отлично подходит для добавления пользовательского интерфейса или текста в уже привязанный обратно буфер.
- Приложение может предоставить компоненту очередь команд вместе с требуемым целевым ресурсом. Это эквивалентно использованию API ClearState или DeviceContextState в D3D11 для предоставления чистого контекста устройства другому компоненту. Вот как работают такие компоненты, как D2D.
- Компонент может выбрать модель, в которой он создает список команд, потенциально в параллельном режиме, который приложение отвечает за отправку позже. По крайней мере один ресурс должен быть предоставлен через границы компонентов. Этот же метод доступен в D3D11 с использованием отложенных контекстов, хотя производительность в D3D12 является более предпочтительной.
- Каждый компонент имеет свои собственные очереди и(или) устройства, а приложение и компоненты должны совместно использовать ресурсы и сведения о синхронизации через границы компонентов. Это похоже на устаревшую версию
ISurfaceQueue
и более современную версию IDXGIKeyedMutex.
Различия между этими сценариями заключается в том, что именно является общим между границами компонентов. Предполагается, что устройство является общим, но так как оно в основном без отслеживания состояния, оно на самом деле не имеет отношения. Ключевыми объектами являются список команд, очередь команд, объекты синхронизации и ресурсы. Каждый из них имеет свои собственные осложнения при совместном использовании.
Общий доступ к списку команд
Самый простой метод взаимодействия требует предоставления общего доступа только к списку команд с частью подсистемы. После завершения операций отрисовки владение списком команд возвращается вызывающей стороне. Владение списком команд можно отследить через стек. Так как списки команд являются однопоточными, приложение не может сделать что-то уникальное или инновационное с помощью этого метода.
Совместное использование очереди команд
Вероятно, наиболее распространенный метод для нескольких компонентов совместного использования устройства в одном процессе.
Если очередь команд является единицей общего доступа, необходимо вызвать компонент, чтобы сообщить ему, что все невыполненные списки команд должны быть немедленно отправлены в очередь команд (и все внутренние очереди команд должны быть синхронизированы). Это эквивалентно API-интерфейсу очистки D3D11 и является единственным способом, которым приложение может отправлять собственные списки команд или синхронизировать примитивы.
Общий доступ к примитивам синхронизации
Ожидаемый шаблон для компонента, который работает на собственных устройствах и (или) очередях команд, будет принимать ID3D12Fence или общий дескриптор, и пару UINT64 после начала работы, которую он будет ожидать, а затем второй ID3D12Fence или общий дескриптор, а также пара UINT64, которая будет сигнализировать о завершении всей работы. Этот шаблон соответствует текущей реализации как IDXGIKeyedMutex , так и модели синхронизации модели переворачивания DWM/DXGI.
Совместное использование ресурсов
На сегодняшний день самой сложной частью написания приложения D3D12, которое использует несколько компонентов, является то, как работать с ресурсами, которые совместно используются через границы компонентов. В основном это связано с концепцией состояний ресурсов. Хотя некоторые аспекты проектирования состояния ресурса предназначены для синхронизации внутри командных списков, другие оказывают влияние между списками команд, влияя на макет ресурсов и либо допустимые наборы операций, либо характеристики производительности при доступе к данным ресурсов.
Существует две модели борьбы с этим осложнением, оба из которых связаны, по сути, с контрактом между компонентами.
- Контракт может быть определен разработчиком компонента и задокументирован. Это может быть так же просто, как "ресурс должен находиться в состоянии по умолчанию при начале работы и вернуться в состояние по умолчанию после завершения работы", или может иметь более сложные правила, позволяющие предоставить общий доступ к буферу глубины без принудительного разрешения промежуточной глубины.
- Контракт может быть определен приложением во время выполнения во время совместного использования ресурса через границы компонентов. Он состоит из двух одинаковых фрагментов информации: состояние ресурса, в которое будет находиться, когда компонент начнет использовать его, и состояние, в которое компонент должен оставить его после завершения.
Выбор модели взаимодействия
Для большинства приложений D3D12 совместное использование очереди команд, вероятно, является идеальной моделью. Это позволяет полностью владеть созданием и отправкой работ, без дополнительных затрат на память из-за избыточных очередей и без влияния производительности при работе с примитивами синхронизации GPU.
Общий доступ к примитивам синхронизации требуется, когда компоненты должны иметь дело с различными свойствами очереди, такими как тип или приоритет, или когда общий доступ должен охватывать границы процесса.
Совместное использование или создание списков команд не широко используются сторонними компонентами, но могут широко использоваться в компонентах, которые являются внутренними для игрового движка.
API взаимодействия
В разделе Direct3D 11 на 12 описывается использование большей части api, связанной с видами взаимодействия, описанными в этом разделе.
См. также метод ID3D12Device::CreateSharedHandle , который можно использовать для совместного использования поверхностей между графическими API Windows.