GLSL Multiview: Avoid Redundant Vertex Transforms
Multiview rendering significantly enhances graphics efficiency in
virtual reality and stereoscopic rendering by drawing a scene to
multiple render targets simultaneously using a single draw call. By
leveraging extensions such as GL_OVR_multiview and
GL_OVR_multiview2, developers can execute vertex shaders
that share common calculations across all views. This architecture
directly addresses performance bottlenecks by decoupling
view-independent geometry operations—such as skeletal animation and
world-space transforms—from view-dependent projections, effectively
eliminating redundant vertex processing passes.
The Redundancy of Traditional Multi-Pass Pipelines
In standard stereoscopic rendering, graphics applications typically generate images for each eye using one of two inefficient methods:
- Multiple Draw Calls: The application issues a separate draw call for each view. This forces the GPU to execute the entire vertex pipeline twice, repeating expensive view-invariant calculations like vertex skinning, morph target blending, physics transformations, and model-to-world matrix multiplications.
- Geometry Shader Instancing: A single draw call passes vertices to a geometry shader, which duplicates primitives and routes them to different array layers. While this avoids multiple API calls, geometry shaders frequently introduce pipeline stalls, expand memory bandwidth consumption, and cannot easily share vertex-stage calculations across duplicates.
Decoupling
Computations via gl_ViewID_OVR
Multiview rendering resolves this problem at the vertex shader stage
through the built-in variable gl_ViewID_OVR. When multiview
is enabled, the vertex shader executes with awareness of which view it
is currently computing, allowing the programmer to structure GLSL code
so that identical calculations are only evaluated once.
Vertex transformations fall into two distinct categories:
- View-Independent Transforms: Skeletal animation matrices, morphing, and transformations from object space to world space depend exclusively on the model and its animation state. These values are identical whether rendered for the left eye or the right eye.
- View-Dependent Transforms: The transformation from world space to clip space (view and projection matrices) and camera-relative calculations (such as specular view vectors) vary by eye.
By isolating these categories in GLSL, the vertex shader evaluates
the heavy view-independent transformations first, saves the intermediate
result in world space, and then applies view-specific transformations
conditionally based on gl_ViewID_OVR.
#version 300 es
#extension GL_OVR_multiview2 : enable
layout(num_views = 2) in;
layout(location = 0) in vec3 aPosition;
layout(location = 1) in vec3 aNormal;
uniform mat4 uModelMatrix;
uniform mat4 uViewProjection[2];
out vec3 vWorldNormal;
void main()
{
// Common, view-independent transform: Computed once
vec4 worldPosition = uModelMatrix * vec4(aPosition, 1.0);
vWorldNormal = mat3(uModelMatrix) * aNormal;
// View-dependent transform: Indexed via gl_ViewID_OVR
gl_Position = uViewProjection[gl_ViewID_OVR] * worldPosition;
}Hardware-Level Execution and Driver Optimization
Under the GL_OVR_multiview specification, the GPU driver
can optimize shader execution beyond simple software indexing. Because
the pipeline evaluates both views within the same drawing context,
modern GPU architectures can execute the scalar and vector instructions
corresponding to view-independent code in shared execution paths.
Rather than re-fetching vertex buffers, decompressing attributes, and recalculating skeletal weights per view, the hardware fetches and processes the raw vertex attributes once. The derived values are then broadcast to the viewport-routing hardware alongside the eye-specific clip-space positions.
Furthermore, using GL_OVR_multiview2 relaxes the
constraint that requires all shader outputs to be view-invariant,
allowing select interpolants to vary per view only when strictly
necessary. This fine-grained control ensures memory bandwidth and
register allocations are utilized solely for data that differs between
viewpoints.