Fragment shader equal conditional optimization

Hi,

I’ve read using conditionals in shaders it’s not good for performance.

I had a case in a fragment shader where I wanted to do this:
vec2 x = uniform == vec2(1.0, 1.0) ? vec2(y) : uniform;

So far it’s working lovely, but I am wondering if I could improve the performance of it by not having the conditional but maybe the step() function or something else?

I haven’t really figured out how to translate that ternary conditional into other built-in shader functions.

Does anybody knows how to do this?

Thx in advance!

1 Like

I feel there’s not enough context.

You could use multiplication instead of an if in some cases, but you need to know the limits of your values typically (e.g. normalized to 0…1 range).

Just a stylistic remark: I suggest not using inline conditional operator where clarity matters. It’s typically easier to read

if(...){
...
}else{
...
}

You could try (for 1D case):

// will be 0 when uniform.x === 1.0, and 1 otherwise
float f_x = step(0.0001, abs(uniform.x - 1.0)) ;

// combine the two in a binary way
x.x = mix(y, uniform.x , f_x);

hope that helps

Hi Usnul,

Thank you so much for the reply!

That still doesn’t clarify how best I could improve the conditional.
My math is not so advance, I feel.

Here is a link for a context: https://codepen.io/ilithya/pen/PoZmOzy

E.g. If you look into the HTML line 48, I have this conditional because there’s a switch I created via JS that allows the user choose the shader color: monochrome vs. multicolored.

And basically this is why I wanted the conditional inside the shader, so that it toggles the color variable depending the user’s choice.

1 Like

okay, i got it. I think you don’t have to improve the performance for this.

If I was doing this, i would either write monocrhome color into the uniform upon the toggle, or I would implement HSL filter, where I would break-down rgb color into HSL and combine it with some specified HSL uniform, this essentially gives you 3 filters:

  • tint / hue-roate
  • brighten/darken
  • saturate/de-saturate

Generally a few conditions (branches) inside the shader are ok, they are not evil. If your condition is not nested and it evaluates to the same value for every pixel (fragment) it should have little to no overhead.

I suggest you pick a target device, run your shader there and see how long it takes to render a frame, if it’s below 15ms (for 60fps) - you’re pretty much golden. There are caveats, such as browser doing it’s thing, and having budget to handle user input etc. I would recommend that anything that renders in 7ms or so doesn’t need optimization, your users won’t feel it.

PS:
for HSL stuff, here’s example pseudo-code:

vec3 uniform uFilterHSL;
vec3 color_rgb = ...

vec3 color_hsl = rgb2hsl(color_rgb);

vec3 color_hsl_filtered = vec3( mod(color_hsl.x + uFilterHSL.x, 1.0), color_hsl.y * uFilterHSL.y, color_hsl.z * uFilterHSL.z);

gl_FragColor = vec4( hsl2rgb(color_hsl_filtered) , 1.0);

uFilterHSL will have following components:

  • x - hue offset
  • y - saturation (1 - full color, 0 - monochome, 2 - oversaturate by 200%)
  • z - brightness (1 - normal, 0 - black, 0.5 - dim, 2 - brightened by 200%)
1 Like

Thank you so much Usnul! <3
This answers my question now.

Honestly the fps, animation and quality of those 4 shaders are totally fine after my testing. Even on an old phone. But I was wondering if still the performance could be improved even more.

I am afraid I am a bit obsessed sometimes intro figuring out the best way to write code, and when I am on my own with these personal projects, I feel I have no idea if it’s good enough or not yet.

I’d love a robot who could do my code reviews for personal projects :grimacing:

Thanks for the bonus tip on HSL stuff. And happy Friday!!

2 Likes