Skip to main content

bevy_render/
material_bind_groups.rs

1//! Material bind group management for bindless resources.
2//!
3//! In bindless mode, Bevy's renderer groups materials into bind groups. This
4//! allocator manages each bind group, assigning slots to materials as
5//! appropriate.
6
7use bevy_app::{App, Plugin};
8use bevy_asset::{Asset, AssetId, Handle, UntypedAssetId};
9use bevy_derive::{Deref, DerefMut};
10use bevy_ecs::{
11    resource::Resource,
12    schedule::IntoScheduleConfigs as _,
13    system::{Commands, Res, ResMut, SystemParamItem},
14};
15use bevy_platform::collections::{hash_map::Entry, HashMap, HashSet};
16use bevy_reflect::{prelude::ReflectDefault, Reflect};
17use bevy_utils::{default, TypeIdHashMap};
18use bytemuck::{Pod, Zeroable};
19use core::hash::Hash;
20use core::{cmp::Ordering, iter, mem, ops::Range};
21use std::any::TypeId;
22use tracing::{error, trace};
23
24use crate::{
25    erased_render_asset::PrepareAssetError,
26    render_asset::RenderAssets,
27    render_resource::{
28        AsBindGroup, AsBindGroupError, BindGroupBuilder, BindlessSlabResourceLimit,
29        OwnedBindingResource, PipelineCache, UnpreparedBindingResource, UnpreparedBindingResources,
30    },
31    storage::ShaderBuffer,
32    GpuResourceAppExt as _, Render, RenderApp, RenderStartup, RenderSystems,
33};
34use crate::{
35    render_resource::{
36        BindGroup, BindGroupEntry, BindGroupLayoutDescriptor, BindingNumber, BindingResource,
37        BindlessDescriptor, BindlessIndex, BindlessIndexTableDescriptor, BindlessResourceType,
38        Buffer, BufferBinding, BufferDescriptor, BufferId, BufferInitDescriptor, BufferUsages,
39        CompareFunction, FilterMode, MipmapFilterMode, PreparedBindGroup, RawBufferVec, Sampler,
40        SamplerDescriptor, SamplerId, TextureView, TextureViewDimension, TextureViewId,
41        WgpuSampler, WgpuTextureView,
42    },
43    renderer::{RenderDevice, RenderQueue},
44    settings::WgpuFeatures,
45    storage::{GpuShaderBuffer, RenderChangedShaderBuffers},
46    texture::FallbackImage,
47};
48
49/// A [`Plugin`] that provides the material bind group allocator.
50///
51/// The material bind group allocator is infrastructure for bindless resources.
52/// It packs multiple materials into a small number of bind groups, allowing
53/// Bevy to render large parts of the scene with a small number of drawcalls.
54pub struct MaterialBindGroupPlugin;
55
56/// A resource that maps each untyped material ID to its binding.
57///
58/// This duplicates information in `RenderAssets<M>`, but it doesn't have the
59/// `M` type parameter, so it can be used in untyped contexts like
60/// `collect_meshes_for_gpu_building`.
61#[derive(impl bevy_ecs::component::Component for RenderMaterialBindings where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {
    const STORAGE_TYPE: bevy_ecs::component::StorageType =
        bevy_ecs::component::StorageType::SparseSet;
    type Mutability = bevy_ecs::component::Mutable;
    fn register_required_components(_requiree:
            bevy_ecs::component::ComponentId,
        required_components:
            &mut bevy_ecs::component::RequiredComponentsRegistrator) {
        let resource_component_id =
            if let ::core::option::Option::Some(id) =
                    required_components.components_registrator().component_id::<RenderMaterialBindings>()
                {
                id
            } else {
                required_components.components_registrator().register_component::<RenderMaterialBindings>()
            };
        required_components.register_required::<bevy_ecs::resource::IsResource>(move
                ||
                bevy_ecs::resource::IsResource::new(resource_component_id));
    }
    fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
        use bevy_ecs::component::{
            DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
        };
        (&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
    }
    fn relationship_accessor()
        ->
            ::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
        ::core::option::Option::None
    }
}
impl bevy_ecs::resource::Resource for RenderMaterialBindings where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource, #[automatically_derived]
impl ::core::default::Default for RenderMaterialBindings {
    #[inline]
    fn default() -> Self {
        Self {
            bindings: ::core::default::Default::default(),
            bind_group_builder: ::core::default::Default::default(),
        }
    }
}Default, impl ::core::ops::Deref for RenderMaterialBindings {
    type Target = HashMap<UntypedAssetId, MaterialBindingId>;
    fn deref(&self) -> &Self::Target { &self.bindings }
}Deref, impl ::core::ops::DerefMut for RenderMaterialBindings {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.bindings }
}DerefMut)]
62pub struct RenderMaterialBindings {
63    /// The mapping from each asset ID to its location within the material bind
64    /// group allocator.
65    #[deref]
66    bindings: HashMap<UntypedAssetId, MaterialBindingId>,
67
68    /// The bind group we're building up.
69    ///
70    /// This is cleared and reused for every asset that we build in order to
71    /// reuse allocations.
72    bind_group_builder: BindGroupBuilder,
73}
74
75/// A resource that places materials into bind groups and tracks their
76/// resources.
77///
78/// Internally, Bevy has separate allocators for bindless and non-bindless
79/// materials. This resource provides a common interface to the specific
80/// allocator in use.
81#[derive(impl bevy_ecs::component::Component for MaterialBindGroupAllocators where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {
    const STORAGE_TYPE: bevy_ecs::component::StorageType =
        bevy_ecs::component::StorageType::SparseSet;
    type Mutability = bevy_ecs::component::Mutable;
    fn register_required_components(_requiree:
            bevy_ecs::component::ComponentId,
        required_components:
            &mut bevy_ecs::component::RequiredComponentsRegistrator) {
        let resource_component_id =
            if let ::core::option::Option::Some(id) =
                    required_components.components_registrator().component_id::<MaterialBindGroupAllocators>()
                {
                id
            } else {
                required_components.components_registrator().register_component::<MaterialBindGroupAllocators>()
            };
        required_components.register_required::<bevy_ecs::resource::IsResource>(move
                ||
                bevy_ecs::resource::IsResource::new(resource_component_id));
    }
    fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
        use bevy_ecs::component::{
            DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
        };
        (&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
    }
    fn relationship_accessor()
        ->
            ::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
        ::core::option::Option::None
    }
}
impl bevy_ecs::resource::Resource for MaterialBindGroupAllocators where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource, impl ::core::ops::Deref for MaterialBindGroupAllocators {
    type Target = TypeIdHashMap<MaterialBindGroupAllocator>;
    fn deref(&self) -> &Self::Target { &self.0 }
}Deref, impl ::core::ops::DerefMut for MaterialBindGroupAllocators {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}DerefMut, #[automatically_derived]
impl ::core::default::Default for MaterialBindGroupAllocators {
    #[inline]
    fn default() -> Self { Self(::core::default::Default::default()) }
}Default)]
82pub struct MaterialBindGroupAllocators(TypeIdHashMap<MaterialBindGroupAllocator>);
83
84/// The allocator that manages a specific material.
85pub enum MaterialBindGroupAllocator {
86    /// The allocator used when the material is bindless.
87    Bindless(Box<MaterialBindGroupBindlessAllocator>),
88    /// The allocator used when the material is non-bindless.
89    NonBindless(Box<MaterialBindGroupNonBindlessAllocator>),
90}
91
92/// The allocator that places bindless materials into bind groups and tracks
93/// their resources.
94pub struct MaterialBindGroupBindlessAllocator {
95    /// The label of the bind group allocator to use for allocated buffers.
96    label: &'static str,
97    /// The slabs, each of which contains a bind group.
98    slabs: Vec<MaterialBindlessSlab>,
99    /// The layout of the bind groups that we produce.
100    bind_group_layout: BindGroupLayoutDescriptor,
101    /// Information about the bindless resources in the material.
102    ///
103    /// We use this information to create and maintain bind groups.
104    bindless_descriptor: BindlessDescriptor,
105
106    /// Dummy buffers that we use to fill empty slots in buffer binding arrays.
107    ///
108    /// There's one fallback buffer for each buffer in the bind group, each
109    /// appropriately sized. Each buffer contains one uninitialized element of
110    /// the applicable type.
111    fallback_buffers: HashMap<BindlessIndex, Buffer>,
112
113    /// The maximum number of resources that can be stored in a slab.
114    ///
115    /// This corresponds to `SLAB_CAPACITY` in the `#[bindless(SLAB_CAPACITY)]`
116    /// attribute, when deriving `AsBindGroup`.
117    slab_capacity: u32,
118}
119
120/// A single bind group and the bookkeeping necessary to allocate into it.
121pub struct MaterialBindlessSlab {
122    /// The current bind group, if it's up to date.
123    ///
124    /// If this is `None`, then the bind group is dirty and needs to be
125    /// regenerated.
126    bind_group: Option<BindGroup>,
127
128    /// The GPU-accessible buffers that hold the mapping from binding index to
129    /// bindless slot.
130    ///
131    /// This is conventionally assigned to bind group binding 0, but it can be
132    /// changed using the `#[bindless(index_table(binding(B)))]` attribute on
133    /// `AsBindGroup`.
134    ///
135    /// Because the slab binary searches this table, the entries within must be
136    /// sorted by bindless index.
137    bindless_index_tables: Vec<MaterialBindlessIndexTable>,
138
139    /// The binding arrays containing samplers.
140    samplers: HashMap<BindlessResourceType, MaterialBindlessBindingArray<Sampler>>,
141    /// The binding arrays containing textures.
142    textures: HashMap<BindlessResourceType, MaterialBindlessBindingArray<TextureView>>,
143    /// The binding arrays containing buffers.
144    buffers: HashMap<BindlessIndex, MaterialBindlessBindingArray<Buffer>>,
145    /// The binding arrays containing shader buffers.
146    ///
147    /// These differ from the binding arrays containing raw buffers because
148    /// shader buffers are assets, and therefore the actual underlying buffer
149    /// can change.
150    shader_buffers: HashMap<BindlessIndex, MaterialBindlessBindingArray<Handle<ShaderBuffer>>>,
151    /// The buffers that contain plain old data (i.e. the structure-level
152    /// `#[data]` attribute of `AsBindGroup`).
153    data_buffers: HashMap<BindlessIndex, MaterialDataBuffer>,
154
155    /// A list of free slot IDs.
156    free_slots: Vec<MaterialBindGroupSlot>,
157    /// The total number of materials currently allocated in this slab.
158    live_allocation_count: u32,
159    /// The total number of resources currently allocated in the binding arrays.
160    allocated_resource_count: u32,
161}
162
163/// A GPU-accessible buffer that holds the mapping from binding index to
164/// bindless slot.
165///
166/// This is conventionally assigned to bind group binding 0, but it can be
167/// changed by altering the [`Self::binding_number`], which corresponds to the
168/// `#[bindless(index_table(binding(B)))]` attribute in `AsBindGroup`.
169struct MaterialBindlessIndexTable {
170    /// The buffer containing the mappings.
171    buffer: RetainedRawBufferVec<u32>,
172    /// The range of bindless indices that this bindless index table covers.
173    ///
174    /// If this range is M..N, then the field at index $i$ maps to bindless
175    /// index $i$ + M. The size of this table is N - M.
176    ///
177    /// This corresponds to the `#[bindless(index_table(range(M..N)))]`
178    /// attribute in `AsBindGroup`.
179    index_range: Range<BindlessIndex>,
180    /// The binding number that this index table is assigned to in the shader.
181    binding_number: BindingNumber,
182}
183
184/// A single binding array for storing bindless resources and the bookkeeping
185/// necessary to allocate into it.
186struct MaterialBindlessBindingArray<R>
187where
188    R: GetBindingResourceId,
189{
190    /// The number of the binding that we attach this binding array to.
191    binding_number: BindingNumber,
192    /// A mapping from bindless slot index to the resource stored in that slot,
193    /// if any.
194    bindings: Vec<Option<MaterialBindlessBinding<R>>>,
195    /// The type of resource stored in this binding array.
196    resource_type: BindlessResourceType,
197    /// Maps a resource ID to the slot in which it's stored.
198    ///
199    /// This is essentially the inverse mapping of [`Self::bindings`].
200    resource_to_slot: HashMap<BindingResourceId, u32>,
201    /// A list of free slots in [`Self::bindings`] that contain no binding.
202    free_slots: Vec<u32>,
203    /// The number of allocated objects in this binding array.
204    len: u32,
205}
206
207/// A single resource (sampler, texture, or buffer) in a binding array.
208///
209/// Resources hold a reference count, which specifies the number of materials
210/// currently allocated within the slab that refer to this resource. When the
211/// reference count drops to zero, the resource is freed.
212struct MaterialBindlessBinding<R>
213where
214    R: GetBindingResourceId,
215{
216    /// The sampler, texture, or buffer.
217    resource: R,
218    /// The number of materials currently allocated within the containing slab
219    /// that use this resource.
220    ref_count: u32,
221}
222
223/// The allocator that stores bind groups for non-bindless materials.
224pub struct MaterialBindGroupNonBindlessAllocator {
225    /// The label of the bind group allocator to use for allocated buffers.
226    label: &'static str,
227    /// A mapping from [`MaterialBindGroupIndex`] to the bind group allocated in
228    /// each slot.
229    bind_groups: Vec<Option<MaterialNonBindlessAllocatedBindGroup>>,
230    /// The bind groups that are dirty and need to be prepared.
231    ///
232    /// To prepare the bind groups, call
233    /// [`MaterialBindGroupAllocator::prepare_bind_groups`].
234    to_prepare: HashSet<MaterialBindGroupIndex>,
235    /// A list of free bind group indices.
236    free_indices: Vec<MaterialBindGroupIndex>,
237}
238
239/// A single bind group that a [`MaterialBindGroupNonBindlessAllocator`] is
240/// currently managing.
241enum MaterialNonBindlessAllocatedBindGroup {
242    /// An unprepared bind group.
243    ///
244    /// The allocator prepares all outstanding unprepared bind groups when
245    /// [`MaterialBindGroupNonBindlessAllocator::prepare_bind_groups`] is
246    /// called.
247    Unprepared {
248        /// The unprepared bind group, including extra data.
249        bind_group: BindGroupBuilder,
250        /// The layout of that bind group.
251        layout: BindGroupLayoutDescriptor,
252    },
253    /// A bind group that's already been prepared.
254    Prepared {
255        /// The actual bind group.
256        bind_group: PreparedBindGroup,
257        /// The layout descriptor that reflects the layout of this bind group.
258        ///
259        /// Rendering doesn't use this, but we need to keep it around in case we
260        /// need to demote a prepared bind group to an unprepared one. That will
261        /// happen if a shader buffer changes, causing the bind group to become
262        /// invalid.
263        layout: BindGroupLayoutDescriptor,
264        /// Uniform buffers referenced by the bind group.
265        #[expect(dead_code, reason = "These buffers are only referenced by bind groups")]
266        uniform_buffers: Vec<Buffer>,
267    },
268}
269
270/// Dummy instances of various resources that we fill unused slots in binding
271/// arrays with.
272#[derive(impl bevy_ecs::component::Component for FallbackBindlessResources where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {
    const STORAGE_TYPE: bevy_ecs::component::StorageType =
        bevy_ecs::component::StorageType::SparseSet;
    type Mutability = bevy_ecs::component::Mutable;
    fn register_required_components(_requiree:
            bevy_ecs::component::ComponentId,
        required_components:
            &mut bevy_ecs::component::RequiredComponentsRegistrator) {
        let resource_component_id =
            if let ::core::option::Option::Some(id) =
                    required_components.components_registrator().component_id::<FallbackBindlessResources>()
                {
                id
            } else {
                required_components.components_registrator().register_component::<FallbackBindlessResources>()
            };
        required_components.register_required::<bevy_ecs::resource::IsResource>(move
                ||
                bevy_ecs::resource::IsResource::new(resource_component_id));
    }
    fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
        use bevy_ecs::component::{
            DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
        };
        (&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
    }
    fn relationship_accessor()
        ->
            ::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
        ::core::option::Option::None
    }
}
impl bevy_ecs::resource::Resource for FallbackBindlessResources where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource)]
273pub struct FallbackBindlessResources {
274    /// A dummy filtering sampler.
275    filtering_sampler: Sampler,
276    /// A dummy non-filtering sampler.
277    non_filtering_sampler: Sampler,
278    /// A dummy comparison sampler.
279    comparison_sampler: Sampler,
280}
281
282/// A dummy buffer that substitutes for a shader buffer if that shader buffer
283/// points to an invalid buffer.
284#[derive(impl bevy_ecs::component::Component for FallbackBuffer where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {
    const STORAGE_TYPE: bevy_ecs::component::StorageType =
        bevy_ecs::component::StorageType::SparseSet;
    type Mutability = bevy_ecs::component::Mutable;
    fn register_required_components(_requiree:
            bevy_ecs::component::ComponentId,
        required_components:
            &mut bevy_ecs::component::RequiredComponentsRegistrator) {
        let resource_component_id =
            if let ::core::option::Option::Some(id) =
                    required_components.components_registrator().component_id::<FallbackBuffer>()
                {
                id
            } else {
                required_components.components_registrator().register_component::<FallbackBuffer>()
            };
        required_components.register_required::<bevy_ecs::resource::IsResource>(move
                ||
                bevy_ecs::resource::IsResource::new(resource_component_id));
    }
    fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
        use bevy_ecs::component::{
            DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
        };
        (&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
    }
    fn relationship_accessor()
        ->
            ::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
        ::core::option::Option::None
    }
}
impl bevy_ecs::resource::Resource for FallbackBuffer where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource, impl ::core::ops::Deref for FallbackBuffer {
    type Target = Buffer;
    fn deref(&self) -> &Self::Target { &self.0 }
}Deref, impl ::core::ops::DerefMut for FallbackBuffer {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}DerefMut)]
285pub struct FallbackBuffer(Buffer);
286
287/// The `wgpu` ID of a single bindless or non-bindless resource.
288#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for BindingResourceId { }
#[automatically_derived]
impl ::core::clone::Clone for BindingResourceId {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<BufferId>;
        let _: ::core::clone::AssertParamIsClone<TextureViewDimension>;
        let _: ::core::clone::AssertParamIsClone<TextureViewId>;
        let _: ::core::clone::AssertParamIsClone<SamplerId>;
        let _: ::core::clone::AssertParamIsClone<AssetId<ShaderBuffer>>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for BindingResourceId { }Copy, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for BindingResourceId { }
#[automatically_derived]
impl ::core::cmp::PartialEq for BindingResourceId {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
                ::core::intrinsics::discriminant_value(other) &&
            match (self, other) {
                (Self::Buffer(__self_0), Self::Buffer(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (Self::TextureView(__self_0, __self_1),
                    Self::TextureView(__arg1_0, __arg1_1)) =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1,
                (Self::Sampler(__self_0), Self::Sampler(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (Self::ShaderBuffer(__self_0), Self::ShaderBuffer(__arg1_0))
                    => __self_0 == __arg1_0,
                _ => true,
            }
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for BindingResourceId {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<BufferId>;
        let _: ::core::cmp::AssertParamIsEq<TextureViewDimension>;
        let _: ::core::cmp::AssertParamIsEq<TextureViewId>;
        let _: ::core::cmp::AssertParamIsEq<SamplerId>;
        let _: ::core::cmp::AssertParamIsEq<AssetId<ShaderBuffer>>;
    }
}Eq, #[automatically_derived]
impl ::core::hash::Hash for BindingResourceId {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        ::core::hash::Hash::hash(&::core::intrinsics::discriminant_value(self),
            state);
        match self {
            Self::Buffer(__self_0) =>
                ::core::hash::Hash::hash(__self_0, state),
            Self::TextureView(__self_0, __self_1) => {
                ::core::hash::Hash::hash(__self_0, state);
                ::core::hash::Hash::hash(__self_1, state)
            }
            Self::Sampler(__self_0) =>
                ::core::hash::Hash::hash(__self_0, state),
            Self::ShaderBuffer(__self_0) =>
                ::core::hash::Hash::hash(__self_0, state),
            _ => {}
        }
    }
}Hash, #[automatically_derived]
impl ::core::fmt::Debug for BindingResourceId {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::Buffer(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Buffer",
                    &__self_0),
            Self::TextureView(__self_0, __self_1) =>
                ::core::fmt::Formatter::debug_tuple_field2_finish(f,
                    "TextureView", __self_0, &__self_1),
            Self::Sampler(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "Sampler", &__self_0),
            Self::DataBuffer =>
                ::core::fmt::Formatter::write_str(f, "DataBuffer"),
            Self::ShaderBuffer(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "ShaderBuffer", &__self_0),
        }
    }
}Debug)]
289enum BindingResourceId {
290    /// A buffer.
291    Buffer(BufferId),
292    /// A texture view, with the given dimension.
293    TextureView(TextureViewDimension, TextureViewId),
294    /// A sampler.
295    Sampler(SamplerId),
296    /// A buffer containing plain old data.
297    ///
298    /// This corresponds to the `#[data]` structure-level attribute on
299    /// `AsBindGroup`.
300    DataBuffer,
301    /// A [`ShaderBuffer`] asset.
302    ShaderBuffer(AssetId<ShaderBuffer>),
303}
304
305/// A temporary list of references to `wgpu` bindless resources.
306///
307/// We need this because the `wgpu` bindless API takes a slice of references.
308/// Thus we need to create intermediate vectors of bindless resources in order
309/// to satisfy `wgpu`'s lifetime requirements.
310enum BindingResourceArray<'a> {
311    /// A list of bindings.
312    Buffers(Vec<BufferBinding<'a>>),
313    /// A list of texture views.
314    TextureViews(Vec<&'a WgpuTextureView>),
315    /// A list of samplers.
316    Samplers(Vec<&'a WgpuSampler>),
317}
318
319/// The location of a material (either bindless or non-bindless) within the
320/// slabs.
321#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for MaterialBindingId { }
#[automatically_derived]
impl ::core::clone::Clone for MaterialBindingId {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<MaterialBindGroupIndex>;
        let _: ::core::clone::AssertParamIsClone<MaterialBindGroupSlot>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for MaterialBindingId { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for MaterialBindingId {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f,
            "MaterialBindingId", "group", &self.group, "slot", &&self.slot)
    }
}Debug, #[automatically_derived]
impl ::core::default::Default for MaterialBindingId {
    #[inline]
    fn default() -> Self {
        Self {
            group: ::core::default::Default::default(),
            slot: ::core::default::Default::default(),
        }
    }
}Default, const _: () =
    {
        if !(::core::mem::size_of::<MaterialBindingId>() ==
                    (::core::mem::size_of::<MaterialBindGroupIndex>() +
                            ::core::mem::size_of::<MaterialBindGroupSlot>())) {
            {
                ::std::rt::begin_panic("derive(Pod) was applied to a type with padding");
            }
        };
    };
const _: fn() =
    ||
        {
            #[allow(clippy :: missing_const_for_fn)]
            #[doc(hidden)]
            fn check() {
                fn assert_impl<T: ::bytemuck::Pod>() {}
                assert_impl::<MaterialBindGroupIndex>();
            }
        };
const _: fn() =
    ||
        {
            #[allow(clippy :: missing_const_for_fn)]
            #[doc(hidden)]
            fn check() {
                fn assert_impl<T: ::bytemuck::Pod>() {}
                assert_impl::<MaterialBindGroupSlot>();
            }
        };
unsafe impl ::bytemuck::Pod for MaterialBindingId {}Pod, const _: fn() =
    ||
        {
            #[allow(clippy :: missing_const_for_fn)]
            #[doc(hidden)]
            fn check() {
                fn assert_impl<T: ::bytemuck::Zeroable>() {}
                assert_impl::<MaterialBindGroupIndex>();
            }
        };
const _: fn() =
    ||
        {
            #[allow(clippy :: missing_const_for_fn)]
            #[doc(hidden)]
            fn check() {
                fn assert_impl<T: ::bytemuck::Zeroable>() {}
                assert_impl::<MaterialBindGroupSlot>();
            }
        };
unsafe impl ::bytemuck::Zeroable for MaterialBindingId {}Zeroable, const _: () =
    {
        impl bevy_reflect::GetTypeRegistration for MaterialBindingId where  {
            fn get_type_registration() -> bevy_reflect::TypeRegistration {
                let mut registration =
                    bevy_reflect::TypeRegistration::of::<Self>();
                registration.insert(<bevy_reflect::ReflectFromPtr as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration.insert(<bevy_reflect::ReflectFromReflect as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration.register_type_data_with::<ReflectDefault, Self,
                    _>(());
                registration
            }
            #[inline(never)]
            fn register_type_dependencies(registry:
                    &mut bevy_reflect::TypeRegistry) {
                <MaterialBindGroupIndex as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
                <MaterialBindGroupSlot as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
            }
        }
        impl bevy_reflect::Typed for MaterialBindingId where  {
            #[inline(never)]
            fn type_info() -> &'static bevy_reflect::TypeInfo {
                static CELL: bevy_reflect::utility::NonGenericTypeInfoCell =
                    bevy_reflect::utility::NonGenericTypeInfoCell::new();
                CELL.get_or_set(#[inline(never)] ||
                        {
                            bevy_reflect::TypeInfo::Struct(bevy_reflect::structs::StructInfo::new::<Self>(&[bevy_reflect::NamedField::new::<MaterialBindGroupIndex>("group"),
                                                bevy_reflect::NamedField::new::<MaterialBindGroupSlot>("slot")]))
                        })
            }
        }
        #[allow(deprecated, reason =
        "derives on a deprecated type shouldn't be considered a usage")]
        impl bevy_reflect::TypePath for MaterialBindingId where  {
            fn type_path() -> &'static str {
                "bevy_render::material_bind_groups::MaterialBindingId"
            }
            fn short_type_path() -> &'static str { "MaterialBindingId" }
            fn type_ident() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("MaterialBindingId")
            }
            fn crate_name() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_render::material_bind_groups".split(':').next().unwrap())
            }
            fn module_path() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_render::material_bind_groups")
            }
        }
        impl bevy_reflect::Reflect for MaterialBindingId where  {
            #[inline]
            fn into_any(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn ::core::any::Any> {
                self
            }
            #[inline]
            fn as_any(&self) -> &dyn ::core::any::Any { self }
            #[inline]
            fn as_any_mut(&mut self) -> &mut dyn ::core::any::Any { self }
            #[inline]
            fn into_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect> {
                self
            }
            #[inline]
            fn as_reflect(&self) -> &dyn bevy_reflect::Reflect { self }
            #[inline]
            fn as_reflect_mut(&mut self) -> &mut dyn bevy_reflect::Reflect {
                self
            }
            #[inline]
            fn set(&mut self,
                value:
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>)
                ->
                    ::core::result::Result<(),
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>> {
                *self = <dyn bevy_reflect::Reflect>::take(value)?;
                ::core::result::Result::Ok(())
            }
        }
        impl bevy_reflect::func::args::GetOwnership for MaterialBindingId
            where  {
            fn ownership() -> bevy_reflect::func::args::Ownership {
                bevy_reflect::func::args::Ownership::Owned
            }
        }
        impl bevy_reflect::func::args::FromArg for MaterialBindingId where  {
            type This<'from_arg> = MaterialBindingId;
            fn from_arg(arg: bevy_reflect::func::args::Arg)
                ->
                    ::core::result::Result<Self::This<'_>,
                    bevy_reflect::func::args::ArgError> {
                arg.take_owned()
            }
        }
        impl bevy_reflect::func::IntoReturn for MaterialBindingId where  {
            fn into_return<'into_return>(self)
                -> bevy_reflect::func::Return<'into_return> where
                Self: 'into_return {
                bevy_reflect::func::Return::Owned(bevy_reflect::__macro_exports::alloc_utils::Box::new(self))
            }
        }
        #[allow(non_upper_case_globals)]
        const _: () =
            {
                static __INVENTORY: ::inventory::Node =
                    ::inventory::Node {
                        value: &{
                                bevy_reflect::__macro_exports::auto_register::AutomaticReflectRegistrations(<MaterialBindingId
                                        as
                                        bevy_reflect::__macro_exports::auto_register::RegisterForReflection>::__register)
                            },
                        next: ::inventory::__private::UnsafeCell::new(::inventory::__private::Option::None),
                    };
                #[link_section = ".text.startup"]
                unsafe extern "C" fn __ctor() {
                    unsafe {
                        ::inventory::ErasedNode::submit(__INVENTORY.value,
                            &__INVENTORY)
                    }
                }
                #[used]
                #[link_section = ".init_array"]
                static __CTOR: unsafe extern "C" fn() = __ctor;
            };
        impl bevy_reflect::structs::Struct for MaterialBindingId where  {
            fn field(&self, name: &str)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match name {
                    "group" => ::core::option::Option::Some(&self.group),
                    "slot" => ::core::option::Option::Some(&self.slot),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_mut(&mut self, name: &str)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match name {
                    "group" => ::core::option::Option::Some(&mut self.group),
                    "slot" => ::core::option::Option::Some(&mut self.slot),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_at(&self, index: usize)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match index {
                    0usize => ::core::option::Option::Some(&self.group),
                    1usize => ::core::option::Option::Some(&self.slot),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_at_mut(&mut self, index: usize)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match index {
                    0usize => ::core::option::Option::Some(&mut self.group),
                    1usize => ::core::option::Option::Some(&mut self.slot),
                    _ => ::core::option::Option::None,
                }
            }
            fn name_at(&self, index: usize) -> ::core::option::Option<&str> {
                match index {
                    0usize => ::core::option::Option::Some("group"),
                    1usize => ::core::option::Option::Some("slot"),
                    _ => ::core::option::Option::None,
                }
            }
            fn index_of_name(&self, name: &str)
                -> ::core::option::Option<usize> {
                match name {
                    "group" => ::core::option::Option::Some(0usize),
                    "slot" => ::core::option::Option::Some(1usize),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_len(&self) -> usize { 2usize }
            fn iter_fields(&self) -> bevy_reflect::structs::FieldIter {
                bevy_reflect::structs::FieldIter::new(self)
            }
            fn to_dynamic_struct(&self)
                ->
                    ::core::result::Result<bevy_reflect::structs::DynamicStruct,
                    bevy_reflect::ReflectCloneError> {
                let mut dynamic: bevy_reflect::structs::DynamicStruct =
                    ::core::default::Default::default();
                dynamic.set_represented_type(bevy_reflect::PartialReflect::get_represented_type_info(self));
                dynamic.insert_boxed("group",
                    bevy_reflect::PartialReflect::to_dynamic(&self.group)?);
                dynamic.insert_boxed("slot",
                    bevy_reflect::PartialReflect::to_dynamic(&self.slot)?);
                ::core::result::Result::Ok(dynamic)
            }
        }
        impl bevy_reflect::PartialReflect for MaterialBindingId where  {
            #[inline]
            fn get_represented_type_info(&self)
                -> ::core::option::Option<&'static bevy_reflect::TypeInfo> {
                ::core::option::Option::Some(<Self as
                            bevy_reflect::Typed>::type_info())
            }
            #[inline]
            fn try_apply(&mut self, value: &dyn bevy_reflect::PartialReflect)
                -> ::core::result::Result<(), bevy_reflect::ApplyError> {
                if let bevy_reflect::ReflectRef::Struct(struct_value) =
                        bevy_reflect::PartialReflect::reflect_ref(value) {
                    for (name, value) in
                        bevy_reflect::structs::Struct::iter_fields(struct_value) {
                        if let ::core::option::Option::Some(v) =
                                bevy_reflect::structs::Struct::field_mut(self, name) {
                            bevy_reflect::PartialReflect::try_apply(v, value)?;
                        }
                    }
                } else {
                    return ::core::result::Result::Err(bevy_reflect::ApplyError::MismatchedKinds {
                                from_kind: bevy_reflect::PartialReflect::reflect_kind(value),
                                to_kind: bevy_reflect::ReflectKind::Struct,
                            });
                }
                ::core::result::Result::Ok(())
            }
            #[inline]
            fn reflect_kind(&self) -> bevy_reflect::ReflectKind {
                bevy_reflect::ReflectKind::Struct
            }
            #[inline]
            fn reflect_ref(&self) -> bevy_reflect::ReflectRef {
                bevy_reflect::ReflectRef::Struct(self)
            }
            #[inline]
            fn reflect_mut(&mut self) -> bevy_reflect::ReflectMut {
                bevy_reflect::ReflectMut::Struct(self)
            }
            #[inline]
            fn reflect_owned(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                -> bevy_reflect::ReflectOwned {
                bevy_reflect::ReflectOwned::Struct(self)
            }
            #[inline]
            fn try_into_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    ::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect>> {
                ::core::result::Result::Ok(self)
            }
            #[inline]
            fn try_as_reflect(&self)
                -> ::core::option::Option<&dyn bevy_reflect::Reflect> {
                ::core::option::Option::Some(self)
            }
            #[inline]
            fn try_as_reflect_mut(&mut self)
                -> ::core::option::Option<&mut dyn bevy_reflect::Reflect> {
                ::core::option::Option::Some(self)
            }
            #[inline]
            fn into_partial_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect> {
                self
            }
            #[inline]
            fn as_partial_reflect(&self)
                -> &dyn bevy_reflect::PartialReflect {
                self
            }
            #[inline]
            fn as_partial_reflect_mut(&mut self)
                -> &mut dyn bevy_reflect::PartialReflect {
                self
            }
            fn reflect_partial_eq(&self,
                value: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<bool> {
                (bevy_reflect::structs::struct_partial_eq)(self, value)
            }
            fn reflect_partial_cmp(&self,
                value: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<::core::cmp::Ordering> {
                (bevy_reflect::structs::struct_partial_cmp)(self, value)
            }
            #[inline]
            fn reflect_clone(&self)
                ->
                    ::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
                    bevy_reflect::ReflectCloneError> {
                ::core::result::Result::Ok(bevy_reflect::__macro_exports::alloc_utils::Box::new(::core::clone::Clone::clone(self)))
            }
        }
        impl bevy_reflect::FromReflect for MaterialBindingId where  {
            fn from_reflect(reflect: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<Self> {
                if let bevy_reflect::ReflectRef::Struct(__ref_struct) =
                        bevy_reflect::PartialReflect::reflect_ref(reflect) {
                    let mut __this =
                        <Self as ::core::default::Default>::default();
                    if let ::core::option::Option::Some(__field) =
                            (||
                                        <MaterialBindGroupIndex as
                                                bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
                                                    "group")?))() {
                        __this.group = __field;
                    }
                    if let ::core::option::Option::Some(__field) =
                            (||
                                        <MaterialBindGroupSlot as
                                                bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
                                                    "slot")?))() {
                        __this.slot = __field;
                    }
                    ::core::option::Option::Some(__this)
                } else { ::core::option::Option::None }
            }
        }
    };Reflect)]
