Сравнение конвейера шейдеров OpenGL ES 2.0 с Direct3D

Важные API

Концептуально конвейер шейдеров Direct3D 11 очень похож на таковой в OpenGL ES 2.0. Однако в терминах проектного решения API главными компонентами для создания и управления стадиями шейдеров являются части двух основных интерфейсов, ID3D11Device1 и ID3D11DeviceContext1. В этом разделе делается попытка сопоставить общие шаблоны API конвейера шейдеров OpenGL ES 2.0 с их эквивалентами Direct3D 11 в этих интерфейсах.

Обзор конвейера шейдеров Direct3D 11

Объекты шейдера создаются с помощью методов в интерфейсе шейдера ID3D11Device1, таких как ID3D11Device1::CreateVertexShader и ID3D11Device1::CreatePixelShader.

Конвейер графики Direct3D 11, который управляется экземплярами интерфейса ID3D11DeviceContext1, имеет следующие стадии.

  • Стадия сборщика входных данных На стадии сборщика входных данных конвейеру поставляются данные (треугольники, линии и точки). Методы ID3D11DeviceContext1, поддерживающие эту стадию, имеют префикс "IA".
  • Стадия вершинного шейдера. На стадии вершинного шейдера обрабатываются вершины, обычно с выполнением таких операций, как преобразования, скиннинг и освещение. Вершинный шейдер всегда принимает одну входную вершину и создает одну выходную вершину. Методы ID3D11DeviceContext1, поддерживающие эту стадию, имеют префикс "VS".
  • Стадия потокового вывода. На стадии потокового вывода поток данных примитивов из конвейера направляется в память на своем пути к средству прорисовки. Поток данных может быть выходным или передаваться в средство программной прорисовки. Поток данных, выведенный в память, можно снова вернуть в конвейер как входные данные или обратно считать из ЦП. Методы ID3D11DeviceContext1, поддерживающие эту стадию, имеют префикс "SO".
  • Стадия средства программной прорисовки. Средство программной прорисовки обрезает примитивы, готовит их для построителя текстуры и определяет, как вызывать построители текстуры. Вы можете отключить растеризацию, указав конвейеру, что не существует пиксельного шейдера (задайте для этапа шейдера пикселей значение NULL с ID3D11DeviceContext::P SSetShader) и отключите тестирование глубины и набора элементов (задайте для параметра DepthEnable и StencilEnable значение FALSE в D3D11_DEPTH_STENCIL_DESC). После отключения не будут обновляться связанные с растеризацией счетчики конвейера.
  • Стадия построителя текстуры. На стадии построителя текстуры принимаются интерполированные данные для примитива и генерируются данные для пикселей, такие как цвет. Методы ID3D11DeviceContext1, поддерживающие эту стадию, имеют префикс "PS".
  • Стадия слияния вывода. На стадии слияния вывода выходные данные различных типов (значения построителя текстуры, информация о глубине и наборе элементов) объединяются с содержимым целевого объекта отрисовки и буферов глубины и набора элементов для создания окончательного результата конвейера. Методы ID3D11DeviceContext1, поддерживающие эту стадию, имеют префикс "OM".

(Существуют также стадии для шейдеров геометрии, шейдеров поверхности, тесселяторов и шейдеров областей, но так как у них нет аналогов в OpenGL ES 2.0, поэтому здесь мы их обсуждать не будем.) Полный перечень методов для этих стадий см. на справочных страницах ID3D11DeviceContext и ID3D11DeviceContext1. ID3D11DeviceContext1 является расширением ID3D11DeviceContext для Direct3D 11.

Создание шейдера

В Direct3D ресурсы шейдера не создаются до компиляции и их загрузки. Ресурсы создаются, когда загружается HLSL. Таким образом, нет непосредственно аналогичной функции glCreateShader, которая создает инициализированный ресурс шейдера определенного типа (например, GL_VERTEX_SHADER или GL_FRAGMENT_SHADER). Вместо этого шейдеры создаются после загрузки HLSL с конкретными функциями, такими как ID3D11Device1::CreateVertexShader и ID3D11Device1::CreatePixelShader, которые принимают тип и компилированный HLSL в качестве параметров.

OpenGL ES 2.0 Direct3D 11
glCreateShader Вызовите ID3D11Device1::CreateVertexShader и ID3D11Device1::CreatePixelShader после успешной загрузки компилированного объекта шейдера (CSO), передавая их в этот объект CSO как буфер.

 

Компиляция шейдера

