1

I have a fairly basic fragment shader which can sample from texture and change the assigned fixed color based on the sample. If I sample from the texture, I do an alpha check and discard low-alpha values. Is there a way to make this process branchless?

#version 460 core
layout (location = 0) out vec4 FragColor;

uniform vec4 color;
uniform sampler2D tex;
uniform bool useTex;
in vec2 texCoords;

void main() {
   vec4 tex = texture(tex, texCoords);
   if(useTex && tex.a < 0.1) { discard; }
   vec4 outColor = mix(color, color * tex, int(useTex));
   FragColor = outColor;
}
Doga Oruc
  • 568
  • 2
  • 15
  • 2
    I don't think that's possible. Why do you want it to be possible? – user253751 May 03 '21 at 17:03
  • actually you could turn on alpha blending and emit a pixel with zero alpha – user253751 May 03 '21 at 17:03
  • If a sampler is used in a shader and the sampler’s associated texture is not complete (or not present), the `texture` function returns (0.0, 0.0, 0.0, 1.0) (in case of a floating point format). Possibly you can use this information to tweak the shader code. – Rabbid76 May 03 '21 at 17:47
  • 1
    a pixel wth 0 alpha is different from a pixel that is not there. https://stackoverflow.com/questions/27575878/opengl-why-isnt-discarding-fragments-the-same-as-setting-their-alpha-to-zero – Doga Oruc May 03 '21 at 17:54
  • @DogaOruc: I think the question about why you need this is valid. You want to do a thing or not do a thing. How is that process not going to involve a branch? – Nicol Bolas May 03 '21 at 18:03
  • 1
    @DogaOruc: Let me explain that a bit more: what do you mean by "branch"? Are you referring to the [performance cost of conditions](https://stackoverflow.com/q/37827216/734069)? The performance cost of [having a `discard` in your shader at all](https://stackoverflow.com/q/8509051/734069)? Or something else? – Nicol Bolas May 03 '21 at 18:05
  • See how I'm using a `mix` instead of an `ìf`? That is what I want to do. I want to be able to discard without using an if statement, in other words, I want the discarding to be branchless. Is this possible? – Doga Oruc May 03 '21 at 18:19
  • 1
    Have you ever considered that your "branchless" mix solution is actually worse than using a proper branching? In your case, a uniform branch which would contain the whole `texture` call would make a gazillion more sense, and if you are really lucky, the shader compiler might even see that, but I wouldn't bet on it. – derhass May 03 '21 at 18:35
  • @derhass please elaborate because I thought avoiding the branch using mix was better. – Doga Oruc May 03 '21 at 18:46
  • 1
    The issue with branching on GPUs is divergent control flow, where the GPU has tro execute both branches just because any single invocation in the group needs it. Your branchless solution is to always unconditionally execute both code paths anyway. However, the original branch would never lead to divergent control flow because the condition `useTex` is an uniform. – derhass May 03 '21 at 19:18
  • 1
    The real performance costs of this shader is a) the texture sampling, and b) the `discard` (which might disable some of your GPUs depth buffer optimizations). – derhass May 03 '21 at 19:20
  • @DogaOruc: "*I thought avoiding the branch using mix was better*" [Stop thinking that](https://stackoverflow.com/questions/37827216/do-conditional-statements-slow-down-shaders). – Nicol Bolas May 03 '21 at 19:56

1 Answers1

2

I recommend always binding a texture, at least a 1x1 texture with the color (1, 1, 1, 1). With this approach, you can omit out useTex entirely and the shader is much simpler:

#version 460 core

layout (location = 0) out vec4 FragColor;

uniform vec4 color;
uniform sampler2D tex;
in vec2 texCoords;

void main() 
{
    vec4 outColor = color * texture(tex, texCoords);
    if (outColor.a < 0.1)
        discard;
    FragColor = outColor;
}

If you could use Blending instead of discard, the shader could be further simplified:

#version 460 core

layout (location = 0) out vec4 FragColor;

uniform vec4 color;
uniform sampler2D tex;
in vec2 texCoords;

void main() 
{
    FragColor = color * texture(tex, texCoords);
}
Rabbid76
  • 142,694
  • 23
  • 71
  • 112