Arseny Kapoulkine:
Давайте продолжать.

Advanced Visual Effects with DirectX 11: Efficient Work Submission in Direct3D

Speaker: Evan Hart (NVIDIA)
Значит, год уже 2014, а драйвер в много потоков генерировать буфер команд еще не научился.
Каноническая модель D3D (впрочем как и GL) - основная часть сборки буфера команд происходит в потоке драйвера, но он один.
Deferred contexts экономят (незначительный) overhead рантайма, плюс позволяют сэкономить на стоимости Map.
При Map операции типично нужно например проаллоцировать свежий буфер (если есть DISCARD)
И еще совершить какое-то количество bookkeeping - это фактически единственное что драйвер умеет делать в несколько потоков.
Соответственно игры у нас часто CPU bound, надо бороться как-то еще.

Во-первых, давайте померяем сколько можно реально делать DIPs

Автор померял и получил 5M draw/sec если не менять вообще стейт между draws.
И 290K draw/sec если между draw менять VS/PS, VB/IB, устанавливать CB и заполнять его данными (Map + Discard), и ставить сколько-то текстур.
Для меня лично 20-кратная разница не удивительна, а вот абсолютный масштаб удивителен.
Я думал что все в несколько раз медленнее.
Возможно DX11 на самом деле очень крут, я вроде максимум что видел - это что даже без учета driver thread около тысячи draw calls занимали около 4 ms с минимальным state setup (VB/IB)
В пятницу еще раз померяю...
Ну да неважно.
Соответственно утверждается что если достигнуть 5M draw/sec в настоящем приложении то наступит нирвана.
И значит надо (шок) менять стейт поменьше.

Предлагается ряд оптимизаций, многие из которых очевидны

  • 1. Чтобы менять шейдеры поменьше, надо вместо шейдеров с пинами (флажками для shader permutations) использовать в шейдере условия и циклы.
Можно в итоге получить менее производительный шейдер - для когерентных условий современным GPU на сами условия в целом пофиг, но может получиться так что шейдер будет использовать больше регистров, ну и неразвернутый цикл может привести к сильно более медленному коду из-за того что вычислить значения зависящие от переменной цикла не удастся.
  • 2. Разделять константы по частоте использования в CB; для CB которые меняются не каждый draw call (например per-frame и per-view данные) их можно статически привязать к binding slot (синтаксис cbuffer blabla: register(cb0) емнип)
И ставить таким образом один раз на кадр (мне стыдно все это даже цитировать, означает ли тот факт что автору не было стыдно рассказывать что такого в играх дофига и больше?)
  • 3. Не обязательно разделять constant buffers для VS и PS - можно использовать один и тот же буфер и ставить его в обе стадии.
Тут следует отметить что значительная часть стоимости state setup в DX11 - работа с различными объектами. Привязка объекта в пайплайн требует от нас например пометить его как использующийся GPU чтобы его случайно не удалить, сообщить WDDM информацию про него чтобы объект оказался в видеопамяти на момент draw итп.
Плюс в случае CB если он один то нужно Map сделать один раз.
  • 4. Аналогично 3, можно уменьшать количество уникальных VB/IB, склеивая VB/IB от разных мешей и используя basevertexindex и прочие полезные параметры DrawIndexed.
Это помогает и на DX9, разумеется.
  • 5. Полезно минимизировать сетап текстур в шейдерные слоты - а) "глобальные" текстуры типа ShadowMap можно статически привязывать чтобы не переставлять их при смене шейдера, б) связанные текстуры которые обычно меняются пачкой типа albedo/normal полезно класть в соседние слоты чтобы ставить несколько текстур за один вызов PSSetShaderResources, в) неиспользуемые текстуры в NULL ставить не надо, это лишнее.
  • 6. Чтобы добить текстуры, можно аналогично 3/4 посклеивать текстуры в массивы текстур. Хорошо работает для всяких ландшафтов где ассеты стандартизированы.
  • 7. Семплер стейты ставить можно один раз за кадр аналогично 2.
  • 8. В DX11.1 есть новая фича - можно цеплять CB к шейдерной стадии с оффсетом.
Это позволяет запаковывать CB аналогично 4.
  • 9. Используйте инстансинг.
Ни одного оригинального совета, пожалуй самое интересное - это количество draw которые можно реально делать в синтетическом тесте...
Arseny Kapoulkine:
По этому поводу см. доклад предыдущего года - http://blog.gamedeff.com/?p=666