322#[reflect(Clone, Default)]
323#[repr(C)]
324pub struct MaterialBindingId {
325    /// The index of the bind group (slab) where the GPU data is located.
326    pub group: MaterialBindGroupIndex,
327    /// The slot within that bind group.
328    ///
329    /// Non-bindless materials will always have a slot of 0.
330    pub slot: MaterialBindGroupSlot,
331}
332
333/// The index of each material bind group.
334///
335/// In bindless mode, each bind group contains multiple materials. In
336/// non-bindless mode, each bind group contains only one material.
337#[derive(
338    #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for MaterialBindGroupIndex { }
#[automatically_derived]
impl ::core::clone::Clone for MaterialBindGroupIndex {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<u32>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for MaterialBindGroupIndex { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for MaterialBindGroupIndex {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f,
            "MaterialBindGroupIndex", &&self.0)
    }
}Debug, #[automatically_derived]
impl ::core::default::Default for MaterialBindGroupIndex {
    #[inline]
    fn default() -> Self { Self(::core::default::Default::default()) }
}Default, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for MaterialBindGroupIndex { }
#[automatically_derived]
impl ::core::cmp::PartialEq for MaterialBindGroupIndex {
    #[inline]
    fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for MaterialBindGroupIndex {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<u32>;
    }
}Eq, #[automatically_derived]
impl ::core::hash::Hash for MaterialBindGroupIndex {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        ::core::hash::Hash::hash(&self.0, state)
    }
}Hash, const _: () =
    {
        if !(::core::mem::size_of::<MaterialBindGroupIndex>() ==
                    (::core::mem::size_of::<u32>())) {
            {
                ::std::rt::begin_panic("derive(Pod) was applied to a type with padding");
            }
        };
    };
const _: fn() =
    ||
        {
            #[allow(clippy :: missing_const_for_fn)]
            #[doc(hidden)]
            fn check() {
                fn assert_impl<T: ::bytemuck::Pod>() {}
                assert_impl::<u32>();
            }
        };
unsafe impl ::bytemuck::Pod for MaterialBindGroupIndex {}Pod, const _: fn() =
    ||
        {
            #[allow(clippy :: missing_const_for_fn)]
            #[doc(hidden)]
            fn check() {
                fn assert_impl<T: ::bytemuck::Zeroable>() {}
                assert_impl::<u32>();
            }
        };
