Publish @Zeux: GDC report – DirectX 11 Performance Reloaded – Nick Thibieroz (AMD) and Holger Grun (NVIDIA)

Меж тем, прошло ежегодное мероприятие Game Developer Conference. Как обычно, можно посетовать, что про графику и суровый тек докладов все меньше, а про универсальные платформы для всех телефонов – все больше, но жизнь движется куда движется. Где, кстати, о GDC в рунете пишут еще?
Как и любая конференция, GDC не столько про доклады, сколько про возможность встретиться и пообщаться – вот например @wat выбрался в кои-то веки. Был очень рад всех увидеть, отлично посидели, надо чаще!
На Кружочках постепенно зарождается традиция транслировать интересные доклады в реалтайме, что по мне отлично дополняет просмотр слайдов, а иногда их и успешно заменяет. Можно у докладчиков спрашивать точнения на вопросы, возникшие по у других ходу итд. Будем их потихоньку выкладывать.
@Zeux ходил на много интересного по графике – спасибо и респекты за трансляции!

Алсо, прикрутил по случаю на http://blog.gamedeff.com Disqus comments, комментировать должно стать гораздо проще.

People in this conversation:
Arseny Kapoulkine (Zeux) Add
CREAT Studios, Saber3D, Sperasoft (EA Sports – FIFA). Currently making kids happy at ROBLOX. http://zeuxcg.org/

Zeux: DirectX 11 Performance Reloaded – Nick Thibieroz (AMD) and Holger Grun (NVIDIA)
Парни из AMD и NVidia рассказывали про то как сделать так чтобы в вашей directx11 игре (если она у вас случайно таки есть) было все хорошо с производительностью – с точки зрения CPU и GPU. Большинство из рассказаного уже было упомянуто в различных talks, большинство советов были универсальны (NV/AMD), про часть была конкретика по вендорам. Поскольку куча материала уже где-то была, я выпишу то что почему-либо отметил.

При создании шейдера драйвер выполняет трансляцию из D3D микрокода в GPU-specific, при большом количестве шейдеров это может отнимать время.
Общая рекомендация – создавать шейдеры заранее (сильно до отрисовки) и с нескольких потоков.
Выглядит так, что у NV компиляция примерно в момент CreateShader, а у AMD в момент CreateShader в один из драйвер тредов делается schedule job на трансляцию
Общая рекомендация про остальные ресурсы – создавать их из нескольких потоков, чтобы у драйвера было больше возможности параллелизации.
В d3d11 рантайме есть способ проверить, поддерживает ли драйвер такое эффективно
См. D3D11_FEATURE_DATA_THREADING

При создании ресурсов традиционно флаги биндинга надо стараться ставить те какие понадобятся
Особенно были выделены флаги RENDER_TARGET и UNORDERED_ACCESS.
Если их ставить ресурсам которые в этих возможностях не нуждаются, будут лишние overheads.

В дополнение к стандартным советам про группировку констант в constant buffers по update frequency прозвучали следующие:
- С доступом к CB связан некий оверхед, поэтому количество CB привязанных к пайплайну следует ограничивать, было рекомендовано <5.
Это связано в основном с каким-то фиксированным overhead, а не с паттернами доступа, вроде как.
- Для улучшения локальности доступа к константам надо стараться читать из одного CB в следующих друг за другом инструкциях

Occlusion Queries рекомендовано использовать ограничено, было названо число несколько сотен на кадр.

При работе с динамической геометрией, традиционно рекомендуется использовать DISCARD + NOOVERWRITE.
Дополнительные советы:
- Не обязательно каждый кадр звать DISCARD если буфер используется как стек - достаточно звать при переполнении, это может происходить реже чем 1 раз за фрейм
- На AMD рекомендуемый размер буферов которым делается DISCARD - 4 Mb и меньше
- На NV специальных ограничений на размер нет, но есть фиксированное ограниченное количество discard-ов на кадр, после которого все становится медленно (CPU-GPU синхронизации)

Оптимизация vertex fetch/shade стадии:
- Традиционно, чем меньше читаем на vertex fetch тем лучше - это значит минимизируем количество input атрибутов в декларации (layout), и по возможности распиливаем стримы (например, атрибуты вертекса для depth-only render и все остальное)
- Традиционно, чем меньше пишем из vertex shader тем лучше - на NVidia вроде просто линейная зависимость скорости от размера, на AMD есть магическое число в 4 float4 атрибута, на котором (и меньше) скорость оптимальна
Поговорили про тесселяцию, highlights которые я запомнил:
- В hull shader бывает полезно делать frustum & back-face culling вручную для патча, чтобы не тратить время на тесселяцию - fixed-function culling работает сильно позже
- Фактор тесселяции >15 на AMD использовать не рекомендуется из соображений скорости
- Как известно, у миллионов маленьких треугольников проблемы с производительностью; было названо число 10-16 пикселей на треугольник как качественное.
Как обычно, чтобы его добиться нужна адаптивная тесселяция (зависящая от расстояния, величины требуемой деформации итп)

Поговорили про geometry shaders, все как обычно – если можете не использовать то лучше не используйте
Если не можете то пишите фиксированное (маленькое) число данных
На AMD есть специальный fast path
На котором GS invocations выдают один и тот же render target index
Если не один и тот же то медленнее.
GS invocations с одного входного примитива.

Поговорили про глубину, на NVidia рекомендуют использовать D24 для скорости (не D16 и не 32-битные форматы)
И при работе с одним буфером глубины не использовать depth test разных направлений (greater/less), иначе ломается early z reject
На AMD рекомендуют использовать D16 глубину для шадовмап.

У обоих вендоров discard/alpha test убивают ранний fine-grained (попиксельный) z test, но оставляют coarse grained
У обоих вендоров depth output в пиксельном шейдере убивает и fine grained и coarse grained early z test
Про консервативную глубину почему-то сказано не было.

Ну и наконец, при работе с MSAA в случае, если есть post-process pass который читает из одного семпла все время, рекомендуется сначала этот семпл зарезолвить в non-MSAA texture, а потом читать из нее – cache coherence лучше при нескольких выборках.
У меня все про этот доклад.

А, там еще авторы пытались рассказывать про deferred contexts, но у них не очень хорошо получилось – лучше получилось у автора тематического доклада, про который я скажу.
Было сказано еще что типично драйвер генерирует команды для GPU в отдельном потоке, и рендер тред с API вызовами выступает как producer команд, так что если у вас между API вызовами много своей какой-то работы типа стейт менеджмента или прочего рендер кода
То драйверный тред слишком много ждет, что плохо – хорошо это когда вы генерируете работу для драйвера быстрее чем он ее может обрабатывать.
Для @Simon – я тут специально все пишу в normal mode, если ты ответишь repost то наверное будет максимальный exposure

Так, здесь еще пропустил про апдейт текстур.
Если у вас есть динамическая текстура, то:
- При ее апдейте пытайтесь обновлять ее целиком или, в случае 3д текстуры, целый слайс. Иначе есть риск нарваться на не оптимальный codepath в драйвере.
- Map на динамические текстуры в целом не рекомендуется, т.к. по контракту API вам выдадут указатель на линейную память (строчки друг за другом в памяти), а на самом деле с высокой вероятностью текстура в памяти лежит в другом формате, тайликами – поэтому драйверу придется ее конвертировать после Unmap (и при Map если не discard/write-only…)