Path: blob/main/crates/bevy_render/src/render_resource/bindless.rs
9350 views
//! Types and functions relating to bindless resources.12use alloc::borrow::Cow;3use core::{4num::{NonZeroU32, NonZeroU64},5ops::Range,6};78use bevy_derive::{Deref, DerefMut};9use wgpu::{10BindGroupLayoutEntry, SamplerBindingType, ShaderStages, TextureSampleType, TextureViewDimension,11};1213use bevy_material::bind_group_layout_entries::binding_types::{14sampler, storage_buffer_read_only_sized, texture_1d, texture_2d, texture_2d_array, texture_3d,15texture_cube, texture_cube_array,16};1718/// The default value for the number of resources that can be stored in a slab19/// on this platform.20///21/// See the documentation for [`BindlessSlabResourceLimit`] for more22/// information.23#[cfg(any(target_os = "macos", target_os = "ios"))]24pub const AUTO_BINDLESS_SLAB_RESOURCE_LIMIT: u32 = 64;25/// The default value for the number of resources that can be stored in a slab26/// on this platform.27///28/// See the documentation for [`BindlessSlabResourceLimit`] for more29/// information.30#[cfg(not(any(target_os = "macos", target_os = "ios")))]31pub const AUTO_BINDLESS_SLAB_RESOURCE_LIMIT: u32 = 2048;3233/// The binding numbers for the built-in binding arrays of each bindless34/// resource type.35///36/// In the case of materials, the material allocator manages these binding37/// arrays.38///39/// `bindless.wgsl` contains declarations of these arrays for use in your40/// shaders. If you change these, make sure to update that file as well.41pub static BINDING_NUMBERS: [(BindlessResourceType, BindingNumber); 9] = [42(BindlessResourceType::SamplerFiltering, BindingNumber(1)),43(BindlessResourceType::SamplerNonFiltering, BindingNumber(2)),44(BindlessResourceType::SamplerComparison, BindingNumber(3)),45(BindlessResourceType::Texture1d, BindingNumber(4)),46(BindlessResourceType::Texture2d, BindingNumber(5)),47(BindlessResourceType::Texture2dArray, BindingNumber(6)),48(BindlessResourceType::Texture3d, BindingNumber(7)),49(BindlessResourceType::TextureCube, BindingNumber(8)),50(BindlessResourceType::TextureCubeArray, BindingNumber(9)),51];5253/// The maximum number of resources that can be stored in a slab.54///55/// This limit primarily exists in order to work around `wgpu` performance56/// problems involving large numbers of bindless resources. Also, some57/// platforms, such as Metal, currently enforce limits on the number of58/// resources in use.59///60/// This corresponds to `LIMIT` in the `#[bindless(LIMIT)]` attribute when61/// deriving [`crate::render_resource::AsBindGroup`].62#[derive(Clone, Copy, Default, PartialEq, Debug)]63pub enum BindlessSlabResourceLimit {64/// Allows the renderer to choose a reasonable value for the resource limit65/// based on the platform.66///67/// This value has been tuned, so you should default to this value unless68/// you have special platform-specific considerations that prevent you from69/// using it.70#[default]71Auto,7273/// A custom value for the resource limit.74///75/// Bevy will allocate no more than this number of resources in a slab,76/// unless exceeding this value is necessary in order to allocate at all77/// (i.e. unless the number of bindless resources in your bind group exceeds78/// this value), in which case Bevy can exceed it.79Custom(u32),80}8182/// Information about the bindless resources in this object.83///84/// The material bind group allocator uses this descriptor in order to create85/// and maintain bind groups. The fields within this bindless descriptor are86/// [`Cow`]s in order to support both the common case in which the fields are87/// simply `static` constants and the more unusual case in which the fields are88/// dynamically generated efficiently. An example of the latter case is89/// `ExtendedMaterial`, which needs to assemble a bindless descriptor from those90/// of the base material and the material extension at runtime.91///92/// This structure will only be present if this object is bindless.93pub struct BindlessDescriptor {94/// The bindless resource types that this object uses, in order of bindless95/// index.96///97/// The resource assigned to binding index 0 will be at index 0, the98/// resource assigned to binding index will be at index 1 in this array, and99/// so on. Unused binding indices are set to [`BindlessResourceType::None`].100pub resources: Cow<'static, [BindlessResourceType]>,101/// The [`BindlessBufferDescriptor`] for each bindless buffer that this102/// object uses.103///104/// The order of this array is irrelevant.105pub buffers: Cow<'static, [BindlessBufferDescriptor]>,106/// The [`BindlessIndexTableDescriptor`]s describing each bindless index107/// table.108///109/// This list must be sorted by the first bindless index.110pub index_tables: Cow<'static, [BindlessIndexTableDescriptor]>,111}112113/// The type of potentially-bindless resource.114#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]115pub enum BindlessResourceType {116/// No bindless resource.117///118/// This is used as a placeholder to fill holes in the119/// [`BindlessDescriptor::resources`] list.120None,121/// A storage buffer.122Buffer,123/// A filtering sampler.124SamplerFiltering,125/// A non-filtering sampler (nearest neighbor).126SamplerNonFiltering,127/// A comparison sampler (typically used for shadow maps).128SamplerComparison,129/// A 1D texture.130Texture1d,131/// A 2D texture.132Texture2d,133/// A 2D texture array.134///135/// Note that this differs from a binding array. 2D texture arrays must all136/// have the same size and format.137Texture2dArray,138/// A 3D texture.139Texture3d,140/// A cubemap texture.141TextureCube,142/// A cubemap texture array.143///144/// Note that this differs from a binding array. Cubemap texture arrays must145/// all have the same size and format.146TextureCubeArray,147/// Multiple instances of plain old data concatenated into a single buffer.148///149/// This corresponds to the `#[data]` declaration in150/// [`crate::render_resource::AsBindGroup`].151///152/// Note that this resource doesn't itself map to a GPU-level binding153/// resource and instead depends on the `MaterialBindGroupAllocator` to154/// create a binding resource for it.155DataBuffer,156}157158/// Describes a bindless buffer.159///160/// Unlike samplers and textures, each buffer in a bind group gets its own161/// unique bind group entry. That is, there isn't any `bindless_buffers` binding162/// array to go along with `bindless_textures_2d`,163/// `bindless_samplers_filtering`, etc. Therefore, this descriptor contains two164/// indices: the *binding number* and the *bindless index*. The binding number165/// is the `@binding` number used in the shader, while the bindless index is the166/// index of the buffer in the bindless index table (which is itself167/// conventionally bound to binding number 0).168///169/// When declaring the buffer in a derived implementation170/// [`crate::render_resource::AsBindGroup`] with syntax like171/// `#[uniform(BINDLESS_INDEX, StandardMaterialUniform,172/// bindless(BINDING_NUMBER)]`, the bindless index is `BINDLESS_INDEX`, and the173/// binding number is `BINDING_NUMBER`. Note the order.174#[derive(Clone, Copy, Debug)]175pub struct BindlessBufferDescriptor {176/// The actual binding number of the buffer.177///178/// This is declared with `@binding` in WGSL. When deriving179/// [`crate::render_resource::AsBindGroup`], this is the `BINDING_NUMBER` in180/// `#[uniform(BINDLESS_INDEX, StandardMaterialUniform,181/// bindless(BINDING_NUMBER)]`.182pub binding_number: BindingNumber,183/// The index of the buffer in the bindless index table.184///185/// In the shader, this is the index into the table bound to binding 0. When186/// deriving [`crate::render_resource::AsBindGroup`], this is the187/// `BINDLESS_INDEX` in `#[uniform(BINDLESS_INDEX, StandardMaterialUniform,188/// bindless(BINDING_NUMBER)]`.189pub bindless_index: BindlessIndex,190/// The size of the buffer in bytes, if known.191pub size: Option<usize>,192}193194/// Describes the layout of the bindless index table, which maps bindless195/// indices to indices within the binding arrays.196#[derive(Clone)]197pub struct BindlessIndexTableDescriptor {198/// The range of bindless indices that this descriptor covers.199pub indices: Range<BindlessIndex>,200/// The binding at which the index table itself will be bound.201///202/// By default, this is binding 0, but it can be changed with the203/// `#[bindless(index_table(binding(B)))]` attribute.204pub binding_number: BindingNumber,205}206207/// The index of the actual binding in the bind group.208///209/// This is the value specified in WGSL as `@binding`.210#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Deref, DerefMut)]211pub struct BindingNumber(pub u32);212213/// The index in the bindless index table.214///215/// This table is conventionally bound to binding number 0.216#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Hash, Debug, Deref, DerefMut)]217pub struct BindlessIndex(pub u32);218219/// Creates the bind group layout entries common to all shaders that use220/// bindless bind groups.221///222/// `bindless_resource_count` specifies the total number of bindless resources.223/// `bindless_slab_resource_limit` specifies the resolved224/// [`BindlessSlabResourceLimit`] value.225pub fn create_bindless_bind_group_layout_entries(226bindless_index_table_length: u32,227bindless_slab_resource_limit: u32,228bindless_index_table_binding_number: BindingNumber,229) -> Vec<BindGroupLayoutEntry> {230let bindless_slab_resource_limit =231NonZeroU32::new(bindless_slab_resource_limit).expect("Bindless slot count must be nonzero");232233// The maximum size of a binding array is the234// `bindless_slab_resource_limit`, which would occur if all of the bindless235// resources were of the same type. So we create our binding arrays with236// that size.237238vec![239// Start with the bindless index table, bound to binding number 0.240storage_buffer_read_only_sized(241false,242NonZeroU64::new(bindless_index_table_length as u64 * size_of::<u32>() as u64),243)244.build(245*bindless_index_table_binding_number,246ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,247),248// Continue with the common bindless resource arrays.249sampler(SamplerBindingType::Filtering)250.count(bindless_slab_resource_limit)251.build(2521,253ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,254),255sampler(SamplerBindingType::NonFiltering)256.count(bindless_slab_resource_limit)257.build(2582,259ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,260),261sampler(SamplerBindingType::Comparison)262.count(bindless_slab_resource_limit)263.build(2643,265ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,266),267texture_1d(TextureSampleType::Float { filterable: true })268.count(bindless_slab_resource_limit)269.build(2704,271ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,272),273texture_2d(TextureSampleType::Float { filterable: true })274.count(bindless_slab_resource_limit)275.build(2765,277ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,278),279texture_2d_array(TextureSampleType::Float { filterable: true })280.count(bindless_slab_resource_limit)281.build(2826,283ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,284),285texture_3d(TextureSampleType::Float { filterable: true })286.count(bindless_slab_resource_limit)287.build(2887,289ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,290),291texture_cube(TextureSampleType::Float { filterable: true })292.count(bindless_slab_resource_limit)293.build(2948,295ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,296),297texture_cube_array(TextureSampleType::Float { filterable: true })298.count(bindless_slab_resource_limit)299.build(3009,301ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,302),303]304}305306impl BindlessSlabResourceLimit {307/// Determines the actual bindless slab resource limit on this platform.308pub fn resolve(&self) -> u32 {309match *self {310BindlessSlabResourceLimit::Auto => AUTO_BINDLESS_SLAB_RESOURCE_LIMIT,311BindlessSlabResourceLimit::Custom(limit) => limit,312}313}314}315316impl BindlessResourceType {317/// Returns the binding number for the common array of this resource type.318///319/// For example, if you pass `BindlessResourceType::Texture2d`, this will320/// return 5, in order to match the `@group(2) @binding(5) var321/// bindless_textures_2d: binding_array<texture_2d<f32>>` declaration in322/// `bindless.wgsl`.323///324/// Not all resource types have fixed binding numbers. If you call325/// [`Self::binding_number`] on such a resource type, it returns `None`.326///327/// Note that this returns a static reference to the binding number, not the328/// binding number itself. This is to conform to an idiosyncratic API in329/// `wgpu` whereby binding numbers for binding arrays are taken by `&u32`330/// *reference*, not by `u32` value.331pub fn binding_number(&self) -> Option<&'static BindingNumber> {332match BINDING_NUMBERS.binary_search_by_key(self, |(key, _)| *key) {333Ok(binding_number) => Some(&BINDING_NUMBERS[binding_number].1),334Err(_) => None,335}336}337}338339impl From<TextureViewDimension> for BindlessResourceType {340fn from(texture_view_dimension: TextureViewDimension) -> Self {341match texture_view_dimension {342TextureViewDimension::D1 => BindlessResourceType::Texture1d,343TextureViewDimension::D2 => BindlessResourceType::Texture2d,344TextureViewDimension::D2Array => BindlessResourceType::Texture2dArray,345TextureViewDimension::Cube => BindlessResourceType::TextureCube,346TextureViewDimension::CubeArray => BindlessResourceType::TextureCubeArray,347TextureViewDimension::D3 => BindlessResourceType::Texture3d,348}349}350}351352impl From<SamplerBindingType> for BindlessResourceType {353fn from(sampler_binding_type: SamplerBindingType) -> Self {354match sampler_binding_type {355SamplerBindingType::Filtering => BindlessResourceType::SamplerFiltering,356SamplerBindingType::NonFiltering => BindlessResourceType::SamplerNonFiltering,357SamplerBindingType::Comparison => BindlessResourceType::SamplerComparison,358}359}360}361362impl From<u32> for BindlessIndex {363fn from(value: u32) -> Self {364Self(value)365}366}367368impl From<u32> for BindingNumber {369fn from(value: u32) -> Self {370Self(value)371}372}373374375