Шейдеры Direct3D должны быть предварительно скомпилированы как скомпилированные cso-файлы в приложениях универсальная платформа Windows (UWP) и загружаться с помощью одного из API среда выполнения Windows файлов. (Классические приложения могут компилировать шейдеры из текстовых файлов или строк во время выполнения.) CSO-файлы создаются из любых HLSL-файлов, являющихся частью проекта Microsoft Visual Studio, и сохраняют те же имена только с расширением CSO-файла. Обеспечивайте их включение в ваш пакет при отправке!

OpenGL ES 2.0 Direct3D 11
glCompileShader Недоступно Компилируйте шейдеры в CSO-файлы в Visual Studio и включайте их в пакет.
Использование glGetShaderiv для получения состояния компиляции Недоступно Проверяйте по выходным результатам компилятора Visual Studio FX (FXC), имели ли место ошибки при компиляции. Если компиляция прошла успешна, создается соответствующий CSO-файл.

 

Загрузка шейдера

Как показано в разделе о создании шейдера, Direct3D 11 создает шейдер, когда соответствующий CSO-файл загружается в буфер и передается в один из методов из следующей таблицы.

OpenGL ES 2.0 Direct3D 11
ShaderSource Вызовите ID3D11Device1::CreateVertexShader и ID3D11Device1::CreatePixelShader после успешной загрузки компилированного объекта шейдера.

 

Установка конвейера

В OpenGL ES 2.0 есть объект "программа-шейдер", который содержит несколько шейдеров для выполнения. Отдельные шейдеры подключены к объекту программы-шейдера. Однако в Direct3D 11 вы работаете непосредственно с контекстом отрисовки (ID3D11DeviceContext1) и создаете шейдеры в нем.

OpenGL ES 2.0 Direct3D 11
glCreateProgram Недоступно Direct3D 11 не использует абстракцию объекта программы-шейдера.
glLinkProgram Недоступно Direct3D 11 не использует абстракцию объекта программы-шейдера.
glUseProgram Недоступно Direct3D 11 не использует абстракцию объекта программы-шейдера.
glGetProgramiv Используйте созданную вами ссылку на ID3D11DeviceContext1.

 

Создайте экземпляр ID3D11DeviceContext1 и ID3D11Device1 с помощью статического метода D3D11CreateDevice.

Microsoft::WRL::ComPtr<ID3D11Device1>          m_d3dDevice;
Microsoft::WRL::ComPtr<ID3D11DeviceContext1>  m_d3dContext;

// ...

D3D11CreateDevice(
  nullptr, // Specify nullptr to use the default adapter.
  D3D_DRIVER_TYPE_HARDWARE,
  nullptr,
  creationFlags, // Set set debug and Direct2D compatibility flags.
  featureLevels, // List of feature levels this app can support.
  ARRAYSIZE(featureLevels),
  D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for UWP apps.
  &device, // Returns the Direct3D device created.
  &m_featureLevel, // Returns feature level of device created.
  &m_d3dContext // Returns the device's immediate context.
);

Установка окон просмотра

Установка окна просмотра Direct3D 11 очень похожа на установку окна просмотра в OpenGL ES 2.0. В Direct3D 11 вызовите ID3D11DeviceContext::RSSetViewports с настроенным CD3D11_VIEWPORT.

Direct3D 11. Установка окна просмотра.

CD3D11_VIEWPORT viewport(
        0.0f,
        0.0f,
        m_d3dRenderTargetSize.Width,
        m_d3dRenderTargetSize.Height
        );
m_d3dContext->RSSetViewports(1, &viewport);
OpenGL ES 2.0 Direct3D 11
glViewport CD3D11_VIEWPORT, ID3D11DeviceContext::RSSetViewports

 

Настройка вершинных шейдеров

Настройка вершинного шейдера в Direct3D 11 выполняется, когда шейдер загружен. Универсальные объекты передаются как буферы констант с помощью ID3D11DeviceContext1::VSSetConstantBuffers1.

OpenGL ES 2.0 Direct3D 11
glAttachShader ID3D11Device1::CreateVertexShader
glGetShaderiv, glGetShaderSource ID3D11DeviceContext1::VSGetShader
glGetUniformfv, glGetUniformiv ID3D11DeviceContext1::VSGetConstantBuffers1.

 

Настройка построителей текстуры

Настройка построителя текстуры в Direct3D 11 выполняется, когда шейдер загружен. Универсальные объекты передаются как буферы констант с помощью ID3D11DeviceContext1::PSSetConstantBuffers1..

OpenGL ES 2.0 Direct3D 11
glAttachShader ID3D11Device1::CreatePixelShader
glGetShaderiv, glGetShaderSource ID3D11DeviceContext1::PSGetShader
glGetUniformfv, glGetUniformiv ID3D11DeviceContext1::PSGetConstantBuffers1.

 

Генерация окончательных результатов

