Bindless & Dynamic Resources
33.1 From slots to bindless
fixed slots: 128 SRVs, 16 samplers, 14 CBs per stage, 8–64 UAVs descriptor tables + unbounded arrays (Texture2D gTex[]: register(t0, space1))
dynamic resources: ResourceDescriptorHeap[i] and SamplerDescriptorHeap[i] — no tables or arrays needed
33.2 SM 6.6 ResourceDescriptorHeap
Requires SM 6.6 and Resource Binding Tier 3 (all D3D12 Ultimate hardware).
If the index can differ across lanes in a wave (e.g. per-pixel material IDs in a deferred pass), wrap it: ResourceDescriptorHeap[NonUniformResourceIndex(idx)]. Omitting it is undefined behavior that often "works" on one vendor and breaks on another.
Bindless pairs naturally with GPU-driven rendering: one ExecuteIndirect or mesh-shader dispatch draws everything, with indices in per-draw data.
Vulkan equivalent: descriptor indexing (VK_EXT_descriptor_indexing), mutable descriptors, or descriptor buffers; DXC can compile ResourceDescriptorHeap to SPIR-V by mapping it onto runtime descriptor arrays (requires descriptor indexing support); see DXC's SPIR-V documentation for the exact set/binding mapping.
// Root signature: only root constants; shaders index the heaps directly
#define BindlessRS \
"RootFlags(CBV_SRV_UAV_HEAP_DIRECTLY_INDEXED | SAMPLER_HEAP_DIRECTLY_INDEXED), " \
"RootConstants(num32BitConstants=4, b0)"
struct DrawConstants
{
uint vertexBufferIndex; // descriptor heap index of a ByteAddressBuffer
uint materialBufferIndex; // StructuredBuffer<MaterialGPU>
uint materialIndex;
uint frameCBIndex; // ConstantBuffer<FrameCB>
};
ConstantBuffer<DrawConstants> gDC : register(b0);
struct FrameCB { float4x4 viewProj; float3 camPos; float time; };
struct MaterialGPU { uint albedoTex; uint normalTex; uint samplerIdx; float roughness; };
struct BLVOut { float4 pos : SV_Position; float2 uv : TEXCOORD0; };
[RootSignature(BindlessRS)]
BLVOut BindlessVS(uint vid : SV_VertexID)
{
ConstantBuffer<FrameCB> frame = ResourceDescriptorHeap[gDC.frameCBIndex];
ByteAddressBuffer vb = ResourceDescriptorHeap[gDC.vertexBufferIndex];
// Vertex pulling: position (12 bytes) + uv (8 bytes) = 20-byte stride
float3 pos = asfloat(vb.Load3(vid * 20));
float2 uv = asfloat(vb.Load2(vid * 20 + 12));
BLVOut o;
o.pos = mul(frame
…