Write to gl_FragDepth with early_fragment_tests


I’ve been conducting some experiments in OpenGL 4.2, and I got stuck in some non-clearly-defined-OpenGL specification (or so I think).
I know that what I want is a little non-usual, but believe me, it is for a good reason: speed up the rendering.

So, I want to perform the early fragment tests (depth and stencil) and, for the fragments that pass the tests, update the depth value.
I started by forcing the early tests with “layout(early_fragment_tests) in” which worked very well.
But when I tried to write to the depth buffer “gl_FragDepth = variableA” it did nothing.

I understand that such action may leave fragments with depth values that do not obey to the previous depth-tests, but this in fact expected by my algorithm.
I see no reason for this not to work.
Well, I can only suspect the following: maybe the depth values (from the depth buffer) are cached, and therefore, the gl_FragDepth writes are discarded? For optimization purposes, due to the usage of early fragment tests?

Thanks in advance,

That’s just how the spec is defined. Section 3.8 says:

In short, early tests are not a way to cheat. If the depth test passes, then the depth buffer will be updated to the value that was tested against, regardless of where that test happened.

Nevertheless, if this was possible it would be a great aquisition :wink:


You may be able to use the conservative depth extension (http://www.opengl.org/registry/specs/ARB/conservative_depth.txt).

For example, if you were rendering with Zfunc = less and your fragment shader was only ever increasing the depth value, the driver could do an early depth test as you’re saying you’ll never decrease Z. Thus (Zframebuffer < Zoriginal) will be always have the same value as (Zframebuffer < Zfragment), since (Zoriginal <= Zfragment). However, if you’re altering Z in a way that it may be greater or less that the Z value computed by the vertex/tess/geo shader, or decreasing with Zfunc=less/increasing with Zfunc=greater, then this won’t help.

Thanks malexander, but that won’t do the trick. =\

Another way to explain my problem is:
I want to perform a depth test before fragment shader execution, and after that, change the depth value based on some calculations.
Basically, I want to disable some fragments as they iterate through the primitives, i.e., during a single draw call. But I want to completley avoid fragment shader execution on the disabled ones (using early_fragment_tests), because my fragment shader may be pretty slow.

Unfortunatly, it seems that this is not possible.

It’s very possible; just not with early depth tests.

Remember: fragment shaders don’t execute one after another; they execute in groups that all execute instructions in lockstep. So even if you discard one fragment shader invocation, if its neighbor needs to run, it will still have to execute. Obviously all actual effects are discarded.

Because of that, if you discard early on in your fragment shader, and this discard triggers on all of the shaders in the group, then the group will be discarded (in all likelihood). So you’ll save the time it takes to execute the shader. But if not all of them would have been discarded, you wouldn’t save anything from your depth test.

In short: there is little advantage to making depth tests remove fragments you don’t like instead of just doing it in the shader. The only significant advantage you might get is z-culling hardware that discards entire fragment groups based on their Z values.

It appears that the conservative depth can’t be used with the early-depth, since the latter makes it ignore the gl_FragDepth output values. Do I understand correctly that the conservative depth is just a hint? There is no way to make sure, in this case, that we get both early and late depth?

Conservative depth doesn’t make sense with early-depth tests. Conservative depth is all about the shader promising how the FS’s written depth will relate to the original depth. With early depth tests, the depth write has already happened (effectively) by the time the FS executes.