Direct3D 10

Direct3D 10 — набор API функций для взаимодействия с видеокартой; поддерживается аппаратно видеокартами класса NV GeForce 8x00, ATI Radeon 2x00 и выше.
Direct3D 10 (D3D10) — компонент интерфейса программирования приложений (англ. API) DirectX 10, 10-я версия Direct3D, преемник Direct3D 9. Direct3D 10 обеспечивает функции для взаимодействия операционной системы и приложений с драйверами видеокарты. Эти функции привязаны к операционной системе в линейке Windows и доступны в Windows Vista и Windows 7 . Частично D3D10 работает на видеокартах уровня Direct3D 9.

Официальная финальная версия вышла 10 ноября 2006 года в составе Windows Vista.

Далее приведены ключевые особенности и отличия от Direct3D версии 9.

Возможности и особенности

Новая модель драйвера 
В Windows Vista совершенно новая модель драйвера — WDDM (Windows Display Driver Model, ранее называемая LDDM — Longhorn Display Driver Model) — серьезное изменение в модели видеодрайвера со времен появления аппаратного ускорения. В XDDM (Windows XP Display Driver Model) каждый вызов DirectX добавлял указатель команды (токен) в буфер команд в независимом от видеокарты формате. Когда DX Runtime решал, что буфер достаточно заполнен, вызывалась функция драйвера (в режиме ядра), которая получала этот буфер. После этого драйвер разбирал этот буфер и передавал данные видеокарте. То есть никаких функций драйвера в пользовательском режиме не было. Развитие видеокарт и, как следствие, усложнение буфера команд привело к тому, что драйвер стал немыслимо большим и в случае любой ошибки провоцировал BSOD. Также в XDDM у операционной системы нет способов установления приоритета, управления видеопамятью, планирования вызовов DX, что затрудняет разделение видеокарты между несколькими процессами — причина «потери устройства».

В новой модели драйвера сделано разделение между пользовательской и работающей в режиме ядра частью драйвера. Все вызовы DX напрямую идут в пользовательский драйвер, который готовит сразу буфер с содержимым, зависящим от оборудования. Этот буфер передаёт данные в ядро операционной системы, откуда они идут на видеокарту. Таким образом вся тяжёлая работа выполняется в пользовательской части, а в ядре —  только пересылка собранного буфера на видеокарту. Как итог, если пользовательский драйвер упадет, ничего страшного не случится — закроется конкретное приложение, но не произойдёт BSOD. Кроме того, теперь драйвер решает когда и сколько вызовов функций ядра делать. Также DX Runtime становится совсем тонкий — нет буферов команд, напрямую вызываются функции драйвера. Кроме этого между пользовательскими и ядерными частями есть планировщик заданий, который выбирает какие собранные буфера отправлять видеокарте (разделение GPU на много процессов).

Виртуализация видеопамяти 
Теперь если не хватает видеопамяти, то ресурсы переносятся в системную (откуда могут быть отсвоплены). За счёт наличия у Windows Vista контроля выделения видеопамяти (ранее, у драйвера) можно распределять её более эффективно, чем POOL_MANAGED в XDDM. На данном этапе это работает на программном уровне — планировщик GPU перед передачей DMA-пакета карте загружает все нужные текстуры в видеопамять (умеет подгружать их заранее, пока GPU занят другим и свободна шина). Если приложение полноэкранное, все лишнее из видеопамяти будет перенесено в системную память по мере необходимости; если в оконном режиме, то происходит разделение памяти между текущими процессами. Если требуется гарантировать 100 % наличие ресурса в видеопамяти, то необходимо использовать полноэкранный режим и контроль над размером выделений.

Отсутствие ситуации «потери устройства» (Device Lost) 
В предыдущих версиях по различным причинам мог происходить Device Lost, после чего требовалось загружать все ресурсы в видеопамять заново и производить восстановление объектов. С новой моделью драйвера этой проблемы больше не существует. Возможна ситуация Device Removed, которая означает, что видеокарта была физически удалена из системы или что видеодрайвер был обновлён. Ситуация встречается очень редко.

