Performance Optimization

39.1 Where time goes

ALU-bound: too much math per pixel/thread.

Memory/bandwidth-bound: texture fetches, cache misses, uncompressed or oversized resources, large render targets.

Occupancy-limited: too many registers (VGPRs) or too much groupshared memory → fewer waves in flight to hide latency.

Fixed-function: overdraw, ROP blending, rasterization of tiny triangles, vertex attribute export.

CPU/API-bound: too many draws, state changes, root signature changes, PSO compilation hitches.

39.2 Shader optimization techniques

Move work to the coarsest frequency: CPU → VS → PS per-object math once, not per pixel

Use half/min16float/float16_t where precision allows 2× ALU rate and fewer registers on many GPUs

Use mad, rsqrt, rcp, saturate, dot map to fast instructions; saturate is often free

Avoid pow with constant integer exponents x*x*x*x is cheaper than pow(x, 4)

Normalize only when necessary; avoid redundant normalize each costs a rsqrt + multiplies

Keep branches uniform (per draw/wave) divergent branches execute both paths

Reduce register pressure: shorter live ranges, fewer large arrays Avoid dynamically indexed local arrays spilled to slow memory (scratch)

Pack data: channel-pack textures (ORM), R11G11B10, BC compression, octahedral normals

Use Load/operator[] instead of Sample when no filtering is needed Use [unroll] for small constant loops removes loop overhead, enables scheduling

Prefer StructuredBuffer/ByteAddressBuffer reads that are coalesced (adjacent threads → adjacent addresses)

Use wave intrinsics to replace groupshared + barriers fewer syncs, fewer atomics

Avoid discard and SV_Depth on opaque geometry keeps early-Z / hierarchical-Z

Render half-res for blurs, SSAO, volumetrics; upsample bilaterally

Use compute for full-screen passes when you can exploit groupshared memory fewer redundant fetches

Prefer branching on constants via permutations or specialization over runtime uniforms (when it reduces registers)

39.3 Measuring

Use GPU timestamps (EndQuery with D3D12_QUERY_TYPE_TIMESTAMP) around passes; use PIX timing captures and vendor counters (occupancy, cache hit rate, VGPR count).

Inspect generated code: dxc -Fc (DXIL), Radeon GPU Analyzer (RDNA ISA, VGPR/SGPR counts), Nsight's shader profiler, Shader Playground.

Change one thing at a time; test on multiple vendors and on low-end hardware.