Опубликован: 28.04.2009 | Доступ: свободный | Студентов: 1840 / 107 | Оценка: 4.36 / 4.40 | Длительность: 16:40:00
Специальности: Программист
Лекция 5:

Вершинные шейдеры

< Лекция 4 || Лекция 5: 123456789101112
5.3. Введение в языки Vertex Shader

Языки семейства Vertex Shader предназначены для программирования виртуальных вершинных процессоров, причем каждому языку соответствует своя модель виртуального вершинного процессора. Вообще в основу каждой модели виртуального вершинного процессора положен вполне определенный реальный вершинный процессор, однако на практике данная особенность не играет особой роли, ведь код для виртуального процессора все равно компилируется драйвером видеокарты в машинный код текущего процессора. Кроме того, все эти модели виртуальных процессоров построены на общих принципах. Так что вы вполне можете думать о языках Vertex Shade r как о вариациях на тему языка IL, заточенных под векторные процессоры.

5.3.1. Регистры

Любой виртуальный вершинный процессор содержит набор регистров, напоминающих регистры SSE процессоров архитектуры x86. Большинство регистров (но отнюдь не все) являются векторными регистрами, рассчитанными на хранение четырехмерных векторов в формате с плавающей точкой. Разрядность регистров может быть произвольной, но на практике обычно равна 128-ми битам, то есть на каждый компонент вектора, как правило, отводится 32-бита (рисунок 5.13).

128-битный векторный регистр (32 бита на компонент).

Рис. 5.13. 128-битный векторный регистр (32 бита на компонент).

Данные, с которыми работает виртуальный процессор можно разделить на три большие группы (рисунок 5.14):

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

Рис. 5.14. . Структура графического процессора

Набор регистров существенно варьируется от версии к версии, поэтому чтобы сделать материал менее запутанным мы сосредоточимся исключительно на версии 1.1.

Примечание

Виртуальные процессоры Vertex Shader 1.1 и Pixel Shader 1.1 практически один в один повторяют архитектуру вершинных и пиксельных процессоров видеокарты GeForce3 (NV20) .

Регистры исходных данных

Регистры исходных данных виртуального процессора делятся на две подгруппы (рисунок 5.15).

  • 16 регистров v0, v1… v15 с информацией, специфичной для текущей вершины ( Input Registers ).
  • Не менее 96-ти векторных константных регистров c0, c1 … c95 ... cN с информацией, общей для всех вершин ( Constant Float Registers ).

Оба типа регистров являются векторными регистрами, в которых хранятся 4 компонента с плавающей точкой. У всех современных графических процессоров разрядность этих регистров равна 128 бит, т.е. каждый компонент вектора является 32-х разрядных числом с плавающей точкой. Но эта разрядность не является фиксированной и может измениться у будущих GPU. Кроме того, различные GPU могут несколько по-разному обрабатывать такие граничные ситуации как переполнение или деление на нуль, поэтому код шейдеров не должен быть заточен под фиксированную разрядность 32-бита на компонент.

В регистры v0, v1 … v15 заносятся атрибуты, специфичные для каждой вершины: информация о координатах вершины, ее цвете, размере точки, текстурных координатах и т.п. Связывание входного регистра с конкретным вершинным атрибутом осуществляется посредством специальных директив вида dclxxx, некоторые из которых перечислены в таблице 5.1. Компилятор HLSL отображает входные параметры вершинного шейдера на регистры v0 ... v15, таким образом, директивы dcl_xxx аналогичны семантикам входных параметров вершинного шейдера. В частности, если вы внимательно посмотрите на ассемблерный код, полученный посредством FX Composer в конце раздела 5.2.4, то обнаружите две директивы:

dcl_position v0
dcl_color v1

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

struct VertexInput
{
float3 pos : POSITION; float4
color : COLOR;
};
Таблица 5.1. Некоторые директивы, связывающие входные параметры с атрибутами вершины
Директива Аналогичная семантика HLSL Информация, которая будет заноситься во входной регистр
dcl_position POSITION Координаты вершины
dcl_color COLOR Информация о цвете вершины
dcl_psize PSIZE Размер визуализируемой точки12Управление размером отдельных точек будет рассмотрено в разделе 6.x.
dcl_texcoord TEXCOORD Текстурные координаты вершины
 Регистры исходных данных

увеличить изображение
Рис. 5.15. Регистры исходных данных

Константные регистры, как следует из названия, используются для хранения различных констант. Задание константы осуществляется посредством директивы def:

def {константный регистр}, {компонент 0}, {компонент 1}, {компонент 2}, {компонент 3}

Например, следующая директива перед началом обработки вершин шейдером заносит в константный регистр c0 вектор (0.333333343, 1, 0, 0).

def c0, 0.333333343, 1, 0, 0

Код данной директивы взят из ассемблерного листинга эффекта черно-белой закраски. Нетрудно догадаться, что компонент вектора со значением 0.333333343 впоследствии используется компилятором для вычисления выражения (input.color.r+input.color.g+input.color.b)/3.0. Так же логично предположить, что компонент со значением 1 используется при добавлении четвертого компонента к координатам вектора:

output.pos = float4(input.pos, 1.0f);

Количество константных регистров зависит от видеокарты, однако поддержка видеокартой Vertex Shader 1.1 гарантирует наличие не менее 96 константных регистров. Точное количество константных регистров вершинных процессоров текущей видеокарты может быть получено посредством метода GraphicsDeviceCapabilities.MaxVertexShaderConstants класса GraphicsDevice. В таблице 5.2 приведена информация о количестве константных регистров у наиболее распространенных видеокарт.

