Wave Intrinsics

SM 6.0 exposes the GPU's SIMD wave (32 or 64 lanes typically; query WaveLaneCountMin/Max from D3D12_FEATURE_DATA_D3D12_OPTIONS1).

SM 6.0 exposes the GPU's SIMD wave (32 or 64 lanes typically; query WaveLaneCountMin/Max from D3D12_FEATURE_DATA_D3D12_OPTIONS1). Lanes in a wave can communicate without groupshared memory or barriers. Available in all stages (pixel shaders operate on quads of lanes, including helper lanes).

30.1 Wave intrinsic reference

WaveGetLaneCount(), WaveGetLaneIndex(), WaveIsFirstLane()

WaveActiveAnyTrue(b), WaveActiveAllTrue(b), WaveActiveBallot(b) → uint4 mask

WaveReadLaneFirst(x), WaveReadLaneAt(x, lane) WaveActiveSum, WaveActiveProduct, WaveActiveMin, WaveActiveMax, WaveActiveBitAnd/BitOr/BitXor, WaveActiveCountBits(b), WaveActiveAllEqual(x)

WavePrefixSum(x), WavePrefixProduct(x), WavePrefixCountBits(b)

QuadReadAcrossX/Y/Diagonal(x), QuadReadLaneAt(x, i); SM 6.7 QuadAny, QuadAll

WaveMatch(x), WaveMultiPrefixSum/Product/CountBits/BitAnd/BitOr/BitXor

[WaveSize(N)] attribute for compute; SM 6.8: [WaveSize(min, max[, preferred])]

"Active" means lanes currently executing (not masked off by divergence or inactive at the end of a dispatch).

30.2 Examples

Caveats: don't assume a wave size of 32 or 64 unless you set [WaveSize]; results across lanes depend on which lanes are active; quad ops in pixel shaders need helper lanes alive (avoid early discard before them).

RWByteAddressBuffer gOutCounter : register(u0);
RWStructuredBuffer<uint> gOutList : register(u1);

// Stream compaction: one atomic per wave instead of per thread
void AppendIfVisible(bool visible, uint item)
{
    uint laneOffset = WavePrefixCountBits(visible);   // index among visible lanes before me
    uint waveCount  = WaveActiveCountBits(visible);
    uint base = 0;
    if (WaveIsFirstLane())
        gOutCounter.InterlockedAdd(0, waveCount, base);
    base = WaveReadLaneFirst(base);                    // broadcast to all lanes
    if (visible) gOutList[base + laneOffset] = item;
}

// Wave reduction in a compute shader: 2-level reduction with fewer barriers
groupshared float sWaveSums[32];
float GroupSum(float v, uint gi)
{
    float waveSum = WaveActiveSum(v);
    uint waveIdx = gi / WaveGetLaneCount();
    if (WaveIsFirstLane()) sWaveSums[waveIdx] = waveSum;
    GroupMemoryBarrierWithGroupSync();
    uint numWaves = (256 + WaveGetLaneCount() - 1) / WaveGetLaneCount();
    float s = gi < numWaves ? sWaveSums[gi] : 0;
    return WaveActiveSum(s);       // valid in the first wave (group of 256 assumed)
}

// Scalarization: loop over unique material IDs in a wave (avoids NonUni
…