UAVs, Atomics & Append/Consume Buffers
UAVs (RW* types) allow arbitrary read/write from compute and pixel shaders (and other stages in D3D12).
28.1 RW resources
UAVs (RW* types) allow arbitrary read/write from compute and pixel shaders (and other stages in D3D12). Writes from different threads are unordered; use atomics or barriers for correctness. globallycoherent makes UAV writes visible across thread groups within a dispatch (with DeviceMemoryBarrier).
28.2 Interlocked (atomic) operations
InterlockedAdd(dest, v [, orig]) signed/unsigned by type InterlockedExchange(dest, v, orig)
InterlockedCompareExchange(dest, cmp, v, orig) InterlockedCompareStore(dest, cmp, v) CAS without return
SM 6.6: 64-bit atomics on uint64_t resources/groupshared; InterlockedCompareExchangeFloatBitwise, InterlockedCompareStoreFloatBitwise
RWByteAddressBuffer gCounters : register(u0);
RWTexture2D<uint> gMaxDepth : register(u1);
RWStructuredBuffer<uint> gHistogram : register(u2);
groupshared uint sLocalHist[256];
[numthreads(16, 16, 1)]
void HistogramCS(uint3 dtid : SV_DispatchThreadID, uint gi : SV_GroupIndex)
{
sLocalHist[gi] = 0; // 256 threads clear 256 bins
GroupMemoryBarrierWithGroupSync();
if (all(dtid.xy < gSize))
{
float lum = dot(gInput[dtid.xy].rgb, float3(0.2126, 0.7152, 0.0722));
uint bin = (uint)(saturate(log2(lum + 1e-4) / 16.0 + 0.5) * 255.0);
InterlockedAdd(sLocalHist[bin], 1); // fast shared-memory atomic
}
GroupMemoryBarrierWithGroupSync();
InterlockedAdd(gHistogram[gi], sLocalHist[gi]); // one global atomic per bin per group
uint original;
gCounters.InterlockedAdd(0, 1, original); // byte-address-buffer method form
InterlockedMax(gMaxDepth[dtid.xy / 8], asuint(0.5f)); // positive floats compare correctly as uint
}28.3 Append / consume buffers & hidden counters
The UAV must be created with a counter (D3D12: pCounterResource + CounterOffsetInBytes in CreateUnorderedAccessView; D3D11: D3D11_BUFFER_UAV_FLAG_APPEND). RWStructuredBuffer with a counter exposes IncrementCounter()/DecrementCounter() for index allocation. Many engines replace append buffers with an explicit RWByteAddressBuffer counter + InterlockedAdd (more portable, works with Vulkan).
struct Particle { float3 pos; float life; float3 vel; float pad; };
ConsumeStructuredBuffer<Particle> gAlive : register(u0); // input list
AppendStructuredBuffer<Particle> gSurvive : register(u1); // output list
cbuffer Sim : register(b0) { float gDt; uint gAliveCount; };
[numthreads(256, 1, 1)]
void UpdateParticlesCS(uint3 id : SV_DispatchThreadID)
{
if (id.x >= gAliveCount) return; // never consume more than the counter holds
Particle p = gAlive.Consume();
p.vel.y -= 9.81 * gDt;
p.pos += p.vel * gDt;
p.life -= gDt;
if (p.life > 0) gSurvive.Append(p);
}28.4 Rasterizer ordered views (ROVs)
RasterizerOrderedTexture2D<float4> guarantees per-pixel accesses happen in primitive order — enabling programmable blending and order-independent transparency. Costly; requires ROVsSupported.