unsafe impl ::bytemuck::Zeroable for MaterialBindGroupIndex {}Zeroable, const _: () =
    {
        impl bevy_reflect::GetTypeRegistration for MaterialBindGroupIndex
            where  {
            fn get_type_registration() -> bevy_reflect::TypeRegistration {
                let mut registration =
                    bevy_reflect::TypeRegistration::of::<Self>();
                registration.insert(<bevy_reflect::ReflectFromPtr as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration.insert(<bevy_reflect::ReflectFromReflect as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration.register_type_data_with::<ReflectDefault, Self,
                    _>(());
                registration
            }
            #[inline(never)]
            fn register_type_dependencies(registry:
                    &mut bevy_reflect::TypeRegistry) {
                <u32 as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
            }
        }
        impl bevy_reflect::Typed for MaterialBindGroupIndex where  {
            #[inline(never)]
            fn type_info() -> &'static bevy_reflect::TypeInfo {
                static CELL: bevy_reflect::utility::NonGenericTypeInfoCell =
                    bevy_reflect::utility::NonGenericTypeInfoCell::new();
                CELL.get_or_set(#[inline(never)] ||
                        {
                            bevy_reflect::TypeInfo::TupleStruct(bevy_reflect::tuple_struct::TupleStructInfo::new::<Self>(&[bevy_reflect::UnnamedField::new::<u32>(0usize)]))
                        })
            }
        }
        #[allow(deprecated, reason =
        "derives on a deprecated type shouldn't be considered a usage")]
        impl bevy_reflect::TypePath for MaterialBindGroupIndex where  {
            fn type_path() -> &'static str {
                "bevy_render::material_bind_groups::MaterialBindGroupIndex"
            }
            fn short_type_path() -> &'static str { "MaterialBindGroupIndex" }
            fn type_ident() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("MaterialBindGroupIndex")
            }
            fn crate_name() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_render::material_bind_groups".split(':').next().unwrap())
            }
            fn module_path() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_render::material_bind_groups")
            }
        }
        impl bevy_reflect::Reflect for MaterialBindGroupIndex where  {
            #[inline]
            fn into_any(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn ::core::any::Any> {
                self
            }
            #[inline]
            fn as_any(&self) -> &dyn ::core::any::Any { self }
            #[inline]
            fn as_any_mut(&mut self) -> &mut dyn ::core::any::Any { self }
            #[inline]
            fn into_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect> {
                self
            }
            #[inline]
            fn as_reflect(&self) -> &dyn bevy_reflect::Reflect { self }
            #[inline]
            fn as_reflect_mut(&mut self) -> &mut dyn bevy_reflect::Reflect {
                self
            }
            #[inline]
            fn set(&mut self,
                value:
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>)
                ->
                    ::core::result::Result<(),
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>> {
                *self = <dyn bevy_reflect::Reflect>::take(value)?;
                ::core::result::Result::Ok(())
            }
        }
        impl bevy_reflect::func::args::GetOwnership for MaterialBindGroupIndex
            where  {
            fn ownership() -> bevy_reflect::func::args::Ownership {
                bevy_reflect::func::args::Ownership::Owned
            }
        }
        impl bevy_reflect::func::args::FromArg for MaterialBindGroupIndex
            where  {
            type This<'from_arg> = MaterialBindGroupIndex;
            fn from_arg(arg: bevy_reflect::func::args::Arg)
                ->
                    ::core::result::Result<Self::This<'_>,
                    bevy_reflect::func::args::ArgError> {
                arg.take_owned()
            }
        }
        impl bevy_reflect::func::IntoReturn for MaterialBindGroupIndex where
            {
            fn into_return<'into_return>(self)
                -> bevy_reflect::func::Return<'into_return> where
                Self: 'into_return {
                bevy_reflect::func::Return::Owned(bevy_reflect::__macro_exports::alloc_utils::Box::new(self))
            }
        }
        #[allow(non_upper_case_globals)]
        const _: () =
            {
                static __INVENTORY: ::inventory::Node =
                    ::inventory::Node {
                        value: &{
                                bevy_reflect::__macro_exports::auto_register::AutomaticReflectRegistrations(<MaterialBindGroupIndex
                                        as
                                        bevy_reflect::__macro_exports::auto_register::RegisterForReflection>::__register)
                            },
                        next: ::inventory::__private::UnsafeCell::new(::inventory::__private::Option::None),
                    };
                #[link_section = ".text.startup"]
                unsafe extern "C" fn __ctor() {
                    unsafe {
                        ::inventory::ErasedNode::submit(__INVENTORY.value,
                            &__INVENTORY)
                    }
                }
                #[used]
                #[link_section = ".init_array"]
                static __CTOR: unsafe extern "C" fn() = __ctor;
            };
        impl bevy_reflect::tuple_struct::TupleStruct for
            MaterialBindGroupIndex where  {
            fn field(&self, index: usize)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match index {
                    0usize => ::core::option::Option::Some(&self.0),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_mut(&mut self, index: usize)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match index {
                    0usize => ::core::option::Option::Some(&mut self.0),
                    _ => ::core::option::Option::None,
                }
            }
            #[inline]
            fn field_len(&self) -> usize { 1usize }
            #[inline]
            fn iter_fields(&self)
                -> bevy_reflect::tuple_struct::TupleStructFieldIter {
                bevy_reflect::tuple_struct::TupleStructFieldIter::new(self)
            }
            fn to_dynamic_tuple_struct(&self)
                ->
                    ::core::result::Result<bevy_reflect::tuple_struct::DynamicTupleStruct,
                    bevy_reflect::ReflectCloneError> {
                let mut dynamic:
                        bevy_reflect::tuple_struct::DynamicTupleStruct =
                    ::core::default::Default::default();
                dynamic.set_represented_type(bevy_reflect::PartialReflect::get_represented_type_info(self));
                dynamic.insert_boxed(bevy_reflect::PartialReflect::to_dynamic(&self.0)?);
                ::core::result::Result::Ok(dynamic)
            }
        }
        impl bevy_reflect::PartialReflect for MaterialBindGroupIndex where  {
            #[inline]
            fn get_represented_type_info(&self)
                -> ::core::option::Option<&'static bevy_reflect::TypeInfo> {
                ::core::option::Option::Some(<Self as
                            bevy_reflect::Typed>::type_info())
            }
            #[inline]
            fn try_apply(&mut self, value: &dyn bevy_reflect::PartialReflect)
                -> ::core::result::Result<(), bevy_reflect::ApplyError> {
                if let bevy_reflect::ReflectRef::TupleStruct(struct_value) =
                        bevy_reflect::PartialReflect::reflect_ref(value) {
                    for (i, value) in
                        ::core::iter::Iterator::enumerate(bevy_reflect::tuple_struct::TupleStruct::iter_fields(struct_value))
                        {
                        if let ::core::option::Option::Some(v) =
                                bevy_reflect::tuple_struct::TupleStruct::field_mut(self, i)
                            {
                            bevy_reflect::PartialReflect::try_apply(v, value)?;
                        }
                    }
                } else {
                    return ::core::result::Result::Err(bevy_reflect::ApplyError::MismatchedKinds {
                                from_kind: bevy_reflect::PartialReflect::reflect_kind(value),
                                to_kind: bevy_reflect::ReflectKind::TupleStruct,
                            });
                }
                ::core::result::Result::Ok(())
            }
            #[inline]
            fn reflect_kind(&self) -> bevy_reflect::ReflectKind {
                bevy_reflect::ReflectKind::TupleStruct
            }
            #[inline]
            fn reflect_ref(&self) -> bevy_reflect::ReflectRef {
                bevy_reflect::ReflectRef::TupleStruct(self)
            }
            #[inline]
            fn reflect_mut(&mut self) -> bevy_reflect::ReflectMut {
                bevy_reflect::ReflectMut::TupleStruct(self)
            }
            #[inline]
            fn reflect_owned(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                -> bevy_reflect::ReflectOwned {
                bevy_reflect::ReflectOwned::TupleStruct(self)
            }
            #[inline]
            fn try_into_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    ::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect>> {
                ::core::result::Result::Ok(self)
            }
            #[inline]
            fn try_as_reflect(&self)
                -> ::core::option::Option<&dyn bevy_reflect::Reflect> {
                ::core::option::Option::Some(self)
            }
            #[inline]
            fn try_as_reflect_mut(&mut self)
                -> ::core::option::Option<&mut dyn bevy_reflect::Reflect> {
                ::core::option::Option::Some(self)
            }
            #[inline]
            fn into_partial_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect> {
                self
            }
            #[inline]
            fn as_partial_reflect(&self)
                -> &dyn bevy_reflect::PartialReflect {
                self
            }
            #[inline]
            fn as_partial_reflect_mut(&mut self)
                -> &mut dyn bevy_reflect::PartialReflect {
                self
            }
            fn reflect_hash(&self) -> ::core::option::Option<u64> {
                use ::core::hash::{Hash, Hasher};
                let mut hasher = bevy_reflect::utility::reflect_hasher();
                Hash::hash(&::core::any::Any::type_id(self), &mut hasher);
                Hash::hash(self, &mut hasher);
                ::core::option::Option::Some(Hasher::finish(&hasher))
            }
            fn reflect_partial_eq(&self,
                value: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<bool> {
                let value =
                    <dyn bevy_reflect::PartialReflect>::try_downcast_ref::<Self>(value);
                if let ::core::option::Option::Some(value) = value {
                    ::core::option::Option::Some(::core::cmp::PartialEq::eq(self,
                            value))
                } else { ::core::option::Option::Some(false) }
            }
            fn reflect_partial_cmp(&self,
                value: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<::core::cmp::Ordering> {
                (bevy_reflect::tuple_struct::tuple_struct_partial_cmp)(self,
                    value)
            }
            #[inline]
            fn reflect_clone(&self)
                ->
                    ::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
                    bevy_reflect::ReflectCloneError> {
                ::core::result::Result::Ok(bevy_reflect::__macro_exports::alloc_utils::Box::new(::core::clone::Clone::clone(self)))
            }
        }
        impl bevy_reflect::FromReflect for MaterialBindGroupIndex where  {
            fn from_reflect(reflect: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<Self> {
                if let bevy_reflect::ReflectRef::TupleStruct(__ref_struct) =
                        bevy_reflect::PartialReflect::reflect_ref(reflect) {
                    let mut __this =
                        <Self as ::core::default::Default>::default();
                    if let ::core::option::Option::Some(__field) =
                            (||
                                        <u32 as
                                                bevy_reflect::FromReflect>::from_reflect(bevy_reflect::tuple_struct::TupleStruct::field(__ref_struct,
                                                    0)?))() {
                        __this.0 = __field;
                    }
                    ::core::option::Option::Some(__this)
                } else { ::core::option::Option::None }
            }
        }
    };Reflect, impl ::core::ops::Deref for MaterialBindGroupIndex {
    type Target = u32;
    fn deref(&self) -> &Self::Target { &self.0 }
}Deref, impl ::core::ops::DerefMut for MaterialBindGroupIndex {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}DerefMut,
339)]
340#[reflect(Default, Clone, PartialEq, Hash)]
341#[repr(C)]
342pub struct MaterialBindGroupIndex(pub u32);
343
344impl From<u32> for MaterialBindGroupIndex {
345    fn from(value: u32) -> Self {
346        MaterialBindGroupIndex(value)
347    }
348}
349
350/// The index of the slot containing material data within each material bind
351/// group.
352///
353/// In bindless mode, this slot is needed to locate the material data in each
354/// bind group, since multiple materials are packed into a single slab. In
355/// non-bindless mode, this slot is always 0.
356#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for MaterialBindGroupSlot { }
#[automatically_derived]
impl ::core::clone::Clone for MaterialBindGroupSlot {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<u32>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for MaterialBindGroupSlot { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for MaterialBindGroupSlot {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f,
            "MaterialBindGroupSlot", &&self.0)
    }
}Debug, #[automatically_derived]
impl ::core::default::Default for MaterialBindGroupSlot {
    #[inline]
    fn default() -> Self { Self(::core::default::Default::default()) }
}Default, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for MaterialBindGroupSlot { }
#[automatically_derived]
impl ::core::cmp::PartialEq for MaterialBindGroupSlot {
    #[inline]
    fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}PartialEq, const _: () =
    {
        if !(::core::mem::size_of::<MaterialBindGroupSlot>() ==
                    (::core::mem::size_of::<u32>())) {
            {
                ::std::rt::begin_panic("derive(Pod) was applied to a type with padding");
            }
        };
    };
const _: fn() =
    ||
        {
            #[allow(clippy :: missing_const_for_fn)]
            #[doc(hidden)]
            fn check() {
                fn assert_impl<T: ::bytemuck::Pod>() {}
                assert_impl::<u32>();
            }
        };
unsafe impl ::bytemuck::Pod for MaterialBindGroupSlot {}Pod, const _: fn() =
    ||
        {
            #[allow(clippy :: missing_const_for_fn)]
            #[doc(hidden)]
            fn check() {
                fn assert_impl<T: ::bytemuck::Zeroable>() {}
                assert_impl::<u32>();
            }
        };
