4
votes

Here's my fragment shader:

#version 420 core
#extension GL_ARB_explicit_uniform_location : enable
#extension GL_ARB_shader_storage_buffer_object : require
layout(early_fragment_tests) in;
layout(binding = 4, offset = 0) uniform atomic_uint num_fragments;
// ...
void main(void)
{
    atomicCounterIncrement(num_fragments);
    frag_color = vec4(1.0, 0.0, 0.0, 0.0);

    atomicAdd(...);
}

My triangles completely cover the screen. The expected behavior is for num_fragments to be equal to the number of pixels (640*480 = 307200), which it is for a single layer of triangles. However, when I add a triangle behind the existing triangles, num_fragments becomes a higher value, like the fragment shader is being executed for the occluded triangle.

Why is this? I though that the early_fragment_tests directive would prevent this behavior. This isn't just an optimization, because there are atomic stores in the shader that should only be run for un-occluded pixels.

1
Are you running into any of these limitations?Andrew Williamson
@Joel: Have you checked that the depth test is actually enabled, and a depth buffer is present?derhass

1 Answers

5
votes

My triangles completely cover the screen. The expected behavior is for num_fragments to be equal to the number of pixels (640*480 = 307200), which it is for a single layer of triangles.

No, that is not the expected behavior.

Early depth tests are not magic. They do not in any way guarantee that a random assortment of triangles will have zero overdraw. They simply force the fragment tests to happen before executing the fragment shader. All this does is ensure that the test has passed or failed. And therefore, the fragment shader will only execute for fragments that have passed.

Overdraw has nothing to do with early depth tests. Overdraw is about the order of the triangles you're rendering. If you render them sorted front-to-back, then you get zero overdraw. If you render them sorted back-to-front, then you get the maximum possible overdraw.

This happens regardless of whether fragment tests happen before the fragment shader or not.

This isn't just an optimization, because there are atomic stores in the shader that should only be run for un-occluded pixels.

The only way to do that is to make a depth-only pre-pass. You have to render your scene to the depth buffer (with no fragment shader). Then when you render the scene for real, the only fragments that pass the depth test will be the ones that are visible.