GLSL early_fragment_tests and Depth Testing

The layout(early_fragment_tests) in; layout qualifier in GLSL explicitly forces the GPU to perform depth and stencil testing prior to executing the fragment shader. Under normal pipeline conditions, drivers may defer depth tests until after fragment shading, or use implementation-dependent optimizations. Declaring this layout qualifier establishes a strict execution order, ensuring that fragments failing the depth or stencil tests are discarded before any fragment shader instructions run, thereby optimizing performance and controlling side effects.

Default Pipeline Execution vs. Early Testing

By default, OpenGL can execute the depth test either before or after fragment shading:

  • Late Fragment Tests: Standard pipeline behavior runs the fragment shader first, followed by depth testing, stencil testing, and blending. This is mandatory if the fragment shader modifies the fragment's depth value via gl_FragDepth.
  • Driver-Assisted Early-Z: Modern GPUs often perform an implicit early depth test (Early-Z) as an optimization. However, the driver can disable this optimization automatically if the fragment shader uses operations with side effects, such as writing to Shader Storage Buffer Objects (SSBOs), modifying image variables via image load/store, or using atomic counters.

What layout(early_fragment_tests) in Enforces

Declaring layout(early_fragment_tests) in; removes driver ambiguity and enforces the following pipeline sequence:

  1. Mandatory Pre-Shader Depth and Stencil Testing: The depth test and stencil test must execute before any fragment shader invocations.
  2. Conditional Execution of Fragment Shaders: If a fragment fails the depth test (for example, if it is occluded by geometry closer to the camera), the fragment shader is completely bypassed.
  3. Suppression of Shader Side Effects: Because occluded fragments are discarded prior to fragment execution, any side effects within the shader—such as atomic operations, SSBO writes, or image stores—are guaranteed not to occur for those fragments. This is crucial for algorithms like order-independent transparency, custom profiling, or software occlusion culling that rely on predictable writes.
  4. Depth Value Immutability: When this qualifier is used, the fragment shader is legally prohibited from modifying gl_FragDepth. The depth value tested and written to the depth buffer is strictly the interpolated depth generated by the primitive's rasterization.

Usage Example

#version 430 core

layout(early_fragment_tests) in;

layout(std430, binding = 0) buffer PixelCounter {
    uint visiblePixels;
};

out vec4 fragColor;

void main() {
    // Only executed for fragments that pass the depth and stencil tests
    atomicAdd(visiblePixels, 1);
    
    fragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

By enforcing early tests, the counter in the example above only increments for genuinely visible fragments, eliminating inaccurate counts caused by overdraw.