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.