2
votes

I'm working on an game, using OpenGL ES 2.0

I would like to eliminate branches in the fragment shader, if possible. But there is a function, which I cannot improve:

float HS(in float p, in float c) {

    float ap = abs(p);

    if( ap > (c*1.5) ) {
        return ap - c ;
    } else {
        return mod(ap+0.5*c,c)-0.5*c;
    }

}

The c is a constant in most of the cases, if it helps in this situation. I use this function like this:

vec3 op = sign(p1)*vec3(
    HS(p1.x, cc),
    HS(p1.y, cc),
    HS(p1.z, cc)
);
1
Do you want to increase the performance of this code, or do you want to remove the condition? Because those are two different things.Nicol Bolas
I would like to increase the performance of this code, by removing if-else branches first.Iter Ator
And what if that's not the most effective way to improve performance here?Nicol Bolas
In that case, I'm curious to any kind optimizations.Iter Ator

1 Answers

3
votes

Here's a trick that "eliminates" the branch. But the more important thing it does is vectorize your code. After all, the compiler probably eliminated the branch for you; it's far less likely that it realized it could do this:

vec3 HSvec(in vec3 p, in const float c)
{
  vec3 ap = abs(p);
  vec3 side1 = ap - c;
  const float val = 0.5 * c;
  vec3 side2 = mod(ap + val, vec3(c)) - val;

  bvec3 tests = greaterThan(ap, vec3(c*1.5));
  return mix(side2, side1, vec3(tests));
}

This eliminates lots of redundant computations, as well as doing lots of computations simultaneously.

The key here is the mix function. mix performs linear interpolation between the two arguments based on the third. But since a bool converted to a float will be exactly 1.0 or 0.0, it's really just selecting either side1 or side2. And this selection is defined by the results of the component-wise greaterThan operation.