Hi! Previously I’ve written a shader for my custom blending algorithm. (For order independant transparency)
The idea is to store the alpha value and the z of the nearest fragment to a texture, and then, I use this texture to apply the blending on the second pass. (I just have to change the blend equation a little when the first object drawn is nearest the next one.)
But, with GLSL 330 the blending is not working. (The second semi-transparent object hide the first one : if I draw a red rectangle and then a green rectangle, the color where the two rectangles overlap is green or it should be yellow.)
You need to do the texture access outside of a condition. Or you need to get derivatives outside of the condition, then use the textureGrad function inside of the condition.
Then the GLSL specification needs to be corrected to match the “actual” meaning (which presumably means whatever vendors’ implementations do in practice).
What it says is:
3.8.2 Uniform and Non-Uniform Control Flow
When executing statements in a fragment shader, control flow starts as uniform control flow; all fragments
enter the same control path into main(). Control flow becomes non-uniform when different fragments
take different paths through control-flow statements (selection, iteration, and jumps). Control flow
subsequently returns to being uniform after such divergent sub-statements or skipped code completes,
until the next time different control paths are taken.
According to that, it’s not about whether multiple control paths exist, but whether different fragments actually take different paths (which clearly cannot happen if the condition involves only uniforms and constants).
Maybe that’s not what the authors meant, but that’s what it says (although I wouldn’t be all that surprised if it’s incorrect, given the attempt at defining “dynamically uniform” in 3.8.3).
And in fact, I’m unsure how any implementation would behave otherwise, unless it’s specifically performing a compile-time check for whether a derivative is being requested within a branch and if so silently eliding it. The “obvious” implementation strategies (linear interpolation with a parameter of zero or one, dynamic recompilation, and conditional store) would only be affected by whether control flow actually diverged at execution time, not whether they might diverge based upon static analysis.
Control flow becomes non-uniform when different fragments take different paths through control-flow statements (selection, iteration, and jumps).
What is not mentioned is whether these “different fragments” are in the same rendering call or not. If the scope of the uniformality is only within the rendering call (or even the same primitive), then conditions based on “uniform” values do represent uniform flow control.
So it’s unclear if it is uniform control flow or not.
Hmmm, my reading of the spec is that “uniform flow control” essentially equates to “no flow control at all”, but the statement that “This is similarly defined for other shader stages, based on the per-instance data items they process” definitely clouds the matter (it doesn’t help either that it’s not totally clear what is meant by “per-instance” here - I’m assuming that the section on “interface blocks” provides the definition, but it would be nicer to have an explicit definition).
D3D is quite a bit more explicit:
When flow control is present in a shader, the result of a gradient calculation requested inside a given branch path is ambiguous when adjacent pixels may execute separate flow control paths. Therefore, it is deemed illegal to use any pixel shader operation that requests a gradient calculation to occur at a location that is inside a flow control construct which could vary across pixels for a given primitive being rasterized.
So there the limit is per-primitive, and it seems reasonable to me to assume that this is a hardware constraint rather than an API constraint; if so then the same should apply to GL.