Skip to content

Instantly share code, notes, and snippets.

@cwfitzgerald
Last active November 26, 2025 16:00
Show Gist options
  • Select an option

  • Save cwfitzgerald/64685976d8ff96e421862d42b9704df5 to your computer and use it in GitHub Desktop.

Select an option

Save cwfitzgerald/64685976d8ff96e421862d42b9704df5 to your computer and use it in GitHub Desktop.
Strawman WebGPU Bindless API
///// Rust strawman API for Resource Tables /////
bitflags! {
struct Features : u64 {
// Supports only sampled texture + samplers in resource tables
const SAMPLING_RESOURCE_TABLE = ..;
// Supports all non-uniform resource types in resource tables
const FULL_RESOURCE_TABLE = ..;
}
}
struct Limits {
// Maximum size of any single resource table. Higher is better.
// Default something like 100k.
max_resource_table_size: u32,
// ..
}
struct PipelineLayoutDescriptor {
resource_table_available: bool,
// ..
};
struct ResourceTableDescriptor {
/// Maximum number of bindings in the resource table.
size: u32,
}
impl Device {
/// Creates a new resource table. The expectation is that there would be
/// a relatively small number of these created during application lifetime,
/// mostly for composability purposes.
fn create_resource_table(ResourceTableDescriptor descriptor) -> ResourceTable;
}
enum BindingInUseError {
BindingInUse,
OutOfBounds(..),
// ...
}
enum NoFreeBindingError {
NoFreeBinding,
/// ...
}
enum RemoveBindingError {
AlreadyInUse,
/// ...
}
struct ResourceTable {
fn destroy();
/// Immediately updates the binding at the given index. The index must be unused.
///
/// BindingResource::*Array() variants are not allowed here and would be eventually removed.
fn update(index: u32, entry: BindingResource) -> Result<(), BindingInUseError>;
/// Inserts a binding at some index in the table. If there are no free slots, return an error.
///
/// This function is guarenteed to return the _lowest_ index currently free slot.
///
/// BindingResource::*Array() variants are not allowed here and would be eventually removed.
fn insert_binding(entry: BindingResource) -> Result<u32, NoFreeBindingError>;
/// Queues an operation that removes the given binding from the index. The index may
/// not be re-used until after queue.on_submitted_work_done() would have completed.
///
/// See https://github.com/gpuweb/gpuweb/blob/main/proposals/bindless.md#updates-of-bindings-in-dynamic-binding-arrays
/// for more discussion.
fn remove_binding(index: u32) -> Result<(), RemoveBindingError>;
};
impl CommandEncoder {
/// Can only ever have one bound resource table at a time. This should
/// be expected to be relatively expensive, so avoid changing
/// it frequently.
fn set_resource_table(ResourceTable table);
}
/// Strawman WGSL Syntax
fn resourceTableLength() -> u32;
fn getBinding<T>(index: u32) -> T
where T: texture_*;
fn hasBinding<T>(index: u32) -> bool;
fn getBufferAt<T>(index: u32, offset: u32) -> T
where T: StorageBufferCompatibleType;
/// Strawman GLSL/SPIR-V injestion syntax
// All binding arrays bound to the shader will be redirected
// to the resource table, regardless of their original binding numbers.
// All point to the same heap. Can do vulkan style type aliasing.
layout(set = 0, binding = 0) uniform texture2D myTextureArray[];
layout(set = 1, binding = 1) uniform texture3D myTextureArray[];
layout(set = 2, binding = 2) buffer MyBufferArray {
uint data[];
} myBufferArray[];
/// Strawman BDA deguaring, using Slang for conciceness
T* my_ptr;
// OMG pointer addition + BDA
my_ptr += 12;
T = *my_ptr;
// Pointer desugars to 32bit index into resource table and 32bit offset into buffer.
// This behavior is well defined for wgpu-native-compatible spirv ingestion.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment