Task Shader Operational Model in GL_EXT_mesh_shader
This article provides an overview of the primary operational model of
task shaders as introduced by the GL_EXT_mesh_shader
extension in GLSL. It explains how task shaders function as a
programmable, compute-centric amplification and culling stage prior to
mesh processing, how they dynamically dispatch mesh shader workgroups,
and how data is transferred between stages using task payloads.
Pipeline Position and Purpose
In the GL_EXT_mesh_shader execution model, the task
shader acts as an optional, coarse-grained pre-pass to the mesh shader.
It replaces fixed-function input assembly and early geometry stages
(such as vertex, tessellation, and geometry shaders) with a flexible
compute-like execution paradigm.
The primary responsibility of the task shader is to evaluate scene data at a high level—such as bounding volumes, instances, or bounding boxes—and determine how many fine-grained mesh shader workgroups are required to process the actual geometry.
Compute-Style Workgroup Dispatch
Task shaders operate identically to compute shaders in terms of execution hierarchy:
- Dispatch: Execution is triggered by draw commands
such as
glDrawMeshTasksEXT, which specify a three-dimensional grid of task workgroups (taskCountX,taskCountY,taskCountZ). - Workgroups: Each task shader runs as a workgroup
composed of a user-defined number of local invocations defined by the
layout(local_size_x, local_size_y, local_size_z) in;declaration. - Shared Memory: Invocations within a task workgroup
can cooperate using standard workgroup shared memory
(
shared) and execution barriers (barrier()).
Dynamic Mesh Amplification and Early Culling
The defining operational feature of a task shader is its ability to dynamically control the number of downstream mesh shader workgroups launched on the GPU.
Instead of outputting vertices or primitives directly, a task shader terminates its execution by calling the built-in function:
void EmitMeshTasksEXT(uint groupCountX, uint groupCountY, uint groupCountZ);- Amplification: A single task workgroup can spawn
thousands of mesh workgroups by passing non-zero dimensions to
EmitMeshTasksEXT. This enables multi-level level-of-detail (LOD) expansion and procedural generation directly on hardware. - Culling: If a task workgroup determines that an
object or cluster is outside the view frustum or occluded, it can call
EmitMeshTasksEXT(0, 0, 0). This discards the workload immediately, preventing downstream mesh shaders from ever running and avoiding memory traffic. - Execution Constraint: Only one invocation in the
task workgroup needs to call
EmitMeshTasksEXT, or all invocations must call it with the same arguments.
The Task Payload
To send contextual information to the generated mesh shader workgroups, the task shader writes data to a dedicated memory allocation termed the task payload.
- Declared using the
taskPayloadSharedEXTstorage qualifier (e.g.,taskPayloadSharedEXT struct TaskData { ... } payload;). - The payload acts as per-task-workgroup shared memory that persists across the boundary into the generated mesh shader workgroups.
- All mesh shader workgroups spawned by a specific task workgroup receive read-only access to this single payload instance, allowing them to index into global memory arrays, determine cluster offsets, or read transform matrices computed by the parent task shader.