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


Обзор привязки ресурсов

Ключи для понимания привязки ресурсов в DirectX 12 — это основные понятия дескрипторов, таблиц дескриптора, кучи дескриптора и корневых подписей.

Ресурсы и графический конвейер

Ресурсы шейдера (например, текстуры, константные таблицы, изображения, буферы и т. д.) не привязаны непосредственно к конвейеру шейдера; Вместо этого они ссылаются через дескриптор . Дескриптор — это небольшой объект, содержащий сведения о одном ресурсе.

Дескрипторы группируются для формирования таблиц дескриптора . Каждая таблица дескриптора хранит сведения о одном диапазоне типов ресурсов. Существует множество различных типов ресурсов. Наиболее распространенные ресурсы:

  • Представления буфера констант (CBV)
  • Неупорядоченные представления доступа (UAV)
  • Представления ресурсов шейдера (SRV)
  • Пробоотборники

Дескрипторы SRV, UAV и CBV можно объединить в одну таблицу дескрипторов.

Графические и вычислительные конвейеры получают доступ к ресурсам, обращаясь к таблицам дескрипторов по индексу.

Таблицы дескрипторов хранятся в куче дескрипторов . Кучи дескрипторов в идеале должны содержать все дескрипторы (в таблицах дескрипторов) для рендеринга одного или нескольких кадров. Все ресурсы будут храниться в кучах пользовательского режима.

Другая концепция заключается в том, что корневой сигнатуры. Корневая сигнатура — это соглашение о привязке, определенное приложением, которое используется шейдерами для поиска необходимых ресурсов. Корневая сигнатура может хранить следующее:

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

Другими словами, корневая сигнатура обеспечивает оптимизацию производительности, подходящую для небольших объемов данных, которые меняются при каждом вызове отрисовки.

Конструкция Direct3D 12 для привязки отделяет ее от других задач, таких как управление памятью, управление временем существования объектов, отслеживание состояния и синхронизация памяти (см. различия в модели привязки от Direct3D 11). Привязка Direct3D 12 предназначена для низкой нагрузки и оптимизирована для вызовов API, которые выполняются чаще всего. Он также масштабируется в низкой части высокого уровня оборудования и масштабируется с более старых (более линейный конвейер Direct3D 11) к более новым (более параллельным) подходам к программированию графического двигателя.

Типы ресурсов и представления

Типы ресурсов совпадают с Direct3D 11, а именно:

  • Texture1D и Texture1DArray
  • Texture2D и Texture2DArray, Texture2DMS, Texture2DMSArray
  • Текстура3D
  • Буферы (типизированные, структурированные и необработанные)

Представления ресурсов похожи, но слегка отличаются от Direct3D 11, были добавлены представления вершин и буферов индексов.

  • Представление буфера констант (CBV)
  • Неупорядоченное представление доступа (UAV)
  • Представление ресурсов шейдера (SRV)
  • Пробоотборники
  • Представление целевого объекта отрисовки (RTV)
  • Представление глубины и трафарета (DSV)
  • Представление буфера индекса (IBV)
  • Представление буфера вершин (VBV)
  • Представление потокового вывода (SOV)

Только первые четыре из этих представлений на самом деле видны шейдерам. Обратите внимание на дескрипторные кучи видимые шейдеров и на не видимые шейдерами дескрипторные кучи.

Управление потоком привязки ресурсов

