Direct3D 10
- 1 year ago
- 0
- 0
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 Removed, которая означает, что видеокарта была физически удалена из системы или что видеодрайвер был обновлён. Ситуация встречается очень редко.
В DX10 гарантируется наличие всей функциональности, то есть если карта поддерживает DX10, то она обязана поддерживать последнюю версию шейдеров в полном объёме, поддерживать все форматы текстур, все возможные режимы фильтрации, и всего остального. Более того, для 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), который может генерировать примитивы. На вход ему подается примитив с информацией о соседях, на выход — можно сгенерировать несколько (не фиксированное число).
Это возможность записывать результат работы 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 можно использовать другой набор текстур/координат/каких-либо данных. Ожидается, что 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 и .