Bindless Table
I’ve been experimenting with Bindless rendering using DirectX12 & Vulkan APIs recently.
Here is the layout I currently use for bindless table :
Table Layout
Type | Range | Space | Binding |
---|---|---|---|
Texture1D (float4) | [0 ..16383] | 10 | 0 |
Texture2D (float4) | [0 ..16383] | 20 | 0 |
Texture2D (uint) | [0 ..16383] | 21 | 0 |
Texture3D (float4) | [0 ..16383] | 30 | 0 |
ByteAddressBuffer | [16384..32767] | - | 1 |
RWTexture1D | [32768..40959] | 210 | 2 |
RWTexture2D | [32768..40959] | 220 | 2 |
RWTexture3D | [32768..40959] | 230 | 2 |
RWByteAddressBuffer | [40960..49151] | 200 | 3 |
RaytracingAccelerationStructure | [49152..49167] | - | 4 |
Notes:
- The DirectX12 version uses the Space value to alias the different types of resource view formats
- The Vulkan version uses the Binding value to differenciate between the different types of resource views
- Several TLAS can be used if multiple views are used. The current value implies a maximum of 16 views using RayTracing.
In case you want more details, check the file table.hlsi in the repo.
Reserved Slots
By default, a new resource requiring a view will be created allocating a new bindless handle in these ranges, but it’s convenient to have some hard-coded slots :
- To avoid an extra indirection (e.g. passing buffer handle as root constant) :
PS_Output PS_Forward(VS_Output _input)
{
PS_Output output = (PS_Output)0;
ViewConstants viewConstants;
viewConstants.Load(getBuffer(RESERVEDSLOT_BUFSRV_VIEWCONSTANTS));
[...]
- To fallback to missing texture :
m_defaultTexture = device->createTexture(texDesc, "DefaultTex2D", initData, ReservedSlot(BINDLESS_TEXTURE_INVALID));
VG_ASSERT(m_defaultTexture->getTextureHandle() == BINDLESS_TEXTURE_INVALID);
// copy descriptor to all 'texture' slots
for (uint i = BINDLESS_TEXTURE_START; i < BINDLESS_TEXTURE_COUNT; ++i)
if (BINDLESS_TEXTURE_INVALID != i)
copyTextureHandle(i, m_defaultTexture);
- To initialize shader structs with proper default values (e.g. default albedo, default normal map etc…) :
struct GPUMaterialData
{
#ifdef __cplusplus
GPUMaterialData()
{
setAlbedoTextureHandle(RESERVEDSLOT_TEXSRV_DEFAULT_ALBEDO);
setNormalTextureHandle(RESERVEDSLOT_TEXSRV_DEFAULT_NORMAL);
setPBRTextureHandle(RESERVEDSLOT_TEXSRV_DEFAULT_PBR);
}
#endif
// .x = albedo | normal
// .y = pbr | unused
uint4 textures;
[...]
};
These custom hardcoded slots are defined compile-time and “allocated” bottom-up before the last value of the range used as invalid value.
Type | Name | Value |
---|---|---|
Texture | PBR | 16379 |
Texture | Normal | 16380 |
Texture | Albedo | 16381 |
Texture | ImGui | 16382 |
Texture | Default | 16383 |
Buffer | InstanceData | 32763 |
Buffer | SkinningMatrices | 32764 |
Buffer | LightsConstants | 32765 |
Buffer | ViewConstants | 32766 |
Buffer | Default | 32767 |
Debugging
Using GPU debug tools like Renderdoc or PIX, it is possible to view the descriptor used and the resource contents.