Подтвердить что ты не робот

Shader optimization: Является ли тройной оператор эквивалентным ветвлению?

Я работаю над вершинным шейдером, в котором я хочу условно удалить некоторые вершины:

float visible = texture(VisibleTexture, index).x;
if (visible > threshold)
    gl_Vertex.z = 9999; // send out of frustum

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

На мой вопрос: лучше ли тернарный оператор (независимо от вопросов читаемости)?

float visible = texture(VisibleTexture, index).x;
gl_Vertex.z = (visible > threshold) ? 9999 : gl_Vertex.z;

Если нет, преобразует его в расчет, стоящий?

float visible = texture(VisibleTexture, index).x;
visible = sign(visible - threshold) * .5 + .5; // 1=visible, 0=invisible
gl_Vertex.z += 9999 * visible; // original value only for visible

Есть ли еще лучший способ сбросить вершины, не полагаясь на шейдер Geometry?

Заранее благодарим за помощь!

4b9b3361

Ответ 1

Тернарный оператор - это просто синтаксический сахар для оператора if. Они одинаковы.

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

Часто разветвление по умолчанию не используется.

В вашем случае тернарный оператор (или оператор if), вероятно, сначала оценивает обе стороны условия, а затем отбрасывает ветвь, которая не удовлетворялась условием.

Чтобы использовать ветвление, вам нужно установить флаг компилятора ветвления в вашем шейдерном коде, чтобы сгенерировать сборку, которая поручает графическому процессору фактически попытаться разветкиться (если GPU поддерживает ветвление). В этом случае GPU попытается разветкиться только в том случае, если предсказатель ветвления говорит, что некоторое предопределенное количество ядер будет принимать одну из ветвей.

Ваш пробег может варьироваться от одного компилятора и графического процессора к другому.

Ответ 2

Это математическое решение может быть использовано для замены условных операторов. Это также реализовано в OpenCL как bitselect(condition, falsereturnvalue, truereturnvalue);

int a = in0[i], b = in1[i];
int cmp = a < b; //if TRUE, cmp has all bits 1, if FALSE all bits 0
// & bitwise AND
// | bitwise OR
// ~ flips all bits
out[i] = (a&cmp) | (b&~cmp); //a when TRUE and b when FALSE

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

Ответ 3

На самом деле это зависит от используемого языка шейдеров.

  • В HLSL и Cg троичный оператор никогда не приведет к ветвлению. Вместо этого оба возможных результата всегда оцениваются, а неиспользованный - отбрасываются. Чтобы привести документацию HLSL:

    В отличие от оценки короткого замыкания &, || и?: в C выражения HLSL никогда не замыкают оценку, потому что они являются векторными операциями. Все стороны выражения всегда оцениваются.

    Для Cg ситуация аналогична, также здесь тернарный условный оператор является векторным оператором. (документация):

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

  • В ESSL и GLSL тернарный оператор всегда приведет к ветвлению. Это не векторный оператор, поэтому условие должно вычисляться до булева. См. спецификация GLSL:

    Он работает на трех выражениях (exp1? exp2: exp3). Эта оператор вычисляет первое выражение, которое должно приводить к скалярному булевому. Если результат верен, выбирает для оценки второго выражения, в противном случае он выбирает для оценки третьего выражения. Только оценивается одно из второго и третьего выражений.

    (Источник для ESSL)

Иллюстрация разницы, например, доступна на тестовом сайте Khronos WebGL для тернарных операторов.

Ответ 4

Ответ зависит от трех вещей:

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

Рассмотрим следующий пример:

int a = condition ? 100 : 0;

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

int a = condition * 100

Такая же оптимизация возможна с эквивалентным условием if:

int a = 0;

if (condition) {
    a = 100;
}

Все зависит от конкретных оптимизаций, выполняемых компилятором.

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

Ответ 5

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