Фокусируясь только на корневых подписях, корневых дескрипторах, корневых константах, таблицах дескриптора и кучах дескриптора, поток логики отрисовки для приложения должен быть похож на следующий:

  • Создайте один или несколько корневых объектов подписи — по одному для каждой конфигурации привязки, необходимой приложению.
  • Создайте шейдеры и состояние конвейера с объектами корневой подписи, с которыми они будут использоваться.
  • Создайте одну (или, если необходимо, несколько) кучи дескрипторов, которые будут содержать все дескрипторы SRV, UAV и CBV для каждого кадра отрисовки.
  • Инициализировать кучу дескриптора с дескрипторами, где это возможно для наборов дескрипторов, которые будут повторно использоваться во многих кадрах.
  • Для каждого кадра, подлежащего отрисовке:
    • Для каждого списка команд:
      • Установите текущую корневую сигнатуру для использования (и при необходимости изменения во время отрисовки, что требуется редко).
      • Обновите константы корневой подписи и (или) дескрипторы корневой подписи для нового представления (например, проекций мира или представления).
      • Для каждого элемента для рисования:
        • Задайте новые дескрипторы в кучах дескрипторов, если необходимо для отрисовки каждого объекта. Для кучи дескриптора, видимого шейдером, приложение должно использовать пространство кучи дескриптора, на которое еще не ссылается отрисовка, которая может находиться в полете, например линейное выделение пространства через кучу дескриптора во время отрисовки.
        • Обновите корневую сигнатуру указателями на необходимые области кучи дескрипторов. Например, одна таблица дескриптора может указывать на некоторые статические дескрипторы (не меняющиеся) инициализированные ранее, а другая таблица дескриптора может указывать на некоторые динамические дескрипторы, настроенные для текущей отрисовки.
        • Обновите константы корневой подписи и (или) дескрипторы корневой подписи для отрисовки каждого элемента.
        • Задайте состояние конвейера для элемента, который необходимо отрисовать (только при необходимости), совместимо с привязанной в данный момент корневой сигнатурой.
        • Рисовать
      • Повторите (следующий элемент)
    • Повторите (следующий список команд)
    • Только после того, как GPU завершит работу с любой памятью, которая больше не будет нужна, ее можно освободить. Ссылки дескрипторов на него не нужно удалять, если дополнительная отрисовка, использующая эти дескрипторы, не отправляется. Таким образом, последующие отрисовки могут указывать на другие области в кучах дескриптора или устаревшие дескрипторы можно перезаписать с допустимыми дескрипторами для повторного использования пространства кучи дескриптора.
  • Повторите (следующий кадр)

Обратите внимание, что другие типы дескрипторов, представления рендер-целей (RTV), представления глубины и трафарета (DSV), представления буфера индексов (IBV), представления буфера вершин (VBV) и представления вывода потока (SOV) управляются по-разному. Драйвер управляет версиями набора дескрипторов, связанных с каждой отрисовкой во время записи списка команд (аналогично тому, как привязки корневой подписи версионируются оборудованием и/или драйвером). Это отличается от содержимого куч дескрипторов, видимых шейдером, для которых приложение должно вручную выделять ресурсы в рамках кучи, так как оно ссылается на различные дескрипторы между вызовами отрисовки. Управление версиями содержимого кучи, видимое шейдером, остается в приложении, так как оно позволяет приложениям выполнять такие действия, как повторное использование дескрипторов, которые не изменяются, или использовать большие статические наборы дескрипторов и использовать индексирование шейдеров (например, по идентификатору материала), чтобы выбрать дескрипторы для использования из кучи дескриптора или использовать сочетания методов для различных наборов дескрипторов. Оборудование не оснащено для обработки этого типа гибкости для других типов дескриптора (RTV, DSV, IBV, VBV, SOV).

Субвыделение

В Direct3D 12 приложение имеет низкоуровневый контроль над управлением памятью. В более ранних версиях Direct3D, включая Direct3D 11, было бы одно выделение на ресурс. В Direct3D 12 приложение может использовать API для выделения большого блока памяти, который больше, чем требуется любому отдельному объекту. После этого приложение может создать дескрипторы для указания разделов этого большого блока памяти. Этот процесс принятия решений о размещении (размещения небольших блоков внутри большого блока) называется подраспределением. Включение приложения для этого может повысить эффективность использования вычислений и памяти. Например, переименование ресурсов становится устарелым. Вместо этого приложения могут использовать ограждения, чтобы определить, когда используется определенный ресурс, а когда нет, через создание ограждений при выполнении списка команд, где требуется использование данного ресурса.

Освобождение ресурсов

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

Ожидание рендеринга кадров, вероятно, самый грубый способ убедиться, что процесс на GPU завершён. При более точном уровне можно снова использовать барьеры — когда команда записывается в список команд, за завершением которого вы хотите следить, вставьте барьер сразу после нее. Затем можно выполнять различные операции синхронизации с забором. Вы отправляете новую работу (списки команд), которая ожидает завершения указанного барьера на GPU, что указывает на то, что все предыдущие задачи завершены, или можете запросить, чтобы событие ЦП было сгенерировано после прохождения барьера (при этом приложение может ожидать этого с помощью спящего потока). В Direct3D 11 это было EnqueueSetEvent().