unsafe impl ::bytemuck::Zeroable for MaterialBindGroupSlot {}Zeroable, const _: () =
    {
        impl bevy_reflect::GetTypeRegistration for MaterialBindGroupSlot where
             {
            fn get_type_registration() -> bevy_reflect::TypeRegistration {
                let mut registration =
                    bevy_reflect::TypeRegistration::of::<Self>();
                registration.insert(<bevy_reflect::ReflectFromPtr as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration.insert(<bevy_reflect::ReflectFromReflect as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration.register_type_data_with::<ReflectDefault, Self,
                    _>(());
                registration
            }
            #[inline(never)]
            fn register_type_dependencies(registry:
                    &mut bevy_reflect::TypeRegistry) {
                <u32 as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
            }
        }
        impl bevy_reflect::Typed for MaterialBindGroupSlot where  {
            #[inline(never)]
            fn type_info() -> &'static bevy_reflect::TypeInfo {
                static CELL: bevy_reflect::utility::NonGenericTypeInfoCell =
                    bevy_reflect::utility::NonGenericTypeInfoCell::new();
                CELL.get_or_set(#[inline(never)] ||
                        {
                            bevy_reflect::TypeInfo::TupleStruct(bevy_reflect::tuple_struct::TupleStructInfo::new::<Self>(&[bevy_reflect::UnnamedField::new::<u32>(0usize)]))
                        })
            }
        }
        #[allow(deprecated, reason =
        "derives on a deprecated type shouldn't be considered a usage")]
        impl bevy_reflect::TypePath for MaterialBindGroupSlot where  {
            fn type_path() -> &'static str {
                "bevy_render::material_bind_groups::MaterialBindGroupSlot"
            }
            fn short_type_path() -> &'static str { "MaterialBindGroupSlot" }
            fn type_ident() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("MaterialBindGroupSlot")
            }
            fn crate_name() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_render::material_bind_groups".split(':').next().unwrap())
            }
            fn module_path() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_render::material_bind_groups")
            }
        }
        impl bevy_reflect::Reflect for MaterialBindGroupSlot where  {
            #[inline]
            fn into_any(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn ::core::any::Any> {
                self
            }
            #[inline]
            fn as_any(&self) -> &dyn ::core::any::Any { self }
            #[inline]
            fn as_any_mut(&mut self) -> &mut dyn ::core::any::Any { self }
            #[inline]
            fn into_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect> {
                self
            }
            #[inline]
            fn as_reflect(&self) -> &dyn bevy_reflect::Reflect { self }
            #[inline]
            fn as_reflect_mut(&mut self) -> &mut dyn bevy_reflect::Reflect {
                self
            }
            #[inline]
            fn set(&mut self,
                value:
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>)
                ->
                    ::core::result::Result<(),
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>> {
                *self = <dyn bevy_reflect::Reflect>::take(value)?;
                ::core::result::Result::Ok(())
            }
        }
        impl bevy_reflect::func::args::GetOwnership for MaterialBindGroupSlot
            where  {
            fn ownership() -> bevy_reflect::func::args::Ownership {
                bevy_reflect::func::args::Ownership::Owned
            }
        }
        impl bevy_reflect::func::args::FromArg for MaterialBindGroupSlot where
             {
            type This<'from_arg> = MaterialBindGroupSlot;
            fn from_arg(arg: bevy_reflect::func::args::Arg)
                ->
                    ::core::result::Result<Self::This<'_>,
                    bevy_reflect::func::args::ArgError> {
                arg.take_owned()
            }
        }
        impl bevy_reflect::func::IntoReturn for MaterialBindGroupSlot where  {
            fn into_return<'into_return>(self)
                -> bevy_reflect::func::Return<'into_return> where
                Self: 'into_return {
                bevy_reflect::func::Return::Owned(bevy_reflect::__macro_exports::alloc_utils::Box::new(self))
            }
        }
        #[allow(non_upper_case_globals)]
        const _: () =
            {
                static __INVENTORY: ::inventory::Node =
                    ::inventory::Node {
                        value: &{
                                bevy_reflect::__macro_exports::auto_register::AutomaticReflectRegistrations(<MaterialBindGroupSlot
                                        as
                                        bevy_reflect::__macro_exports::auto_register::RegisterForReflection>::__register)
                            },
                        next: ::inventory::__private::UnsafeCell::new(::inventory::__private::Option::None),
                    };
                #[link_section = ".text.startup"]
                unsafe extern "C" fn __ctor() {
                    unsafe {
                        ::inventory::ErasedNode::submit(__INVENTORY.value,
                            &__INVENTORY)
                    }
                }
                #[used]
                #[link_section = ".init_array"]
                static __CTOR: unsafe extern "C" fn() = __ctor;
            };
        impl bevy_reflect::tuple_struct::TupleStruct for MaterialBindGroupSlot
            where  {
            fn field(&self, index: usize)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match index {
                    0usize => ::core::option::Option::Some(&self.0),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_mut(&mut self, index: usize)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match index {
                    0usize => ::core::option::Option::Some(&mut self.0),
                    _ => ::core::option::Option::None,
                }
            }
            #[inline]
            fn field_len(&self) -> usize { 1usize }
            #[inline]
            fn iter_fields(&self)
                -> bevy_reflect::tuple_struct::TupleStructFieldIter {
                bevy_reflect::tuple_struct::TupleStructFieldIter::new(self)
            }
            fn to_dynamic_tuple_struct(&self)
                ->
                    ::core::result::Result<bevy_reflect::tuple_struct::DynamicTupleStruct,
                    bevy_reflect::ReflectCloneError> {
                let mut dynamic:
                        bevy_reflect::tuple_struct::DynamicTupleStruct =
                    ::core::default::Default::default();
                dynamic.set_represented_type(bevy_reflect::PartialReflect::get_represented_type_info(self));
                dynamic.insert_boxed(bevy_reflect::PartialReflect::to_dynamic(&self.0)?);
                ::core::result::Result::Ok(dynamic)
            }
        }
        impl bevy_reflect::PartialReflect for MaterialBindGroupSlot where  {
            #[inline]
            fn get_represented_type_info(&self)
                -> ::core::option::Option<&'static bevy_reflect::TypeInfo> {
                ::core::option::Option::Some(<Self as
                            bevy_reflect::Typed>::type_info())
            }
            #[inline]
            fn try_apply(&mut self, value: &dyn bevy_reflect::PartialReflect)
                -> ::core::result::Result<(), bevy_reflect::ApplyError> {
                if let bevy_reflect::ReflectRef::TupleStruct(struct_value) =
                        bevy_reflect::PartialReflect::reflect_ref(value) {
                    for (i, value) in
                        ::core::iter::Iterator::enumerate(bevy_reflect::tuple_struct::TupleStruct::iter_fields(struct_value))
                        {
                        if let ::core::option::Option::Some(v) =
                                bevy_reflect::tuple_struct::TupleStruct::field_mut(self, i)
                            {
                            bevy_reflect::PartialReflect::try_apply(v, value)?;
                        }
                    }
                } else {
                    return ::core::result::Result::Err(bevy_reflect::ApplyError::MismatchedKinds {
                                from_kind: bevy_reflect::PartialReflect::reflect_kind(value),
                                to_kind: bevy_reflect::ReflectKind::TupleStruct,
                            });
                }
                ::core::result::Result::Ok(())
            }
            #[inline]
            fn reflect_kind(&self) -> bevy_reflect::ReflectKind {
                bevy_reflect::ReflectKind::TupleStruct
            }
            #[inline]
            fn reflect_ref(&self) -> bevy_reflect::ReflectRef {
                bevy_reflect::ReflectRef::TupleStruct(self)
            }
            #[inline]
            fn reflect_mut(&mut self) -> bevy_reflect::ReflectMut {
                bevy_reflect::ReflectMut::TupleStruct(self)
            }
            #[inline]
            fn reflect_owned(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                -> bevy_reflect::ReflectOwned {
                bevy_reflect::ReflectOwned::TupleStruct(self)
            }
            #[inline]
            fn try_into_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    ::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect>> {
                ::core::result::Result::Ok(self)
            }
            #[inline]
            fn try_as_reflect(&self)
                -> ::core::option::Option<&dyn bevy_reflect::Reflect> {
                ::core::option::Option::Some(self)
            }
            #[inline]
            fn try_as_reflect_mut(&mut self)
                -> ::core::option::Option<&mut dyn bevy_reflect::Reflect> {
                ::core::option::Option::Some(self)
            }
            #[inline]
            fn into_partial_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect> {
                self
            }
            #[inline]
            fn as_partial_reflect(&self)
                -> &dyn bevy_reflect::PartialReflect {
                self
            }
            #[inline]
            fn as_partial_reflect_mut(&mut self)
                -> &mut dyn bevy_reflect::PartialReflect {
                self
            }
            fn reflect_partial_eq(&self,
                value: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<bool> {
                let value =
                    <dyn bevy_reflect::PartialReflect>::try_downcast_ref::<Self>(value);
                if let ::core::option::Option::Some(value) = value {
                    ::core::option::Option::Some(::core::cmp::PartialEq::eq(self,
                            value))
                } else { ::core::option::Option::Some(false) }
            }
            fn reflect_partial_cmp(&self,
                value: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<::core::cmp::Ordering> {
                (bevy_reflect::tuple_struct::tuple_struct_partial_cmp)(self,
                    value)
            }
            #[inline]
            fn reflect_clone(&self)
                ->
                    ::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
                    bevy_reflect::ReflectCloneError> {
                ::core::result::Result::Ok(bevy_reflect::__macro_exports::alloc_utils::Box::new(::core::clone::Clone::clone(self)))
            }
        }
        impl bevy_reflect::FromReflect for MaterialBindGroupSlot where  {
            fn from_reflect(reflect: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<Self> {
                if let bevy_reflect::ReflectRef::TupleStruct(__ref_struct) =
                        bevy_reflect::PartialReflect::reflect_ref(reflect) {
                    let mut __this =
                        <Self as ::core::default::Default>::default();
                    if let ::core::option::Option::Some(__field) =
                            (||
                                        <u32 as
                                                bevy_reflect::FromReflect>::from_reflect(bevy_reflect::tuple_struct::TupleStruct::field(__ref_struct,
                                                    0)?))() {
                        __this.0 = __field;
                    }
                    ::core::option::Option::Some(__this)
                } else { ::core::option::Option::None }
            }
        }
    };Reflect, impl ::core::ops::Deref for MaterialBindGroupSlot {
    type Target = u32;
    fn deref(&self) -> &Self::Target { &self.0 }
}Deref, impl ::core::ops::DerefMut for MaterialBindGroupSlot {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}DerefMut)]
357#[reflect(Default, Clone, PartialEq)]
358#[repr(C)]
359pub struct MaterialBindGroupSlot(pub u32);
360
361/// The CPU/GPU synchronization state of a buffer that we maintain.
362///
363/// Currently, the only buffer that we maintain is the
364/// [`MaterialBindlessIndexTable`].
365#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for BufferDirtyState { }
#[automatically_derived]
impl ::core::clone::Clone for BufferDirtyState {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for BufferDirtyState { }Copy, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for BufferDirtyState { }
#[automatically_derived]
impl ::core::cmp::PartialEq for BufferDirtyState {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
            ::core::intrinsics::discriminant_value(other)
    }
}PartialEq)]
366enum BufferDirtyState {
367    /// The buffer is currently synchronized between the CPU and GPU.
368    Clean,
369    /// The buffer hasn't been created yet.
370    NeedsReserve,
371    /// The buffer exists on both CPU and GPU, but the GPU data is out of date.
372    NeedsUpload,
373}
374
375/// Information that describes a potential allocation of a [`BindGroupBuilder`]
376/// into a slab.
377struct BindlessAllocationCandidate {
378    /// A map that, for every resource in the [`BindGroupBuilder`] that already
379    /// existed in this slab, maps bindless index of that resource to its slot
380    /// in the appropriate binding array.
381    pre_existing_resources: HashMap<BindlessIndex, u32>,
382    /// Stores the number of free slots that are needed to satisfy this
383    /// allocation.
384    needed_free_slots: u32,
385}
386
387/// A trait that allows fetching the [`BindingResourceId`] from a
388/// [`BindlessResourceType`].
389///
390/// This is used when freeing bindless resources, in order to locate the IDs
391/// assigned to each resource so that they can be removed from the appropriate
392/// maps.
393trait GetBindingResourceId {
394    /// Returns the [`BindingResourceId`] for this resource.
395    ///
396    /// `resource_type` specifies this resource's type. This is used for
397    /// textures, as a `wgpu` [`TextureView`] doesn't store enough information
398    /// itself to determine its dimension.
399    fn binding_resource_id(&self, resource_type: BindlessResourceType) -> BindingResourceId;
400}
401
402/// The public interface to a slab, which represents a single bind group.
403pub struct MaterialSlab<'a>(MaterialSlabImpl<'a>);
404
405/// The actual implementation of a material slab.
406///
407/// This has bindless and non-bindless variants.
408enum MaterialSlabImpl<'a> {
409    /// The implementation of the slab interface we use when the slab
410    /// is bindless.
411    Bindless(&'a MaterialBindlessSlab),
412    /// The implementation of the slab interface we use when the slab
413    /// is non-bindless.
414    NonBindless(MaterialNonBindlessSlab<'a>),
415}
416
417/// A single bind group that the [`MaterialBindGroupNonBindlessAllocator`]
418/// manages.
419enum MaterialNonBindlessSlab<'a> {
420    /// A slab that has a bind group.
421    Prepared(&'a PreparedBindGroup),
422    /// A slab that doesn't yet have a bind group.
423    Unprepared,
424}
425
426/// Manages an array of untyped plain old data on GPU and allocates individual
427/// slots within that array.
428///
429/// This supports the `#[data]` attribute of `AsBindGroup`.
430struct MaterialDataBuffer {
431    /// The number of the binding that we attach this storage buffer to.
432    binding_number: BindingNumber,
433    /// The actual data.
434    ///
435    /// Note that this is untyped (`u8`); the actual aligned size of each
436    /// element is given by [`Self::aligned_element_size`];
437    buffer: RetainedRawBufferVec<u8>,
438    /// The size of each element in the buffer, including padding and alignment
439    /// if any.
440    aligned_element_size: u32,
441    /// A list of free slots within the buffer.
442    free_slots: Vec<u32>,
443    /// The actual number of slots that have been allocated.
444    len: u32,
445}
446
447/// A buffer containing plain old data, already packed into the appropriate GPU
448/// format, and that can be updated incrementally.
449///
450/// This structure exists in order to encapsulate the lazy update
451/// ([`BufferDirtyState`]) logic in a single place.
452#[derive(impl<T> ::core::ops::Deref for RetainedRawBufferVec<T> where T: Pod {
    type Target = RawBufferVec<T>;
    fn deref(&self) -> &Self::Target { &self.buffer }
}Deref, impl<T> ::core::ops::DerefMut for RetainedRawBufferVec<T> where T: Pod {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.buffer }
}DerefMut)]
453struct RetainedRawBufferVec<T>
454where
455    T: Pod,
456{
457    /// The contents of the buffer.
458    #[deref]
459    buffer: RawBufferVec<T>,
460    /// Whether the contents of the buffer have been uploaded to the GPU.
461    dirty: BufferDirtyState,
462}
463
464/// The size of the buffer that we assign to unused buffer slots, in bytes.
465///
466/// This is essentially arbitrary, as it doesn't seem to matter to `wgpu` what
467/// the size is.
468const DEFAULT_BINDLESS_FALLBACK_BUFFER_SIZE: u64 = 16;
469
470impl From<u32> for MaterialBindGroupSlot {
471    fn from(value: u32) -> Self {
472        MaterialBindGroupSlot(value)
473    }
474}
475
476impl From<MaterialBindGroupSlot> for u32 {
477    fn from(value: MaterialBindGroupSlot) -> Self {
478        value.0
479    }
480}
481
482impl<'a> From<&'a UnpreparedBindingResource> for BindingResourceId {
483    fn from(value: &'a UnpreparedBindingResource) -> Self {
484        match *value {
485            UnpreparedBindingResource::Buffer(ref buffer) => BindingResourceId::Buffer(buffer.id()),
486            UnpreparedBindingResource::ShaderBuffer(ref shader_buffer) => {
487                BindingResourceId::ShaderBuffer(shader_buffer.id())
488            }
489            UnpreparedBindingResource::Data(_) => BindingResourceId::DataBuffer,
490            UnpreparedBindingResource::TextureView(
491                ref texture_view_dimension,
492                ref texture_view,
493            ) => BindingResourceId::TextureView(*texture_view_dimension, texture_view.id()),
494            UnpreparedBindingResource::Sampler(_, ref sampler) => {
495                BindingResourceId::Sampler(sampler.id())
496            }
497        }
498    }
499}
500
501impl GetBindingResourceId for Buffer {
502    fn binding_resource_id(&self, _: BindlessResourceType) -> BindingResourceId {
503        BindingResourceId::Buffer(self.id())
504    }
505}
506
507impl GetBindingResourceId for Sampler {
508    fn binding_resource_id(&self, _: BindlessResourceType) -> BindingResourceId {
509        BindingResourceId::Sampler(self.id())
510    }
511}
512
513impl GetBindingResourceId for TextureView {
514    fn binding_resource_id(&self, resource_type: BindlessResourceType) -> BindingResourceId {
515        let texture_view_dimension = match resource_type {
516            BindlessResourceType::Texture1d => TextureViewDimension::D1,
517            BindlessResourceType::Texture2d => TextureViewDimension::D2,
518            BindlessResourceType::Texture2dArray => TextureViewDimension::D2Array,
519            BindlessResourceType::Texture3d => TextureViewDimension::D3,
520            BindlessResourceType::TextureCube => TextureViewDimension::Cube,
521            BindlessResourceType::TextureCubeArray => TextureViewDimension::CubeArray,
522            _ => {
    ::core::panicking::panic_fmt(format_args!("Resource type is not a texture"));
}panic!("Resource type is not a texture"),
523        };
524        BindingResourceId::TextureView(texture_view_dimension, self.id())
525    }
526}
527
528impl Plugin for MaterialBindGroupPlugin {
529    fn build(&self, app: &mut App) {
530        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
531            return;
532        };
533
534        render_app
535            .init_resource::<MaterialBindGroupAllocators>()
536            .allow_ambiguous_resource::<MaterialBindGroupAllocators>()
537            .init_gpu_resource::<RenderMaterialBindings>()
538            .allow_ambiguous_resource::<RenderMaterialBindings>()
539            .add_systems(RenderStartup, init_fallback_resources)
540            .add_systems(
541                Render,
542                (
543                    prepare_material_bind_groups,
544                    write_material_bind_group_buffers,
545                )
546                    .chain()
547                    .in_set(RenderSystems::PrepareBindGroups),
548            );
549    }
550}
551
552impl GetBindingResourceId for Handle<ShaderBuffer> {
553    fn binding_resource_id(&self, _: BindlessResourceType) -> BindingResourceId {
554        BindingResourceId::ShaderBuffer(self.id())
555    }
556}
557
558impl MaterialBindGroupAllocator {
559    /// Creates a new [`MaterialBindGroupAllocator`] managing the data for a
560    /// single material.
561    pub fn new(
562        render_device: &RenderDevice,
563        label: &'static str,
564        bindless_descriptor: Option<BindlessDescriptor>,
565        bind_group_layout: BindGroupLayoutDescriptor,
566        slab_capacity: Option<BindlessSlabResourceLimit>,
567    ) -> MaterialBindGroupAllocator {
568        if let Some(bindless_descriptor) = bindless_descriptor {
569            MaterialBindGroupAllocator::Bindless(Box::new(MaterialBindGroupBindlessAllocator::new(
570                render_device,
571                label,
572                bindless_descriptor,
573                bind_group_layout,
574                slab_capacity,
575            )))
576        } else {
577            MaterialBindGroupAllocator::NonBindless(Box::new(
578                MaterialBindGroupNonBindlessAllocator::new(label),
579            ))
580        }
581    }
582
583    /// Returns the slab with the given index, if one exists.
584    pub fn get(&self, group: MaterialBindGroupIndex) -> Option<MaterialSlab<'_>> {
585        match *self {
586            MaterialBindGroupAllocator::Bindless(ref bindless_allocator) => bindless_allocator
587                .get(group)
588                .map(|bindless_slab| MaterialSlab(MaterialSlabImpl::Bindless(bindless_slab))),
589            MaterialBindGroupAllocator::NonBindless(ref non_bindless_allocator) => {
590                non_bindless_allocator.get(group).map(|non_bindless_slab| {
591                    MaterialSlab(MaterialSlabImpl::NonBindless(non_bindless_slab))
592                })
593            }
594        }
595    }
596
597    /// Allocates the resources within a [`BindGroupBuilder`] and returns the
598    /// resulting binding ID.
599    ///
600    /// This method should generally be preferred over
601    /// [`Self::allocate_prepared`], because this method supports both bindless
602    /// and non-bindless bind groups. Only use [`Self::allocate_prepared`] if
603    /// you need to prepare the bind group yourself.
604    pub fn allocate_unprepared(
605        &mut self,
606        unprepared_bind_group: &mut BindGroupBuilder,
607        bind_group_layout: &BindGroupLayoutDescriptor,
608    ) -> MaterialBindingId {
609        match *self {
610            MaterialBindGroupAllocator::Bindless(
611                ref mut material_bind_group_bindless_allocator,
612            ) => material_bind_group_bindless_allocator.allocate_unprepared(unprepared_bind_group),
613            MaterialBindGroupAllocator::NonBindless(
614                ref mut material_bind_group_non_bindless_allocator,
615            ) => material_bind_group_non_bindless_allocator
616                .allocate_unprepared(unprepared_bind_group, (*bind_group_layout).clone()),
617        }
618    }
619
620    /// Places a pre-prepared bind group into a slab.
621    ///
622    /// For bindless materials, the allocator internally manages the bind
623    /// groups, so calling this method will panic if this is a bindless
624    /// allocator. Only non-bindless allocators support this method.
625    ///
626    /// It's generally preferred to use [`Self::allocate_unprepared`], because
627    /// that method supports both bindless and non-bindless allocators. Only use
628    /// this method if you need to prepare the bind group yourself.
629    pub fn allocate_prepared(
630        &mut self,
631        prepared_bind_group: PreparedBindGroup,
632        bind_group_layout: BindGroupLayoutDescriptor,
633    ) -> MaterialBindingId {
634        match *self {
635            MaterialBindGroupAllocator::Bindless(_) => {
636                {
    ::core::panicking::panic_fmt(format_args!("Bindless resources are incompatible with implementing `as_bind_group` directly; implement `unprepared_bind_group` instead or disable bindless"));
}panic!(
637                    "Bindless resources are incompatible with implementing `as_bind_group` \
638                     directly; implement `unprepared_bind_group` instead or disable bindless"
639                )
640            }
641            MaterialBindGroupAllocator::NonBindless(ref mut non_bindless_allocator) => {
642                non_bindless_allocator.allocate_prepared(prepared_bind_group, bind_group_layout)
643            }
644        }
645    }
646
647    /// Deallocates the material with the given binding ID.
648    ///
649    /// Any resources that are no longer referenced are removed from the slab.
650    pub fn free(&mut self, material_binding_id: MaterialBindingId) {
651        match *self {
652            MaterialBindGroupAllocator::Bindless(
653                ref mut material_bind_group_bindless_allocator,
654            ) => material_bind_group_bindless_allocator.free(material_binding_id),
655            MaterialBindGroupAllocator::NonBindless(
656                ref mut material_bind_group_non_bindless_allocator,
657            ) => material_bind_group_non_bindless_allocator.free(material_binding_id),
658        }
659    }
660
661    /// Attempts to replace the given `existing_binding_id` with a new material,
662    /// without actually reallocating any GPU resources.
663    ///
664    /// That is, if the given `unprepared_bind_group` represents exactly the
665    /// same GPU resource bindings as the already-allocated material, this
666    /// method updates the POD only and returns true. Otherwise, if the supplied
667    /// bind group represents different resources, this method returns false.
668    ///
669    /// This is an optional fast path to handle POD-only material changes. If
670    /// this method returns false, then the caller should call [`Self::free`]
671    /// and [`Self::allocate_unprepared`] to reallocate the GPU resources.
672    pub fn try_update_data(
673        &mut self,
674        existing_binding_id: MaterialBindingId,
675        unprepared_bind_group: &mut BindGroupBuilder,
676    ) -> bool {
677        match *self {
678            MaterialBindGroupAllocator::Bindless(
679                ref mut material_bind_group_bindless_allocator,
680            ) => material_bind_group_bindless_allocator
681                .try_update_data(existing_binding_id, unprepared_bind_group),
682            MaterialBindGroupAllocator::NonBindless(_) => {
683                // TODO: Have a fast path for non-bindless materials.
684                false
685            }
686        }
687    }
688
689    /// Recreates any bind groups corresponding to slabs that have been modified
690    /// since last calling [`MaterialBindGroupAllocator::prepare_bind_groups`].
691    pub fn prepare_bind_groups(
692        &mut self,
693        render_device: &RenderDevice,
694        pipeline_cache: &PipelineCache,
695        fallback_bindless_resources: &FallbackBindlessResources,
696        fallback_image: &FallbackImage,
697        fallback_buffer: &FallbackBuffer,
698        shader_buffer_assets: &RenderAssets<GpuShaderBuffer>,
699        changed_shader_buffers: &RenderChangedShaderBuffers,
700    ) {
701        match *self {
702            MaterialBindGroupAllocator::Bindless(
703                ref mut material_bind_group_bindless_allocator,
704            ) => material_bind_group_bindless_allocator.prepare_bind_groups(
705                render_device,
706                pipeline_cache,
707                fallback_bindless_resources,
708                fallback_image,
709                shader_buffer_assets,
710                changed_shader_buffers,
711            ),
712            MaterialBindGroupAllocator::NonBindless(
713                ref mut material_bind_group_non_bindless_allocator,
714            ) => material_bind_group_non_bindless_allocator.prepare_bind_groups(
715                render_device,
716                pipeline_cache,
717                fallback_buffer,
718                shader_buffer_assets,
719                changed_shader_buffers,
720            ),
721        }
722    }
723
724    /// Uploads the contents of all buffers that this
725    /// [`MaterialBindGroupAllocator`] manages to the GPU.
726    ///
727    /// Non-bindless allocators don't currently manage any buffers, so this
728    /// method only has an effect for bindless allocators.
729    pub fn write_buffers(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
730        match *self {
731            MaterialBindGroupAllocator::Bindless(
732                ref mut material_bind_group_bindless_allocator,
733            ) => material_bind_group_bindless_allocator.write_buffers(render_device, render_queue),
734            MaterialBindGroupAllocator::NonBindless(_) => {
735                // Not applicable.
736            }
737        }
738    }
739
740    /// Get number of allocated slabs for bindless material, returns 0 if it is
741    /// [`Self::NonBindless`].
742    pub fn slab_count(&self) -> usize {
743        match self {
744            Self::Bindless(bless) => bless.slabs.len(),
745            Self::NonBindless(_) => 0,
746        }
747    }
748
749    /// Get total size of slabs allocated for bindless material, returns 0 if it is
750    /// [`Self::NonBindless`].
751    pub fn slabs_size(&self) -> usize {
752        match self {
753            Self::Bindless(bless) => bless
754                .slabs
755                .iter()
756                .flat_map(|slab| {
757                    slab.data_buffers
758                        .iter()
759                        .map(|(_, buffer)| buffer.buffer.len())
760                })
761                .sum(),
762            Self::NonBindless(_) => 0,
763        }
764    }
765
766    /// Get number of bindless material allocations in slabs, returns 0 if it is
767    /// [`Self::NonBindless`].
768    pub fn allocations(&self) -> u64 {
769        match self {
770            Self::Bindless(bless) => bless
771                .slabs
772                .iter()
773                .map(|slab| u64::from(slab.allocated_resource_count))
774                .sum(),
775            Self::NonBindless(_) => 0,
776        }
777    }
778}
779
780impl MaterialBindlessIndexTable {
781    /// Creates a new [`MaterialBindlessIndexTable`] for a single slab.
782    fn new(
783        bindless_index_table_descriptor: &BindlessIndexTableDescriptor,
784    ) -> MaterialBindlessIndexTable {
785        // Preallocate space for one bindings table, so that there will always be a buffer.
786        let mut buffer = RetainedRawBufferVec::new(BufferUsages::STORAGE);
787        for _ in *bindless_index_table_descriptor.indices.start
788            ..*bindless_index_table_descriptor.indices.end
789        {
790            buffer.push(0);
791        }
792
793        MaterialBindlessIndexTable {
794            buffer,
795            index_range: bindless_index_table_descriptor.indices.clone(),
796            binding_number: bindless_index_table_descriptor.binding_number,
797        }
798    }
799
800    /// Returns the bindings in the binding index table.
801    ///
802    /// If the current [`MaterialBindlessIndexTable::index_range`] is M..N, then
803    /// element *i* of the returned binding index table contains the slot of the
804    /// bindless resource with bindless index *i* + M.
805    fn get(&self, slot: MaterialBindGroupSlot) -> &[u32] {
806        let struct_size = *self.index_range.end as usize - *self.index_range.start as usize;
807        let start = struct_size * slot.0 as usize;
808        &self.buffer.values()[start..(start + struct_size)]
809    }
810
811    /// Returns a single binding from the binding index table.
812    fn get_binding(
813        &self,
814        slot: MaterialBindGroupSlot,
815        bindless_index: BindlessIndex,
816    ) -> Option<u32> {
817        if bindless_index < self.index_range.start || bindless_index >= self.index_range.end {
818            return None;
819        }
820        self.get(slot)
821            .get((*bindless_index - *self.index_range.start) as usize)
822            .copied()
823    }
824
825    fn table_length(&self) -> u32 {
826        self.index_range.end.0 - self.index_range.start.0
827    }
828
829    /// Updates the binding index table for a single material.
830    ///
831    /// The `allocated_resource_slots` map contains a mapping from the
832    /// [`BindlessIndex`] of each resource that the material references to the
833    /// slot that that resource occupies in the appropriate binding array. This
834    /// method serializes that map into a binding index table that the shader
835    /// can read.
836    fn set(
837        &mut self,
838        slot: MaterialBindGroupSlot,
839        allocated_resource_slots: &HashMap<BindlessIndex, u32>,
840    ) {
841        let table_len = self.table_length() as usize;
842        let range = (slot.0 as usize * table_len)..((slot.0 as usize + 1) * table_len);
843        while self.buffer.len() < range.end {
844            self.buffer.push(0);
845        }
846
847        for (&bindless_index, &resource_slot) in allocated_resource_slots {
848            if self.index_range.contains(&bindless_index) {
849                self.buffer.set(
850                    *bindless_index + range.start as u32 - *self.index_range.start,
851                    resource_slot,
852                );
853            }
854        }
855
856        // Mark the buffer as needing to be recreated, in case we grew it.
857        self.buffer.dirty = BufferDirtyState::NeedsReserve;
858    }
859
860    /// Returns the [`BindGroupEntry`] for the index table itself.
861    fn bind_group_entry(&self) -> BindGroupEntry<'_> {
862        BindGroupEntry {
863            binding: *self.binding_number,
864            resource: self
865                .buffer
866                .buffer()
867                .expect("Bindings buffer must exist")
868                .as_entire_binding(),
869        }
870    }
871}
872
873impl<T> RetainedRawBufferVec<T>
874where
875    T: Pod,
876{
877    /// Creates a new empty [`RetainedRawBufferVec`] supporting the given
878    /// [`BufferUsages`].
879    fn new(buffer_usages: BufferUsages) -> RetainedRawBufferVec<T> {
880        RetainedRawBufferVec {
881            buffer: RawBufferVec::new(buffer_usages),
882            dirty: BufferDirtyState::NeedsUpload,
883        }
884    }
885
886    /// Recreates the GPU backing buffer if needed.
887    fn prepare(&mut self, render_device: &RenderDevice) {
888        match self.dirty {
889            BufferDirtyState::Clean | BufferDirtyState::NeedsUpload => {}
890            BufferDirtyState::NeedsReserve => {
891                let capacity = self.buffer.len();
892                self.buffer.reserve(capacity, render_device);
893                self.dirty = BufferDirtyState::NeedsUpload;
894            }
895        }
896    }
897
898    /// Writes the current contents of the buffer to the GPU if necessary.
899    fn write(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
900        match self.dirty {
901            BufferDirtyState::Clean => {}
902            BufferDirtyState::NeedsReserve | BufferDirtyState::NeedsUpload => {
903                self.buffer.write_buffer(render_device, render_queue);
904                self.dirty = BufferDirtyState::Clean;
905            }
906        }
907    }
908}
909
910impl MaterialBindGroupBindlessAllocator {
911    /// Creates a new [`MaterialBindGroupBindlessAllocator`] managing the data
912    /// for a single bindless material.
913    fn new(
914        render_device: &RenderDevice,
915        label: &'static str,
916        bindless_descriptor: BindlessDescriptor,
917        bind_group_layout: BindGroupLayoutDescriptor,
918        slab_capacity: Option<BindlessSlabResourceLimit>,
919    ) -> MaterialBindGroupBindlessAllocator {
920        let fallback_buffers = bindless_descriptor
921            .buffers
922            .iter()
923            .map(|bindless_buffer_descriptor| {
924                (
925                    bindless_buffer_descriptor.bindless_index,
926                    render_device.create_buffer(&BufferDescriptor {
927                        label: Some("bindless fallback buffer"),
928                        size: match bindless_buffer_descriptor.size {
929                            Some(size) => size as u64,
930                            None => DEFAULT_BINDLESS_FALLBACK_BUFFER_SIZE,
931                        },
932                        usage: BufferUsages::STORAGE,
933                        mapped_at_creation: false,
934                    }),
935                )
936            })
937            .collect();
938
939        MaterialBindGroupBindlessAllocator {
940            label,
941            slabs: ::alloc::vec::Vec::new()vec![],
942            bind_group_layout,
943            bindless_descriptor,
944            fallback_buffers,
945            slab_capacity: slab_capacity
946                .expect("Non-bindless materials should use the non-bindless allocator")
947                .resolve(),
948        }
949    }
950
951    /// Allocates the resources for a single material into a slab and returns
952    /// the resulting ID.
953    ///
954    /// The returned [`MaterialBindingId`] can later be used to fetch the slab
955    /// that was used.
956    ///
957    /// This function can't fail. If all slabs are full, then a new slab is
958    /// created, and the material is allocated into it.
959    fn allocate_unprepared(
960        &mut self,
961        unprepared_bind_group: &mut BindGroupBuilder,
962    ) -> MaterialBindingId {
963        for (slab_index, slab) in self.slabs.iter_mut().enumerate() {
964            {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event src/material_bind_groups.rs:964",
                        "bevy_render::material_bind_groups",
                        ::tracing::Level::TRACE,
                        ::tracing_core::__macro_support::Option::Some("src/material_bind_groups.rs"),
                        ::tracing_core::__macro_support::Option::Some(964u32),
                        ::tracing_core::__macro_support::Option::Some("bevy_render::material_bind_groups"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Trying to allocate in slab {0}",
                                                    slab_index) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};trace!("Trying to allocate in slab {}", slab_index);
965            if let Ok(slot) = slab.try_allocate(unprepared_bind_group, self.slab_capacity) {
966                return MaterialBindingId {
967                    group: MaterialBindGroupIndex(slab_index as u32),
968                    slot,
969                };
970            }
971        }
972
973        let group = MaterialBindGroupIndex(self.slabs.len() as u32);
974        self.slabs
975            .push(MaterialBindlessSlab::new(&self.bindless_descriptor));
976
977        // Allocate into the newly-pushed slab.
978        let Ok(slot) = self
979            .slabs
980            .last_mut()
981            .expect("We just pushed a slab")
982            .try_allocate(unprepared_bind_group, self.slab_capacity)
983        else {
984            {
    ::core::panicking::panic_fmt(format_args!("An allocation into an empty slab should always succeed"));
}panic!("An allocation into an empty slab should always succeed")
985        };
986
987        MaterialBindingId { group, slot }
988    }
989
990    /// Attempts to replace the given `existing_binding_id` with a new material,
991    /// without actually reallocating any GPU resources.
992    ///
993    /// See [`MaterialBindGroupAllocator::try_update_data`] for more
994    /// information.
995    fn try_update_data(
996        &mut self,
997        existing_binding_id: MaterialBindingId,
998        unprepared_bind_group: &mut BindGroupBuilder,
999    ) -> bool {
1000        self.slabs
1001            .get_mut(existing_binding_id.group.0 as usize)
1002            .is_some_and(|slab| {
1003                slab.try_update_data(existing_binding_id.slot, unprepared_bind_group)
1004            })
1005    }
1006
1007    /// Deallocates the material with the given binding ID.
1008    ///
1009    /// Any resources that are no longer referenced are removed from the slab.
1010    fn free(&mut self, material_binding_id: MaterialBindingId) {
1011        self.slabs
1012            .get_mut(material_binding_id.group.0 as usize)
1013            .expect("Slab should exist")
1014            .free(material_binding_id.slot, &self.bindless_descriptor);
1015    }
1016
1017    /// Returns the slab with the given bind group index.
1018    ///
1019    /// A [`MaterialBindGroupIndex`] can be fetched from a
1020    /// [`MaterialBindingId`].
1021    fn get(&self, group: MaterialBindGroupIndex) -> Option<&MaterialBindlessSlab> {
1022        self.slabs.get(group.0 as usize)
1023    }
1024
1025    /// Recreates any bind groups corresponding to slabs that have been modified
1026    /// since last calling
1027    /// [`MaterialBindGroupBindlessAllocator::prepare_bind_groups`].
1028    fn prepare_bind_groups(
1029        &mut self,
1030        render_device: &RenderDevice,
1031        pipeline_cache: &PipelineCache,
1032        fallback_bindless_resources: &FallbackBindlessResources,
1033        fallback_image: &FallbackImage,
1034        shader_buffer_assets: &RenderAssets<GpuShaderBuffer>,
1035        changed_shader_buffers: &RenderChangedShaderBuffers,
1036    ) {
1037        for slab in &mut self.slabs {
1038            slab.prepare(
1039                render_device,
1040                pipeline_cache,
1041                self.label,
1042                &self.bind_group_layout,
1043                fallback_bindless_resources,
1044                &self.fallback_buffers,
1045                fallback_image,
1046                shader_buffer_assets,
1047                changed_shader_buffers,
1048                &self.bindless_descriptor,
1049                self.slab_capacity,
1050            );
1051        }
1052    }
1053
1054    /// Writes any buffers that we're managing to the GPU.
1055    ///
1056    /// Currently, this only consists of the bindless index tables.
1057    fn write_buffers(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
1058        for slab in &mut self.slabs {
1059            slab.write_buffer(render_device, render_queue);
1060        }
1061    }
1062}
1063
1064impl MaterialBindlessSlab {
1065    /// Attempts to allocate the given unprepared bind group in this slab.
1066    ///
1067    /// If the allocation succeeds, this method returns the slot that the
1068    /// allocation was placed in. If the allocation fails because the slab was
1069    /// full, this method returns the unprepared bind group back to the caller
1070    /// so that it can try to allocate again.
1071    fn try_allocate(
1072        &mut self,
1073        unprepared_bind_group: &mut BindGroupBuilder,
1074        slot_capacity: u32,
1075    ) -> Result<MaterialBindGroupSlot, ()> {
1076        // Locate pre-existing resources, and determine how many free slots we need.
1077        let Some(allocation_candidate) =
1078            self.check_allocation(&unprepared_bind_group.binding_resources)
1079        else {
1080            return Err(());
1081        };
1082
1083        // Check to see if we have enough free space.
1084        //
1085        // As a special case, note that if *nothing* is allocated in this slab,
1086        // then we always allow a material to be placed in it, regardless of the
1087        // number of bindings the material has. This is so that, if the
1088        // platform's maximum bindless count is set too low to hold even a
1089        // single material, we can still place each material into a separate
1090        // slab instead of failing outright.
1091        if self.allocated_resource_count > 0
1092            && self.allocated_resource_count + allocation_candidate.needed_free_slots
1093                > slot_capacity
1094        {
1095            {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event src/material_bind_groups.rs:1095",
                        "bevy_render::material_bind_groups",
                        ::tracing::Level::TRACE,
                        ::tracing_core::__macro_support::Option::Some("src/material_bind_groups.rs"),
                        ::tracing_core::__macro_support::Option::Some(1095u32),
                        ::tracing_core::__macro_support::Option::Some("bevy_render::material_bind_groups"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Slab is full, can\'t allocate")
                                            as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};trace!("Slab is full, can't allocate");
1096            return Err(());
1097        }
1098
1099        // OK, we can allocate in this slab. Assign a slot ID.
1100        let slot = match self.free_slots.pop() {
1101            Some(slot) => slot,
1102            None => {
1103                // The material bind group slot is packed into 16 bits on
1104                // the GPU, so spill to a new slab before we would overflow.
1105                if self.live_allocation_count > 0xFFFF {
1106                    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event src/material_bind_groups.rs:1106",
                        "bevy_render::material_bind_groups",
                        ::tracing::Level::TRACE,
                        ::tracing_core::__macro_support::Option::Some("src/material_bind_groups.rs"),
                        ::tracing_core::__macro_support::Option::Some(1106u32),
                        ::tracing_core::__macro_support::Option::Some("bevy_render::material_bind_groups"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Slab material bind group slot would overflow, can\'t allocate")
                                            as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};trace!("Slab material bind group slot would overflow, can't allocate");
1107                    return Err(());
1108                }
1109                MaterialBindGroupSlot(self.live_allocation_count)
1110            }
1111        };
1112
1113        // Bump the live allocation count.
1114        self.live_allocation_count += 1;
1115
1116        // Insert the resources into the binding arrays.
1117        let allocated_resource_slots =
1118            self.insert_resources(unprepared_bind_group, allocation_candidate);
1119
1120        // Serialize the allocated resource slots.
1121        for bindless_index_table in &mut self.bindless_index_tables {
1122            bindless_index_table.set(slot, &allocated_resource_slots);
1123        }
1124
1125        // Invalidate the cached bind group.
1126        self.bind_group = None;
1127
1128        Ok(slot)
1129    }
1130
1131    /// Attempts to replace the material in the `existing_slot` with a new
1132    /// material, without actually reallocating any GPU resources.
1133    ///
1134    /// See [`MaterialBindGroupAllocator::try_update_data`] for more
1135    /// information.
1136    fn try_update_data(
1137        &mut self,
1138        existing_slot: MaterialBindGroupSlot,
1139        unprepared_bind_group: &mut BindGroupBuilder,
1140    ) -> bool {
1141        // If the GPU resources have changed, bail. The caller will need to
1142        // explicitly deallocate and reallocate the bind group.
1143        if !self
1144            .bindless_index_tables
1145            .iter()
1146            .all(|bindless_index_table| {
1147                self.gpu_resources_are_unchanged(
1148                    &unprepared_bind_group.binding_resources,
1149                    existing_slot,
1150                    bindless_index_table,
1151                )
1152            })
1153        {
1154            return false;
1155        }
1156
1157        // Update all data in each bindless index table.
1158        // We can't directly iterate over `Self::bindless_index_tables` for
1159        // borrow check reasons.
1160        for bindless_index_table_index in 0..self.bindless_index_tables.len() {
1161            self.update_data(
1162                unprepared_bind_group,
1163                existing_slot,
1164                bindless_index_table_index,
1165            );
1166        }
1167
1168        true
1169    }
1170
1171    /// Gathers the information needed to determine whether the given unprepared
1172    /// bind group can be allocated in this slab.
1173    fn check_allocation(
1174        &self,
1175        unprepared_binding_resources: &UnpreparedBindingResources,
1176    ) -> Option<BindlessAllocationCandidate> {
1177        let mut allocation_candidate = BindlessAllocationCandidate {
1178            pre_existing_resources: HashMap::default(),
1179            needed_free_slots: 0,
1180        };
1181
1182        for &(bindless_index, ref unprepared_binding_resource) in
1183            unprepared_binding_resources.iter()
1184        {
1185            let bindless_index = BindlessIndex(bindless_index);
1186            match *unprepared_binding_resource {
1187                UnpreparedBindingResource::Buffer(ref buffer) => {
1188                    let Some(binding_array) = self.buffers.get(&bindless_index) else {
1189                        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event src/material_bind_groups.rs:1189",
                        "bevy_render::material_bind_groups",
                        ::tracing::Level::ERROR,
                        ::tracing_core::__macro_support::Option::Some("src/material_bind_groups.rs"),
                        ::tracing_core::__macro_support::Option::Some(1189u32),
                        ::tracing_core::__macro_support::Option::Some("bevy_render::material_bind_groups"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::ERROR <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::ERROR <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Binding array wasn\'t present for buffer at index {0:?}",
                                                    bindless_index) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};error!(
1190                            "Binding array wasn't present for buffer at index {:?}",
1191                            bindless_index
1192                        );
1193                        return None;
1194                    };
1195                    match binding_array.find(BindingResourceId::Buffer(buffer.id())) {
1196                        Some(slot) => {
1197                            allocation_candidate
1198                                .pre_existing_resources
1199                                .insert(bindless_index, slot);
1200                        }
1201                        None => allocation_candidate.needed_free_slots += 1,
1202                    }
1203                }
1204
1205                UnpreparedBindingResource::ShaderBuffer(ref shader_buffer) => {
1206                    let Some(binding_array) = self.shader_buffers.get(&bindless_index) else {
1207                        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event src/material_bind_groups.rs:1207",
                        "bevy_render::material_bind_groups",
                        ::tracing::Level::ERROR,
                        ::tracing_core::__macro_support::Option::Some("src/material_bind_groups.rs"),
                        ::tracing_core::__macro_support::Option::Some(1207u32),
                        ::tracing_core::__macro_support::Option::Some("bevy_render::material_bind_groups"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::ERROR <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::ERROR <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Binding array wasn\'t present for shader buffer at index {0:?}",
                                                    bindless_index) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};error!(
1208                            "Binding array wasn't present for shader buffer at index {:?}",
1209                            bindless_index
1210                        );
1211                        return None;
1212                    };
1213                    match binding_array.find(BindingResourceId::ShaderBuffer(shader_buffer.id())) {
1214                        Some(slot) => {
1215                            allocation_candidate
1216                                .pre_existing_resources
1217                                .insert(bindless_index, slot);
1218                        }
1219                        None => allocation_candidate.needed_free_slots += 1,
1220                    }
1221                }
1222
1223                UnpreparedBindingResource::Data(_) => {
1224                    // The size of a data buffer is unlimited.
1225                }
1226
1227                UnpreparedBindingResource::TextureView(
1228                    texture_view_dimension,
1229                    ref texture_view,
1230                ) => {
1231                    let bindless_resource_type = BindlessResourceType::from(texture_view_dimension);
1232                    match self
1233                        .textures
1234                        .get(&bindless_resource_type)
1235                        .expect("Missing binding array for texture")
1236                        .find(BindingResourceId::TextureView(
1237                            texture_view_dimension,
1238                            texture_view.id(),
1239                        )) {
1240                        Some(slot) => {
1241                            allocation_candidate
1242                                .pre_existing_resources
1243                                .insert(bindless_index, slot);
1244                        }
1245                        None => {
1246                            allocation_candidate.needed_free_slots += 1;
1247                        }
1248                    }
1249                }
1250
1251                UnpreparedBindingResource::Sampler(sampler_binding_type, ref sampler) => {
1252                    let bindless_resource_type = BindlessResourceType::from(sampler_binding_type);
1253                    match self
1254                        .samplers
1255                        .get(&bindless_resource_type)
1256                        .expect("Missing binding array for sampler")
1257                        .find(BindingResourceId::Sampler(sampler.id()))
1258                    {
1259                        Some(slot) => {
1260                            allocation_candidate
1261                                .pre_existing_resources
1262                                .insert(bindless_index, slot);
1263                        }
1264                        None => {
1265                            allocation_candidate.needed_free_slots += 1;
1266                        }
1267                    }
1268                }
1269            }
1270        }
1271
1272        Some(allocation_candidate)
1273    }
1274
1275    /// Inserts the bind group resources described by the given
1276    /// [`BindGroupBuilder`] into this slab.
1277    ///
1278    /// Returns a table that maps the bindless index of each resource to its
1279    /// slot in its binding array.
1280    fn insert_resources(
1281        &mut self,
1282        unprepared_bind_group: &BindGroupBuilder,
1283        allocation_candidate: BindlessAllocationCandidate,
1284    ) -> HashMap<BindlessIndex, u32> {
1285        let mut allocated_resource_slots = HashMap::default();
1286
1287        for (bindless_index, unprepared_binding_resource) in unprepared_bind_group.iter() {
1288            let bindless_index = BindlessIndex(*bindless_index);
1289
1290            let pre_existing_slot = allocation_candidate
1291                .pre_existing_resources
1292                .get(&bindless_index);
1293
1294            // Otherwise, we need to insert it anew.
1295            let binding_resource_id = BindingResourceId::from(unprepared_binding_resource);
1296            let increment_allocated_resource_count = match unprepared_binding_resource {
1297                UnpreparedBindingResource::Buffer(buffer) => {
1298                    let slot = self
1299                        .buffers
1300                        .get_mut(&bindless_index)
1301                        .expect("Buffer binding array should exist")
1302                        .insert(binding_resource_id, buffer.clone());
1303                    allocated_resource_slots.insert(bindless_index, slot);
1304
1305                    if true {
    if !pre_existing_slot.is_none_or(|pre_existing_slot|
                    *pre_existing_slot == slot) {
        ::core::panicking::panic("assertion failed: pre_existing_slot.is_none_or(|pre_existing_slot| *pre_existing_slot == slot)")
    };
};debug_assert!(pre_existing_slot
1306                        .is_none_or(|pre_existing_slot| *pre_existing_slot == slot));
1307                    pre_existing_slot.is_none()
1308                }
1309
1310                UnpreparedBindingResource::ShaderBuffer(shader_buffer) => {
1311                    let slot = self
1312                        .shader_buffers
1313                        .get_mut(&bindless_index)
1314                        .expect("Shader buffer binding array should exist")
1315                        .insert(binding_resource_id, shader_buffer.clone());
1316                    allocated_resource_slots.insert(bindless_index, slot);
1317
1318                    if true {
    if !pre_existing_slot.is_none_or(|pre_existing_slot|
                    *pre_existing_slot == slot) {
        ::core::panicking::panic("assertion failed: pre_existing_slot.is_none_or(|pre_existing_slot| *pre_existing_slot == slot)")
    };
};debug_assert!(pre_existing_slot
1319                        .is_none_or(|pre_existing_slot| *pre_existing_slot == slot));
1320                    pre_existing_slot.is_none()
1321                }
1322
1323                UnpreparedBindingResource::Data(data_range) => {
1324                    if pre_existing_slot.is_some() {
1325                        {
    ::core::panicking::panic_fmt(format_args!("Data buffers can\'t be deduplicated"));
}panic!("Data buffers can't be deduplicated")
1326                    }
1327
1328                    let slot = self
1329                        .data_buffers
1330                        .get_mut(&bindless_index)
1331                        .expect("Data buffer binding array should exist")
1332                        .insert(
1333                            &unprepared_bind_group.data_buffer
1334                                [(data_range.start as usize)..(data_range.end as usize)],
1335                        );
1336                    allocated_resource_slots.insert(bindless_index, slot);
1337                    false
1338                }
1339
1340                UnpreparedBindingResource::TextureView(texture_view_dimension, texture_view) => {
1341                    let bindless_resource_type =
1342                        BindlessResourceType::from(*texture_view_dimension);
1343                    let slot = self
1344                        .textures
1345                        .get_mut(&bindless_resource_type)
1346                        .expect("Texture array should exist")
1347                        .insert(binding_resource_id, texture_view.clone());
1348                    allocated_resource_slots.insert(bindless_index, slot);
1349
1350                    if true {
    if !pre_existing_slot.is_none_or(|pre_existing_slot|
                    *pre_existing_slot == slot) {
        ::core::panicking::panic("assertion failed: pre_existing_slot.is_none_or(|pre_existing_slot| *pre_existing_slot == slot)")
    };
};debug_assert!(pre_existing_slot
1351                        .is_none_or(|pre_existing_slot| *pre_existing_slot == slot));
1352                    pre_existing_slot.is_none()
1353                }
1354
1355                UnpreparedBindingResource::Sampler(sampler_binding_type, sampler) => {
1356                    let bindless_resource_type = BindlessResourceType::from(*sampler_binding_type);
1357                    let slot = self
1358                        .samplers
1359                        .get_mut(&bindless_resource_type)
1360                        .expect("Sampler should exist")
1361                        .insert(binding_resource_id, sampler.clone());
1362                    allocated_resource_slots.insert(bindless_index, slot);
1363
1364                    if true {
    if !pre_existing_slot.is_none_or(|pre_existing_slot|
                    *pre_existing_slot == slot) {
        ::core::panicking::panic("assertion failed: pre_existing_slot.is_none_or(|pre_existing_slot| *pre_existing_slot == slot)")
    };
};debug_assert!(pre_existing_slot
1365                        .is_none_or(|pre_existing_slot| *pre_existing_slot == slot));
1366                    pre_existing_slot.is_none()
1367                }
1368            };
1369
1370            // Bump the allocated resource count.
1371            if increment_allocated_resource_count {
1372                self.allocated_resource_count += 1;
1373            }
1374        }
1375
1376        allocated_resource_slots
1377    }
1378
1379    /// Removes the material allocated in the given slot, with the given
1380    /// descriptor, from this slab.
1381    fn free(&mut self, slot: MaterialBindGroupSlot, bindless_descriptor: &BindlessDescriptor) {
1382        // Loop through each binding.
1383        for (bindless_index, bindless_resource_type) in
1384            bindless_descriptor.resources.iter().enumerate()
1385        {
1386            let bindless_index = BindlessIndex::from(bindless_index as u32);
1387            let Some(bindless_index_table) = self.get_bindless_index_table(bindless_index) else {
1388                continue;
1389            };
1390            let Some(bindless_binding) = bindless_index_table.get_binding(slot, bindless_index)
1391            else {
1392                continue;
1393            };
1394
1395            // Free the binding. If the resource in question was anything other
1396            // than a data buffer, then it has a reference count and
1397            // consequently we need to decrement it.
1398            let decrement_allocated_resource_count = match *bindless_resource_type {
1399                BindlessResourceType::None => false,
1400                BindlessResourceType::Buffer => self
1401                    .buffers
1402                    .get_mut(&bindless_index)
1403                    .expect("Buffer should exist with that bindless index")
1404                    .remove(bindless_binding),
1405                BindlessResourceType::ShaderBuffer => self
1406                    .shader_buffers
1407                    .get_mut(&bindless_index)
1408                    .expect("Shader buffer should exist with that bindless index")
1409                    .remove(bindless_binding),
1410                BindlessResourceType::DataBuffer => {
1411                    self.data_buffers
1412                        .get_mut(&bindless_index)
1413                        .expect("Data buffer should exist with that bindless index")
1414                        .remove(bindless_binding);
1415                    false
1416                }
1417                BindlessResourceType::SamplerFiltering
1418                | BindlessResourceType::SamplerNonFiltering
1419                | BindlessResourceType::SamplerComparison => self
1420                    .samplers
1421                    .get_mut(bindless_resource_type)
1422                    .expect("Sampler array should exist")
1423                    .remove(bindless_binding),
1424                BindlessResourceType::Texture1d
1425                | BindlessResourceType::Texture2d
1426                | BindlessResourceType::Texture2dArray
1427                | BindlessResourceType::Texture3d
1428                | BindlessResourceType::TextureCube
1429                | BindlessResourceType::TextureCubeArray => self
1430                    .textures
1431                    .get_mut(bindless_resource_type)
1432                    .expect("Texture array should exist")
1433                    .remove(bindless_binding),
1434            };
1435
1436            // If the slot is now free, decrement the allocated resource
1437            // count.
1438            if decrement_allocated_resource_count {
1439                self.allocated_resource_count -= 1;
1440            }
1441        }
1442
1443        // Invalidate the cached bind group.
1444        self.bind_group = None;
1445
1446        // Release the slot ID.
1447        self.free_slots.push(slot);
1448        self.live_allocation_count -= 1;
1449    }
1450
1451    /// Returns true if all the GPU resources in the given
1452    /// [`UnpreparedBindingResources`] described by the given bindless index
1453    /// table are identical to the resources referenced in the
1454    /// `bind_group_slot`.
1455    ///
1456    /// The [`Self::try_update_data`] fast path uses this method to determine
1457    /// whether the fast path can be used.
1458    fn gpu_resources_are_unchanged(
1459        &self,
1460        binding_resources: &UnpreparedBindingResources,
1461        bind_group_slot: MaterialBindGroupSlot,
1462        bindless_index_table: &MaterialBindlessIndexTable,
1463    ) -> bool {
1464        for (bindless_index, unprepared_binding_resource) in binding_resources.iter() {
1465            let bindless_index = BindlessIndex(*bindless_index);
1466            let binding_resource_id = BindingResourceId::from(unprepared_binding_resource);
1467
1468            match unprepared_binding_resource {
1469                UnpreparedBindingResource::Buffer(_)
1470                | UnpreparedBindingResource::ShaderBuffer(_) => {
1471                    // Ignore this buffer if it's not in our bindless index
1472                    // table.
1473                    if bindless_index_table
1474                        .get_binding(bind_group_slot, bindless_index)
1475                        .is_some_and(|binding_index| {
1476                            // Make sure that the slot references the correct
1477                            // buffer.
1478                            !self
1479                                .buffers
1480                                .get(&bindless_index)
1481                                .is_some_and(|binding_array| {
1482                                    binding_array
1483                                        .slot_references(binding_index, binding_resource_id)
1484                                })
1485                        })
1486                    {
1487                        return false;
1488                    }
1489                }
1490
1491                UnpreparedBindingResource::TextureView(texture_view_dimension, _) => {
1492                    let bindless_resource_type =
1493                        BindlessResourceType::from(*texture_view_dimension);
1494                    // Ignore this texture if it's not in our bindless index
1495                    // table.
1496                    if bindless_index_table
1497                        .get_binding(bind_group_slot, bindless_index)
1498                        .is_some_and(|binding_index| {
1499                            // Make sure that the slot references the correct
1500                            // texture.
1501                            !self.textures.get(&bindless_resource_type).is_some_and(
1502                                |binding_array| {
1503                                    binding_array
1504                                        .slot_references(binding_index, binding_resource_id)
1505                                },
1506                            )
1507                        })
1508                    {
1509                        return false;
1510                    }
1511                }
1512
1513                UnpreparedBindingResource::Sampler(sampler_binding_type, _) => {
1514                    let bindless_resource_type = BindlessResourceType::from(*sampler_binding_type);
1515                    // Ignore this sampler if it's not in our bindless index
1516                    // table.
1517                    if bindless_index_table
1518                        .get_binding(bind_group_slot, bindless_index)
1519                        .is_some_and(|binding_index| {
1520                            // Make sure that the slot references the correct
1521                            // sampler.
1522                            !self.samplers.get(&bindless_resource_type).is_some_and(
1523                                |binding_array| {
1524                                    binding_array
1525                                        .slot_references(binding_index, binding_resource_id)
1526                                },
1527                            )
1528                        })
1529                    {
1530                        return false;
1531                    }
1532                }
1533
1534                UnpreparedBindingResource::Data(_) => {
1535                    // Ignore POD.
1536                }
1537            }
1538        }
1539
1540        true
1541    }
1542
1543    // TODO: In the future, this could be `&self`, which would allow this method
1544    // to operate in parallel. We would probably want `Self::data_buffers` to be
1545    // `AtomicSparseBufferVec`s in that case.
1546
1547    /// Replaces the POD ([`UnpreparedBindingResource::Data`]) with new data,
1548    /// without touching the allocation.
1549    ///
1550    /// It's the caller's responsibility to ensure that this is a reasonable
1551    /// thing to do (probably by calling [`Self::gpu_resources_are_unchanged`]).
1552    fn update_data(
1553        &mut self,
1554        unprepared_bind_group: &BindGroupBuilder,
1555        bind_group_slot: MaterialBindGroupSlot,
1556        bindless_index_table_index: usize,
1557    ) {
1558        let bindless_index_table = &self.bindless_index_tables[bindless_index_table_index];
1559
1560        // Go over the index table looking for POD.
1561        for (bindless_index, unprepared_binding_resource) in
1562            unprepared_bind_group.binding_resources.iter()
1563        {
1564            let bindless_index = BindlessIndex(*bindless_index);
1565
1566            match unprepared_binding_resource {
1567                // Ignore all resources other than data.
1568                UnpreparedBindingResource::Buffer(_)
1569                | UnpreparedBindingResource::ShaderBuffer(_)
1570                | UnpreparedBindingResource::TextureView(..)
1571                | UnpreparedBindingResource::Sampler(..) => {}
1572
1573                UnpreparedBindingResource::Data(data_range) => {
1574                    // Ignore this resource unless it represents data we're
1575                    // responsible for.
1576                    let Some(binding_index) =
1577                        bindless_index_table.get_binding(bind_group_slot, bindless_index)
1578                    else {
1579                        continue;
1580                    };
1581
1582                    // Poke the new data into the buffer.
1583                    self.data_buffers
1584                        .get_mut(&bindless_index)
1585                        .expect("Data buffer should exist if we're in the fast path")
1586                        .set(
1587                            binding_index,
1588                            &unprepared_bind_group.data_buffer
1589                                [(data_range.start as usize)..(data_range.end as usize)],
1590                        );
1591                }
1592            }
1593        }
1594    }
1595
1596    /// Recreates the bind group and bindless index table buffer if necessary.
1597    fn prepare(
1598        &mut self,
1599        render_device: &RenderDevice,
1600        pipeline_cache: &PipelineCache,
1601        label: &'static str,
1602        bind_group_layout: &BindGroupLayoutDescriptor,
1603        fallback_bindless_resources: &FallbackBindlessResources,
1604        fallback_buffers: &HashMap<BindlessIndex, Buffer>,
1605        fallback_image: &FallbackImage,
1606        shader_buffer_assets: &RenderAssets<GpuShaderBuffer>,
1607        changed_shader_buffers: &RenderChangedShaderBuffers,
1608        bindless_descriptor: &BindlessDescriptor,
1609        slab_capacity: u32,
1610    ) {
1611        self.invalidate_bind_group_for_changed_shader_buffers_if_needed(changed_shader_buffers);
1612
1613        // Create the bindless index table buffers if needed.
1614        for bindless_index_table in &mut self.bindless_index_tables {
1615            bindless_index_table.buffer.prepare(render_device);
1616        }
1617
1618        // Create any data buffers we were managing if necessary.
1619        for data_buffer in self.data_buffers.values_mut() {
1620            data_buffer.buffer.prepare(render_device);
1621        }
1622
1623        // Create the bind group if needed.
1624        self.prepare_bind_group(
1625            render_device,
1626            pipeline_cache,
1627            label,
1628            bind_group_layout,
1629            fallback_bindless_resources,
1630            fallback_buffers,
1631            fallback_image,
1632            shader_buffer_assets,
1633            bindless_descriptor,
1634            slab_capacity,
1635        );
1636    }
1637
1638    /// Recreates the bind group if this slab has been changed since the last
1639    /// time we created it.
1640    fn prepare_bind_group(
1641        &mut self,
1642        render_device: &RenderDevice,
1643        pipeline_cache: &PipelineCache,
1644        label: &'static str,
1645        bind_group_layout: &BindGroupLayoutDescriptor,
1646        fallback_bindless_resources: &FallbackBindlessResources,
1647        fallback_buffers: &HashMap<BindlessIndex, Buffer>,
1648        fallback_image: &FallbackImage,
1649        shader_buffer_assets: &RenderAssets<GpuShaderBuffer>,
1650        bindless_descriptor: &BindlessDescriptor,
1651        slab_capacity: u32,
1652    ) {
1653        // If the bind group is clean, then do nothing.
1654        if self.bind_group.is_some() {
1655            return;
1656        }
1657
1658        // Determine whether we need to pad out our binding arrays with dummy
1659        // resources.
1660        let required_binding_array_size = if render_device
1661            .features()
1662            .contains(WgpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY)
1663        {
1664            None
1665        } else {
1666            Some(slab_capacity)
1667        };
1668
1669        let binding_resource_arrays = self.create_binding_resource_arrays(
1670            fallback_bindless_resources,
1671            fallback_buffers,
1672            fallback_image,
1673            shader_buffer_assets,
1674            bindless_descriptor,
1675            required_binding_array_size,
1676        );
1677
1678        let mut bind_group_entries: Vec<_> = self
1679            .bindless_index_tables
1680            .iter()
1681            .map(|bindless_index_table| bindless_index_table.bind_group_entry())
1682            .collect();
1683
1684        for &(&binding, ref binding_resource_array) in binding_resource_arrays.iter() {
1685            bind_group_entries.push(BindGroupEntry {
1686                binding,
1687                resource: match *binding_resource_array {
1688                    BindingResourceArray::Buffers(ref buffer_bindings) => {
1689                        BindingResource::BufferArray(&buffer_bindings[..])
1690                    }
1691                    BindingResourceArray::TextureViews(ref texture_views) => {
1692                        BindingResource::TextureViewArray(&texture_views[..])
1693                    }
1694                    BindingResourceArray::Samplers(ref samplers) => {
1695                        BindingResource::SamplerArray(&samplers[..])
1696                    }
1697                },
1698            });
1699        }
1700
1701        // Create bind group entries for any data buffers we're managing.
1702        for data_buffer in self.data_buffers.values() {
1703            bind_group_entries.push(BindGroupEntry {
1704                binding: *data_buffer.binding_number,
1705                resource: data_buffer
1706                    .buffer
1707                    .buffer()
1708                    .expect("Backing data buffer must have been uploaded by now")
1709                    .as_entire_binding(),
1710            });
1711        }
1712
1713        self.bind_group = Some(render_device.create_bind_group(
1714            Some(label),
1715            &pipeline_cache.get_bind_group_layout(bind_group_layout),
1716            &bind_group_entries,
1717        ));
1718    }
1719
1720    /// If this bind group contains a [`ShaderBuffer`], and the shader buffer
1721    /// has changed, marks the bind group as invalid so that it'll be rebuilt.
1722    ///
1723    /// Shader buffers are assets that can legally be updated to point to a
1724    /// different raw buffer. This will happen when, for example, the shader
1725    /// buffer grows. We must detect this circumstance and rebuild the bind
1726    /// group when it happens.
1727    fn invalidate_bind_group_for_changed_shader_buffers_if_needed(
1728        &mut self,
1729        changed_shader_buffers: &RenderChangedShaderBuffers,
1730    ) {
1731        if !changed_shader_buffers.is_empty()
1732            && self.bind_group.is_some()
1733            && self.shader_buffers.values().any(|buffer| {
1734                #[allow(non_exhaustive_omitted_patterns)] match buffer.resource_type {
    BindlessResourceType::ShaderBuffer => true,
    _ => false,
}matches!(buffer.resource_type, BindlessResourceType::ShaderBuffer)
1735                    && buffer.resource_to_slot.keys().any(|binding_resource_id| {
1736                        #[allow(non_exhaustive_omitted_patterns)] match *binding_resource_id {
    BindingResourceId::ShaderBuffer(ref shader_buffer) if
        changed_shader_buffers.contains(shader_buffer) => true,
    _ => false,
}matches!(
1737                            *binding_resource_id,
1738                            BindingResourceId::ShaderBuffer(ref shader_buffer) if
1739                                changed_shader_buffers.contains(shader_buffer)
1740                        )
1741                    })
1742            })
1743        {
1744            self.bind_group = None;
1745        }
1746    }
1747
1748    /// Writes any buffers that we're managing to the GPU.
1749    ///
1750    /// Currently, this consists of the bindless index table plus any data
1751    /// buffers we're managing.
1752    fn write_buffer(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
1753        for bindless_index_table in &mut self.bindless_index_tables {
1754            bindless_index_table
1755                .buffer
1756                .write(render_device, render_queue);
1757        }
1758
1759        for data_buffer in self.data_buffers.values_mut() {
1760            data_buffer.buffer.write(render_device, render_queue);
1761        }
1762    }
1763
1764    /// Converts our binding arrays into binding resource arrays suitable for
1765    /// passing to `wgpu`.
1766    fn create_binding_resource_arrays<'a>(
1767        &'a self,
1768        fallback_bindless_resources: &'a FallbackBindlessResources,
1769        fallback_buffers: &'a HashMap<BindlessIndex, Buffer>,
1770        fallback_image: &'a FallbackImage,
1771        shader_buffer_assets: &'a RenderAssets<GpuShaderBuffer>,
1772        bindless_descriptor: &'a BindlessDescriptor,
1773        required_binding_array_size: Option<u32>,
1774    ) -> Vec<(&'a u32, BindingResourceArray<'a>)> {
1775        let mut binding_resource_arrays = ::alloc::vec::Vec::new()vec![];
1776
1777        // Build sampler bindings.
1778        self.create_sampler_binding_resource_arrays(
1779            &mut binding_resource_arrays,
1780            fallback_bindless_resources,
1781            bindless_descriptor,
1782            required_binding_array_size,
1783        );
1784
1785        // Build texture bindings.
1786        self.create_texture_binding_resource_arrays(
1787            &mut binding_resource_arrays,
1788            fallback_image,
1789            bindless_descriptor,
1790            required_binding_array_size,
1791        );
1792
1793        // Build buffer bindings.
1794        self.create_buffer_binding_resource_arrays(
1795            &mut binding_resource_arrays,
1796            fallback_buffers,
1797            shader_buffer_assets,
1798            bindless_descriptor,
1799            required_binding_array_size,
1800        );
1801
1802        binding_resource_arrays
1803    }
1804
1805    /// Accumulates sampler binding arrays into binding resource arrays suitable
1806    /// for passing to `wgpu`.
1807    fn create_sampler_binding_resource_arrays<'a, 'b>(
1808        &'a self,
1809        binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
1810        fallback_bindless_resources: &'a FallbackBindlessResources,
1811        bindless_descriptor: &'a BindlessDescriptor,
1812        required_binding_array_size: Option<u32>,
1813    ) {
1814        // We have one binding resource array per sampler type.
1815        for (bindless_resource_type, fallback_sampler) in [
1816            (
1817                BindlessResourceType::SamplerFiltering,
1818                &fallback_bindless_resources.filtering_sampler,
1819            ),
1820            (
1821                BindlessResourceType::SamplerNonFiltering,
1822                &fallback_bindless_resources.non_filtering_sampler,
1823            ),
1824            (
1825                BindlessResourceType::SamplerComparison,
1826                &fallback_bindless_resources.comparison_sampler,
1827            ),
1828        ] {
1829            // Skip resource types not used by this material.
1830            if !bindless_descriptor
1831                .resources
1832                .contains(&bindless_resource_type)
1833            {
1834                continue;
1835            }
1836
1837            let mut sampler_bindings = ::alloc::vec::Vec::new()vec![];
1838
1839            match self.samplers.get(&bindless_resource_type) {
1840                Some(sampler_bindless_binding_array) => {
1841                    for maybe_bindless_binding in sampler_bindless_binding_array.bindings.iter() {
1842                        match *maybe_bindless_binding {
1843                            Some(ref bindless_binding) => {
1844                                sampler_bindings.push(&*bindless_binding.resource);
1845                            }
1846                            None => sampler_bindings.push(&**fallback_sampler),
1847                        }
1848                    }
1849                }
1850
1851                None => {
1852                    // Fill with a single fallback sampler.
1853                    sampler_bindings.push(&**fallback_sampler);
1854                }
1855            }
1856
1857            if let Some(required_binding_array_size) = required_binding_array_size {
1858                sampler_bindings.extend(iter::repeat_n(
1859                    &**fallback_sampler,
1860                    required_binding_array_size as usize - sampler_bindings.len(),
1861                ));
1862            }
1863
1864            let binding_number = bindless_resource_type
1865                .binding_number()
1866                .expect("Sampler bindless resource type must have a binding number");
1867
1868            binding_resource_arrays.push((
1869                &**binding_number,
1870                BindingResourceArray::Samplers(sampler_bindings),
1871            ));
1872        }
1873    }
1874
1875    /// Accumulates texture binding arrays into binding resource arrays suitable
1876    /// for passing to `wgpu`.
1877    fn create_texture_binding_resource_arrays<'a, 'b>(
1878        &'a self,
1879        binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
1880        fallback_image: &'a FallbackImage,
1881        bindless_descriptor: &'a BindlessDescriptor,
1882        required_binding_array_size: Option<u32>,
1883    ) {
1884        for (bindless_resource_type, fallback_image) in [
1885            (BindlessResourceType::Texture1d, &fallback_image.d1),
1886            (BindlessResourceType::Texture2d, &fallback_image.d2),
1887            (
1888                BindlessResourceType::Texture2dArray,
1889                &fallback_image.d2_array,
1890            ),
1891            (BindlessResourceType::Texture3d, &fallback_image.d3),
1892            (BindlessResourceType::TextureCube, &fallback_image.cube),
1893            (
1894                BindlessResourceType::TextureCubeArray,
1895                &fallback_image.cube_array,
1896            ),
1897        ] {
1898            // Skip texture types that this material doesn't use.
1899            if !bindless_descriptor
1900                .resources
1901                .contains(&bindless_resource_type)
1902            {
1903                continue;
1904            }
1905
1906            let mut texture_bindings = ::alloc::vec::Vec::new()vec![];
1907
1908            let binding_number = bindless_resource_type
1909                .binding_number()
1910                .expect("Texture bindless resource type must have a binding number");
1911
1912            match self.textures.get(&bindless_resource_type) {
1913                Some(texture_bindless_binding_array) => {
1914                    for maybe_bindless_binding in texture_bindless_binding_array.bindings.iter() {
1915                        match *maybe_bindless_binding {
1916                            Some(ref bindless_binding) => {
1917                                texture_bindings.push(&*bindless_binding.resource);
1918                            }
1919                            None => texture_bindings.push(&*fallback_image.texture_view),
1920                        }
1921                    }
1922                }
1923
1924                None => {
1925                    // Fill with a single fallback image.
1926                    texture_bindings.push(&*fallback_image.texture_view);
1927                }
1928            }
1929
1930            if let Some(required_binding_array_size) = required_binding_array_size {
1931                texture_bindings.extend(iter::repeat_n(
1932                    &*fallback_image.texture_view,
1933                    required_binding_array_size as usize - texture_bindings.len(),
1934                ));
1935            }
1936
1937            binding_resource_arrays.push((
1938                binding_number,
1939                BindingResourceArray::TextureViews(texture_bindings),
1940            ));
1941        }
1942    }
1943
1944    /// Accumulates buffer binding arrays into binding resource arrays suitable
1945    /// for `wgpu`.
1946    fn create_buffer_binding_resource_arrays<'a, 'b>(
1947        &'a self,
1948        binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
1949        fallback_buffers: &'a HashMap<BindlessIndex, Buffer>,
1950        shader_buffer_assets: &'a RenderAssets<GpuShaderBuffer>,
1951        bindless_descriptor: &'a BindlessDescriptor,
1952        required_binding_array_size: Option<u32>,
1953    ) {
1954        for bindless_buffer_descriptor in bindless_descriptor.buffers.iter() {
1955            // If this is a raw buffer, create the binding.
1956            if let Some(buffer_bindless_binding_array) =
1957                self.buffers.get(&bindless_buffer_descriptor.bindless_index)
1958            {
1959                let fallback_buffer = fallback_buffers
1960                    .get(&bindless_buffer_descriptor.bindless_index)
1961                    .expect("Fallback buffer should exist");
1962
1963                let mut buffer_bindings: Vec<_> = buffer_bindless_binding_array
1964                    .bindings
1965                    .iter()
1966                    .map(|maybe_bindless_binding| {
1967                        let buffer = match *maybe_bindless_binding {
1968                            None => fallback_buffer,
1969                            Some(ref bindless_binding) => &bindless_binding.resource,
1970                        };
1971                        BufferBinding {
1972                            buffer,
1973                            offset: 0,
1974                            size: None,
1975                        }
1976                    })
1977                    .collect();
1978
1979                if let Some(required_binding_array_size) = required_binding_array_size {
1980                    buffer_bindings.extend(iter::repeat_n(
1981                        BufferBinding {
1982                            buffer: fallback_buffer,
1983                            offset: 0,
1984                            size: None,
1985                        },
1986                        required_binding_array_size as usize - buffer_bindings.len(),
1987                    ));
1988                }
1989
1990                binding_resource_arrays.push((
1991                    &*buffer_bindless_binding_array.binding_number,
1992                    BindingResourceArray::Buffers(buffer_bindings),
1993                ));
1994
1995                continue;
1996            }
1997
1998            // If this is a shader buffer, look up the asset ID in the
1999            // `shader_buffer_assets` and fetch the raw buffer from there.
2000            if let Some(shader_buffer_bindless_binding_array) = self
2001                .shader_buffers
2002                .get(&bindless_buffer_descriptor.bindless_index)
2003            {
2004                let fallback_buffer = fallback_buffers
2005                    .get(&bindless_buffer_descriptor.bindless_index)
2006                    .expect("Fallback buffer should exist");
2007
2008                let mut buffer_bindings: Vec<_> = shader_buffer_bindless_binding_array
2009                    .bindings
2010                    .iter()
2011                    .map(|maybe_bindless_binding| {
2012                        let buffer =
2013                            match maybe_bindless_binding
2014                                .as_ref()
2015                                .and_then(|bindless_binding| {
2016                                    shader_buffer_assets.get(bindless_binding.resource.id())
2017                                }) {
2018                                None => fallback_buffer,
2019                                Some(gpu_shader_buffer) => &gpu_shader_buffer.buffer,
2020                            };
2021                        BufferBinding {
2022                            buffer,
2023                            offset: 0,
2024                            size: None,
2025                        }
2026                    })
2027                    .collect();
2028
2029                if let Some(required_binding_array_size) = required_binding_array_size {
2030                    buffer_bindings.extend(iter::repeat_n(
2031                        BufferBinding {
2032                            buffer: fallback_buffer,
2033                            offset: 0,
2034                            size: None,
2035                        },
2036                        required_binding_array_size as usize - buffer_bindings.len(),
2037                    ));
2038                }
2039
2040                binding_resource_arrays.push((
2041                    &*shader_buffer_bindless_binding_array.binding_number,
2042                    BindingResourceArray::Buffers(buffer_bindings),
2043                ));
2044
2045                continue;
2046            };
2047
2048            // If we got here, just ignore the buffer and continue. For example,
2049            // index buffers are present in `BindlessDescriptor::buffers` but
2050            // not in `BindlessDescriptor::resources`, so we need to ignore them
2051            // here.
2052        }
2053    }
2054
2055    /// Returns the [`BindGroup`] corresponding to this slab, if it's been
2056    /// prepared.
2057    fn bind_group(&self) -> Option<&BindGroup> {
2058        self.bind_group.as_ref()
2059    }
2060
2061    /// Returns the bindless index table containing the given bindless index.
2062    fn get_bindless_index_table(
2063        &self,
2064        bindless_index: BindlessIndex,
2065    ) -> Option<&MaterialBindlessIndexTable> {
2066        let table_index = self
2067            .bindless_index_tables
2068            .binary_search_by(|bindless_index_table| {
2069                if bindless_index_table.index_range.end <= bindless_index {
2070                    Ordering::Less
2071                } else if bindless_index_table.index_range.start > bindless_index {
2072                    Ordering::Greater
2073                } else {
2074                    Ordering::Equal
2075                }
2076            })
2077            .ok()?;
2078        self.bindless_index_tables.get(table_index)
2079    }
2080}
2081
2082impl<R> MaterialBindlessBindingArray<R>
2083where
2084    R: GetBindingResourceId,
2085{
2086    /// Creates a new [`MaterialBindlessBindingArray`] with the given binding
2087    /// number, managing resources of the given type.
2088    fn new(
2089        binding_number: BindingNumber,
2090        resource_type: BindlessResourceType,
2091    ) -> MaterialBindlessBindingArray<R> {
2092        MaterialBindlessBindingArray {
2093            binding_number,
2094            bindings: ::alloc::vec::Vec::new()vec![],
2095            resource_type,
2096            resource_to_slot: HashMap::default(),
2097            free_slots: ::alloc::vec::Vec::new()vec![],
2098            len: 0,
2099        }
2100    }
2101
2102    /// Returns the slot corresponding to the given resource, if that resource
2103    /// is located in this binding array.
2104    ///
2105    /// If the resource isn't in this binding array, this method returns `None`.
2106    fn find(&self, binding_resource_id: BindingResourceId) -> Option<u32> {
2107        self.resource_to_slot.get(&binding_resource_id).copied()
2108    }
2109
2110    /// Returns true if the given slot points to the given binding resource and
2111    /// false otherwise.
2112    fn slot_references(&self, slot: u32, binding_resource_id: BindingResourceId) -> bool {
2113        self.bindings
2114            .get(slot as usize)
2115            .is_some_and(|maybe_binding| {
2116                maybe_binding.as_ref().is_some_and(|binding| {
2117                    binding.resource.binding_resource_id(self.resource_type) == binding_resource_id
2118                })
2119            })
2120    }
2121
2122    /// Inserts a bindless resource into a binding array and returns the index
2123    /// of the slot it was inserted into.
2124    fn insert(&mut self, binding_resource_id: BindingResourceId, resource: R) -> u32 {
2125        match self.resource_to_slot.entry(binding_resource_id) {
2126            Entry::Occupied(o) => {
2127                let slot = *o.get();
2128
2129                self.bindings[slot as usize]
2130                    .as_mut()
2131                    .expect("A slot in the resource_to_slot map should have a value")
2132                    .ref_count += 1;
2133
2134                slot
2135            }
2136            Entry::Vacant(v) => {
2137                let slot = self.free_slots.pop().unwrap_or(self.len);
2138                v.insert(slot);
2139
2140                if self.bindings.len() < slot as usize + 1 {
2141                    self.bindings.resize_with(slot as usize + 1, || None);
2142                }
2143                if true {
    if !self.bindings[slot as usize].is_none() {
        ::core::panicking::panic("assertion failed: self.bindings[slot as usize].is_none()")
    };
};debug_assert!(self.bindings[slot as usize].is_none());
2144                self.bindings[slot as usize] = Some(MaterialBindlessBinding::new(resource));
2145
2146                self.len += 1;
2147                slot
2148            }
2149        }
2150    }
2151
2152    /// Removes a reference to an object from the slot.
2153    ///
2154    /// If the reference count dropped to 0 and the object was freed, this
2155    /// method returns true. If the object was still referenced after removing
2156    /// it, returns false.
2157    fn remove(&mut self, slot: u32) -> bool {
2158        let maybe_binding = &mut self.bindings[slot as usize];
2159        let binding = maybe_binding
2160            .as_mut()
2161            .expect("Attempted to free an already-freed binding");
2162
2163        binding.ref_count -= 1;
2164        if binding.ref_count != 0 {
2165            return false;
2166        }
2167
2168        let binding_resource_id = binding.resource.binding_resource_id(self.resource_type);
2169        self.resource_to_slot.remove(&binding_resource_id);
2170
2171        *maybe_binding = None;
2172        self.free_slots.push(slot);
2173        self.len -= 1;
2174        true
2175    }
2176}
2177
2178impl<R> MaterialBindlessBinding<R>
2179where
2180    R: GetBindingResourceId,
2181{
2182    /// Creates a new [`MaterialBindlessBinding`] for a freshly-added resource.
2183    ///
2184    /// The reference count is initialized to 1.
2185    fn new(resource: R) -> MaterialBindlessBinding<R> {
2186        MaterialBindlessBinding {
2187            resource,
2188            ref_count: 1,
2189        }
2190    }
2191}
2192
2193/// Returns true if the material will *actually* use bindless resources or false
2194/// if it won't.
2195///
2196/// This takes the platform support (or lack thereof) for bindless resources
2197/// into account.
2198pub fn material_uses_bindless_resources<M>(render_device: &RenderDevice) -> bool
2199where
2200    M: AsBindGroup,
2201{
2202    M::bindless_slot_count().is_some_and(|bindless_slot_count| {
2203        M::bindless_supported(render_device) && bindless_slot_count.resolve() > 1
2204    })
2205}
2206
2207impl MaterialBindlessSlab {
2208    /// Creates a new [`MaterialBindlessSlab`] for a material with the given
2209    /// bindless descriptor.
2210    ///
2211    /// We use this when no existing slab could hold a material to be allocated.
2212    fn new(bindless_descriptor: &BindlessDescriptor) -> MaterialBindlessSlab {
2213        let mut buffers = HashMap::default();
2214        let mut samplers = HashMap::default();
2215        let mut textures = HashMap::default();
2216        let mut data_buffers = HashMap::default();
2217        let mut shader_buffers = HashMap::default();
2218
2219        for (bindless_index, bindless_resource_type) in
2220            bindless_descriptor.resources.iter().enumerate()
2221        {
2222            let bindless_index = BindlessIndex(bindless_index as u32);
2223            match *bindless_resource_type {
2224                BindlessResourceType::None => {}
2225                BindlessResourceType::Buffer => {
2226                    let binding_number = bindless_descriptor
2227                        .buffers
2228                        .iter()
2229                        .find(|bindless_buffer_descriptor| {
2230                            bindless_buffer_descriptor.bindless_index == bindless_index
2231                        })
2232                        .expect(
2233                            "Bindless buffer descriptor matching that bindless index should be \
2234                             present",
2235                        )
2236                        .binding_number;
2237                    buffers.insert(
2238                        bindless_index,
2239                        MaterialBindlessBindingArray::new(binding_number, *bindless_resource_type),
2240                    );
2241                }
2242                BindlessResourceType::ShaderBuffer => {
2243                    let binding_number = bindless_descriptor
2244                        .buffers
2245                        .iter()
2246                        .find(|bindless_buffer_descriptor| {
2247                            bindless_buffer_descriptor.bindless_index == bindless_index
2248                        })
2249                        .expect(
2250                            "Bindless buffer descriptor matching that bindless index should be \
2251                             present",
2252                        )
2253                        .binding_number;
2254                    shader_buffers.insert(
2255                        bindless_index,
2256                        MaterialBindlessBindingArray::new(binding_number, *bindless_resource_type),
2257                    );
2258                }
2259                BindlessResourceType::DataBuffer => {
2260                    // Copy the data in.
2261                    let buffer_descriptor = bindless_descriptor
2262                        .buffers
2263                        .iter()
2264                        .find(|bindless_buffer_descriptor| {
2265                            bindless_buffer_descriptor.bindless_index == bindless_index
2266                        })
2267                        .expect(
2268                            "Bindless buffer descriptor matching that bindless index should be \
2269                             present",
2270                        );
2271                    data_buffers.insert(
2272                        bindless_index,
2273                        MaterialDataBuffer::new(
2274                            buffer_descriptor.binding_number,
2275                            buffer_descriptor
2276                                .size
2277                                .expect("Data buffers should have a size")
2278                                as u32,
2279                        ),
2280                    );
2281                }
2282                BindlessResourceType::SamplerFiltering
2283                | BindlessResourceType::SamplerNonFiltering
2284                | BindlessResourceType::SamplerComparison => {
2285                    samplers.insert(
2286                        *bindless_resource_type,
2287                        MaterialBindlessBindingArray::new(
2288                            *bindless_resource_type.binding_number().unwrap(),
2289                            *bindless_resource_type,
2290                        ),
2291                    );
2292                }
2293                BindlessResourceType::Texture1d
2294                | BindlessResourceType::Texture2d
2295                | BindlessResourceType::Texture2dArray
2296                | BindlessResourceType::Texture3d
2297                | BindlessResourceType::TextureCube
2298                | BindlessResourceType::TextureCubeArray => {
2299                    textures.insert(
2300                        *bindless_resource_type,
2301                        MaterialBindlessBindingArray::new(
2302                            *bindless_resource_type.binding_number().unwrap(),
2303                            *bindless_resource_type,
2304                        ),
2305                    );
2306                }
2307            }
2308        }
2309
2310        let bindless_index_tables = bindless_descriptor
2311            .index_tables
2312            .iter()
2313            .map(MaterialBindlessIndexTable::new)
2314            .collect();
2315
2316        MaterialBindlessSlab {
2317            bind_group: None,
2318            bindless_index_tables,
2319            samplers,
2320            textures,
2321            buffers,
2322            shader_buffers,
2323            data_buffers,
2324            free_slots: ::alloc::vec::Vec::new()vec![],
2325            live_allocation_count: 0,
2326            allocated_resource_count: 0,
2327        }
2328    }
2329}
2330
2331/// Creates and inserts the [`FallbackBindlessResources`] and [`FallbackBuffer`].
2332fn init_fallback_resources(mut commands: Commands, render_device: Res<RenderDevice>) {
2333    // Create the `FallbackBindlessResources`.
2334    commands.insert_resource(FallbackBindlessResources {
2335        filtering_sampler: render_device.create_sampler(&SamplerDescriptor {
2336            label: Some("fallback filtering sampler"),
2337            ..default()
2338        }),
2339        non_filtering_sampler: render_device.create_sampler(&SamplerDescriptor {
2340            label: Some("fallback non-filtering sampler"),
2341            mag_filter: FilterMode::Nearest,
2342            min_filter: FilterMode::Nearest,
2343            mipmap_filter: MipmapFilterMode::Nearest,
2344            ..default()
2345        }),
2346        comparison_sampler: render_device.create_sampler(&SamplerDescriptor {
2347            label: Some("fallback comparison sampler"),
2348            compare: Some(CompareFunction::Always),
2349            ..default()
2350        }),
2351    });
2352
2353    // Creates the `FallbackBuffer`.
2354    commands.insert_resource(FallbackBuffer(render_device.create_buffer(
2355        &BufferDescriptor {
2356            label: Some("fallback buffer"),
2357            size: 1,
2358            usage: BufferUsages::COPY_SRC
2359                | BufferUsages::COPY_DST
2360                | BufferUsages::STORAGE
2361                | BufferUsages::UNIFORM,
2362            mapped_at_creation: false,
2363        },
2364    )));
2365}
2366
2367impl MaterialBindGroupNonBindlessAllocator {
2368    /// Creates a new [`MaterialBindGroupNonBindlessAllocator`] managing the
2369    /// bind groups for a single non-bindless material.
2370    fn new(label: &'static str) -> MaterialBindGroupNonBindlessAllocator {
2371        MaterialBindGroupNonBindlessAllocator {
2372            label,
2373            bind_groups: ::alloc::vec::Vec::new()vec![],
2374            to_prepare: HashSet::default(),
2375            free_indices: ::alloc::vec::Vec::new()vec![],
2376        }
2377    }
2378
2379    /// Inserts a bind group, either unprepared or prepared, into this allocator
2380    /// and returns a [`MaterialBindingId`].
2381    ///
2382    /// The returned [`MaterialBindingId`] can later be used to fetch the bind
2383    /// group.
2384    fn allocate(&mut self, bind_group: MaterialNonBindlessAllocatedBindGroup) -> MaterialBindingId {
2385        let group_id = self
2386            .free_indices
2387            .pop()
2388            .unwrap_or(MaterialBindGroupIndex(self.bind_groups.len() as u32));
2389        if self.bind_groups.len() < *group_id as usize + 1 {
2390            self.bind_groups
2391                .resize_with(*group_id as usize + 1, || None);
2392        }
2393
2394        if #[allow(non_exhaustive_omitted_patterns)] match bind_group {
    MaterialNonBindlessAllocatedBindGroup::Unprepared { .. } => true,
    _ => false,
}matches!(
2395            bind_group,
2396            MaterialNonBindlessAllocatedBindGroup::Unprepared { .. }
2397        ) {
2398            self.to_prepare.insert(group_id);
2399        }
2400
2401        self.bind_groups[*group_id as usize] = Some(bind_group);
2402
2403        MaterialBindingId {
2404            group: group_id,
2405            slot: default(),
2406        }
2407    }
2408
2409    /// Inserts an unprepared bind group into this allocator and returns a
2410    /// [`MaterialBindingId`].
2411    fn allocate_unprepared(
2412        &mut self,
2413        unprepared_bind_group: &mut BindGroupBuilder,
2414        bind_group_layout: BindGroupLayoutDescriptor,
2415    ) -> MaterialBindingId {
2416        self.allocate(MaterialNonBindlessAllocatedBindGroup::Unprepared {
2417            // FIXME: This is slow as it takes all the allocations, meaning that
2418            // the next material to be prepared is going to have to reallocate
2419            // the unprepared bind group. But there's a reason for it: we have
2420            // to hold onto the data allocations so that we can upload them
2421            // later, in `Self::prepare_bind_groups`. Addressing this will
2422            // require either moving those allocations to happen earlier or
2423            // setting up some sort of buffer recycling scheme.
2424            bind_group: mem::take(unprepared_bind_group),
2425            layout: bind_group_layout,
2426        })
2427    }
2428
2429    /// Inserts an prepared bind group into this allocator and returns a
2430    /// [`MaterialBindingId`].
2431    fn allocate_prepared(
2432        &mut self,
2433        prepared_bind_group: PreparedBindGroup,
2434        bind_group_layout: BindGroupLayoutDescriptor,
2435    ) -> MaterialBindingId {
2436        self.allocate(MaterialNonBindlessAllocatedBindGroup::Prepared {
2437            bind_group: prepared_bind_group,
2438            layout: bind_group_layout,
2439            uniform_buffers: ::alloc::vec::Vec::new()vec![],
2440        })
2441    }
2442
2443    /// Deallocates the bind group with the given binding ID.
2444    fn free(&mut self, binding_id: MaterialBindingId) {
2445        if true {
    {
        match (&binding_id.slot, &MaterialBindGroupSlot(0)) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    let kind = ::core::panicking::AssertKind::Eq;
                    ::core::panicking::assert_failed(kind, &*left_val,
                        &*right_val, ::core::option::Option::None);
                }
            }
        }
    };
};debug_assert_eq!(binding_id.slot, MaterialBindGroupSlot(0));
2446        if true {
    if !self.bind_groups[*binding_id.group as usize].is_some() {
        ::core::panicking::panic("assertion failed: self.bind_groups[*binding_id.group as usize].is_some()")
    };
};debug_assert!(self.bind_groups[*binding_id.group as usize].is_some());
2447        self.bind_groups[*binding_id.group as usize] = None;
2448        self.to_prepare.remove(&binding_id.group);
2449        self.free_indices.push(binding_id.group);
2450    }
2451
2452    /// Returns a wrapper around the bind group with the given index.
2453    fn get(&self, group: MaterialBindGroupIndex) -> Option<MaterialNonBindlessSlab<'_>> {
2454        self.bind_groups[group.0 as usize]
2455            .as_ref()
2456            .map(|bind_group| match bind_group {
2457                MaterialNonBindlessAllocatedBindGroup::Prepared { bind_group, .. } => {
2458                    MaterialNonBindlessSlab::Prepared(bind_group)
2459                }
2460                MaterialNonBindlessAllocatedBindGroup::Unprepared { .. } => {
2461                    MaterialNonBindlessSlab::Unprepared
2462                }
2463            })
2464    }
2465
2466    /// Prepares any as-yet unprepared bind groups that this allocator is
2467    /// managing.
2468    ///
2469    /// Unprepared bind groups can be added to this allocator with
2470    /// [`Self::allocate_unprepared`]. Such bind groups will defer being
2471    /// prepared until the next time this method is called.
2472    fn prepare_bind_groups(
2473        &mut self,
2474        render_device: &RenderDevice,
2475        pipeline_cache: &PipelineCache,
2476        fallback_buffer: &FallbackBuffer,
2477        shader_buffer_assets: &RenderAssets<GpuShaderBuffer>,
2478        changed_shader_buffers: &RenderChangedShaderBuffers,
2479    ) {
2480        self.invalidate_bind_group_for_changed_shader_buffers_if_needed(changed_shader_buffers);
2481
2482        for bind_group_index in mem::take(&mut self.to_prepare) {
2483            let Some(MaterialNonBindlessAllocatedBindGroup::Unprepared {
2484                bind_group: unprepared_bind_group,
2485                layout: bind_group_layout,
2486            }) = mem::take(&mut self.bind_groups[*bind_group_index as usize])
2487            else {
2488                {
    ::core::panicking::panic_fmt(format_args!("Allocation didn\'t exist or was already prepared"));
};panic!("Allocation didn't exist or was already prepared");
2489            };
2490
2491            // Pack any `Data` into uniform buffers.
2492            let mut uniform_buffers = ::alloc::vec::Vec::new()vec![];
2493            for (index, binding) in unprepared_bind_group.binding_resources.iter() {
2494                let UnpreparedBindingResource::Data(ref data_range) = *binding else {
2495                    continue;
2496                };
2497                let label = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("material uniform data {0}",
                *index))
    })format!("material uniform data {}", *index);
