Буферы в Direct3D10 и Direct3D11 - SIISII/DirectX GitHub Wiki

Виды буферов

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

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

По своему назначению буферы делятся на буферы вершин, буферы индексов и буферы констант.

Для работы с любым видом буферов в Direct3D10 используется интерфейс ID3D10Buffer, а создание буфера выполняется методом ID3D10Device::CreateBuffer. В Direct3D11 для тех же целей используются интерфейс ID3D11Buffer и метод ID3D11Device::CreateBuffer.

Вершинные буферы

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

В простейшем случае для каждой вершины могут указываться лишь её координаты (P — position), и тогда вершинный буфер будет иметь такой логический вид:

В более сложном случае для каждой вершины могут задаваться, например, её координаты (P), нормаль (N) и соответствующие данной вершине текстурные координаты (T), причём в буфере может содержаться больше вершин, чем используется в данном конкретном случае, поэтому Direct3D даёт возможность указать, сколько вершин будет использоваться и откуда они начинаются:

Координаты и нормали вершин обычно задаются компонентами типа DXGI_FORMAT_R32G32B32_FLOAT (то есть тремя 32-разрядными вещественными числами), текстурные координаты — компонентами типа DXGI_FORMAT_R32G32_FLOAT (двумя 32-разрядными вещественными числами). Тем не менее, могут использоваться и другие типы, что определяется ожиданиями вершинного шейдера, на вход которого поступают эти данные.

Вершинных буферов может быть несколько, в этом случае они используются совместно (в каждом буфере хранится какая-то часть информации о вершинах), а объект формата описывает содержимое всех используемых буферов одновременно. В зависимости от уровня аппаратной поддержки допускается использование до 16 (DirectX 10 и ниже) или до 32 (DirectX 10.1 и выше) вершинных буферов.

Вершинные буферы привязываются к стадии сбора исходных данных. Последняя выбирает из них необходимую информацию и передаёт её в качестве входных параметров вершинному шейдеру.

Как уже упоминалось, для использования вершинного буфера необходимо указать соответствующий объект формата, представленный интерфейсом ID3D10InputLayout или ID3D11InputLayout; этот объект создаётся вызовом метода ID3D10Device::CreateInputLayout или ID3D11Device::CreateInputLayout. После создания объекта формата производится его привязка к стадии сбора исходных данных вызовом метода ID3D10Device::IASetInputLayout или ID3D11DeviceContext::IASetInputLayout. Сами вершинные буферы привязываются методами ID3D10Device::IASetVertexBuffers или ID3D11DeviceContext::IASetVertexBuffers.

Индексные буферы

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

Индексный буфер может быть представлен следующим образом:

Для использования индексного буфера при рендеринге задаются три величины:

  • смещение (offset) задаёт количество байтов от начала буфера до начала области, которая фактически привязывается к графическому конвейеру;
  • положение начального индекса (start index location) указывает, с какой позиции начинаются индексы, используемые в данной конкретной операции рендеринга;
  • количество индексов (index count) задаёт число индексов, а соответственно, вершин, которые участвуют в данной операции рендеринга.

Шейдеры не имеют прямого доступа к индексному буферу: он привязывается к стадии сбора исходных данных и используется ей для выборки из вершинных буферов информации, передаваемой вершинному шейдеру. Индексный буфер может быть лишь один; если он отсутствует, примитивы образуются из вершин в порядке следования последних в вершинном буфере.

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

Заметим, что эта возможность гарантированно доступна лишь на оборудовании уровня DirectX 10, более ранние графические процессоры не обязаны её поддерживать.

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

Индексный буфер привязывается к конвейеру вызовом метода ID3D10Device::IASetIndexBuffer или ID3D11DeviceContext::IASetIndexBuffer. Чтобы он был задействован при рендеринге, последний должен запускаться вызовом метода с индексированием, например, ID3D11DeviceContext::DrawIndexed.

Константные буферы

Константный буфер, впервые введённый в Direct3D10, содержит константы, которые могут использоваться шейдерами:

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

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

Константные буферы могут привязываться к любым шейдерным стадиям конвейера, для чего используются следующие методы:

Каждая стадия может использовать до 15 буферов, а каждый буфер может содержать до 4096 констант. Для выборки информации из константного буфера шейдер должен использовать соответствующую функцию, например, Load.

Если ОС поддерживает Direct3D версии 11.1 или более поздний, возможно создание константного буфера размером более 64 Кбайтов, т. е. имеющего более 4096 элементов, причём она поддерживается для любого функционального уровня устройства (для уровней, начиная с 10, такая поддержка обеспечивается соответствующими версиями драйверов; для устройств уровней 9.1—9.3 соответствующее поведение эмулируется самим Direct3D). Однако при привязке к конвейеру необходимо будет указать, какая именно часть буфера (не свыше 4096 элементов) будет использоваться.

Создание буферов

Для создания буфера используется метод ID3D10Device::CreateBuffer или ID3D11Device::CreateBuffer; принципиальных различий они не имеют.

Метод ID3D11Device::CreateBuffer имеет следующий прототип:

HRESULT  ID3D11Device::CreateBuffer(
    const D3D11_BUFFER_DESC       *pDesc,
    const D3D11_SUBRESOURCE_DATA  *pInitialData,
    ID3D11Buffer*                 *ppBuffer);
  • pDesc — адрес структуры типа D3D11_BUFFER_DESC, содержащей описание параметров буфера (см. ниже).

  • pInitialData — адрес структуры типа D3D11_SUBRESOURCE_DATA, служащей для инициализации буфера. Если требуется лишь создать буфер, но не заносить в него данные, этот параметр должен быть нулевым. Нельзя не указывать эту структуру, если создаётся неизменяемый буфер (с флагом D3D11_USAGE_IMMUTABLE).

  • ppBuffer — адрес переменной, в которую будет помещён указатель на интерфейс созданного буфера ID3D11Buffer. Если этот параметр равен nullptr, метод проверяет другие параметры, но буфер не создаёт; в случае, если все параметры корректны, будет возвращён результат S_FALSE.

Структура D3D11_BUFFER_DESC, описывающая характеристики буфера, содержит следующие поля.

  • ByteWidth — размер буфера в байтах.

  • Usage — один из атрибутов буфера, задающий способ его использования; возможные значения определены в перечислении D3D11_USAGE.

  • BindFlags — атрибуты привязки ресурса к конвейеру, определённые перечислением D3D11_BIND_FLAG.

  • CPUAccessFlags — атрибуты доступа к ресурсу со стороны центрального процессора, задаваемые комбинацией флагов из перечисления D3D11_CPU_ACCESS_FLAG. Если доступ со стороны ЦП не нужен, следует указывать нулевое значение: это повышает производительность.

  • MiscFlags — различные атрибуты ресурса, задаваемые набором флагов из перечисления D3D11_RESOURCE_MISC_FLAG.

  • StructureByteStride — размер в байтах каждого элемента структурного буфера. Размер формата, указываемый при создании представления буфера, должен совпадать с этой величиной.

    Заметим, что вершинный буфер по своей сути практически всегда является структурным, так как вершина описывается некоей структурой. Однако этот параметр относится к буферам, используемым в качестве шейдерных ресурсов и привязываемым к конвейеру через представления, а не к вершинным буферам, привязываемым напрямую.