Замедляет ли if-операторы мой шейдер?

Я хочу знать, есть ли "if-операторы" внутри шейдеров (вершина / фрагмент / пиксель...) действительно замедляют производительность шейдеров. Например:

лучше ли использовать это:

vec3 output;
output = input*enable + input2*(1-enable);

вместо этого:

vec3 output;
if(enable == 1)
{
    output = input;
}
else
{
    output = input2;
}

на другом форуме был разговор об этом (2013): http://answers.unity3d.com/questions/442688/shader-if-else-performance.html Здесь ребята говорят, что If-заявления действительно плохо для производительность шейдера.

также здесь они говорят о том, сколько находится внутри операторов if / else (2012): https://www.opengl.org/discussion_boards/showthread.php/177762-Performance-alternative-for-if-(-)

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

EDIT:

что с этим делом, вот допустим, включить единый переменной и всегда устанавливайте в 0:

if(enable == 1) //never happens
{
    output = vec4(0,0,0,0);
}
else  //always happens
{
    output = calcPhong(normal, lightDir);
}

Я думаю, что здесь у нас есть ветвь внутри шейдера, которая замедляет шейдер. Это верно?

имеет ли смысл сделать 2 разных шейдера, как один для другого, а другой для части if?

2 ответов


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

отдельные вызовы шейдеров обычно выполняются последовательно, выполняя одни и те же инструкции одновременно. Они просто выполняют их на разных наборах входных значений; они разделяют униформу, но имеют разные внутренние переменные. Один термин для группы шейдеры, выполняющие одну и ту же последовательность операций, - это "wavefront".

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

пока... это не так.

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

здесь есть несколько случаев, которые представляют, как выглядит ваш код для компилятора:

  • статические ветвления. Условие основано на константах времени компиляции; таким образом, вы знаете, глядя на код, и знаете, какие ветви будут приняты. Практически любой компилятор обрабатывает это как часть базовой оптимизации.
  • статически равномерное ветвление. Условие основано на выражениях, включающих только униформы или константы. Вы не можете знать априори, какая ветвь будет взята, но компилятор может быть статически уверен, что волновые фронты никогда не будут нарушены этим if.
  • динамическое ветвление. Условие основано на выражениях, которые включают больше, чем просто константы и униформы. Здесь компилятор не может априори сказать, если a волновой фронт будет разбит или нет. Произойдет ли это, зависит от оценки выполнения выражения условия.

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

кроме того, даже если условие принимается разными волновыми фронтами, можно реструктурировать код, чтобы не требовать фактического ветвление. Вы привели прекрасный пример:--3--> - это функционально эквивалентно if заявление. Компилятор может обнаружить что if используется для установки переменной и, таким образом, выполнения обеих сторон. Это часто делается для случаев, когда аппаратное обеспечение не может сказать, можно ли выполнить условие без расхождения, но тела двух условий малы.

почти все оборудование может обрабатывать var = bool ? val1 : val2 без необходимости расходиться. Это было возможно еще в 2002 году.

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

рабочий стол, Pre-D3D10

там, это своего рода Дикий Запад. Компилятор NVIDIA для такого оборудования был известен для обнаружения таких условий и на самом деле перекомпиляции шейдера всякий раз, когда вы меняли униформу, которая влияла на такие условия.

в общем, эта эпоха, где около 80% "никогда не использовать if заявления" происходит от. Но даже здесь, это не обязательно правда.

вы можете ожидайте оптимизации статического ветвления. Ты можешь!--19-->Надежда это статически равномерное ветвление не вызовет дополнительного замедления (хотя тот факт, что NVIDIA думала, что перекомпиляция будет быстрее, чем выполнение, делает ее маловероятной, по крайней мере, для их оборудования). Но динамическое ветвление будет стоить вам чего-то, даже если все вызовы принимают одну и ту же ветвь.

компиляторы этой эпохи делают все возможное для оптимизации шейдеров, чтобы выполнить простые условия просто. Например,output = input*enable + input2*(1-enable); - это то, что приличный компилятор может сгенерировать из своего эквивалента if заявление.

рабочий стол, пост-D3D10

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

рабочий стол, D3D11+

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

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

мобильный, ES 2.0

Добро пожаловать в дикий запад. Хотя в отличие от pre-d3d10 desktop, это в основном связано с огромным разнообразием аппаратного обеспечения ES 2.0. Там такое огромное количество вещей, которые могут обрабатывать ES 2.0 и все они работают очень по-разному друг от друга.

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

мобильный, ES 3.0+

оборудование здесь довольно более зрелое и способное чем ES 2.0. Таким образом, вы можете ожидать, что статически однородные ветви будут выполняться достаточно хорошо. И некоторые аппаратные средства, вероятно, могут обрабатывать динамические ветви так, как это делает современное настольное оборудование.


Это сильно зависит от оборудования и на состояние.

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

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

например, если одна из ветвей берется 99% случая и отбрасывает фрагмент, то, скорее всего, вы хотите сохранить условное. Но OTOH в вашем простом примере выше, если enable является некоторым динамическим условием, арифметический выбор может быть лучше.

Если у вас нет четкого случая, как указано выше, или если вы не оптимизируете для одной фиксированной известной архитектуры, вам, вероятно, лучше, чтобы компилятор понял это для вас.