Когда конвейер завершает работу, результаты стадий шейдеров извлекаются в задний буфер. В Direct3D 11, так же как и в Open GL ES 2.0, это включает вызов команды рисования для вывода результатов в виде цветовой карты в задний буфер с последующей отправкой содержимого заднего буфера на экран.

OpenGL ES 2.0 Direct3D 11
glDrawElements ID3D11DeviceContext1::D raw, ID3D11DeviceContext1::D rawIndexed (или другие методы Draw* в ID3D11DeviceContext1).
eglSwapBuffers IDXGISwapChain1::Present1

 

Перенос GLSL в HLSL

Языки GLSL и HLSL не имеют существенных отличий, за исключением поддержки сложных типов и некоторых отличий в общем синтаксисе. Многие разработчики считают простейшим способом переноса альтернативное именование общих инструкций и определений OpenGL ES 2.0 для совмещения имен с их эквивалентами в HLSL. Отметим, что Direct3D использует версию модели шейдера для выражения набора функций HLSL, поддерживаемых графическим интерфейсом, тогда как OpenGL имеет другую спецификацию версии для HLSL. Следующая таблица должна дать вам примерное представление о наборе функций языка шейдера, определенных для Direct3D 11 и OpenGL ES 2.0, в терминах версии другого языка.

Язык шейдера Версия функций GLSL Модель шейдера Direct3D
Direct3D 11 HLSL ~4.30. SM 5.0
GLSL ES для OpenGL ES 2.0 1.40. Другие (более старые) реализации GLSL ES для OpenGL ES 2.0 могут использовать версии от 1.10 до 1.30. Проверьте исходный код с помощью glGetString(GL_SHADING_LANGUAGE_VERSION) или glGetString(SHADING_LANGUAGE_VERSION), чтобы определить его. ~SM 2.0

 

Подробно о различиях двух языков шейдера, а также об общем сопоставлении синтаксиса см. в справочнике по сопоставлению GLSL с HLSL.

Перенос встроенных элементов OpenGL в семантику HLSL

Семантика Direct3D 11 HLSL представляет строки, которые аналогично универсальным именам и именам атрибутов используются для идентификации значения, передаваемого между приложением и программой-шейдером. Хотя можно использовать любые из возможных строк, рекомендуется использовать строки, указывающие на использование, такие как POSITION или COLOR. Вы назначаете такую семантику при построении буфера констант или макета входного буфера. Можно также добавлять к семантике цифру от 0 до 7, чтобы использовать отдельные регистры для сходных значений. Например: COLOR0, COLOR1, COLOR2...

Семантика с префиксом "SV_" — это семантика системных значений, записанная программой шейдера; ваше приложение (работающее на ЦП) не может изменить их. Обычно они содержат значения, которые являются входными или выходными для другой стадии шейдера в конвейере графики или полностью генерируются ЦП.

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

Перечислим некоторые сопоставления для общих встроенных элементов шейдера OpenGL ES 2.0:

Системное значение OpenGL Используйте семантику HLSL
gl_Position POSITION(n) для данных буфера вершин. SV_POSITION обеспечивает положение пикселей для шейдера пикселей и не может быть записано приложением.
gl_Normal NORMAL(n) для данных нормали, предоставленных буфером вершин.
gl_TexCoord[n] TEXCOORD(n) для данных UV-координат (ST в некоторой документации OpenGL) текстуры, которые предоставляются шейдером.
gl_FragColor COLOR(n) для данных цветов RGBA, которые предоставляются шейдеру. Отметим, что они обрабатываются аналогично данным координат. Семантика просто помогает понять, что они являются данными цветов.
gl_FragData[n] SV_Target[n] для записи из пиксельного шейдера в целевую текстуру или другой буфер пикселей.

 

Метод, с помощью которого вы кодируете семантику, не совпадает с методом, используемым компонентами OpenGL ES 2.0. В OpenGL вы можете получить доступ ко многим внутренним элементам без настройки или объявления. В Direct3D для использования конкретной семантики необходимо объявить поле в определенном буфере констант. Можно также объявить возвращаемое значение метода main() шейдера.

Приведем пример семантики, используемой в определении буфера констант:

struct VertexShaderInput
{
  float3 pos : POSITION;
  float3 color : COLOR0;
};

// The position is interpolated to the pixel value by the system. The per-vertex color data is also interpolated and passed through the pixel shader. 
struct PixelShaderInput
{
  float4 pos : SV_POSITION;
  float3 color : COLOR0;
};

Этот код определяет пару простых буферов констант

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

// A pass-through for the (interpolated) color data.
float4 main(PixelShaderInput input) : SV_TARGET
{
  return float4(input.color,1.0f);
}

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

Подробнее об использовании семантики с Direct3D: Семантики HLSL.