Over the past 20 years, the graphics pipeline has evolved to reach its now familiar state. As independent hardware vendors (IHVs) focus on an ever-widening battle of core count, memory capacity, and bandwidth, certain fixed-function bottlenecks have become apparent in the geometric processing model.Figure 1. The legacy graphics pipeline. Blue-tinted stages are fixed-function. Green-tinted stages are programmable shaders.One of the most problematic of these bottlenecks is the input assembler (IA). The IA is a hardware component that sits at the top of the graphics pipeline that’s responsible for processing indices and fetching vertex data. The number of IA units hasn’t scaled at the same rate as other GPU resources. The small, fixed number of IA units introduces a restrictive launch rate that can often fail to saturate GPU cores with work.At the bottom of the geometry pipeline is the primitive assembler (PA). This hardware component composes transformed vertices into primitives, performs culling and clipping routines, and then forwards the surviving instances to rasterization. It has a fixed export rate, and maintaining this rate can be challenging. In some cases, this could be due to the IA - if shader waves could be launched into the pipeline faster, more primitives could be submitted in parallel. More in-flight, parallel workloads can better maintain pressure on the PA.Another limitation of the legacy graphics pipeline is that the methods of geometry culling in the traditional pipeline are limited. Culled geometry reduces effective throughput, and wastes memory bandwidth and ALU loading and shading vertex attributes that will never see the light of day. Ideally, we’d like the ability to cull at a myriad of granularities to cull geometry at the earliest possible moment.
The Mesh Shader pipeline targets these legacy graphics pipeline limitations. First of all, it removes the IA and tessellator from the pipeline. This is intended to maximize the shader wave launch rate and shift IA to shader code that can scale with GPUs. This can help saturate GPU cores and keep pressure on the PA.This pipeline also provides the ability to cull workloads at various points in the pipeline. Amplification shaders may be used to do low-granularity culling, such as against grouped objects, instances, or meshlets. Mesh Shaders can do culling at a per-primitive granularity. This provides a path for discarding trivially cullable primitives early and often - saving precious computation, memory bandwidth, and PA throughput.Figure 2. The stages of the Mesh Shader pipeline. Green-tinted stages are programmable. Blue-tinted stages are fixed-function.
Mesh Shaders were designed to provide a level of generality to the geometry pipeline only found in compute shaders. This gives the developer full control over each thread’s workload. Moving index and vertex attribute fetches to shader code removes the dependency on fixed-function hardware, maximizing wave launch rates and allowing IA to scale with the rest of the GPU.Their ability to dynamically specify vertex and primitive export counts opens the gate for efficient, onchip support for primitive culling. This can be done per-meshlet or per-primitive against any arbitrary culling criteria inside the Mesh Shader. This allows shaders to defer vertex attribute fetches and shading until the end of the pipeline, after several layers of culling have already been performed.
Compute shaders are regularly used to preprocess resources, followed up with draw calls that consume their results by using ExecuteIndirect. This is a common technique for many forms of per-view culling. This has a couple of subtle drawbacks that could be optimized with proper hardware support.For one, the compute shader instances must write their data to global memory. This incurs fetch latency and makes it subject to GPU-wide memory bandwidth contention. Secondly, if the compute producer and graphics consumer are consecutive operations, the GPU must wait for all compute shader instances to retire before launching the consuming draw calls. This is caused by an unordered access view (UAV) resource barrier that must enforce all compute shader writes to the final UAV are completed before any shader can consume it.Figure 3. Simplified wave scheduling visualization of a compute shader with immediately subsequent ExecuteIndirect “DispatchMesh” draw. Compute shader waves (yellow) are separated from Mesh Shader wave (green) execution by a pipeline stall because of a UAV barrier. The numbered IDs relate the invoking computer shader threadgroup to its cluster of Mesh Shader children.The Amplification Shader stage is meant to tackle these issues. The producer and consumer are pipelined - onchip interstage memory with no intermediate pipeline flush. Scheduling of the Amplification Shader and Mesh Shader waves are interspersed, which can more fully saturate the GPU with work.It also opens up a level of generalized workload reduction or amplification capability that simply doesn’t exist in the legacy graphics pipeline. This can be used to implement features such as culling techniques, dynamic instance level of detail (LOD) selection, or custom tessellation routines.Figure 4. Visualization of the wave scheduling of an Amplification Shader workload. The stall is eliminated from the pipeline. The numbered IDs relate the invoking Amplification Shader threadgroup to its cluster of Mesh Shader children.