1

Suppose we have the following fragment shader:

varying vec2 vUv; // uv coordinates
varying float vTexture; // texture index in array of textures

uniform sampler2D textures[2]; // number of textures

void main() {
  int textureIndex = int(floor(vTexture)); // convert texture index to int for equality checks

  if (textureIndex == 0) {
    gl_FragColor = texture2D(textures[0], vUv);
  }

  if (textureIndex == 1) {
    gl_FragColor = texture2D(textures[1], vUv);
  } 
}

If a given value coming through this shader has a textureIndex = 0, will that value check the second if conditional, or will the establishment of gl_FragColor cause the main() function to "return" or exit? Is there a way to force a fragment shader to exit or return at a given point?

duhaime
  • 25,611
  • 17
  • 169
  • 224
  • Shaders execute _very differently_ from normal CPU programs. It'll very likely not only evaluate the condition, but also evaluate _both_ blocks and then drop one result because it's not needed. – tkausl Apr 30 '18 at 22:10
  • @tkausl thanks for your response. Is there a way to prevent the shader loop from evaluating all of these conditions? – duhaime Apr 30 '18 at 22:13
  • I don't think so, this would really hurt performance, why do you want them to not be evaluated? – tkausl Apr 30 '18 at 22:14
  • Ah, I assumed that the evaluations would hurt performance more than just exiting. In my case I have a number of these conditionals (20 or so, each of which uses a distinct texture as above) – duhaime Apr 30 '18 at 22:17
  • 1
    Well in your case, why couldn't you just `gl_FragColor = texture2D(textures[textureIndex], vUv);`? – tkausl Apr 30 '18 at 22:21
  • I also feel like that syntax should compile, but it throws: `Index expression must be constant` – duhaime Apr 30 '18 at 22:26
  • Yeah GLSLES 1.0 requires index expressions to be constant, also note that this kind of access is slower than just sampling from both and discarding one result because of prefetching, what @tkausl sais about branching is not true either, modern GPUs are capable of 'real' branching(as opposed to value masking). Regarding your question there's no special hidden actions happening when you assignt o `gl_FragColor`, so if you want to "return or exit" you will have to actually `return`(yes you can return `void` by just calling `return;`) or you know, make that other `if` an `else if`. – LJᛃ May 01 '18 at 00:41
  • @LJᛃ Thanks for your response. The if blocks are actually all if/else blocks, but I wanted to ask the question. Can you say more about what you mean when you say "this kind of access is even slower than just sampling from both and discarding one result because of prefetching"? – duhaime May 01 '18 at 00:44
  • Anything that's not a very basic texture lookup is classified as a [dependent texture read](https://stackoverflow.com/questions/1054096/what-is-a-dependent-texture-read)([apple developer guide](https://stackoverflow.com/questions/1054096/what-is-a-dependent-texture-read)) and stalls the pipeline(texture will be accessed and read on demand) while a basic texture lookup with a `varying` uv(with no component swizzle on it) and a hardcoded sampler will be prefetched hiding quite a lot of the latency(gpu-memory). – LJᛃ May 01 '18 at 00:47
  • @LJᛃ Is there a more performant way to do texture lookups if there are multiple textures in one mesh? Should I pass each texture in as a named texture instead of packing them all into a sampler2D? Does that make a big difference? – duhaime May 01 '18 at 01:06
  • you indices and samplers are static so after compilation it should be the same as if you had used individually named textures. – LJᛃ May 01 '18 at 09:31

1 Answers1

0

New Answer (specific to the question)

void main() {
  int textureIndex = int(floor(vTexture)); // convert texture index to int for equality checks

  if (textureIndex == 0) {
    gl_FragColor = texture2D(textures[0], vUv);
  }

  if (textureIndex == 1) {
    gl_FragColor = texture2D(textures[1], vUv);
  } 

  // <--- HERE (ONCE IT RUNS THE CODE ABOVE, IT REACHES THE "END" AND EXITS)

}
pailhead
  • 5,162
  • 2
  • 25
  • 46
  • Great question disguised as an answer. That being said, branching language constructs are there since the incarnation of GLSL, there is no code that is more or less "shaderlike", intrinsic value masking is still better than lerping(`mix`) between the values(i know from experience) since you make a binary "a or b" question into an unpredictable "some of a and some of b", most optimizers are not smart enough to realize that your float is actually an int with the range between `0` and `1` and even then your approach is not scalable, you'd be adding lerps on lerps on lerps... – LJᛃ May 01 '18 at 09:47
  • Regarding `discard` and `return` I don't see how this is related, `discard` discards the currently rendered fragment so its certainly not applicable to use as an "early out" once you have the value you're looking for, also it causes other pipeline related changes impeding with performance. – LJᛃ May 01 '18 at 09:51
  • Your last example of "shaderlike calls" is an example of using vectorized instructions([SIMD](https://en.wikipedia.org/wiki/SIMD)) and not related to this question nor exclusive to GPUs([SSE](https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions)). – LJᛃ May 01 '18 at 09:56
  • 2
    BY ALL MEANS provide an answer that is correct and related to this question. – pailhead May 01 '18 at 17:15
  • Rather than scattering "you've no idea what you're talking about" as many comments. – pailhead May 01 '18 at 17:17
  • ` what @tkausl sais about branching is not true either, modern GPUs are capable of 'real' branching(as opposed to value masking).` <- like this, what counts as a modern gpu for example. – pailhead May 01 '18 at 17:19
  • There we go, i edited it, no misinformation, no irrelevance, straight to the point, in form of an answer. I feel that this awesome title is being wasted on a senseless example, but if there's no will to make a meaningful question here, and write a correct answer, this seems like it will do. – pailhead May 01 '18 at 17:29
  • I've attempted to post my question not disguised as an answer :) https://stackoverflow.com/questions/50144002/glsl-vertex-shader-early-return-and-branching/50145763#50145763 – pailhead May 03 '18 at 03:56