Compute Shaders
A dispatch launches a grid of thread groups; each group has numthreads(X, Y, Z) threads that can share groupshared memory and synchronize.
27.1 Execution model
A dispatch launches a grid of thread groups; each group has numthreads(X, Y, Z) threads that can share groupshared memory and synchronize.
which group (0..Dispatch-1) Group thread ID position within the group Dispatch thread ID
SV_GroupID * numthreads + SV_GroupThreadID flattened SV_GroupThreadID
Limits (SM 5.0+): max 1024 threads per group, X and Y ≤ 1024, Z ≤ 64; 32 KB of groupshared memory; dispatch up to 65,535 groups per dimension. Choose group sizes that are multiples of the wave size (64 or 128 threads is a good default; 8×8 for 2-D image work).
27.2 Image processing
Texture2D<float4> gInput : register(t0);
RWTexture2D<float4> gOutput : register(u0);
cbuffer Params : register(b0) { uint2 gSize; float gContrast; };
[numthreads(8, 8, 1)]
void InvertCS(uint3 id : SV_DispatchThreadID)
{
if (any(id.xy >= gSize)) return; // guard the edges
float4 c = gInput[id.xy];
gOutput[id.xy] = float4((1.0 - c.rgb - 0.5) * gContrast + 0.5, c.a);
}
// Host: Dispatch((width + 7) / 8, (height + 7) / 8, 1)27.3 Shared memory & barriers: parallel reduction
Barrier family: GroupMemoryBarrier (memory only), GroupMemoryBarrierWithGroupSync (memory + execution), DeviceMemoryBarrier[WithGroupSync] (UAV memory), AllMemoryBarrier[WithGroupSync].
StructuredBuffer<float> gValues : register(t0);
RWStructuredBuffer<float> gPartial : register(u0);
cbuffer RCB : register(b0) { uint gCount; };
#define GROUP_SIZE 256
groupshared float sData[GROUP_SIZE];
[numthreads(GROUP_SIZE, 1, 1)]
void ReduceSumCS(uint gi : SV_GroupIndex, uint3 gid : SV_GroupID, uint3 dtid : SV_DispatchThreadID)
{
sData[gi] = dtid.x < gCount ? gValues[dtid.x] : 0;
GroupMemoryBarrierWithGroupSync(); // all writes visible, all threads arrived
[unroll]
for (uint stride = GROUP_SIZE / 2; stride > 0; stride >>= 1)
{
if (gi < stride) sData[gi] += sData[gi + stride];
GroupMemoryBarrierWithGroupSync(); // barrier must be reached by ALL threads (keep it outside the if)
}
if (gi == 0) gPartial[gid.x] = sData[0];
}27.4 Tiled blur with shared memory
#define TILE 16
#define R 4
groupshared float3 sTile[TILE + 2 * R][TILE + 2 * R];
SamplerState gClampSamp : register(s0);
[numthreads(TILE, TILE, 1)]
void BoxBlurCS(uint3 gtid : SV_GroupThreadID, uint3 gid : SV_GroupID)
{
int2 base = int2(gid.xy) * TILE - R;
// cooperative load of the (TILE+2R)^2 tile, several texels per thread
for (uint y = gtid.y; y < TILE + 2 * R; y += TILE)
for (uint x = gtid.x; x < TILE + 2 * R; x += TILE)
{
int2 p = clamp(base + int2(x, y), 0, int2(gSize) - 1);
sTile[y][x] = gInput[p].rgb;
}
GroupMemoryBarrierWithGroupSync();
float3 sum = 0;
[unroll] for (int dy = -R; dy <= R; ++dy)
[unroll] for (int dx = -R; dx <= R; ++dx)
sum += sTile[gtid.y + R + dy][gtid.x + R + dx];
uint2 outP = gid.xy * TILE + gtid.xy;
if (all(outP < gSize)) gOutput[outP] = float4(sum / ((2 * R + 1) * (2 * R + 1)), 1);
}27.5 Applications & indirect dispatch
GPU particles, culling (frustum/occlusion with Hi-Z), light clustering, histograms and auto-exposure, prefix sums and sorting (radix/bitonic), cloth/fluid simulation, mip generation (single-pass downsampler, AMD FidelityFX SPD), skinning, BVH building, ML inference. Indirect dispatch: a compute pass writes D3D12_DISPATCH_ARGUMENTS (uint3) into a buffer consumed by ExecuteIndirect — the GPU decides how much work to run.