Skip to content

Instantly share code, notes, and snippets.

@gkaerts
Last active April 28, 2025 00:05
Show Gist options
  • Save gkaerts/526bc6134499599132323f4a185e997d to your computer and use it in GitHub Desktop.
Save gkaerts/526bc6134499599132323f4a185e997d to your computer and use it in GitHub Desktop.
Vulkan and D3D12 typed bindless resources in one shader-side API
// This file should be compiled with DXC against shader model 6.6
// Change the TARGET_API define here to either D3D or VK and switch compiler output formats (DXIL or SPIR-V) to match
#define D3D 1
#define VK 2
#define TARGET_API D3D
// Begin macro magic
#if TARGET_API == D3D
// No special root signature needed!
#define BINDLESS_RESOURCE_RANGE(type, vkBinding, rangeName, accessor) \
type accessor(uint handle) { return ResourceDescriptorHeap[handle]; }
#define BINDLESS_UNIFORM_BUFFER_RANGE(type, rangeName, accessor) \
type accessor(uint handle) { ConstantBuffer<type> data = ResourceDescriptorHeap[handle]; return data; }
#define BINDLESS_SAMPLER_RANGE(type, vkBinding, rangeName, accessor) \
type accessor(uint handle) { return SamplerDescriptorHeap[handle]; }
#elif TARGET_API == VK
// These need to match with the C++ Vulkan pipeline layout code
#define VK_RESOURCE_DESCRIPTOR_SET_INDEX 0
#define VK_SAMPLER_DESCRIPTOR_SET_INDEX 1
#define VK_BINDING_UNIFORM_BUFFER 0
#define VK_BINDING_SAMPLED_IMAGE 1
#define VK_BINDING_STORAGE_IMAGE 2
#define VK_BINDING_STORAGE_BUFFER 3
#define VK_BINDING_SAMPLER 0
#define BINDLESS_RESOURCE_RANGE(type, vkBinding, rangeName, accessor) [[vk::binding(vkBinding, VK_RESOURCE_DESCRIPTOR_SET_INDEX)]] type rangeName[]; \
type accessor(uint handle) { return rangeName[handle]; }
#define BINDLESS_UNIFORM_BUFFER_RANGE(type, rangeName, accessor) [[vk::binding(VK_BINDING_UNIFORM_BUFFER, VK_RESOURCE_DESCRIPTOR_SET_INDEX)]] ConstantBuffer<type> rangeName[]; \
type accessor(uint handle) { type data = rangeName[handle]; return data; }
#define BINDLESS_SAMPLER_RANGE(type, vkBinding, rangeName, accessor) [[vk::binding(vkBinding, VK_SAMPLER_DESCRIPTOR_SET_INDEX)]] type rangeName[]; \
type accessor(uint handle) { return rangeName[handle]; }
#endif
// End macro magic
// Begin shader code
struct MyConstants
{
float4 foo;
};
struct Vertex
{
float4 position;
float2 uv;
};
// This should technically work, but it currently crashes the SPIR-V codegen path at the time of writing (04/29/2022)
// BINDLESS_RESOURCE_RANGE(ConstantBuffer<MyConstants>, VK_BINDING_UNIFORM_BUFFER, bindless_my_constant_buffers, get_my_constants)
BINDLESS_UNIFORM_BUFFER_RANGE(MyConstants, bindless_my_constants, get_my_constants)
BINDLESS_RESOURCE_RANGE(Texture2D<float4>, VK_BINDING_SAMPLED_IMAGE, bindless_2d_textures, get_2d_texture)
BINDLESS_RESOURCE_RANGE(Texture3D<float4>, VK_BINDING_SAMPLED_IMAGE, bindless_3d_textures, get_3d_texture)
BINDLESS_RESOURCE_RANGE(ByteAddressBuffer, VK_BINDING_STORAGE_BUFFER, bindless_raw_buffers, get_raw_buffer)
BINDLESS_RESOURCE_RANGE(StructuredBuffer<Vertex>, VK_BINDING_STORAGE_BUFFER, bindless_vertex_buffers, get_vertex_buffer)
BINDLESS_SAMPLER_RANGE(SamplerState, VK_BINDING_SAMPLER, bindless_samplers, get_sampler)
struct PSInput
{
float2 uv : TEXCOORD0;
};
float4 PSMain(PSInput input) : SV_TARGET
{
// See above as to why this is currently broken
// ConstantBuffer<MyConstants> constants = get_my_constants(0)
MyConstants constants = get_my_constants(0);
SamplerState sampler = get_sampler(0);
Texture2D<float4> texture = get_2d_texture(0);
return constants.foo * texture.SampleLevel(sampler, input.uv, 0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment