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