Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shader optimization: Is a ternary operator equivalent to branching?

I'm working on a vertex shader in which I want to conditionally drop some vertices:

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

I know that branches kill performance when there's little commonality between neighboring data. In this case, every other vertex may get a different 'visible' value, which would be bad for the performance of the local shader core cluster (from my understanding).

To my question: Is a ternary operator better (irrespective of readability issues)?

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

If not, is converting it into a calculation worthwhile?

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

Is there an even better way to drop vertices without relying on a Geometry shader?

Thanks in advance for any help!

like image 527
sharoz Avatar asked Feb 06 '11 03:02

sharoz


People also ask

Is ternary operator branching?

In ESSL and GLSL the ternary operator will always lead to branching. It is not a vector operator, so the condition has to evaluate to a boolean.

Which of the following is ternary operator?

Remarks. The conditional operator (? :) is a ternary operator (it takes three operands). The conditional operator works as follows: The first operand is implicitly converted to bool .


3 Answers

Actually this depends on the shader language one uses.

  • In HLSL and Cg a ternary operator will never lead to branching. Instead both possible results are always evaluated and the not used one is being discarded. To quote the HLSL documentation:

    Unlike short-circuit evaluation of &&, ||, and ?: in C, HLSL expressions never short-circuit an evaluation because they are vector operations. All sides of the expression are always evaluated.

    For Cg the situation is similar, also here the ternary conditional operator is a vector operator. (documentation):

    Unlike C, side effects in the expressions in the second and third operands are always executed, regardless of the condition.

  • In ESSL and GLSL the ternary operator will always lead to branching. It is not a vector operator, so the condition has to evaluate to a boolean. See the GLSL specification:

    It operates on three expressions (exp1 ? exp2 : exp3). This operator evaluates the first expression, which must result in a scalar Boolean. If the result is true, it selects to evaluate the second expression, otherwise it selects to evaluate the third expression. Only one of the second and third expressions is evaluated.

    (Source for ESSL)

An illustration of the difference is for instance available on the Khronos WebGL test site for ternary operators.

like image 177
soulsource Avatar answered Sep 21 '22 03:09

soulsource


A ternary operator is just syntactic sugar for an if statement. They are the same.

If you had more to write inside of your if statement, there might be some optimization that could be done here, but with so little inside of either branch, there is nothing to optimize really.

Often branching is not used by default.

In your case, the ternary operator (or if statement) is probably evaluating both sides of the condition first and then discarding the branch that was not satisfied by the condition.

In order to use branching, you need to set the branching compiler flag in your shader code, to generate assembly that instructs the GPU to actually attempt to branch (if the GPU supports branching). In that case, the GPU will try to branch only if the branch predictor says that some predefined number of cores will take one of the branches.

Your mileage may vary from one compiler and GPU to another.

like image 23
Olhovsky Avatar answered Sep 20 '22 03:09

Olhovsky


This mathematical solution may be used for your replacement of conditional statements. This is also implemented in OpenCL as 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

I am however unsure about implementing this in your situation , I'm not sure I fully understood your code, but I do hope supplying you with this answer would help, or others.

like image 23
Mnescat Avatar answered Sep 22 '22 03:09

Mnescat