Управление потоком в HLSL

Недавно я прочитал эту статью о raymarching облаках (осторожно, это PDF-файл, если вам это не нужно: http://www.diva-portal.org/smash/get/diva2:1223894/FULLTEXT01.pdf), где автор продолжает оптимизацию (стр. 22 и далее) алгоритм через репроекцию. Он утверждает, что путем raymarching только 1/16 всех пикселей в кадре (выбранный пиксель прыгает по сетке 4x4) и повторного проецирования остальных, он получил увеличение производительности примерно в 10 раз.

Теперь я попытался реализовать это и в Unreal Engine 4 (пользовательский шейдер HLSL), и теперь у меня работает raymarching и репроекция. Однако я застрял на самом деле только в том, чтобы запускать raymarching на необходимых пикселях. Насколько мне известно, при любом ответвлении в HLSL будут вычисляться обе стороны ответвления, а одно будет отбрасываться. Поэтому я не могу сделать что-то вроде этого псевдокода в пиксельном шейдере: if(!PixelReprojection) { return 0;} else { return Raymarch(...); }, так как он будет вычислять Raymarch даже для пикселей, которые перепроецируются.

Я не вижу другого способа архивировать это... Есть ли в HLSL какое-либо ветвление, которое позволяет это сделать? Он не может быть статичным, поскольку пиксели, подвергнутые raymarching и репроецированию, меняются каждый кадр. Насколько мне известно, мне действительно любопытны любые идеи, как автор мог добиться такого десятикратного увеличения производительности, поскольку он пишет код и на графическом процессоре.

Я был бы очень признателен за любой вклад здесь.

С уважением, фудий


person foodius    schedule 15.11.2019    source источник


Ответы (1)


TLDR: используйте атрибут [branch] перед оператором if.


Насколько мне известно, при любом ответвлении в HLSL будут вычисляться обе стороны ответвления, а одно будет отбрасываться.

На самом деле это не совсем правильно. Да, ветвь может быть сглажена, что означает, что обе стороны рассчитываются так, как вы описали, но она также может быть не сглажена (так называемое динамическое ветвление).

Теперь, отсутствие выравнивания ветви имеет некоторые недостатки: если два потока в одной и той же волне идут разными путями в ветви, должна быть создана вторая волна, потому что все потоки в волне должны выполнять один и тот же код (поэтому некоторые потоки будут перемещен в только что созданную волну). Следовательно, в таком случае многие потоки «отключены» (это означает, что они выполняют тот же код, что и другие потоки в своей волне, но фактически ничего не записывают в память). Тем не менее, этот динамический вид ветвления может быть быстрее, чем выполнение обеих сторон ветвления, но это зависит от фактического кода.

Можно даже устранить этот недостаток с помощью разумного проектирования шейдеров (а именно, гарантировать, что потоки, которые принимают одну сторону ветви, находятся в одной и той же волне, поэтому внутри волны не происходит расхождения. Однако это требует некоторого знания базового оборудования, как размер волны и так далее)

В любом случае: Если не указано иное, компилятор HLSL самостоятельно решает, использует ли ветвь динамическое ветвление или сглаживает. Однако можно применить один из двух способов с помощью добавление атрибута в оператор if, например:

//Enforce dynamic branching:
[branch] 
if (...) { ... }
else { ... }

//Enforce flattening of the branch:
[flatten]
if (...) { ... }
else { ... }
person Bizzarrus    schedule 16.11.2019
comment
Эй, большое спасибо за ответ! Это действительно помогло и немного сократило время рендеринга! На самом деле я наткнулся на флаг [branch] раньше, пытаясь решить проблему, я просто думал, что это не сработает, потому что они сказали, что несколько графических процессоров поддерживают динамическое ветвление. Оглядываясь назад на те страницы, я заметил, что они были немного старше :D Так что еще раз большое спасибо! - person foodius; 18.11.2019
comment
Компилятор, вероятно, все равно сочтет и if, и movc одним и тем же типом разветвления графа зависимостей SSA, и будет вставлять код как для маскирования векторной записи — когда разные дорожки в волне берут разные ветви, потому что это просто необходимо — так и для скалярного перехода, когда вся волна пошла по тому же пути, что и оптимизация. - person Triang3l; 19.01.2020