GLSL ES 2.0 highp: Vertex vs Fragment Stage
In legacy GLSL ES 2.0, the primary difference between the vertex and
fragment stages regarding highp precision is that
highp support is mandatory in the vertex stage but entirely
optional in the fragment stage. This architectural distinction was
introduced to accommodate early mobile GPUs with limited silicon area,
requiring developers to write conditional code when targeting hardware
that could not process 32-bit floating-point values during
rasterization.
Vertex Shader Precision Requirements
In the vertex stage, hardware implementations are strictly required
to support highp floating-point and integer types. Vertex
transformations rely heavily on high numerical precision to calculate
accurate coordinate projections and prevent geometry jitter, vertex
snapping, or matrix drift.
Because highp is guaranteed:
- Compilers provide built-in default precisions: both
floatandintdefault tohighp. - Explicit precision declarations are optional for standard types in vertex shaders.
- Numerical operations typically conform to standard 32-bit IEEE 754 floating-point ranges (a minimum relative precision of \(2^{-62}\) and a dynamic range spanning at least \(2^{-62}\) to \(2^{62}\)).
Fragment Shader Precision Requirements
In the fragment stage, highp floating-point support is
optional. Many early OpenGL ES 2.0 mobile GPUs saved power and die space
by providing only 16-bit half-precision (mediump)
arithmetic units in their fragment pipelines, reserving full 32-bit
operations exclusively for the vertex pipeline.
Key behaviors in the fragment stage include:
- No Default Float Precision: Unlike the vertex
stage, the fragment stage does not specify a default precision for
floating-point values. Omitting a precision declaration (such as
precision mediump float;) causes a compile-time error. - Optional Hardware Support: An implementation can
legally support only
lowpandmediumpfor floating-point calculations within fragment code. - Integer Precision:
highpintegers are also optional in the fragment stage, defaulting tomediumpif an integer precision is not declared.
Checking for
Support: GL_FRAGMENT_PRECISION_HIGH
To write portable shaders that run across all compliant OpenGL ES 2.0
devices, GLSL ES 2.0 defines a preprocessor macro:
GL_FRAGMENT_PRECISION_HIGH.
If an implementation supports highp in the fragment
stage, the compiler sets this macro to 1. If the
implementation does not support it, the macro remains undefined.
Developers handle this disparity using preprocessor directives:
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endifPractical Implications
Relying on mediump fallbacks on hardware lacking
fragment highp creates several rendering challenges:
- Banding and Truncation: Calculations involving world-space coordinates, large UV domains, or high-frequency procedural noise often display severe visual banding or precision artifacts when clamped to 16-bit floating-point limits.
- Non-Linear Depth: Computing or reconstructing depth
values inside the fragment shader often breaks down under
mediump, as the 10-bit mantissa lacks the resolution needed for non-linear depth buffers. - Cross-Platform Portability: Desktop shaders ported
directly to mobile GLSL ES 2.0 frequently fail to compile if they
declare
precision highp float;on devices whereGL_FRAGMENT_PRECISION_HIGHis absent.