Path: blob/main/crates/bevy_render/src/render_resource/bindless.rs
6596 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 crate::render_resource::binding_types::storage_buffer_read_only_sized;1415use super::binding_types::{16sampler, texture_1d, texture_2d, texture_2d_array, texture_3d, texture_cube, texture_cube_array,17};1819/// The default value for the number of resources that can be stored in a slab20/// on this platform.21///22/// See the documentation for [`BindlessSlabResourceLimit`] for more23/// information.24#[cfg(any(target_os = "macos", target_os = "ios"))]25pub const AUTO_BINDLESS_SLAB_RESOURCE_LIMIT: u32 = 64;26/// The default value for the number of resources that can be stored in a slab27/// on this platform.28///29/// See the documentation for [`BindlessSlabResourceLimit`] for more30/// information.31#[cfg(not(any(target_os = "macos", target_os = "ios")))]32pub const AUTO_BINDLESS_SLAB_RESOURCE_LIMIT: u32 = 2048;3334/// The binding numbers for the built-in binding arrays of each bindless35/// resource type.36///37/// In the case of materials, the material allocator manages these binding38/// arrays.39///40/// `bindless.wgsl` contains declarations of these arrays for use in your41/// shaders. If you change these, make sure to update that file as well.42pub static BINDING_NUMBERS: [(BindlessResourceType, BindingNumber); 9] = [43(BindlessResourceType::SamplerFiltering, BindingNumber(1)),44(BindlessResourceType::SamplerNonFiltering, BindingNumber(2)),45(BindlessResourceType::SamplerComparison, BindingNumber(3)),46(BindlessResourceType::Texture1d, BindingNumber(4)),47(BindlessResourceType::Texture2d, BindingNumber(5)),48(BindlessResourceType::Texture2dArray, BindingNumber(6)),49(BindlessResourceType::Texture3d, BindingNumber(7)),50(BindlessResourceType::TextureCube, BindingNumber(8)),51(BindlessResourceType::TextureCubeArray, BindingNumber(9)),52];5354/// The maximum number of resources that can be stored in a slab.55///56/// This limit primarily exists in order to work around `wgpu` performance57/// problems involving large numbers of bindless resources. Also, some58/// platforms, such as Metal, currently enforce limits on the number of59/// resources in use.60///61/// This corresponds to `LIMIT` in the `#[bindless(LIMIT)]` attribute when62/// deriving [`crate::render_resource::AsBindGroup`].63#[derive(Clone, Copy, Default, PartialEq, Debug)]64pub enum BindlessSlabResourceLimit {65/// Allows the renderer to choose a reasonable value for the resource limit66/// based on the platform.67///68/// This value has been tuned, so you should default to this value unless69/// you have special platform-specific considerations that prevent you from70/// using it.71#[default]72Auto,7374/// A custom value for the resource limit.75///76/// Bevy will allocate no more than this number of resources in a slab,77/// unless exceeding this value is necessary in order to allocate at all78/// (i.e. unless the number of bindless resources in your bind group exceeds79/// this value), in which case Bevy can exceed it.80Custom(u32),81}8283/// Information about the bindless resources in this object.84///85/// The material bind group allocator uses this descriptor in order to create86/// and maintain bind groups. The fields within this bindless descriptor are87/// [`Cow`]s in order to support both the common case in which the fields are88/// simply `static` constants and the more unusual case in which the fields are89/// dynamically generated efficiently. An example of the latter case is90/// `ExtendedMaterial`, which needs to assemble a bindless descriptor from those91/// of the base material and the material extension at runtime.92///93/// This structure will only be present if this object is bindless.94pub struct BindlessDescriptor {95/// The bindless resource types that this object uses, in order of bindless96/// index.97///98/// The resource assigned to binding index 0 will be at index 0, the99/// resource assigned to binding index will be at index 1 in this array, and100/// so on. Unused binding indices are set to [`BindlessResourceType::None`].101pub resources: Cow<'static, [BindlessResourceType]>,102/// The [`BindlessBufferDescriptor`] for each bindless buffer that this103/// object uses.104///105/// The order of this array is irrelevant.106pub buffers: Cow<'static, [BindlessBufferDescriptor]>,107/// The [`BindlessIndexTableDescriptor`]s describing each bindless index108/// table.109///110/// This list must be sorted by the first bindless index.111pub index_tables: Cow<'static, [BindlessIndexTableDescriptor]>,112}113114/// The type of potentially-bindless resource.115#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]116pub enum BindlessResourceType {117/// No bindless resource.118///119/// This is used as a placeholder to fill holes in the120/// [`BindlessDescriptor::resources`] list.121None,122/// A storage buffer.123Buffer,124/// A filtering sampler.125SamplerFiltering,126/// A non-filtering sampler (nearest neighbor).127SamplerNonFiltering,128/// A comparison sampler (typically used for shadow maps).129SamplerComparison,130/// A 1D texture.131Texture1d,132/// A 2D texture.133Texture2d,134/// A 2D texture array.135///136/// Note that this differs from a binding array. 2D texture arrays must all137/// have the same size and format.138Texture2dArray,139/// A 3D texture.140Texture3d,141/// A cubemap texture.142TextureCube,143/// A cubemap texture array.144///145/// Note that this differs from a binding array. Cubemap texture arrays must146/// all have the same size and format.147TextureCubeArray,148/// Multiple instances of plain old data concatenated into a single buffer.149///150/// This corresponds to the `#[data]` declaration in151/// [`crate::render_resource::AsBindGroup`].152///153/// Note that this resource doesn't itself map to a GPU-level binding154/// resource and instead depends on the `MaterialBindGroupAllocator` to155/// create a binding resource for it.156DataBuffer,157}158159/// Describes a bindless buffer.160///161/// Unlike samplers and textures, each buffer in a bind group gets its own162/// unique bind group entry. That is, there isn't any `bindless_buffers` binding163/// array to go along with `bindless_textures_2d`,164/// `bindless_samplers_filtering`, etc. Therefore, this descriptor contains two165/// indices: the *binding number* and the *bindless index*. The binding number166/// is the `@binding` number used in the shader, while the bindless index is the167/// index of the buffer in the bindless index table (which is itself168/// conventionally bound to binding number 0).169///170/// When declaring the buffer in a derived implementation171/// [`crate::render_resource::AsBindGroup`] with syntax like172/// `#[uniform(BINDLESS_INDEX, StandardMaterialUniform,173/// bindless(BINDING_NUMBER)]`, the bindless index is `BINDLESS_INDEX`, and the174/// binding number is `BINDING_NUMBER`. Note the order.175#[derive(Clone, Copy, Debug)]176pub struct BindlessBufferDescriptor {177/// The actual binding number of the buffer.178///179/// This is declared with `@binding` in WGSL. When deriving180/// [`crate::render_resource::AsBindGroup`], this is the `BINDING_NUMBER` in181/// `#[uniform(BINDLESS_INDEX, StandardMaterialUniform,182/// bindless(BINDING_NUMBER)]`.183pub binding_number: BindingNumber,184/// The index of the buffer in the bindless index table.185///186/// In the shader, this is the index into the table bound to binding 0. When187/// deriving [`crate::render_resource::AsBindGroup`], this is the188/// `BINDLESS_INDEX` in `#[uniform(BINDLESS_INDEX, StandardMaterialUniform,189/// bindless(BINDING_NUMBER)]`.190pub bindless_index: BindlessIndex,191/// The size of the buffer in bytes, if known.192pub size: Option<usize>,193}194195/// Describes the layout of the bindless index table, which maps bindless196/// indices to indices within the binding arrays.197#[derive(Clone)]198pub struct BindlessIndexTableDescriptor {199/// The range of bindless indices that this descriptor covers.200pub indices: Range<BindlessIndex>,201/// The binding at which the index table itself will be bound.202///203/// By default, this is binding 0, but it can be changed with the204/// `#[bindless(index_table(binding(B)))]` attribute.205pub binding_number: BindingNumber,206}207208/// The index of the actual binding in the bind group.209///210/// This is the value specified in WGSL as `@binding`.211#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Deref, DerefMut)]212pub struct BindingNumber(pub u32);213214/// The index in the bindless index table.215///216/// This table is conventionally bound to binding number 0.217#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Hash, Debug, Deref, DerefMut)]218pub struct BindlessIndex(pub u32);219220/// Creates the bind group layout entries common to all shaders that use221/// bindless bind groups.222///223/// `bindless_resource_count` specifies the total number of bindless resources.224/// `bindless_slab_resource_limit` specifies the resolved225/// [`BindlessSlabResourceLimit`] value.226pub fn create_bindless_bind_group_layout_entries(227bindless_index_table_length: u32,228bindless_slab_resource_limit: u32,229bindless_index_table_binding_number: BindingNumber,230) -> Vec<BindGroupLayoutEntry> {231let bindless_slab_resource_limit =232NonZeroU32::new(bindless_slab_resource_limit).expect("Bindless slot count must be nonzero");233234// The maximum size of a binding array is the235// `bindless_slab_resource_limit`, which would occur if all of the bindless236// resources were of the same type. So we create our binding arrays with237// that size.238239vec![240// Start with the bindless index table, bound to binding number 0.241storage_buffer_read_only_sized(242false,243NonZeroU64::new(bindless_index_table_length as u64 * size_of::<u32>() as u64),244)245.build(246*bindless_index_table_binding_number,247ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,248),249// Continue with the common bindless resource arrays.250sampler(SamplerBindingType::Filtering)251.count(bindless_slab_resource_limit)252.build(2531,254ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,255),256sampler(SamplerBindingType::NonFiltering)257.count(bindless_slab_resource_limit)258.build(2592,260ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,261),262sampler(SamplerBindingType::Comparison)263.count(bindless_slab_resource_limit)264.build(2653,266ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,267),268texture_1d(TextureSampleType::Float { filterable: true })269.count(bindless_slab_resource_limit)270.build(2714,272ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,273),274texture_2d(TextureSampleType::Float { filterable: true })275.count(bindless_slab_resource_limit)276.build(2775,278ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,279),280texture_2d_array(TextureSampleType::Float { filterable: true })281.count(bindless_slab_resource_limit)282.build(2836,284ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,285),286texture_3d(TextureSampleType::Float { filterable: true })287.count(bindless_slab_resource_limit)288.build(2897,290ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,291),292texture_cube(TextureSampleType::Float { filterable: true })293.count(bindless_slab_resource_limit)294.build(2958,296ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,297),298texture_cube_array(TextureSampleType::Float { filterable: true })299.count(bindless_slab_resource_limit)300.build(3019,302ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,303),304]305}306307impl BindlessSlabResourceLimit {308/// Determines the actual bindless slab resource limit on this platform.309pub fn resolve(&self) -> u32 {310match *self {311BindlessSlabResourceLimit::Auto => AUTO_BINDLESS_SLAB_RESOURCE_LIMIT,312BindlessSlabResourceLimit::Custom(limit) => limit,313}314}315}316317impl BindlessResourceType {318/// Returns the binding number for the common array of this resource type.319///320/// For example, if you pass `BindlessResourceType::Texture2d`, this will321/// return 5, in order to match the `@group(2) @binding(5) var322/// bindless_textures_2d: binding_array<texture_2d<f32>>` declaration in323/// `bindless.wgsl`.324///325/// Not all resource types have fixed binding numbers. If you call326/// [`Self::binding_number`] on such a resource type, it returns `None`.327///328/// Note that this returns a static reference to the binding number, not the329/// binding number itself. This is to conform to an idiosyncratic API in330/// `wgpu` whereby binding numbers for binding arrays are taken by `&u32`331/// *reference*, not by `u32` value.332pub fn binding_number(&self) -> Option<&'static BindingNumber> {333match BINDING_NUMBERS.binary_search_by_key(self, |(key, _)| *key) {334Ok(binding_number) => Some(&BINDING_NUMBERS[binding_number].1),335Err(_) => None,336}337}338}339340impl From<TextureViewDimension> for BindlessResourceType {341fn from(texture_view_dimension: TextureViewDimension) -> Self {342match texture_view_dimension {343TextureViewDimension::D1 => BindlessResourceType::Texture1d,344TextureViewDimension::D2 => BindlessResourceType::Texture2d,345TextureViewDimension::D2Array => BindlessResourceType::Texture2dArray,346TextureViewDimension::Cube => BindlessResourceType::TextureCube,347TextureViewDimension::CubeArray => BindlessResourceType::TextureCubeArray,348TextureViewDimension::D3 => BindlessResourceType::Texture3d,349}350}351}352353impl From<SamplerBindingType> for BindlessResourceType {354fn from(sampler_binding_type: SamplerBindingType) -> Self {355match sampler_binding_type {356SamplerBindingType::Filtering => BindlessResourceType::SamplerFiltering,357SamplerBindingType::NonFiltering => BindlessResourceType::SamplerNonFiltering,358SamplerBindingType::Comparison => BindlessResourceType::SamplerComparison,359}360}361}362363impl From<u32> for BindlessIndex {364fn from(value: u32) -> Self {365Self(value)366}367}368369impl From<u32> for BindingNumber {370fn from(value: u32) -> Self {371Self(value)372}373}374375376