DX 10 post, part 1 – Infrastructure

DX 10 post, part 1 – Infrastructure
DX 10 post, part 2 – API changes
DX 10 post, part 3 – New hardware features

Этот пост надо было написать год назад, потом полгода назад, потом месяц назад и неделю назад.
Год назад я был под впечатлением от MS вообще, полгода назад я был в неправильной команде, а месяц и неделю назад жизнь кипела и бурлила. Однако, наконец, случился неожиданный перерыв и можно, наконец, написать про DX10.

Сразу скажу, я как девелопер на DX10 писал не больше маленькой демки, но в принципе уже имел счастье копаться во внутренностях. Если что, считайте меня евангелистом, гыгыгы.
Буду описывать впечатления высокого уровня, низкого – детали реализации. Опять же, каждый пункт очень не подробный, про каждый – можно писать большой детальный отдельный пост. Пожелания принимаются :)

За один раз все не описать, сегодня будут, пожалуй, самые большие изменения – инфраструктурные. Нужно отметить, что большинство из них относятся не только к DX10, но и вообще ко всем версиям DX на Висте.


- Новая driver model.
В Висте совершенно новая driver model (WDDM – Windows Display Driver Model, в девичестве LDDM – Longhorn Display Driver Model), это самое большое изменение в модели видео-драйвера со времен, когда вообще появилось hardware acceleration.
Очень поверхностно о том, как было раньше, в XDDM (XP Display Driver Model), по хорошему тут надо писать отдельный пост.
В XDDM каждый вызов DX добавлял токен в так называемый “command buffer”, в независимом от видеокарты формате. Когда рантайм решал, что буфер уже достаточно велик, звалась функция драйвера в kernel mode, ей передавался этот буфер, и драйверу нужно его разобрать и каким-то одному ему известным способом передать видеокарте. Никаких функций драйвера в user mode не было как таковых.
К сожалению (нет, к счастью!), оказалось, что видеокарты очень динамично развиваются, и формат command buffer усложнялся и усложнялся. Появились сложные шейдеры, которые нужно компилировать в микрокод видеокарты, появились заточки под разные игры и так далее. В результате, в kernel mode оказался десятимегабайтный с гаком кусок кода, который в случае любой ошибки вообще говоря убивает систему. Подавляющее большинство BSOD в XP – display driver, самый сложный и опасный драйвер в системе.
Вторая часть проблем про разделение видеокарты между несколькими процессами. В XDDM у OS нет ни способа установить приоритет, ни менеджить видеопамять, ни вообще выполнять какой-то разумный шедулинг вызовов DX от разных процессов. Чуть что – Device Lost, и живой девайс остается у одного процесса.

В новой driver model есть разделение между user mode и kernel mode частью драйвера. Все вызовы DX напрямую идут в user-mode driver, который подготавливает сразу hardware-dependent буфер, который иногда флашит в kernel, где он идет в видеокарту. Идея в том, что всю тяжелую работу можно выполнять в user-mode части, а в kernel фактически только переслать собранный буфер в DMA-трансфер видеокарте. Во-первых, если user-mode driver упадет, ничего страшного не случится – закроется конкретное приложение, но не вылетит OS. Во-вторых, у драйвера больше контроля, когда и сколько kernel-mode calls делать, причем эти вызовы не тяжелые. В-третьих, рантайм становится совсем тонкий – нет никаких command buffers, просто напрямую вызываются функции драйвера.
Кроме того, между user-mode и kernel-mode частями есть наш GPU Scheduler, который может выбирать, какие собранные буфера отправлять видеокарте, то есть менеджить GPU на много процессов.

Там еще куча важных моментов, но все их описывать надо не в рамках этого поста, а то я его никогда не напишу.

