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 напрямую идут в пользовательский драйвер, который готовит сразу буфер с содержимым, зависящим от оборудования. Этот буфер передаёт данные в ядро операционной системы, откуда они идут на видеокарту. Таким образом вся тяжёлая работа выполняется в пользовательской части, а в ядре - пересылка собранного буфера в DMA-трансфер видеокарты. Как итог, если пользовательский драйвер упадет, ничего страшного не случится — закроется конкретное приложение (но не BSOD). И у драйвера больше контроля (когда и сколько вызовов функций ядра делать). Также DX Runtime становится совсем тонкий — нет буферов команд, напрямую вызываются функции драйвера. Кроме этого между пользовательскими и ядерными частями есть планировщик заданий, который выбирает какие собранные буфера отправлять видеокарте (разделение GPU на много процессов).
Теперь если не хватает видеопамяти, то ресурсы переносятся в системную (откуда могут быть отсвоплены). За счёт наличия у Windows Vista контроля выделения видеопамяти (ранее, у драйвера) можно распределять её более эффективно, чем POOL_MANAGED в XDDM. На данном этапе это работает на программном уровне — планировщик GPU перед передачей DMA-пакета карте загружает все нужные текстуры в видеопамять (умеет подгружать их заранее, пока GPU занят другим и свободна шина). Если приложение полноэкранное, все лишнее из видеопамяти будет перенесено в системную память по мере необходимости; если в оконном режиме, то происходит разделение памяти между текущими процессами. Если требуется гарантировать 100 % наличие ресурса в видеопамяти, то необходимо использовать полноэкранный режим и контроль над размером выделений.
В предыдущих версиях по различным причинам мог происходить Device Lost, после чего требовалось загружать все ресурсы в видеопамять заново и производить восстановление объектов. С новой моделью драйвера этой проблемы больше не существует. Возможен только Device Removed, который означает что-то вроде «выдернули видеокарту»/«поставили новую версию драйвера» и встречается очень редко.
В DX10 больше нет капсов, как таковых. Гарантируется наличие всей функциональности, то есть если карта поддерживает DX10, то она обязана поддерживать последнюю версию шейдеров в полном объёме, поддерживать все форматы текстур, все возможные режимы фильтрации, шаблона (stencil) и всего остального. Более того, для DX10 написали спецификацию правил растеризации, то есть теперь картинка на разных видеокартах на одинаковом коде всегда должна быть одинаковой и совпадать с эталонным программным растеризатором. Если это не так, то это баг производителя видеокарты. В дальнейшем функциональность будет расширяться (пакет DX10.1, DX11 и т.д.).
Уменьшена стоимость вызова функций (в том числе DIP) на CPU. По данным презентаций Microsoft можно наблюдать 10x уменьшение стоимости. Это существенно, так как тяжёлая игра может проводить около 10+ миллисекунд в вызовах DX. Большую часть времени вызова ранее уходило на Runtime и Driver. теперь driver model фактически ничего не делает, а сразу предоставляет исполнение драйверу.
Появились State Objects — объекты, которые можно предварительно собрать при создании и потом быстро устанавливать на видеокарте. Добавлены Constant Buffers, позволяющие более эффективно выставлять константы шейдеров.
Все Set*State заменены на объекты-состояния (State Objects). Состояния разделены по нескольким группам:
Состояния для каждой группы ставятся целиком, а не каждый по отдельности, как в 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 жёстко привязан ко входу шейдера, что позволяет драйверу предвычислять все заранее.
Шейдеры больше нельзя писать на ассемблере — нужно пользоваться HLSL. Хотя ассемблер для shader model 4.x есть и можно смотреть результат компиляции шейдеров в него, но больше нет возможности получить бинарный код шейдера из текста ассемблера (то что делали psa.exe/vsa.exe). Отреверсинженирить бинарный код, впрочем, никто не мешает.
Чтобы было легче портировать код шейдеров, компилятор умеет компилировать HLSL-шейдеры старых версий (SM2.0, SM 3.0) в SM4.0. В новом HLSL добавили атрибуты для хинтов компилятору — размотку циклов и выбор dynamic vs static branching для условных переходов.
В Shader Model 4 добавлены целочисленные инструкции и битовые операции (можно считать в честном fixed point и передавать булевые флажки), убрано ограничение на количество инструкций (но очень длинный шейдер может упереться в ограничение по времени выполнения пакета на GPU, до 10 сек)
Геометрический шейдер — дополнительный шейдер между вершинным (Vertex Shader) и пиксельным (Pixel Shader), который может генерировать примитивы. На вход ему подается примитив с информацией о соседях, на выход — можно сгенерировать несколько (не фиксированное число). Основная идея — наконец генерировать геометрию на GPU. Geometry Shaders очень плохо подходят для тесселяции и нужны для всяческой локальной генерации геометрии по мелочи. Например, можно патикл строить по одному вертексу или использовать стрипы геометрии в post-processing (например, в motion blur — из карты скоростей генерировать геометрические линии, по которым блурится картинка (так делает Lost Planet)). Совсем из другой области — например, делать рендер в Cubemap за один проход, GS выясняет в какие стороны cubemap попадает треугольник, и разбивает его на несколько. Основное препятствие использования GS — их скорость на современном железе. Тормозят. Основная причина — «анти-параллельность» GS, то есть так как шейдер может выдавать variable input и GPU обязана сохранять последовательность вывода треугольников, нужно вводить промежуточную стадию, пишушую в память, а потом делать финальный Gather, убивающий дырки.
Это возможность записывать результат работы Vertex Shader/Geometry Shader в память. Например, кешировать обработку геометрии или вообще геометрию, созданную GS. Можно считать итеративные эффекты, типа Cloth/Water. То есть теперь можно напрямую трансформить и записывать геометрию на GPU, не только рисовать пиксели в Render Target. Также есть возможность читать в шейдере из буфера в памяти по индексу, то есть иметь достаточно большую read-only shared memory. NV например предлагает там константы анимации хранить для инстансинга.
Появились массивы текстур, то есть контейнер одинаковых по размеру и формату текстур, из которого шейдер может выбирать по индексу (в DX10.1 — можно и cubemap arrays). Это тот самый atlasing done right — раньше когда в одной текстуре хранили несколько разных, приходилось беспокоиться за мип-левелы, оставлять зазор между текстурами и т. д. Теперь не надо. В шейдер приходят primitive/instance id, в зависимости от instance ID можно использовать другой набор текстур/координат/whatever. Ожидается, что dynamic branch в шейдере быстрый (лучше, чем в DX9-hardware), поэтому можно передавать Material ID и бранчиться по материалам в шейдере. То есть, в теории, можно за один вызов генерировать большое количество геометрии с разными параметрами, текстурами и вообще материалами. На практике, больше всего мешает таки стоимость dynamic branch и проблем, с ним связанных (вычисление градиентов текстурных координат). А остальное — вполне можно и нужно использовать.
Небольшая фича, ради одной которой можно переходить на DX10. Теперь в шейдере можно читать каждый MSAA-семпл отдельно, то есть писать свой собственный AA-фильтр, вменяемо семплить при процессинге и вообще использовать MSAA RT как текстуру. Ещё и AlphaToCoverage вместе с этим теперь официально присутствует. В D3D10.1 это можно делать и с depth textures.
Теперь depth buffer можно использовать как текстуру. Можно сказать, чтобы при семплинге сравнивал со значением и делал фильтрацию соседей, можно достать чистый depth value. Можно даже stencil value достать.
Операционная система Windows XP не поддерживает DX10 Причина в том, что перенос новой драйверной модели невозможен — требуется слишком много изменений в ядре операционной системы. Если же переносить только набор новых функциональных возможностей DX10, то тоже возникают проблемы: виртуализацию и шедулинг невозможно осуществить в старой модели драйвера, перенос аппаратных возможностей — слишком большой объём работы для Microsoft и IHV.
DirectX 10.1.