Убраны списки возможностей (D3D caps) 
В DX10 гарантируется наличие всей функциональности, то есть если карта поддерживает DX10, то она обязана поддерживать последнюю версию шейдеров в полном объёме, поддерживать все форматы текстур, все возможные режимы фильтрации, шаблона (stencil) и всего остального. Более того, для DX10 написали спецификацию правил растеризации, то есть теперь картинка на разных видеокартах на одинаковом коде всегда должна быть одинаковой и совпадать с эталонным программным растеризатором. Если это не так, то это баг производителя видеокарты.
В дальнейшем функциональность будет расширяться (пакет DX10.1, DX11 и т. д.).

Уменьшено время вызова функций DirectX 
Уменьшено время вызова функций (в том числе DIP) на CPU. По данным презентаций Microsoft можно наблюдать 10x уменьшение времени. Это существенно, так как тяжёлая игра может проводить около 10+ миллисекунд в вызовах DX. Большую часть времени вызова ранее уходило на Runtime и Driver, теперь же driver model фактически ничего не делает, а сразу предоставляет исполнение драйверу.

State Objects и Constant Buffers 
Появились State Objects — объекты, которые можно предварительно собрать при создании и потом быстро устанавливать на видеокарте. Добавлены Constant Buffers, позволяющие более эффективно выставлять константы шейдеров.

Использование объектов-состояний 
Все Set*State заменены на объекты-состояния (State Objects). Состояния разделены по нескольким группам:
 Rasterizer State — fill mode, cull mode, depth bias, multisample, scissor и т. д.
 Blend State — alpha blend, color write mask, blend op и т. д.
 Depth State — depth func, stencil func и т. д.
 SamplerState — tex filtering, clamping и т. д.
Состояния для каждой группы ставятся целиком, а не каждый по отдельности, как в D3D9. Для каждой группы можно создать State Object, которому при создании указывается полный набор состояний группы, и «установить» можно только его.
Создание State Object — дорогая и медленная операция и должна вызываться редко.
Мотивация этого нововведения — такой API позволяет драйверу сгенерировать набор команд видеокарте заранее (при создании State Object) и не генерировать его каждый раз во время рендера при вызовах Set*State.

Буфера и биндинг 
Для основных типов данных (вершин, индексов, констант) введён единый буфер — ID3D10Buffer — набор байтов в памяти. Type safe обеспечивается за счёт указания при создании содержимого буфера. Для ресурсов введены отдельные объекты для привязки к конвейеру — resource views. То есть сначала создаем текстуру как объект в памяти, а потом её resource view как входные дынные для шейдера или как render target, и уже с этим view вызываем PSSetShaderResources (вместо SetTexture) и OMSetRenderTargets (вместо SetRenderTarget). Стоит отметить, что у одного ресурса может быть несколько resource views.

Такой принцип позволяет работать обобщенно. Существуют «бестипные» (typeless) ресурсы, то есть ресурсы, которые не имеют определённого типа (не указан при создании) — например, DXGI_FORMAT_R32G32B32_TYPELESS. Тип таких ресурсов выбирается во время создания view (например, DXGI_FORMAT_R32G32B32_UINT или DXGI_FORMAT_R32G32B32_FLOAT) и выбора элемента/слайса из массива текстур/объёмной текстуры.

Использование буферов констант 
Set*ShaderConstant заменены на Constant Buffers — группы констант (буфер на n констант), устанавливающихся за раз. Группу можно блокировать и записывать как обычный буфер. Привязка к шейдеру производится начиная с некоторого слота.

Таким образом использование констант сводится к разделению их на несколько групп по частоте обновления (per-object, per-material, per-pass, per-scene) и созданию для каждой группы Constant Buffer. Помимо дополнительной производительности такой подход даёт драйверу высокоуровневую картину — больше возможностей для оптимизации.

Параметры шейдеров 
VertexDeclaration заменён на Input Layout. Он требует при создании Shader Input Signature, то есть список input(входных)-параметров шейдера. Созданный объект можно использовать как Vertex Declaration с любым шейдером, имеющим такой же список input-параметров. В D3D9 Vertex Declaration устанавливался независимо от шейдера при рендере и поэтому драйверам приходилось серьёзно модифицировать сетап при смене vdecl. Сейчас vdecl жёстко привязан ко входу шейдера, что позволяет драйверу предвычислять все заранее.

