What Does the Precise Qualifier Do in GLSL?
The precise qualifier in the OpenGL Shading Language
(GLSL) ensures exact reproducibility of floating-point calculations
across different shaders or stages of a graphics pipeline. Modern GPU
shader compilers aggressively optimize math operations through
reordering, algebraic simplification, and hardware-specific instruction
fusion like Fused Multiply-Add (FMA). While these optimizations increase
execution speed, they introduce tiny rounding discrepancies that cause
rendering artifacts such as visual seams, cracking between tessellated
geometry, or Z-fighting. Declaring a variable, function return value, or
computation with precise forces the compiler to disable
unsafe optimizations and evaluate the expression in strict evaluation
order, guaranteeing bit-exact consistency.
The Problem: Compiler Optimizations and Invariance Issues
GPU architectures prioritize throughput and parallel execution. To achieve high performance, GLSL compilers routinely perform optimizations on floating-point expressions:
- Algebraic Reassociation: The compiler may treat
operations as mathematically associative even when floating-point math
is not. An expression written as
(a + b) + cmight be executed asa + (b + c). Because floating-point rounding depends heavily on the magnitudes of intermediate values, the two results rarely match at the lowest bit level. - Expression Substitution and Factoring: Compilers
may simplify expressions like
a * c + b * cinto(a + b) * cto reduce total instruction count. - Fused Multiply-Add (FMA): Modern GPUs support
dedicated hardware instructions that compute
(a * b) + cin a single cycle with only one final rounding step instead of two separate roundings. If one shader evaluates an expression using an FMA instruction while another evaluates separate multiply and add steps, their outputs will diverge.
In graphics rendering, identical math must often produce identical outputs. For instance, when adjacent polygons share vertices computed in different shader stages or draw calls, slight floating-point divergences cause the vertices to separate, creating visible cracks or flickering geometry.
How the Precise Qualifier Works
The precise keyword acts as a compiler constraint. It
alters code generation rules for any calculation contributing to the
declared variable:
precise vec4 position;
position = mvpMatrix * vertex;When a variable is marked precise, the compiler must
adhere to strict behavioral rules:
- Strict Evaluation Order: Operations must follow standard operator precedence and grouping parentheses exactly as written in the source code.
- Prevention of Unsafe Algebraic Reductions: The compiler is forbidden from restructuring expressions using distributive, associative, or commutative identities that could alter precision.
- Consistent FMA Usage: The compiler must either avoid fusing operations arbitrarily or ensure operations fuse identically everywhere the precise value is evaluated.
Decorating Functions and Return Values
The precise qualifier can also be applied to function
declarations to ensure that all internal computations producing the
return value maintain precision:
precise float calculateDepth(float near, float far, float z) {
return (far + near) / (far - z);
}precise vs. The
invariant Qualifier
GLSL provides two primary qualifiers for consistency:
invariant and precise. While they address
similar problems, they operate at different scopes:
invariant: Applied to shader outputs (e.g.,invariant out vec4 gl_Position;). It requires two separate shader programs to produce the exact same final output value, provided both shaders contain identical code paths and uniforms. However,invariantrelies on complete source code parity and does not give fine-grained control over internal intermediate calculations.precise: Applied directly to variables, expressions, or function outputs. It provides fine-grained, algorithmic-level guarantees.preciseensures deterministic evaluation even if the surrounding shader code changes or if calculations are structured across different functions.
Performance Considerations
Restricting compiler optimizations prevents the GPU driver from
scheduling instructions in the most latency-tolerant or parallel manner.
Overusing the precise qualifier across an entire shader can
lead to measurable frame rate drops due to higher register pressure and
lost FMA optimization opportunities.
To maintain optimal rendering performance, the precise
qualifier should be restricted strictly to critical calculations where
geometric continuity and bit-level invariance are necessary, such as
shared screen-space coordinates, displacement mapping calculations,
tessellation evaluation shaders, and shadow-map depth matching.