Примечание

Число физических константных регистров может несколько превышать значение, возвращаемое GraphicsDeviceCapabilities.MaxVertexShaderConstants. Дополнительные регистры, как правило, используются драйвером для внутренних нужд, например, при эмуляции фиксированного графического конвейера из прошлых версий DirectX.

Таблица 5.2. Количество константных регистров у некоторых GPU
GPU Количество константных регистров у вершинных процессоров
NV2x 96
NV3x 256
NV4x 256
G7x 256
R2xx 192
R3xx 256
R4xx 256
Intel GMA 9xx13Вершинные шейдеры на Intel GMA 9xx и GMA 3000 выполняются посредством CPU. 8192
Intel GMA 3000 8192

Забегая вперед, стоит отметить, что значения константных регистров могут изменяться приложением, что делает их идеальным средством для передачи параметров в вершинный шейдер (см. раздел 5.4).

Регистры общего назначения

Регистры общего назначения используются для хранения операндов и результатов команд, а так же для адресации массива константных регистров. Виртуальный вершинный процессор Vertex Shader 1.1 предполагает наличие двух типов временных регистров (рисунок 5.16):

  • 12 временных векторных регистров r0, r1 … r11 для хранения промежуточных результатов вычислений ( Temporary Registers ).
  • Один скалярный адресный регистр a0, используемый для косвенной адресации константных регистров ( Address Register ).
 Регистры общего назначения

Рис. 5.16. Регистры общего назначения

Временные регистры являются аналогом регистров SSE процессоров архитектуры x86, и поэтому вряд ли нуждаются в каких-либо комментариях. Адресный регистр хранит смещение, которое может применяться при обращении к константному регистру в режиме косвенной адресации. Например, если адресный регистр содержит значение 2, то при обращении в режиме косвенной адресации к константному регистру c5 в реальности произойдет обращение к регистру c7. В ассемблерном коде такое обращение будет выглядеть как c[a0.x + 5] .

Примечание

Примитивная косвенная адресация, используемая в шейдерах, довольно сильно напоминает косвенную адресацию первых программируемых калькуляторов вроде HP-11C.

Регистры итоговых результатов

Данная группа регистров используется для передачи результатов работы вершинного шейдера дальше по графическому конвейеру: сначала полученные результаты интерполируются вдоль поверхности примитива, а затем поступают на вход пиксельного шейдера. Так как первые версии вершинных и пиксельных шейдеров предполагалось применять только для визуализации примитивов с использованием незначительных вариаций классических алгоритмов, все выходные регистры Vertex Shader 1.1 являются специализированными и предназначены для хранения определенного типа данных (рисунок 5.17):

  • Векторный регистр oPos трансформированных координат вершины ( Position Register ).
  • Два векторных регистра oD0 и oD1 цветов вершины ( Color Registers ). Изначально предполагалось, что регистр oD0 будет использоваться для хранения основного цвета вершины, а регистр oD1 - цвета блика.
  • Восемь векторных регистров oT0, oT1, oT2, oT3, oT4, oT5, oT6, oT7 текстурных координат вершины ( Texture Coordinate Register ). Таким образом, с каждой вершиной может быть связано до восьми текстурных координат.
  • Скалярный регистр oPts размера точки ( Point Size Register ). Используется для коррекции размера точки при визуализации массива точек ( PrimitiveType.PointList ).
  • Скалярный регистр oFog, задающий плотность тумана в окрестностях данной вершины ( Fog Register ).
 Выходные регистры

увеличить изображение
Рис. 5.17. Выходные регистры

Первые GPU в точности следовали спецификации Vertex Shader 1.1, поэтому их выходные регистры были жестко заточены под хранение специализированных типов данных. Соответственно, любая попытка использования данных регистров не по прямому назначению была чревата различными побочными эффектами вроде переполнения или потери точности. Но по мере развития графических процессоров данная специализация становилась все более условной: все современные GPU начиная с NV4x и R5xx содержат универсальные выходные регистры, на которые отображаются выходные регистры виртуального вершинного процессора.

Вероятно, вы уже обратили внимание, что названия и назначения выходных регистров удивительно напоминают семантики HLSL выходных данных вершинного шейдера. Это не случайно: исторически семантики предназначались именно для привязки выходных данных вершинного шейдера HLSL к регистрам виртуального вершинного процессора (таблица 5.3). И только потом, по мере развития GPU их функция свелась к банальной стыковке между собой выходных данных вершинных и входных данных пиксельных шейдеров. Таким образом, для написания на языке HLSL качественных шейдеров для старых GPU очень важно представлять себе архитектуру виртуального вершинного процессора и его физическую реализацию.

Таблица 5.3. Соответствие между выходными регистрами вершинного процессора и семантиками HLSL
Регистр Семанитики
oPos POSITION
oD0 COLOR, COLOR0
oD1 COLOR1
oT0 TEXCOORD, TEXCOORD0
0T1 TEXCOORD1
oT2 TEXCOORD2
oT3 TEXCOORD3
oT4 TEXCOORD4
oT5 TEXCOORD5
oT6 TEXCOORD6
oT7 TEXCOORD7
oPts PSIZE
oFog FOG

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

< Лекция 4 || Лекция 5: 123456789101112
Андрей Леонов
Андрей Леонов

Reference = add reference, в висуал студия 2010 не могу найти в вкладке Solution Explorer, Microsoft.Xna.Framework. Его нету.