Убраны asm-шейдеры 
Шейдеры больше нельзя писать на ассемблере — нужно пользоваться HLSL. Хотя ассемблер для shader model 4.x есть и можно смотреть результат компиляции шейдеров в него, больше нет возможности получить бинарный код шейдера из текста ассемблера (то что делали psa.exe/vsa.exe), но можно воспользоваться для этого методом  обратной разработки.

Компилятор HLSL 4.0 
Чтобы было легче портировать код шейдеров, компилятор умеет компилировать HLSL-шейдеры старых версий (SM2.0, SM 3.0) в SM4.0. В новом HLSL добавили атрибуты для хинтов компилятору — размотку циклов и выбор dynamic vs static branching для условных переходов.

Эволюционные изменения в шейдерах 
В Shader Model 4 добавлены целочисленные инструкции и битовые операции (можно считать в честном fixed point и передавать булевые флажки), убрано ограничение на количество инструкций (но очень длинный шейдер может упереться в ограничение по времени выполнения пакета на GPU - 10 сек)

Геометрические шейдеры (Geometry Shader) 
Геометрический шейдер — дополнительный шейдер между вершинным (Vertex Shader) и пиксельным (Pixel Shader), который может генерировать примитивы. На вход ему подается примитив с информацией о соседях, на выход — можно сгенерировать несколько (не фиксированное число).

Stream Out 
Это возможность записывать результат работы Vertex Shader/Geometry Shader в память. Например, кешировать обработку геометрии или вообще геометрию, созданную GS. Можно считать итеративные эффекты, типа Cloth/Water. То есть теперь можно напрямую трансформить и записывать геометрию на GPU, а не только рисовать пиксели в Render Target.
Также есть возможность читать в шейдере из буфера в памяти по индексу, то есть иметь достаточно большую read-only shared memory. NV например предлагает там константы анимации хранить для инстансинга.

Уменьшение количества draw calls и переключений состояний 
Появились массивы текстур, то есть контейнер одинаковых по размеру и формату текстур, из которого шейдер может выбирать по индексу (в DX10.1 — можно и cubemap arrays). Это тот самый atlasing done right — раньше когда в одной текстуре хранили несколько разных, приходилось беспокоиться за мип-левелы, оставлять зазор между текстурами и т. д. Теперь не надо.
В шейдер приходят primitive/instance id, в зависимости от instance ID можно использовать другой набор текстур/координат/каких-либо данных.
Ожидается, что dynamic branch в шейдере быстрый (лучше, чем в DX9-hardware), поэтому можно передавать Material ID и выбирать материала в шейдере. То есть, в теории, можно за один вызов генерировать большое количество геометрии с разными параметрами, текстурами и материалами.
На практике, dynamic branch имеет довольно-таки большую стоимость по времени и ряд других проблем (вычисление градиентов текстурных координат).

Multi-sampling antialiasing features 
Одно из наиболее полезных нововведений, ради которого многие перешли на DX10. Теперь в шейдере можно читать каждый MSAA-семпл отдельно, то есть писать свой собственный AA-фильтр, семплить при процессинге без лишних проблем и даже использовать MSAA RT как текстуру. Кроме того, теперь официально присутствует AlphaToCoverage. В D3D10.1 эти возможности появились и у depth textures.

Поддержка depth textures 
Теперь depth buffer можно использовать как текстуру. Можно сказать, чтобы при семплинге сравнивал со значением и делал фильтрацию соседей, можно получить чистый depth value и stencil value.

Другие интересные возможности 
 есть рендер в volume texture
 в DX10.1 можно скопировать из обычной текстуры в сжатую на GPU
 есть настоящий conditional render, то есть возможность уменьшить количество draw call по результатам работы GPU асинхронно (можно делать occlusion culling полноценно)

Дополнительные факты 
Операционная система Windows XP не поддерживает DX10.
Причина в том, что перенос новой драйверной модели невозможен — требуется слишком много изменений в ядре операционной системы. Если же переносить только набор новых функциональных возможностей DX10, то тоже возникают проблемы: виртуализацию и шедулинг невозможно осуществить в старой модели драйвера, перенос аппаратных возможностей — слишком большой объём работы для Microsoft и IHV.

См. также 
 Direct3D 11
 Direct3D 12

Ссылки 
 
 

Библиотеки трёхмерной графики
DirectX