Viewport Arrays for Single-Pass Stereo VR in DirectX

This article explores how Viewport Array functionality in DirectX enables single-pass stereo rendering for virtual reality (VR) applications. By allowing the graphics pipeline to direct geometry to distinct viewports within a single draw call, developers can render both the left and right eye views simultaneously. This approach dramatically cuts down CPU draw call overhead, minimizes redundant state changes, and streamlines GPU vertex processing compared to traditional multi-pass stereo rendering methods.

The Challenge of Traditional VR Rendering

Virtual reality requires rendering a scene twice per frame—once for each eye—from slightly offset camera positions. In traditional dual-pass rendering pipelines, the application submits the entire scene's draw calls for the left eye and then repeats the process for the right eye.

This approach introduces significant performance bottlenecks:

  • CPU Overhead: The CPU must generate and submit twice as many draw calls, often leading to CPU-bound frame times.
  • Pipeline State Thrashing: Switching render targets or view matrices between passes requires pipeline state changes that stall the GPU.
  • Redundant Geometry Processing: The vertex stage frequently reprocesses the same scene geometry without taking advantage of cross-eye data reuse.

The Role of Viewport Arrays in DirectX

DirectX (starting with Direct3D 11.1 and expanded in Direct3D 12) supports Viewport Arrays, an architectural feature allowing up to 16 independent viewports to be bound to the rasterizer stage simultaneously.

Along with the system-generated value semantic SV_ViewportArrayIndex, shaders can programmatically assign individual primitives to a specific viewport. In a VR pipeline, the left and right eye render targets are typically packed side-by-side onto a single render texture (an atlas) or handled across render target arrays. Viewport 0 corresponds to the left eye's region, while Viewport 1 corresponds to the right eye's region.

How Single-Pass Stereo Uses Viewport Arrays

Single-pass stereo rendering consolidates two separate render passes into one continuous execution:

  1. Unified Draw Submission: The CPU submits the scene geometry only once per frame, containing camera transforms for both eyes in a constant buffer.
  2. Primitive Duplication and Routing:
    • Using Geometry Shaders: A Geometry Shader receives incoming primitives, duplicates them, applies the left-eye view-projection matrix to the first copy (setting SV_ViewportArrayIndex = 0), and applies the right-eye view-projection matrix to the second copy (setting SV_ViewportArrayIndex = 1).
    • Using Hardware Extensions (Fast Paths): Modern hardware and DirectX features (such as VPAndRTArrayIndexFromAnyShaderFeedingRasterizer in DirectX 12) allow the Vertex Shader or Amplification/Mesh Shaders to write SV_ViewportArrayIndex directly, bypassing the potential performance cost of geometry shader amplification.
  3. Hardware Rasterization: The rasterizer automatically clips and scales the primitives to the coordinates specified by the selected viewport. The pixel shader executes normally across both views in parallel.

Key Performance Benefits

  • Halved CPU Draw Calls: Because geometry is submitted once, the CPU load for draw-call generation is reduced by nearly 50%, helping maintain the strict 90+ FPS thresholds required by modern VR headsets.
  • Cache Coherency: Shared resources, constant buffers, and textures remain hot in the GPU L1/L2 caches across both eyes instead of being evicted and reloaded between sequential passes.
  • Zero Target Swapping: Rendering to both viewports simultaneously eliminates the need to flush command queues or swap render targets between the left- and right-eye passes.