2498                let uniform_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
2499                    label: Some(&label),
2500                    contents: &unprepared_bind_group.data_buffer
2501                        [(data_range.start as usize)..(data_range.end as usize)],
2502                    usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM,
2503                });
2504                uniform_buffers.push(uniform_buffer);
2505            }
2506
2507            // Create bind group entries.
2508            let mut bind_group_entries = ::alloc::vec::Vec::new()vec![];
2509            let mut uniform_buffers_iter = uniform_buffers.iter();
2510            for (index, binding) in unprepared_bind_group.binding_resources.iter() {
2511                match *binding {
2512                    UnpreparedBindingResource::Data(_) => {
2513                        bind_group_entries.push(BindGroupEntry {
2514                            binding: *index,
2515                            resource: uniform_buffers_iter
2516                                .next()
2517                                .expect("We should have created uniform buffers for each `Data`")
2518                                .as_entire_binding(),
2519                        });
2520                    }
2521                    _ => bind_group_entries.push(BindGroupEntry {
2522                        binding: *index,
2523                        resource: binding.get_binding(fallback_buffer, shader_buffer_assets),
2524                    }),
2525                }
2526            }
2527
2528            // Create the bind group.
2529            let bind_group = render_device.create_bind_group(
2530                self.label,
2531                &pipeline_cache.get_bind_group_layout(&bind_group_layout),
2532                &bind_group_entries,
2533            );
2534
2535            self.bind_groups[*bind_group_index as usize] =
2536                Some(MaterialNonBindlessAllocatedBindGroup::Prepared {
2537                    bind_group: PreparedBindGroup {
2538                        bindings: unprepared_bind_group.into_binding_resources(),
2539                        bind_group,
2540                    },
2541                    layout: bind_group_layout,
2542                    uniform_buffers,
2543                });
2544        }
2545    }
2546
2547    /// Invalidates any bind group that contains a shader buffer if the shader
2548    /// buffer has changed identity.
2549    fn invalidate_bind_group_for_changed_shader_buffers_if_needed(
2550        &mut self,
2551        changed_shader_buffers: &RenderChangedShaderBuffers,
2552    ) {
2553        // Early out if there are no changed shader buffers. This is important,
2554        // as scanning for invalidated bind groups can be expensive.
2555        if changed_shader_buffers.is_empty() {
2556            return;
2557        }
2558
2559        // Look for bind groups that are now invalid.
2560        for (allocated_bind_group_index, maybe_allocated_bind_group) in
2561            self.bind_groups.iter_mut().enumerate()
2562        {
2563            // We only care about prepared bind groups. Unprepared bind groups
2564            // store only the asset ID of the shader buffer, which is stable,
2565            // rather than the buffer itself, which is not.
2566            let Some(MaterialNonBindlessAllocatedBindGroup::Prepared {
2567                bind_group: ref mut prepared_bind_group,
2568                ref mut layout,
2569                ..
2570            }) = *maybe_allocated_bind_group
2571            else {
2572                continue;
2573            };
2574
2575            // Check to see if any shader buffer within the bind group was
2576            // invalidated.
2577            if prepared_bind_group
2578                .bindings
2579                .iter()
2580                .all(|(_, binding_resource)| match *binding_resource {
2581                    OwnedBindingResource::ShaderBuffer(ref shader_buffer) => {
2582                        !changed_shader_buffers.contains(&shader_buffer.id())
2583                    }
2584                    _ => true,
2585                })
2586            {
2587                continue;
2588            }
2589
2590            // If we got here, we know the bind group is invalid. Mark it as
2591            // such by "un-preparing" it. The logic in `prepare_bind_groups`
2592            // will detect that the bind group is now unprepared and will
2593            // rebuild it.
2594            let layout = mem::take(layout);
2595            *maybe_allocated_bind_group = Some(MaterialNonBindlessAllocatedBindGroup::Unprepared {
2596                bind_group: prepared_bind_group.unprepare(),
2597                layout,
2598            });
2599            self.to_prepare
2600                .insert(MaterialBindGroupIndex(allocated_bind_group_index as u32));
2601        }
2602    }
2603}
2604
2605impl<'a> MaterialSlab<'a> {
2606    /// Returns the [`BindGroup`] corresponding to this slab, if it's been
2607    /// prepared.
2608    ///
2609    /// You can prepare bind groups by calling
2610    /// [`MaterialBindGroupAllocator::prepare_bind_groups`]. If the bind group
2611    /// isn't ready, this method returns `None`.
2612    pub fn bind_group(&self) -> Option<&'a BindGroup> {
2613        match self.0 {
2614            MaterialSlabImpl::Bindless(material_bindless_slab) => {
2615                material_bindless_slab.bind_group()
2616            }
2617            MaterialSlabImpl::NonBindless(MaterialNonBindlessSlab::Prepared(
2618                prepared_bind_group,
2619            )) => Some(&prepared_bind_group.bind_group),
2620            MaterialSlabImpl::NonBindless(MaterialNonBindlessSlab::Unprepared) => None,
2621        }
2622    }
2623}
2624
2625impl MaterialDataBuffer {
2626    /// Creates a new [`MaterialDataBuffer`] managing a buffer of elements of
2627    /// size `aligned_element_size` that will be bound to the given binding
2628    /// number.
2629    fn new(binding_number: BindingNumber, aligned_element_size: u32) -> MaterialDataBuffer {
2630        MaterialDataBuffer {
2631            binding_number,
2632            buffer: RetainedRawBufferVec::new(BufferUsages::STORAGE),
2633            aligned_element_size,
2634            free_slots: ::alloc::vec::Vec::new()vec![],
2635            len: 0,
2636        }
2637    }
2638
2639    /// Allocates a slot for a new piece of data, copies the data into that
2640    /// slot, and returns the slot ID.
2641    ///
2642    /// The size of the piece of data supplied to this method must equal the
2643    /// [`Self::aligned_element_size`] provided to [`MaterialDataBuffer::new`].
2644    fn insert(&mut self, data: &[u8]) -> u32 {
2645        // Make sure the data is of the right length.
2646        if true {
    {
        match (&data.len(), &(self.aligned_element_size as usize)) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    let kind = ::core::panicking::AssertKind::Eq;
                    ::core::panicking::assert_failed(kind, &*left_val,
                        &*right_val, ::core::option::Option::None);
                }
            }
        }
    };
};debug_assert_eq!(data.len(), self.aligned_element_size as usize);
2647
2648        // Grab a slot.
2649        let slot = self.free_slots.pop().unwrap_or(self.len);
2650
2651        // Calculate the range we're going to copy to.
2652        let start = slot as usize * self.aligned_element_size as usize;
2653        let end = (slot as usize + 1) * self.aligned_element_size as usize;
2654
2655        // Resize the buffer if necessary.
2656        if self.buffer.len() < end {
2657            self.buffer.reserve_internal(end);
2658        }
2659        while self.buffer.values().len() < end {
2660            self.buffer.push(0);
2661        }
2662
2663        // Copy in the data.
2664        self.buffer.values_mut()[start..end].copy_from_slice(data);
2665
2666        // Mark the buffer dirty, and finish up.
2667        self.len += 1;
2668        self.buffer.dirty = BufferDirtyState::NeedsReserve;
2669        slot
2670    }
2671
2672    /// Updates the given slot with new data.
2673    fn set(&mut self, slot: u32, data: &[u8]) {
2674        // Make sure the data is of the right length.
2675        if true {
    {
        match (&data.len(), &(self.aligned_element_size as usize)) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    let kind = ::core::panicking::AssertKind::Eq;
                    ::core::panicking::assert_failed(kind, &*left_val,
                        &*right_val, ::core::option::Option::None);
                }
            }
        }
    };
};debug_assert_eq!(data.len(), self.aligned_element_size as usize);
2676
2677        // Calculate the range we're going to copy to.
2678        let start = slot as usize * self.aligned_element_size as usize;
2679        let end = (slot as usize + 1) * self.aligned_element_size as usize;
2680
2681        self.buffer.values_mut()[start..end].copy_from_slice(data);
2682
2683        if self.buffer.dirty == BufferDirtyState::Clean {
2684            self.buffer.dirty = BufferDirtyState::NeedsUpload;
2685        }
2686    }
2687
2688    /// Marks the given slot as free.
2689    fn remove(&mut self, slot: u32) {
2690        self.free_slots.push(slot);
2691        self.len -= 1;
2692    }
2693}
2694
2695/// Creates and/or recreates any bind groups that contain materials that were
2696/// modified this frame.
2697pub fn prepare_material_bind_groups(
2698    mut allocators: ResMut<MaterialBindGroupAllocators>,
2699    render_device: Res<RenderDevice>,
2700    pipeline_cache: Res<PipelineCache>,
2701    fallback_image: Res<FallbackImage>,
2702    fallback_buffer: Res<FallbackBuffer>,
2703    fallback_resources: Res<FallbackBindlessResources>,
2704    shader_buffer_assets: Res<RenderAssets<GpuShaderBuffer>>,
2705    changed_shader_buffers: Res<RenderChangedShaderBuffers>,
2706) {
2707    for (_, allocator) in allocators.iter_mut() {
2708        allocator.prepare_bind_groups(
2709            &render_device,
2710            &pipeline_cache,
2711            &fallback_resources,
2712            &fallback_image,
2713            &fallback_buffer,
2714            &shader_buffer_assets,
2715            &changed_shader_buffers,
2716        );
2717    }
2718}
2719
2720/// Uploads the contents of all buffers that the [`MaterialBindGroupAllocator`]
2721/// manages to the GPU.
2722///
2723/// Non-bindless allocators don't currently manage any buffers, so this method
2724/// only has an effect for bindless allocators.
2725pub fn write_material_bind_group_buffers(
2726    mut allocators: ResMut<MaterialBindGroupAllocators>,
2727    render_device: Res<RenderDevice>,
2728    render_queue: Res<RenderQueue>,
2729) {
2730    for (_, allocator) in allocators.iter_mut() {
2731        allocator.write_buffers(&render_device, &render_queue);
2732    }
2733}
2734
2735impl MaterialBindGroupAllocators {
2736    /// Adds a new [`MaterialBindGroupAllocator`] to this resource to manage
2737    /// materials of the given type.
2738    pub fn add<M>(&mut self, render_device: &RenderDevice)
2739    where
2740        M: AsBindGroup + 'static,
2741    {
2742        self.insert(
2743            TypeId::of::<M>(),
2744            MaterialBindGroupAllocator::new(
2745                render_device,
2746                M::label(),
2747                material_uses_bindless_resources::<M>(render_device)
2748                    .then(|| M::bindless_descriptor())
2749                    .flatten(),
2750                M::bind_group_layout_descriptor(render_device),
2751                M::bindless_slot_count(),
2752            ),
2753        );
2754    }
2755}
2756
2757impl RenderMaterialBindings {
2758    /// Prepares a material asset for the render world.
2759    ///
2760    /// This method allocates the material in the bind group allocators and
2761    /// returns its [`MaterialBindingId`].
2762    pub fn prepare_material<M>(
2763        &mut self,
2764        material: &M,
2765        material_id: AssetId<M>,
2766        material_param: &mut SystemParamItem<'_, '_, M::Param>,
2767        material_layout: &BindGroupLayoutDescriptor,
2768        bind_group_allocators: &mut MaterialBindGroupAllocators,
2769        render_device: &RenderDevice,
2770        pipeline_cache: &PipelineCache,
2771        fallback_buffer: &FallbackBuffer,
2772        shader_buffer_assets: &RenderAssets<GpuShaderBuffer>,
2773    ) -> Result<MaterialBindingId, PrepareAssetError<M>>
2774    where
2775        M: AsBindGroup + Asset + Clone,
2776    {
2777        let actual_material_layout = pipeline_cache.get_bind_group_layout(material_layout);
2778
2779        // Ask the material to build the bind group.
2780        let result = match material.build_bind_group(
2781            &actual_material_layout,
2782            render_device,
2783            material_param,
2784            false,
2785            &mut self.bind_group_builder,
2786        ) {
2787            Ok(()) => {
2788                let bind_group_allocator =
2789                    bind_group_allocators.get_mut(&TypeId::of::<M>()).unwrap();
2790                // Allocate or update the material.
2791                match self.bindings.entry(material_id.into()) {
2792                    Entry::Occupied(mut occupied_entry) => {
2793                        // First, try a fast path. If none of the GPU resource
2794                        // bindings have changed, then we can just update the
2795                        // POD and don't have to update any of the bindings in
2796                        // the bind group allocator.
2797                        let old_binding = *occupied_entry.get();
2798                        if bind_group_allocator
2799                            .try_update_data(old_binding, &mut self.bind_group_builder)
2800                        {
2801                            Ok(old_binding)
2802                        } else {
2803                            // Otherwise, fall back to the slow path. Deallocate
2804                            // the GPU resource bindings, and reallocate them.
2805                            bind_group_allocator.free(old_binding);
2806                            let new_binding = bind_group_allocator
2807                                .allocate_unprepared(&mut self.bind_group_builder, material_layout);
2808                            *occupied_entry.get_mut() = new_binding;
2809                            Ok(new_binding)
2810                        }
2811                    }
2812                    Entry::Vacant(vacant_entry) => Ok(*vacant_entry.insert(
2813                        bind_group_allocator
2814                            .allocate_unprepared(&mut self.bind_group_builder, material_layout),
2815                    )),
2816                }
2817            }
2818            Err(AsBindGroupError::RetryNextUpdate) => {
2819                Err(PrepareAssetError::RetryNextUpdate((*material).clone()))
2820            }
2821            Err(AsBindGroupError::CreateBindGroupDirectly) => {
2822                match material.as_bind_group(
2823                    material_layout,
2824                    render_device,
2825                    pipeline_cache,
2826                    fallback_buffer,
2827                    shader_buffer_assets,
2828                    material_param,
2829                ) {
2830                    Ok(prepared_bind_group) => {
2831                        let bind_group_allocator =
2832                            bind_group_allocators.get_mut(&TypeId::of::<M>()).unwrap();
2833                        // Store the resulting bind group directly in the slot.
2834                        let material_binding_id = bind_group_allocator
2835                            .allocate_prepared(prepared_bind_group, material_layout.clone());
2836                        self.insert(material_id.into(), material_binding_id);
2837                        Ok(material_binding_id)
2838                    }
2839                    Err(AsBindGroupError::RetryNextUpdate) => {
2840                        Err(PrepareAssetError::RetryNextUpdate((*material).clone()))
2841                    }
2842                    Err(other) => Err(PrepareAssetError::AsBindGroupError(other)),
2843                }
2844            }
2845            Err(other) => Err(PrepareAssetError::AsBindGroupError(other)),
2846        };
2847
2848        // Clear out the bind group builder (but retain its heap allocations; we
2849        // don't want to allocate here).
2850        self.bind_group_builder.clear();
2851
2852        result
2853    }
2854
2855    /// Removes a material asset from the render world.
2856    pub fn unload_material<M>(
2857        &mut self,
2858        source_asset: AssetId<M>,
2859        bind_group_allocators: &mut MaterialBindGroupAllocators,
2860    ) where
2861        M: Asset + AsBindGroup + Clone,
2862    {
2863        if let Some(material_binding_id) = self.remove(&source_asset.untyped()) {
2864            let bind_group_allocator = bind_group_allocators.get_mut(&TypeId::of::<M>()).unwrap();
2865            bind_group_allocator.free(material_binding_id);
2866        }
2867    }
2868}
2869
2870#[cfg(test)]
2871mod tests {
2872    use super::*;
2873    use crate::render_resource::{BindingNumber, BindlessIndexTableDescriptor};
2874
2875    #[test]
2876    fn get_bindless_index_table() {
2877        let descriptor = BindlessDescriptor {
2878            resources: Default::default(),
2879            buffers: Default::default(),
2880            index_tables: vec![
2881                BindlessIndexTableDescriptor {
2882                    indices: BindlessIndex(1)..BindlessIndex(2),
2883                    binding_number: BindingNumber(0),
2884                },
2885                BindlessIndexTableDescriptor {
2886                    indices: BindlessIndex(2)..BindlessIndex(3),
2887                    binding_number: BindingNumber(1),
2888                },
2889                BindlessIndexTableDescriptor {
2890                    indices: BindlessIndex(4)..BindlessIndex(5),
2891                    binding_number: BindingNumber(2),
2892                },
2893            ]
2894            .into(),
2895        };
2896        let slab = MaterialBindlessSlab::new(&descriptor);
2897
2898        // Out of range to the left/right is None.
2899        assert!(slab.get_bindless_index_table(BindlessIndex(0)).is_none());
2900        assert!(slab.get_bindless_index_table(BindlessIndex(5)).is_none());
2901
2902        // Between entries is None.
2903        assert!(slab.get_bindless_index_table(BindlessIndex(3)).is_none());
2904
2905        // Can find an actual entry.
2906        let table = slab.get_bindless_index_table(BindlessIndex(2)).unwrap();
2907        assert_eq!(table.binding_number, BindingNumber(1));
2908    }
2909}