- Video memory virtualization
В новой driver model есть виртуализована видеопамять – то есть, если ее не хватает, ресурсы будут переноситься в системную. Так как контроль за аллокациями видеопамяти теперь у OS, а не у драйвера, это можно делать гораздо эффективнее, чем POOL_MANAGED в XDDM. Сейчас это работает без железной поддержки – GPU Scheduler перед передачей DMA-пакета карточке загружает все нужные текстуры в видеопамять. Он достаточно умный и умеет подгружать их заранее, пока GPU занят другим и свободна шина. Если приложение уйдет в fullscreen – все остальное из видеопамяти выбросят по мере необходимости, если в windowed – то будет пытаться распределять память по текущим процессам. Собственно, совершенно аналогично обычной памяти.
Важный вопрос – контроль над производительностью со стороны приложения, когда хочется гарантировать наличие ресурса в видеопамяти. Насколько я понимаю, ответ сейчас такой – fullscreen и контроль над размером аллокаций.
То есть, мужики, больше не бывает Device Lost, никогда, всем радовацца полчиса. Если станет активным другое приложение – OS в случае необходимости выгрузит ресурсы и восстановит их обратно. Все еще есть “Device Removed”, но это уже экзотические случаи вроде “выдернули видеокарту” или “поставили новую версию драйвера”, о таких сценариях не надо беспокоиться играм.

- No caps!
В DX10 больше нет капсов, как таковых. Гарантируется наличие всей функциональности, за редкими исключениями типа наличия блендинга для некоторых текстурных форматов (это поправили в DX10.1), то есть если карта поддерживает DX10, то она обязана держать последнюю версию шейдеров в полном объеме, поддерживать все форматы текстур, все возможные режимы фильтрации, стенсила и всего-всего-всего. Более того, для DX10 написали спек правил растеризации треугольников и прочего добра, то есть теперь ожидается и то, что картинка на разных видеокартах на одинаковом коде всегда будет одинаковой и совпадать с эталонным софтверным растеризатором. Где не так – баг производителя карточки.
В будущем новая функциональность будет появляться кусками, типа набор фич DX10.1, DX11 и т.д.
Это в общем-то должно очень радовать девелоперов и существенно улучшать тот бардак, какой был в DX9.

- Lean & mean runtime
Много в API и driver model изменилось для того, чтобы уменьшить DIP cost на CPU. На XP он очень серьезный, тяжелая игра может проводить около десятка миллисекунд (а то и больше), в вызовах DX. В них мало времени занимает сам рантайм, и много драйвер.
Основное, что изменилось – сама driver model, в которой рантайм фактически ничего не делает, а сразу предоставляет исполнение драйверу.
На стороне API появились State Objects, которые можно прекомпилировать при создании и потом быстро устанавливать на видеокарте и Constant Buffers как средство более эффективно менеджить обновления констант шейдеров. Об этом подробнее – в следующих сериях.
Мужики из Крайтека пишут, что действительно видели серьезное уменьшение DIP cost в Крайзисе на DX10, впрочем, я уверен, текущим драйверам еще есть куда стремиться.

Вся эта инфраструктурная радость и является основной технической причиной того, что DX10 нет на XP. Переносить новую driver model на XP не представляется возможным – слишком много изменений в ядре OS, переносить все фичи DX10 на старую driver model невозможно (виртуализация и шедулинг, к примеру, принципиально не заживет). Есть все еще компромиссы – переносить только хардверные фичи DX10 на старую driver model, это прилично работы для нас и очень много работы для IHV – писать поддержку новых фич для новой и старой driver model, и поведение API будет разным на разных OS. Или предоставить Layer, который будет предоставлять DX9-функциональность с DX10-API, но это может и так написать девелопер, и остается открытым вопрос с представленим капсов с DX10 API.
Видимо, уперлись в нехватку ресурсов для девелопмента. Мне лично кажется, что технически это все сложно и тяжело, но теоретически возможно.

Слушайте, как же я соскучился по блогу за месяц без постов, пальцы прямо радуются и с удовольствием тыкают по кнопочкам. До скорой встречи, про свое добро я могу рассказывать долго.

  • http://kss.livejournal.com/ Joes

    Как девелопер античита могу сказать – теперь станет намного сложнее ловить ring3 хуки, ибо теперь слишком много ring3 кода появилось.

  • CEMEH

    Ммм, раскрой тему? ring3 хуки – это в смысле хуки на runtime calls? Их столько же и осталось.

  • my.name

    СЕМЕН пиши еще !

  • IronPeter

    Я не совсем понимаю “переслать собранный буфер в DMA-трансфер видеокарте”. Вообще-то DMA engine видеокарточки сам кушает командный буфер – достаточно указать адрес.

    В этой связи вопрос – в пользовательский уровень мапится прямо место, где лежит push buffer? Или данные таки копируются? Если они копируются – то может ли драйвер пользовательского режима криптовать push buffer.

    Если push buffer не криптуется – я первым делом покупаю Vista. И начинаю смотреть, как качественно сделана защита от несанкционированных DMA железкой. У PS3 было некачественно :).

  • http://kss.livejournal.com/ Joes

    Не, не совсем.
    Как народ обходит античиты:
    1. Патчи на таблицу импортов d3d9.dll
    2. Патч прямо в теле функции которая что то выполняет (внутри d3d9.dll)
    3. Патч интерфейсов d3d
    4. Спрятанный патч где то внутри d3d9.dll (ну или другой версии), поскольку некоторые функции являются промежуточными и сильно погоды не меняет, что там делается в промежуточной функции.

    Каждый из подходов детектируем (скажем d3d9.dll версий не много было, можно было определить версию и по мелкой базе сравнивать CRC). Теперь считать CRC уже нельзя – драйвера там разные, от разных производителей и все такое. Читать из файла – смысла нет, легко обойдут.

    Теперь, поскольку драйвер в юзер-моде частично ушел, возможностей где-то втиснуться стало больше. И всего то.

  • http://kss.livejournal.com/ Joes

    Т.е. я все к тому, что чит может таргетить конкретный драйвер от конкретного производителя, а вот античитеры должны ловить всех, вне зависимости от драйвера или оси.

  • http://__vortex__.livejournal.com vortex

    Очень интересно и познавательно. Желаем продолжения поскорее! :)

  • http://virtul.livejournal.com virtul

    Пасиб, жжошь. Только вот почему так получается, что кривой 10-мб драйвер на хр работает без проблем, а видеодрайвер на висте падает без конца (мопед не мой, в смысле у меня хр, но регулярно слышу маты от коллег)?

  • DigiMind

    > а видеодрайвер на висте падает без конца (мопед не мой, в смысле у меня хр, но регулярно слышу маты от коллег)?

    Абсолютно ни о чем не говорит.
    У меня ни разу не падал. У коллег – тоже.
    Такая же “никакая” статистика :)

    Ну и… Пользуясь случаем, передаю превед драйверописателям.

  • CEMEH

    IronPeter
    Насколько я понимаю, там есть kernel-mode часть драйвера, которая собственно и передает этот накопленный буфер видеокарте плюс правит оффсеты на ресурсы. Она может копировать, валидировать буфер, при желании – и криптовать. Совсем не уверен, имеет ли смысл криптовать, ибо теоретически ты в user mode итак всю user-mode память видишь, и наверное и до ключей криптования доберешься. Покупай, проверяй, да :)

    Joes
    Хмм. А не мог чит как драйвер поставиться, и подменять указатели на функции драйвера?

    virtul
    Да понятно совершенно, переписывать 10-мб кода под новую driver model получается очень тяжело и все очень нестабильно до сих пор. Тем не менее, падения на Висте не перегружают тебе OS, а только вырубают приложение, это как бы и есть бонус. Кстати, за год там очень серьезный прогресс, нам показывали цифры, стало падать значительно меньше.

  • http://www.sdl.ru TSS

    CEMEH, респект, жжошь глаголом!

    Только вот почему при видимом “бусте”, который дает изменение архитектуры, мы видим “нерф” производительности DX10 игр на Висте? Да и старые DX9-тые игры теряют в среднем 15-20% производительности. DX9 эмулируется через DX10 что-ли?

  • IronPeter

    >правит оффсеты на ресурсы

    Эм… странный push buffer. Может быть несколько вхождений одного и того же ресурса в push buffer. И совершенно непонятно, какие смещения в буфере фиксапить. Там что, реально какие-то линк-таблицы?

    Я уже не говорю о том, что отдельные вендоры любят использовать старшие или младшие биты адреса ресурса под флажки. Флажок там DMA-канала. Чтобы профиксапить push buffer – надо заниматься очень странной деятельностью.

    Я же собственно не про копирования спрашивал. Если я в юзермоде правлю прямо куски push buffer, инплейс – то это значит, что кто-то специально отмапил память и на железку и в юзермоду. Возникает вопрос – кто это делает. Кто говорит gpu_ioremap на куски памяти. Это относительно злая операция, потому как требует настройки секьюрности и прав доступа на стороне gpu. Специфичных для вендора операций.

  • http://kss.livejournal.com/ Joes

    > Хмм. А не мог чит как драйвер поставиться, и подменять указатели на функции драйвера?
    В смысле?
    Ок, что такое чит и как он работает: есть игра, скажем всеми любимый Half-Life. Модельки грузятся в vertex buffer’а, а потом анимируются шейдером (скелетка). По шейдеру + набору вертексов (ну и индексов) можно однозначно идентифицировать модель. Из мешанины вертексов можно выделить где голова и т.д. – оно всегда в одном и том же месте. После этого при рендере данной модельки выключается Z test или SetCursorPos на голову делается или еще что то такое.
    Читы, в основном, делаются под юзер-мод, ибо не нужна убер квалификация. Намного проще работать с high-level API, чем разбирать в ring0 буффер D3D, хоть это и возможно. Потому, хукают интерфейсы, патчах D3D прямо в памяти, но до драйвера не добирались.
    Теперь что – D3D это “прослойка” между драйвером в ring3. Получается – зачем патчить D3D, если можно запатчить драйвер и сделать reroute того же Draw() или DrawIndexed() на код чита. А как уже говорил – драйверов много, всех их не проконтролируешь.
    Отаке.

  • dDIMA

    2 virtul
    > Только вот почему так получается, что кривой 10-мб драйвер на хр работает без проблем, а видеодрайвер на висте падает без конца
    У нас есть и наоборот, кстати.
    На GF8800 на XP падает в синий экран смерти, на Висте – работает.

  • Xunter

    2 dDIMA
    вот только сегодня gf8800gt видеодрайвер свалился на висте до синего экрана и ребута
    не везет видимо просто

  • CEMEH

    IronPeter
    Именно потому что, все специфично для карты эти оффсеты правит kernel-mode driver. Ему даются таблицы, что куда замапится в видеопамять, и он вструняет их в push buffer, c нужными флажками в старших/младших битах и т.д.

    То что заполняет user-mode driver – это никак не отмапленная память железки. Думаю, этот remap делается в kernel mode на фиксированную область памяти, и туда копируется этот буфер после правки оффсетов. Собственно, именно для этих операций есть kernel-mode часть драйвера. Пойнт в том, что ей нужно делать много меньше, чем было в XP.

    Joes
    Ага. Ну вот кажется, что в крайнем случае чит мог установиться и драйвером, и с ним тоже ничего не поделать. Но в общем, твой пойнт понятен, в user mode действительно проще. Буду подумать.

    TSS
    Почему тормозит – см ответ выше по треду. DX9 на Висте не эмулируется, а нативно работает через новую driver model.

  • http://kss.livejournal.com/ Joes

    > Ну вот кажется, что в крайнем случае чит мог установиться и драйвером, и с ним тоже ничего не поделать.
    Ага, мог :-)
    Вопрос – доки по интерфейсу D3D-драйвер открытые для D3D9? Например формат промежуточного буффера и все такое? Я не видел, но может пропустил. Если да – тогда, может быть, прослойка ввиде драйвера и пойдет.
    Если нет – то это надо совершать подвиг и реверсить все возможные операции, которые D3D может положить в буффер. Или не все возможные, а только те, которые надо. Вобщем, тот еще гимморой.

  • CEMEH

    Joes
    Ага, открытые. Там фактически есть только одна главная функция, которой скармливается command buffer – http://msdn2.microsoft.com/en-us/library/ms791706.aspx

  • http://devlog.gw-labs.net/ dorfe

    Ну у меня к примеру иногда на висте/GF8800 user-mode драйвер падает 4-5 раз подряд, после чего ось уходит в BSOD ;-) Так что пока все еще сырое, разницы особо нетути :)

  • IronPeter

    Забавно. Про тонкий kernel-mode драйвер. В свете таких операций.

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

    Интересно, драйвер пользовательского режима использует Get* функции рантайма или не засцав зеркалирует еще раз?

  • CEMEH

    Ну, он действительно тонкий. Да, надо распарсить буфер.
    Я не очень понимаю про зеркалирование, но понимаю про изменения местоположения текстур. Надо спросить про детали установки стейта. Драйвер пользовательского режима не знает об адресах ресурсов в видеопамяти, о них узнает только kernel в процессе установки.

  • arabesc

    CEMEH, можно пару неудобных вопросов, как человеку близкому к теме? Что происходит с DX9 SDK? Не знаю, как работает DX10, но честное слово, такое впечатление, что DX9 пытаются как бы дискредитировать. Например, многим известно, что где-то с октября 2005г. хелперный класс ID3DXFont существенно потерял в производительности, но непонятно, что мы приобрели взамен. Не могу найти стабильный SDK где-то с июня 2007 года, постоянно какие-то недоработки, которые, причём, нигде не описаны, как известные ошибки. Только что наткнулся на баг в прешейдерах в Jun2007. На более поздних версиях SDK вообще невозможно работать, т.к. они откровенно неправильно компилировали некоторые шейдеры – работавший ранее код вдруг начал выдавать какой-то бред. Сейчас у нас при компиляции FX’а с примерно полутора тысячами вариантами независящих друг от друга шейдерных программ, объём используемой компилятором памяти превышает 500Мб! Нет слов…

  • CEMEH

    Не надо поддаваться панике. Я слабо помню историю с ID3DXFont, неужели ее не зафиксили в более поздних версиях SDK?
    Про компилятор – в определенный момент народ мощно переписал HLSL compiler (со старой кодобазой становилось реально тяжело жить, особенно с DX10), и поэтому с новой версией тянутся баги несовместимости с предыдущей версией.
    Полторы тысячи вариантов – это как бы много. 500 mb жрется при компиляции отдельной комбинации этих вариантов или в каком сценарии?

    Мое видение такое – с фокусом на DX10 там произошли всяческие рефакторы и изменения, народ полон решимости это все фиксать. Лучшее что можно делать – репортить баги и зудеть на форумах msdn.

  • arabesc

    Это не паника, просто ситуация тянется и тянется, а тут на очередной баг напоролся.
    ID3DXFont, насколько знаю, так и не поправили. Сделал собственную реализацию. Смотрели на ID3DXFont через nvPerfHUD – там, насколько помню, поменялся алгоритм аллокации глифов, они кладутся на текстурку меньшего, чем раньше, размера, отсюда заметный рост количества DIP’ов при рисовании текста.
    Компилятор вроде бы раньше переписали, чем Jun2007? Там ещё ключик обратной совместимости появился. Так после Jun2007 в следующих релизах ещё хуже стало. А сейчас наткнулся на баг с прешейдерами. Без них нормально работает, с ними глючит. Повторяется и в Jun2007 и в March2008.
    Да, полторы тысячи шейдеров не очень мало, но для прекомпиляции вариантов убершейдера нормально. Убершейдер лежит в FX’е, инварианты конфигурятся статическими булевскими параметрами через простой препроцессор метапрограмминг (как BOOST_PP_LOCAL_ITERATE). Варианты складываются в массив. Из массива потом в технике по индексу выставляются. Шейдеры используются ps_2_0 и ps_3_0. На каждый в отдельности тратить 500 метров памяти представить не могу как можно. На все вместе тоже не нужно, т.к. они между собой не связаны.
    MSDN форумы по DX вроде закрыли и перенесли обсуждение на forums.xna.com. Туда писал в конце прошлого года. Спасибо, кстати, за напоминание, сейчас проверил, кто-то из MS ответил, попросил тестовый пример. Писал и в форуме на http://www.gamedev.net, но осталось без внимания.

  • Pingback: highly professional scums » Blog Archive » DX 10 post, part 3 - New hardware features()

  • Pingback: highly professional scums » Blog Archive » DX 10 post, part 2 - API changes()