Skip to main content

bevy_pbr/
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 crate::Material;
8use bevy_derive::{Deref, DerefMut};
9use bevy_ecs::{
10    resource::Resource,
11    system::{Commands, Res},
12};
13use bevy_platform::collections::{HashMap, HashSet};
14use bevy_reflect::{prelude::ReflectDefault, Reflect};
15use bevy_render::render_resource::{BindlessSlabResourceLimit, PipelineCache};
16use bevy_render::{
17    render_resource::{
18        BindGroup, BindGroupEntry, BindGroupLayoutDescriptor, BindingNumber, BindingResource,
19        BindingResources, BindlessDescriptor, BindlessIndex, BindlessIndexTableDescriptor,
20        BindlessResourceType, Buffer, BufferBinding, BufferDescriptor, BufferId,
21        BufferInitDescriptor, BufferUsages, CompareFunction, FilterMode, MipmapFilterMode,
22        OwnedBindingResource, PreparedBindGroup, RawBufferVec, Sampler, SamplerDescriptor,
23        SamplerId, TextureView, TextureViewDimension, TextureViewId, UnpreparedBindGroup,
24        WgpuSampler, WgpuTextureView,
25    },
26    renderer::{RenderDevice, RenderQueue},
27    settings::WgpuFeatures,
28    texture::FallbackImage,
29};
30use bevy_utils::{default, TypeIdMap};
31use bytemuck::{Pod, Zeroable};
32use core::hash::Hash;
33use core::{cmp::Ordering, iter, mem, ops::Range};
34use tracing::{error, trace};
35
36#[derive(Resource, Deref, DerefMut, Default)]
37pub struct MaterialBindGroupAllocators(TypeIdMap<MaterialBindGroupAllocator>);
38
39/// A resource that places materials into bind groups and tracks their
40/// resources.
41///
42/// Internally, Bevy has separate allocators for bindless and non-bindless
43/// materials. This resource provides a common interface to the specific
44/// allocator in use.
45pub enum MaterialBindGroupAllocator {
46    /// The allocator used when the material is bindless.
47    Bindless(Box<MaterialBindGroupBindlessAllocator>),
48    /// The allocator used when the material is non-bindless.
49    NonBindless(Box<MaterialBindGroupNonBindlessAllocator>),
50}
51
52/// The allocator that places bindless materials into bind groups and tracks
53/// their resources.
54pub struct MaterialBindGroupBindlessAllocator {
55    /// The label of the bind group allocator to use for allocated buffers.
56    label: &'static str,
57    /// The slabs, each of which contains a bind group.
58    slabs: Vec<MaterialBindlessSlab>,
59    /// The layout of the bind groups that we produce.
60    bind_group_layout: BindGroupLayoutDescriptor,
61    /// Information about the bindless resources in the material.
62    ///
63    /// We use this information to create and maintain bind groups.
64    bindless_descriptor: BindlessDescriptor,
65
66    /// Dummy buffers that we use to fill empty slots in buffer binding arrays.
67    ///
68    /// There's one fallback buffer for each buffer in the bind group, each
69    /// appropriately sized. Each buffer contains one uninitialized element of
70    /// the applicable type.
71    fallback_buffers: HashMap<BindlessIndex, Buffer>,
72
73    /// The maximum number of resources that can be stored in a slab.
74    ///
75    /// This corresponds to `SLAB_CAPACITY` in the `#[bindless(SLAB_CAPACITY)]`
76    /// attribute, when deriving `AsBindGroup`.
77    slab_capacity: u32,
78}
79
80/// A single bind group and the bookkeeping necessary to allocate into it.
81pub struct MaterialBindlessSlab {
82    /// The current bind group, if it's up to date.
83    ///
84    /// If this is `None`, then the bind group is dirty and needs to be
85    /// regenerated.
86    bind_group: Option<BindGroup>,
87
88    /// The GPU-accessible buffers that hold the mapping from binding index to
89    /// bindless slot.
90    ///
91    /// This is conventionally assigned to bind group binding 0, but it can be
92    /// changed using the `#[bindless(index_table(binding(B)))]` attribute on
93    /// `AsBindGroup`.
94    ///
95    /// Because the slab binary searches this table, the entries within must be
96    /// sorted by bindless index.
97    bindless_index_tables: Vec<MaterialBindlessIndexTable>,
98
99    /// The binding arrays containing samplers.
100    samplers: HashMap<BindlessResourceType, MaterialBindlessBindingArray<Sampler>>,
101    /// The binding arrays containing textures.
102    textures: HashMap<BindlessResourceType, MaterialBindlessBindingArray<TextureView>>,
103    /// The binding arrays containing buffers.
104    buffers: HashMap<BindlessIndex, MaterialBindlessBindingArray<Buffer>>,
105    /// The buffers that contain plain old data (i.e. the structure-level
106    /// `#[data]` attribute of `AsBindGroup`).
107    data_buffers: HashMap<BindlessIndex, MaterialDataBuffer>,
108
109    /// A list of free slot IDs.
110    free_slots: Vec<MaterialBindGroupSlot>,
111    /// The total number of materials currently allocated in this slab.
112    live_allocation_count: u32,
113    /// The total number of resources currently allocated in the binding arrays.
114    allocated_resource_count: u32,
115}
116
117/// A GPU-accessible buffer that holds the mapping from binding index to
118/// bindless slot.
119///
120/// This is conventionally assigned to bind group binding 0, but it can be
121/// changed by altering the [`Self::binding_number`], which corresponds to the
122/// `#[bindless(index_table(binding(B)))]` attribute in `AsBindGroup`.
123struct MaterialBindlessIndexTable {
124    /// The buffer containing the mappings.
125    buffer: RetainedRawBufferVec<u32>,
126    /// The range of bindless indices that this bindless index table covers.
127    ///
128    /// If this range is M..N, then the field at index $i$ maps to bindless
129    /// index $i$ + M. The size of this table is N - M.
130    ///
131    /// This corresponds to the `#[bindless(index_table(range(M..N)))]`
132    /// attribute in `AsBindGroup`.
133    index_range: Range<BindlessIndex>,
134    /// The binding number that this index table is assigned to in the shader.
135    binding_number: BindingNumber,
136}
137
138/// A single binding array for storing bindless resources and the bookkeeping
139/// necessary to allocate into it.
140struct MaterialBindlessBindingArray<R>
141where
142    R: GetBindingResourceId,
143{
144    /// The number of the binding that we attach this binding array to.
145    binding_number: BindingNumber,
146    /// A mapping from bindless slot index to the resource stored in that slot,
147    /// if any.
148    bindings: Vec<Option<MaterialBindlessBinding<R>>>,
149    /// The type of resource stored in this binding array.
150    resource_type: BindlessResourceType,
151    /// Maps a resource ID to the slot in which it's stored.
152    ///
153    /// This is essentially the inverse mapping of [`Self::bindings`].
154    resource_to_slot: HashMap<BindingResourceId, u32>,
155    /// A list of free slots in [`Self::bindings`] that contain no binding.
156    free_slots: Vec<u32>,
157    /// The number of allocated objects in this binding array.
158    len: u32,
159}
160
161/// A single resource (sampler, texture, or buffer) in a binding array.
162///
163/// Resources hold a reference count, which specifies the number of materials
164/// currently allocated within the slab that refer to this resource. When the
165/// reference count drops to zero, the resource is freed.
166struct MaterialBindlessBinding<R>
167where
168    R: GetBindingResourceId,
169{
170    /// The sampler, texture, or buffer.
171    resource: R,
172    /// The number of materials currently allocated within the containing slab
173    /// that use this resource.
174    ref_count: u32,
175}
176
177/// The allocator that stores bind groups for non-bindless materials.
178pub struct MaterialBindGroupNonBindlessAllocator {
179    /// The label of the bind group allocator to use for allocated buffers.
180    label: &'static str,
181    /// A mapping from [`MaterialBindGroupIndex`] to the bind group allocated in
182    /// each slot.
183    bind_groups: Vec<Option<MaterialNonBindlessAllocatedBindGroup>>,
184    /// The bind groups that are dirty and need to be prepared.
185    ///
186    /// To prepare the bind groups, call
187    /// [`MaterialBindGroupAllocator::prepare_bind_groups`].
188    to_prepare: HashSet<MaterialBindGroupIndex>,
189    /// A list of free bind group indices.
190    free_indices: Vec<MaterialBindGroupIndex>,
191}
192
193/// A single bind group that a [`MaterialBindGroupNonBindlessAllocator`] is
194/// currently managing.
195enum MaterialNonBindlessAllocatedBindGroup {
196    /// An unprepared bind group.
197    ///
198    /// The allocator prepares all outstanding unprepared bind groups when
199    /// [`MaterialBindGroupNonBindlessAllocator::prepare_bind_groups`] is
200    /// called.
201    Unprepared {
202        /// The unprepared bind group, including extra data.
203        bind_group: UnpreparedBindGroup,
204        /// The layout of that bind group.
205        layout: BindGroupLayoutDescriptor,
206    },
207    /// A bind group that's already been prepared.
208    Prepared {
209        bind_group: PreparedBindGroup,
210        #[expect(dead_code, reason = "These buffers are only referenced by bind groups")]
211        uniform_buffers: Vec<Buffer>,
212    },
213}
214
215/// Dummy instances of various resources that we fill unused slots in binding
216/// arrays with.
217#[derive(Resource)]
218pub struct FallbackBindlessResources {
219    /// A dummy filtering sampler.
220    filtering_sampler: Sampler,
221    /// A dummy non-filtering sampler.
222    non_filtering_sampler: Sampler,
223    /// A dummy comparison sampler.
224    comparison_sampler: Sampler,
225}
226
227/// The `wgpu` ID of a single bindless or non-bindless resource.
228#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
229enum BindingResourceId {
230    /// A buffer.
231    Buffer(BufferId),
232    /// A texture view, with the given dimension.
233    TextureView(TextureViewDimension, TextureViewId),
234    /// A sampler.
235    Sampler(SamplerId),
236    /// A buffer containing plain old data.
237    ///
238    /// This corresponds to the `#[data]` structure-level attribute on
239    /// `AsBindGroup`.
240    DataBuffer,
241}
242
243/// A temporary list of references to `wgpu` bindless resources.
244///
245/// We need this because the `wgpu` bindless API takes a slice of references.
246/// Thus we need to create intermediate vectors of bindless resources in order
247/// to satisfy `wgpu`'s lifetime requirements.
248enum BindingResourceArray<'a> {
249    /// A list of bindings.
250    Buffers(Vec<BufferBinding<'a>>),
251    /// A list of texture views.
252    TextureViews(Vec<&'a WgpuTextureView>),
253    /// A list of samplers.
254    Samplers(Vec<&'a WgpuSampler>),
255}
256
257/// The location of a material (either bindless or non-bindless) within the
258/// slabs.
259#[derive(Clone, Copy, Debug, Default, Pod, Zeroable, Reflect)]
260#[reflect(Clone, Default)]
261#[repr(C)]
262pub struct MaterialBindingId {
263    /// The index of the bind group (slab) where the GPU data is located.
264    pub group: MaterialBindGroupIndex,
265    /// The slot within that bind group.
266    ///
267    /// Non-bindless materials will always have a slot of 0.
268    pub slot: MaterialBindGroupSlot,
269}
270
271/// The index of each material bind group.
272///
273/// In bindless mode, each bind group contains multiple materials. In
274/// non-bindless mode, each bind group contains only one material.
275#[derive(
276    Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Pod, Zeroable, Reflect, Deref, DerefMut,
277)]
278#[reflect(Default, Clone, PartialEq, Hash)]
279#[repr(C)]
280pub struct MaterialBindGroupIndex(pub u32);
281
282impl From<u32> for MaterialBindGroupIndex {
283    fn from(value: u32) -> Self {
284        MaterialBindGroupIndex(value)
285    }
286}
287
288/// The index of the slot containing material data within each material bind
289/// group.
290///
291/// In bindless mode, this slot is needed to locate the material data in each
292/// bind group, since multiple materials are packed into a single slab. In
293/// non-bindless mode, this slot is always 0.
294#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable, Reflect, Deref, DerefMut)]
295#[reflect(Default, Clone, PartialEq)]
296#[repr(C)]
297pub struct MaterialBindGroupSlot(pub u32);
298
299/// The CPU/GPU synchronization state of a buffer that we maintain.
300///
301/// Currently, the only buffer that we maintain is the
302/// [`MaterialBindlessIndexTable`].
303enum BufferDirtyState {
304    /// The buffer is currently synchronized between the CPU and GPU.
305    Clean,
306    /// The buffer hasn't been created yet.
307    NeedsReserve,
308    /// The buffer exists on both CPU and GPU, but the GPU data is out of date.
309    NeedsUpload,
310}
311
312/// Information that describes a potential allocation of an
313/// [`UnpreparedBindGroup`] into a slab.
314struct BindlessAllocationCandidate {
315    /// A map that, for every resource in the [`UnpreparedBindGroup`] that
316    /// already existed in this slab, maps bindless index of that resource to
317    /// its slot in the appropriate binding array.
318    pre_existing_resources: HashMap<BindlessIndex, u32>,
319    /// Stores the number of free slots that are needed to satisfy this
320    /// allocation.
321    needed_free_slots: u32,
322}
323
324/// A trait that allows fetching the [`BindingResourceId`] from a
325/// [`BindlessResourceType`].
326///
327/// This is used when freeing bindless resources, in order to locate the IDs
328/// assigned to each resource so that they can be removed from the appropriate
329/// maps.
330trait GetBindingResourceId {
331    /// Returns the [`BindingResourceId`] for this resource.
332    ///
333    /// `resource_type` specifies this resource's type. This is used for
334    /// textures, as a `wgpu` [`TextureView`] doesn't store enough information
335    /// itself to determine its dimension.
336    fn binding_resource_id(&self, resource_type: BindlessResourceType) -> BindingResourceId;
337}
338
339/// The public interface to a slab, which represents a single bind group.
340pub struct MaterialSlab<'a>(MaterialSlabImpl<'a>);
341
342/// The actual implementation of a material slab.
343///
344/// This has bindless and non-bindless variants.
345enum MaterialSlabImpl<'a> {
346    /// The implementation of the slab interface we use when the slab
347    /// is bindless.
348    Bindless(&'a MaterialBindlessSlab),
349    /// The implementation of the slab interface we use when the slab
350    /// is non-bindless.
351    NonBindless(MaterialNonBindlessSlab<'a>),
352}
353
354/// A single bind group that the [`MaterialBindGroupNonBindlessAllocator`]
355/// manages.
356enum MaterialNonBindlessSlab<'a> {
357    /// A slab that has a bind group.
358    Prepared(&'a PreparedBindGroup),
359    /// A slab that doesn't yet have a bind group.
360    Unprepared,
361}
362
363/// Manages an array of untyped plain old data on GPU and allocates individual
364/// slots within that array.
365///
366/// This supports the `#[data]` attribute of `AsBindGroup`.
367struct MaterialDataBuffer {
368    /// The number of the binding that we attach this storage buffer to.
369    binding_number: BindingNumber,
370    /// The actual data.
371    ///
372    /// Note that this is untyped (`u8`); the actual aligned size of each
373    /// element is given by [`Self::aligned_element_size`];
374    buffer: RetainedRawBufferVec<u8>,
375    /// The size of each element in the buffer, including padding and alignment
376    /// if any.
377    aligned_element_size: u32,
378    /// A list of free slots within the buffer.
379    free_slots: Vec<u32>,
380    /// The actual number of slots that have been allocated.
381    len: u32,
382}
383
384/// A buffer containing plain old data, already packed into the appropriate GPU
385/// format, and that can be updated incrementally.
386///
387/// This structure exists in order to encapsulate the lazy update
388/// ([`BufferDirtyState`]) logic in a single place.
389#[derive(Deref, DerefMut)]
390struct RetainedRawBufferVec<T>
391where
392    T: Pod,
393{
394    /// The contents of the buffer.
395    #[deref]
396    buffer: RawBufferVec<T>,
397    /// Whether the contents of the buffer have been uploaded to the GPU.
398    dirty: BufferDirtyState,
399}
400
401/// The size of the buffer that we assign to unused buffer slots, in bytes.
402///
403/// This is essentially arbitrary, as it doesn't seem to matter to `wgpu` what
404/// the size is.
405const DEFAULT_BINDLESS_FALLBACK_BUFFER_SIZE: u64 = 16;
406
407impl From<u32> for MaterialBindGroupSlot {
408    fn from(value: u32) -> Self {
409        MaterialBindGroupSlot(value)
410    }
411}
412
413impl From<MaterialBindGroupSlot> for u32 {
414    fn from(value: MaterialBindGroupSlot) -> Self {
415        value.0
416    }
417}
418
419impl<'a> From<&'a OwnedBindingResource> for BindingResourceId {
420    fn from(value: &'a OwnedBindingResource) -> Self {
421        match *value {
422            OwnedBindingResource::Buffer(ref buffer) => BindingResourceId::Buffer(buffer.id()),
423            OwnedBindingResource::Data(_) => BindingResourceId::DataBuffer,
424            OwnedBindingResource::TextureView(ref texture_view_dimension, ref texture_view) => {
425                BindingResourceId::TextureView(*texture_view_dimension, texture_view.id())
426            }
427            OwnedBindingResource::Sampler(_, ref sampler) => {
428                BindingResourceId::Sampler(sampler.id())
429            }
430        }
431    }
432}
433
434impl GetBindingResourceId for Buffer {
435    fn binding_resource_id(&self, _: BindlessResourceType) -> BindingResourceId {
436        BindingResourceId::Buffer(self.id())
437    }
438}
439
440impl GetBindingResourceId for Sampler {
441    fn binding_resource_id(&self, _: BindlessResourceType) -> BindingResourceId {
442        BindingResourceId::Sampler(self.id())
443    }
444}
445
446impl GetBindingResourceId for TextureView {
447    fn binding_resource_id(&self, resource_type: BindlessResourceType) -> BindingResourceId {
448        let texture_view_dimension = match resource_type {
449            BindlessResourceType::Texture1d => TextureViewDimension::D1,
450            BindlessResourceType::Texture2d => TextureViewDimension::D2,
451            BindlessResourceType::Texture2dArray => TextureViewDimension::D2Array,
452            BindlessResourceType::Texture3d => TextureViewDimension::D3,
453            BindlessResourceType::TextureCube => TextureViewDimension::Cube,
454            BindlessResourceType::TextureCubeArray => TextureViewDimension::CubeArray,
455            _ => panic!("Resource type is not a texture"),
456        };
457        BindingResourceId::TextureView(texture_view_dimension, self.id())
458    }
459}
460
461impl MaterialBindGroupAllocator {
462    /// Creates a new [`MaterialBindGroupAllocator`] managing the data for a
463    /// single material.
464    pub fn new(
465        render_device: &RenderDevice,
466        label: &'static str,
467        bindless_descriptor: Option<BindlessDescriptor>,
468        bind_group_layout: BindGroupLayoutDescriptor,
469        slab_capacity: Option<BindlessSlabResourceLimit>,
470    ) -> MaterialBindGroupAllocator {
471        if let Some(bindless_descriptor) = bindless_descriptor {
472            MaterialBindGroupAllocator::Bindless(Box::new(MaterialBindGroupBindlessAllocator::new(
473                render_device,
474                label,
475                bindless_descriptor,
476                bind_group_layout,
477                slab_capacity,
478            )))
479        } else {
480            MaterialBindGroupAllocator::NonBindless(Box::new(
481                MaterialBindGroupNonBindlessAllocator::new(label),
482            ))
483        }
484    }
485
486    /// Returns the slab with the given index, if one exists.
487    pub fn get(&self, group: MaterialBindGroupIndex) -> Option<MaterialSlab<'_>> {
488        match *self {
489            MaterialBindGroupAllocator::Bindless(ref bindless_allocator) => bindless_allocator
490                .get(group)
491                .map(|bindless_slab| MaterialSlab(MaterialSlabImpl::Bindless(bindless_slab))),
492            MaterialBindGroupAllocator::NonBindless(ref non_bindless_allocator) => {
493                non_bindless_allocator.get(group).map(|non_bindless_slab| {
494                    MaterialSlab(MaterialSlabImpl::NonBindless(non_bindless_slab))
495                })
496            }
497        }
498    }
499
500    /// Allocates an [`UnpreparedBindGroup`] and returns the resulting binding ID.
501    ///
502    /// This method should generally be preferred over
503    /// [`Self::allocate_prepared`], because this method supports both bindless
504    /// and non-bindless bind groups. Only use [`Self::allocate_prepared`] if
505    /// you need to prepare the bind group yourself.
506    pub fn allocate_unprepared(
507        &mut self,
508        unprepared_bind_group: UnpreparedBindGroup,
509        bind_group_layout: &BindGroupLayoutDescriptor,
510    ) -> MaterialBindingId {
511        match *self {
512            MaterialBindGroupAllocator::Bindless(
513                ref mut material_bind_group_bindless_allocator,
514            ) => material_bind_group_bindless_allocator.allocate_unprepared(unprepared_bind_group),
515            MaterialBindGroupAllocator::NonBindless(
516                ref mut material_bind_group_non_bindless_allocator,
517            ) => material_bind_group_non_bindless_allocator
518                .allocate_unprepared(unprepared_bind_group, (*bind_group_layout).clone()),
519        }
520    }
521
522    /// Places a pre-prepared bind group into a slab.
523    ///
524    /// For bindless materials, the allocator internally manages the bind
525    /// groups, so calling this method will panic if this is a bindless
526    /// allocator. Only non-bindless allocators support this method.
527    ///
528    /// It's generally preferred to use [`Self::allocate_unprepared`], because
529    /// that method supports both bindless and non-bindless allocators. Only use
530    /// this method if you need to prepare the bind group yourself.
531    pub fn allocate_prepared(
532        &mut self,
533        prepared_bind_group: PreparedBindGroup,
534    ) -> MaterialBindingId {
535        match *self {
536            MaterialBindGroupAllocator::Bindless(_) => {
537                panic!(
538                    "Bindless resources are incompatible with implementing `as_bind_group` \
539                     directly; implement `unprepared_bind_group` instead or disable bindless"
540                )
541            }
542            MaterialBindGroupAllocator::NonBindless(ref mut non_bindless_allocator) => {
543                non_bindless_allocator.allocate_prepared(prepared_bind_group)
544            }
545        }
546    }
547
548    /// Deallocates the material with the given binding ID.
549    ///
550    /// Any resources that are no longer referenced are removed from the slab.
551    pub fn free(&mut self, material_binding_id: MaterialBindingId) {
552        match *self {
553            MaterialBindGroupAllocator::Bindless(
554                ref mut material_bind_group_bindless_allocator,
555            ) => material_bind_group_bindless_allocator.free(material_binding_id),
556            MaterialBindGroupAllocator::NonBindless(
557                ref mut material_bind_group_non_bindless_allocator,
558            ) => material_bind_group_non_bindless_allocator.free(material_binding_id),
559        }
560    }
561
562    /// Recreates any bind groups corresponding to slabs that have been modified
563    /// since last calling [`MaterialBindGroupAllocator::prepare_bind_groups`].
564    pub fn prepare_bind_groups(
565        &mut self,
566        render_device: &RenderDevice,
567        pipeline_cache: &PipelineCache,
568        fallback_bindless_resources: &FallbackBindlessResources,
569        fallback_image: &FallbackImage,
570    ) {
571        match *self {
572            MaterialBindGroupAllocator::Bindless(
573                ref mut material_bind_group_bindless_allocator,
574            ) => material_bind_group_bindless_allocator.prepare_bind_groups(
575                render_device,
576                pipeline_cache,
577                fallback_bindless_resources,
578                fallback_image,
579            ),
580            MaterialBindGroupAllocator::NonBindless(
581                ref mut material_bind_group_non_bindless_allocator,
582            ) => material_bind_group_non_bindless_allocator
583                .prepare_bind_groups(render_device, pipeline_cache),
584        }
585    }
586
587    /// Uploads the contents of all buffers that this
588    /// [`MaterialBindGroupAllocator`] manages to the GPU.
589    ///
590    /// Non-bindless allocators don't currently manage any buffers, so this
591    /// method only has an effect for bindless allocators.
592    pub fn write_buffers(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
593        match *self {
594            MaterialBindGroupAllocator::Bindless(
595                ref mut material_bind_group_bindless_allocator,
596            ) => material_bind_group_bindless_allocator.write_buffers(render_device, render_queue),
597            MaterialBindGroupAllocator::NonBindless(_) => {
598                // Not applicable.
599            }
600        }
601    }
602
603    /// Get number of allocated slabs for bindless material, returns 0 if it is
604    /// [`Self::NonBindless`].
605    pub fn slab_count(&self) -> usize {
606        match self {
607            Self::Bindless(bless) => bless.slabs.len(),
608            Self::NonBindless(_) => 0,
609        }
610    }
611
612    /// Get total size of slabs allocated for bindless material, returns 0 if it is
613    /// [`Self::NonBindless`].
614    pub fn slabs_size(&self) -> usize {
615        match self {
616            Self::Bindless(bless) => bless
617                .slabs
618                .iter()
619                .flat_map(|slab| {
620                    slab.data_buffers
621                        .iter()
622                        .map(|(_, buffer)| buffer.buffer.len())
623                })
624                .sum(),
625            Self::NonBindless(_) => 0,
626        }
627    }
628
629    /// Get number of bindless material allocations in slabs, returns 0 if it is
630    /// [`Self::NonBindless`].
631    pub fn allocations(&self) -> u64 {
632        match self {
633            Self::Bindless(bless) => bless
634                .slabs
635                .iter()
636                .map(|slab| u64::from(slab.allocated_resource_count))
637                .sum(),
638            Self::NonBindless(_) => 0,
639        }
640    }
641}
642
643impl MaterialBindlessIndexTable {
644    /// Creates a new [`MaterialBindlessIndexTable`] for a single slab.
645    fn new(
646        bindless_index_table_descriptor: &BindlessIndexTableDescriptor,
647    ) -> MaterialBindlessIndexTable {
648        // Preallocate space for one bindings table, so that there will always be a buffer.
649        let mut buffer = RetainedRawBufferVec::new(BufferUsages::STORAGE);
650        for _ in *bindless_index_table_descriptor.indices.start
651            ..*bindless_index_table_descriptor.indices.end
652        {
653            buffer.push(0);
654        }
655
656        MaterialBindlessIndexTable {
657            buffer,
658            index_range: bindless_index_table_descriptor.indices.clone(),
659            binding_number: bindless_index_table_descriptor.binding_number,
660        }
661    }
662
663    /// Returns the bindings in the binding index table.
664    ///
665    /// If the current [`MaterialBindlessIndexTable::index_range`] is M..N, then
666    /// element *i* of the returned binding index table contains the slot of the
667    /// bindless resource with bindless index *i* + M.
668    fn get(&self, slot: MaterialBindGroupSlot) -> &[u32] {
669        let struct_size = *self.index_range.end as usize - *self.index_range.start as usize;
670        let start = struct_size * slot.0 as usize;
671        &self.buffer.values()[start..(start + struct_size)]
672    }
673
674    /// Returns a single binding from the binding index table.
675    fn get_binding(
676        &self,
677        slot: MaterialBindGroupSlot,
678        bindless_index: BindlessIndex,
679    ) -> Option<u32> {
680        if bindless_index < self.index_range.start || bindless_index >= self.index_range.end {
681            return None;
682        }
683        self.get(slot)
684            .get((*bindless_index - *self.index_range.start) as usize)
685            .copied()
686    }
687
688    fn table_length(&self) -> u32 {
689        self.index_range.end.0 - self.index_range.start.0
690    }
691
692    /// Updates the binding index table for a single material.
693    ///
694    /// The `allocated_resource_slots` map contains a mapping from the
695    /// [`BindlessIndex`] of each resource that the material references to the
696    /// slot that that resource occupies in the appropriate binding array. This
697    /// method serializes that map into a binding index table that the shader
698    /// can read.
699    fn set(
700        &mut self,
701        slot: MaterialBindGroupSlot,
702        allocated_resource_slots: &HashMap<BindlessIndex, u32>,
703    ) {
704        let table_len = self.table_length() as usize;
705        let range = (slot.0 as usize * table_len)..((slot.0 as usize + 1) * table_len);
706        while self.buffer.len() < range.end {
707            self.buffer.push(0);
708        }
709
710        for (&bindless_index, &resource_slot) in allocated_resource_slots {
711            if self.index_range.contains(&bindless_index) {
712                self.buffer.set(
713                    *bindless_index + range.start as u32 - *self.index_range.start,
714                    resource_slot,
715                );
716            }
717        }
718
719        // Mark the buffer as needing to be recreated, in case we grew it.
720        self.buffer.dirty = BufferDirtyState::NeedsReserve;
721    }
722
723    /// Returns the [`BindGroupEntry`] for the index table itself.
724    fn bind_group_entry(&self) -> BindGroupEntry<'_> {
725        BindGroupEntry {
726            binding: *self.binding_number,
727            resource: self
728                .buffer
729                .buffer()
730                .expect("Bindings buffer must exist")
731                .as_entire_binding(),
732        }
733    }
734}
735
736impl<T> RetainedRawBufferVec<T>
737where
738    T: Pod,
739{
740    /// Creates a new empty [`RetainedRawBufferVec`] supporting the given
741    /// [`BufferUsages`].
742    fn new(buffer_usages: BufferUsages) -> RetainedRawBufferVec<T> {
743        RetainedRawBufferVec {
744            buffer: RawBufferVec::new(buffer_usages),
745            dirty: BufferDirtyState::NeedsUpload,
746        }
747    }
748
749    /// Recreates the GPU backing buffer if needed.
750    fn prepare(&mut self, render_device: &RenderDevice) {
751        match self.dirty {
752            BufferDirtyState::Clean | BufferDirtyState::NeedsUpload => {}
753            BufferDirtyState::NeedsReserve => {
754                let capacity = self.buffer.len();
755                self.buffer.reserve(capacity, render_device);
756                self.dirty = BufferDirtyState::NeedsUpload;
757            }
758        }
759    }
760
761    /// Writes the current contents of the buffer to the GPU if necessary.
762    fn write(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
763        match self.dirty {
764            BufferDirtyState::Clean => {}
765            BufferDirtyState::NeedsReserve | BufferDirtyState::NeedsUpload => {
766                self.buffer.write_buffer(render_device, render_queue);
767                self.dirty = BufferDirtyState::Clean;
768            }
769        }
770    }
771}
772
773impl MaterialBindGroupBindlessAllocator {
774    /// Creates a new [`MaterialBindGroupBindlessAllocator`] managing the data
775    /// for a single bindless material.
776    fn new(
777        render_device: &RenderDevice,
778        label: &'static str,
779        bindless_descriptor: BindlessDescriptor,
780        bind_group_layout: BindGroupLayoutDescriptor,
781        slab_capacity: Option<BindlessSlabResourceLimit>,
782    ) -> MaterialBindGroupBindlessAllocator {
783        let fallback_buffers = bindless_descriptor
784            .buffers
785            .iter()
786            .map(|bindless_buffer_descriptor| {
787                (
788                    bindless_buffer_descriptor.bindless_index,
789                    render_device.create_buffer(&BufferDescriptor {
790                        label: Some("bindless fallback buffer"),
791                        size: match bindless_buffer_descriptor.size {
792                            Some(size) => size as u64,
793                            None => DEFAULT_BINDLESS_FALLBACK_BUFFER_SIZE,
794                        },
795                        usage: BufferUsages::STORAGE,
796                        mapped_at_creation: false,
797                    }),
798                )
799            })
800            .collect();
801
802        MaterialBindGroupBindlessAllocator {
803            label,
804            slabs: vec![],
805            bind_group_layout,
806            bindless_descriptor,
807            fallback_buffers,
808            slab_capacity: slab_capacity
809                .expect("Non-bindless materials should use the non-bindless allocator")
810                .resolve(),
811        }
812    }
813
814    /// Allocates the resources for a single material into a slab and returns
815    /// the resulting ID.
816    ///
817    /// The returned [`MaterialBindingId`] can later be used to fetch the slab
818    /// that was used.
819    ///
820    /// This function can't fail. If all slabs are full, then a new slab is
821    /// created, and the material is allocated into it.
822    fn allocate_unprepared(
823        &mut self,
824        mut unprepared_bind_group: UnpreparedBindGroup,
825    ) -> MaterialBindingId {
826        for (slab_index, slab) in self.slabs.iter_mut().enumerate() {
827            trace!("Trying to allocate in slab {}", slab_index);
828            match slab.try_allocate(unprepared_bind_group, self.slab_capacity) {
829                Ok(slot) => {
830                    return MaterialBindingId {
831                        group: MaterialBindGroupIndex(slab_index as u32),
832                        slot,
833                    };
834                }
835                Err(bind_group) => unprepared_bind_group = bind_group,
836            }
837        }
838
839        let group = MaterialBindGroupIndex(self.slabs.len() as u32);
840        self.slabs
841            .push(MaterialBindlessSlab::new(&self.bindless_descriptor));
842
843        // Allocate into the newly-pushed slab.
844        let Ok(slot) = self
845            .slabs
846            .last_mut()
847            .expect("We just pushed a slab")
848            .try_allocate(unprepared_bind_group, self.slab_capacity)
849        else {
850            panic!("An allocation into an empty slab should always succeed")
851        };
852
853        MaterialBindingId { group, slot }
854    }
855
856    /// Deallocates the material with the given binding ID.
857    ///
858    /// Any resources that are no longer referenced are removed from the slab.
859    fn free(&mut self, material_binding_id: MaterialBindingId) {
860        self.slabs
861            .get_mut(material_binding_id.group.0 as usize)
862            .expect("Slab should exist")
863            .free(material_binding_id.slot, &self.bindless_descriptor);
864    }
865
866    /// Returns the slab with the given bind group index.
867    ///
868    /// A [`MaterialBindGroupIndex`] can be fetched from a
869    /// [`MaterialBindingId`].
870    fn get(&self, group: MaterialBindGroupIndex) -> Option<&MaterialBindlessSlab> {
871        self.slabs.get(group.0 as usize)
872    }
873
874    /// Recreates any bind groups corresponding to slabs that have been modified
875    /// since last calling
876    /// [`MaterialBindGroupBindlessAllocator::prepare_bind_groups`].
877    fn prepare_bind_groups(
878        &mut self,
879        render_device: &RenderDevice,
880        pipeline_cache: &PipelineCache,
881        fallback_bindless_resources: &FallbackBindlessResources,
882        fallback_image: &FallbackImage,
883    ) {
884        for slab in &mut self.slabs {
885            slab.prepare(
886                render_device,
887                pipeline_cache,
888                self.label,
889                &self.bind_group_layout,
890                fallback_bindless_resources,
891                &self.fallback_buffers,
892                fallback_image,
893                &self.bindless_descriptor,
894                self.slab_capacity,
895            );
896        }
897    }
898
899    /// Writes any buffers that we're managing to the GPU.
900    ///
901    /// Currently, this only consists of the bindless index tables.
902    fn write_buffers(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
903        for slab in &mut self.slabs {
904            slab.write_buffer(render_device, render_queue);
905        }
906    }
907}
908
909impl MaterialBindlessSlab {
910    /// Attempts to allocate the given unprepared bind group in this slab.
911    ///
912    /// If the allocation succeeds, this method returns the slot that the
913    /// allocation was placed in. If the allocation fails because the slab was
914    /// full, this method returns the unprepared bind group back to the caller
915    /// so that it can try to allocate again.
916    fn try_allocate(
917        &mut self,
918        unprepared_bind_group: UnpreparedBindGroup,
919        slot_capacity: u32,
920    ) -> Result<MaterialBindGroupSlot, UnpreparedBindGroup> {
921        // Locate pre-existing resources, and determine how many free slots we need.
922        let Some(allocation_candidate) = self.check_allocation(&unprepared_bind_group) else {
923            return Err(unprepared_bind_group);
924        };
925
926        // Check to see if we have enough free space.
927        //
928        // As a special case, note that if *nothing* is allocated in this slab,
929        // then we always allow a material to be placed in it, regardless of the
930        // number of bindings the material has. This is so that, if the
931        // platform's maximum bindless count is set too low to hold even a
932        // single material, we can still place each material into a separate
933        // slab instead of failing outright.
934        if self.allocated_resource_count > 0
935            && self.allocated_resource_count + allocation_candidate.needed_free_slots
936                > slot_capacity
937        {
938            trace!("Slab is full, can't allocate");
939            return Err(unprepared_bind_group);
940        }
941
942        // OK, we can allocate in this slab. Assign a slot ID.
943        let slot = match self.free_slots.pop() {
944            Some(slot) => slot,
945            None => {
946                // The material bind group slot is packed into 16 bits on
947                // the GPU, so spill to a new slab before we would overflow.
948                if self.live_allocation_count > 0xFFFF {
949                    trace!("Slab material bind group slot would overflow, can't allocate");
950                    return Err(unprepared_bind_group);
951                }
952                MaterialBindGroupSlot(self.live_allocation_count)
953            }
954        };
955
956        // Bump the live allocation count.
957        self.live_allocation_count += 1;
958
959        // Insert the resources into the binding arrays.
960        let allocated_resource_slots =
961            self.insert_resources(unprepared_bind_group.bindings, allocation_candidate);
962
963        // Serialize the allocated resource slots.
964        for bindless_index_table in &mut self.bindless_index_tables {
965            bindless_index_table.set(slot, &allocated_resource_slots);
966        }
967
968        // Invalidate the cached bind group.
969        self.bind_group = None;
970
971        Ok(slot)
972    }
973
974    /// Gathers the information needed to determine whether the given unprepared
975    /// bind group can be allocated in this slab.
976    fn check_allocation(
977        &self,
978        unprepared_bind_group: &UnpreparedBindGroup,
979    ) -> Option<BindlessAllocationCandidate> {
980        let mut allocation_candidate = BindlessAllocationCandidate {
981            pre_existing_resources: HashMap::default(),
982            needed_free_slots: 0,
983        };
984
985        for &(bindless_index, ref owned_binding_resource) in unprepared_bind_group.bindings.iter() {
986            let bindless_index = BindlessIndex(bindless_index);
987            match *owned_binding_resource {
988                OwnedBindingResource::Buffer(ref buffer) => {
989                    let Some(binding_array) = self.buffers.get(&bindless_index) else {
990                        error!(
991                            "Binding array wasn't present for buffer at index {:?}",
992                            bindless_index
993                        );
994                        return None;
995                    };
996                    match binding_array.find(BindingResourceId::Buffer(buffer.id())) {
997                        Some(slot) => {
998                            allocation_candidate
999                                .pre_existing_resources
1000                                .insert(bindless_index, slot);
1001                        }
1002                        None => allocation_candidate.needed_free_slots += 1,
1003                    }
1004                }
1005
1006                OwnedBindingResource::Data(_) => {
1007                    // The size of a data buffer is unlimited.
1008                }
1009
1010                OwnedBindingResource::TextureView(texture_view_dimension, ref texture_view) => {
1011                    let bindless_resource_type = BindlessResourceType::from(texture_view_dimension);
1012                    match self
1013                        .textures
1014                        .get(&bindless_resource_type)
1015                        .expect("Missing binding array for texture")
1016                        .find(BindingResourceId::TextureView(
1017                            texture_view_dimension,
1018                            texture_view.id(),
1019                        )) {
1020                        Some(slot) => {
1021                            allocation_candidate
1022                                .pre_existing_resources
1023                                .insert(bindless_index, slot);
1024                        }
1025                        None => {
1026                            allocation_candidate.needed_free_slots += 1;
1027                        }
1028                    }
1029                }
1030
1031                OwnedBindingResource::Sampler(sampler_binding_type, ref sampler) => {
1032                    let bindless_resource_type = BindlessResourceType::from(sampler_binding_type);
1033                    match self
1034                        .samplers
1035                        .get(&bindless_resource_type)
1036                        .expect("Missing binding array for sampler")
1037                        .find(BindingResourceId::Sampler(sampler.id()))
1038                    {
1039                        Some(slot) => {
1040                            allocation_candidate
1041                                .pre_existing_resources
1042                                .insert(bindless_index, slot);
1043                        }
1044                        None => {
1045                            allocation_candidate.needed_free_slots += 1;
1046                        }
1047                    }
1048                }
1049            }
1050        }
1051
1052        Some(allocation_candidate)
1053    }
1054
1055    /// Inserts the given [`BindingResources`] into this slab.
1056    ///
1057    /// Returns a table that maps the bindless index of each resource to its
1058    /// slot in its binding array.
1059    fn insert_resources(
1060        &mut self,
1061        mut binding_resources: BindingResources,
1062        allocation_candidate: BindlessAllocationCandidate,
1063    ) -> HashMap<BindlessIndex, u32> {
1064        let mut allocated_resource_slots = HashMap::default();
1065
1066        for (bindless_index, owned_binding_resource) in binding_resources.drain(..) {
1067            let bindless_index = BindlessIndex(bindless_index);
1068
1069            let pre_existing_slot = allocation_candidate
1070                .pre_existing_resources
1071                .get(&bindless_index);
1072
1073            // Otherwise, we need to insert it anew.
1074            let binding_resource_id = BindingResourceId::from(&owned_binding_resource);
1075            let increment_allocated_resource_count = match owned_binding_resource {
1076                OwnedBindingResource::Buffer(buffer) => {
1077                    let slot = self
1078                        .buffers
1079                        .get_mut(&bindless_index)
1080                        .expect("Buffer binding array should exist")
1081                        .insert(binding_resource_id, buffer);
1082                    allocated_resource_slots.insert(bindless_index, slot);
1083
1084                    if let Some(pre_existing_slot) = pre_existing_slot {
1085                        assert_eq!(*pre_existing_slot, slot);
1086
1087                        false
1088                    } else {
1089                        true
1090                    }
1091                }
1092                OwnedBindingResource::Data(data) => {
1093                    if pre_existing_slot.is_some() {
1094                        panic!("Data buffers can't be deduplicated")
1095                    }
1096
1097                    let slot = self
1098                        .data_buffers
1099                        .get_mut(&bindless_index)
1100                        .expect("Data buffer binding array should exist")
1101                        .insert(&data);
1102                    allocated_resource_slots.insert(bindless_index, slot);
1103                    false
1104                }
1105                OwnedBindingResource::TextureView(texture_view_dimension, texture_view) => {
1106                    let bindless_resource_type = BindlessResourceType::from(texture_view_dimension);
1107                    let slot = self
1108                        .textures
1109                        .get_mut(&bindless_resource_type)
1110                        .expect("Texture array should exist")
1111                        .insert(binding_resource_id, texture_view);
1112                    allocated_resource_slots.insert(bindless_index, slot);
1113
1114                    if let Some(pre_existing_slot) = pre_existing_slot {
1115                        assert_eq!(*pre_existing_slot, slot);
1116
1117                        false
1118                    } else {
1119                        true
1120                    }
1121                }
1122                OwnedBindingResource::Sampler(sampler_binding_type, sampler) => {
1123                    let bindless_resource_type = BindlessResourceType::from(sampler_binding_type);
1124                    let slot = self
1125                        .samplers
1126                        .get_mut(&bindless_resource_type)
1127                        .expect("Sampler should exist")
1128                        .insert(binding_resource_id, sampler);
1129                    allocated_resource_slots.insert(bindless_index, slot);
1130
1131                    if let Some(pre_existing_slot) = pre_existing_slot {
1132                        assert_eq!(*pre_existing_slot, slot);
1133
1134                        false
1135                    } else {
1136                        true
1137                    }
1138                }
1139            };
1140
1141            // Bump the allocated resource count.
1142            if increment_allocated_resource_count {
1143                self.allocated_resource_count += 1;
1144            }
1145        }
1146
1147        allocated_resource_slots
1148    }
1149
1150    /// Removes the material allocated in the given slot, with the given
1151    /// descriptor, from this slab.
1152    fn free(&mut self, slot: MaterialBindGroupSlot, bindless_descriptor: &BindlessDescriptor) {
1153        // Loop through each binding.
1154        for (bindless_index, bindless_resource_type) in
1155            bindless_descriptor.resources.iter().enumerate()
1156        {
1157            let bindless_index = BindlessIndex::from(bindless_index as u32);
1158            let Some(bindless_index_table) = self.get_bindless_index_table(bindless_index) else {
1159                continue;
1160            };
1161            let Some(bindless_binding) = bindless_index_table.get_binding(slot, bindless_index)
1162            else {
1163                continue;
1164            };
1165
1166            // Free the binding. If the resource in question was anything other
1167            // than a data buffer, then it has a reference count and
1168            // consequently we need to decrement it.
1169            let decrement_allocated_resource_count = match *bindless_resource_type {
1170                BindlessResourceType::None => false,
1171                BindlessResourceType::Buffer => self
1172                    .buffers
1173                    .get_mut(&bindless_index)
1174                    .expect("Buffer should exist with that bindless index")
1175                    .remove(bindless_binding),
1176                BindlessResourceType::DataBuffer => {
1177                    self.data_buffers
1178                        .get_mut(&bindless_index)
1179                        .expect("Data buffer should exist with that bindless index")
1180                        .remove(bindless_binding);
1181                    false
1182                }
1183                BindlessResourceType::SamplerFiltering
1184                | BindlessResourceType::SamplerNonFiltering
1185                | BindlessResourceType::SamplerComparison => self
1186                    .samplers
1187                    .get_mut(bindless_resource_type)
1188                    .expect("Sampler array should exist")
1189                    .remove(bindless_binding),
1190                BindlessResourceType::Texture1d
1191                | BindlessResourceType::Texture2d
1192                | BindlessResourceType::Texture2dArray
1193                | BindlessResourceType::Texture3d
1194                | BindlessResourceType::TextureCube
1195                | BindlessResourceType::TextureCubeArray => self
1196                    .textures
1197                    .get_mut(bindless_resource_type)
1198                    .expect("Texture array should exist")
1199                    .remove(bindless_binding),
1200            };
1201
1202            // If the slot is now free, decrement the allocated resource
1203            // count.
1204            if decrement_allocated_resource_count {
1205                self.allocated_resource_count -= 1;
1206            }
1207        }
1208
1209        // Invalidate the cached bind group.
1210        self.bind_group = None;
1211
1212        // Release the slot ID.
1213        self.free_slots.push(slot);
1214        self.live_allocation_count -= 1;
1215    }
1216
1217    /// Recreates the bind group and bindless index table buffer if necessary.
1218    fn prepare(
1219        &mut self,
1220        render_device: &RenderDevice,
1221        pipeline_cache: &PipelineCache,
1222        label: &'static str,
1223        bind_group_layout: &BindGroupLayoutDescriptor,
1224        fallback_bindless_resources: &FallbackBindlessResources,
1225        fallback_buffers: &HashMap<BindlessIndex, Buffer>,
1226        fallback_image: &FallbackImage,
1227        bindless_descriptor: &BindlessDescriptor,
1228        slab_capacity: u32,
1229    ) {
1230        // Create the bindless index table buffers if needed.
1231        for bindless_index_table in &mut self.bindless_index_tables {
1232            bindless_index_table.buffer.prepare(render_device);
1233        }
1234
1235        // Create any data buffers we were managing if necessary.
1236        for data_buffer in self.data_buffers.values_mut() {
1237            data_buffer.buffer.prepare(render_device);
1238        }
1239
1240        // Create the bind group if needed.
1241        self.prepare_bind_group(
1242            render_device,
1243            pipeline_cache,
1244            label,
1245            bind_group_layout,
1246            fallback_bindless_resources,
1247            fallback_buffers,
1248            fallback_image,
1249            bindless_descriptor,
1250            slab_capacity,
1251        );
1252    }
1253
1254    /// Recreates the bind group if this slab has been changed since the last
1255    /// time we created it.
1256    fn prepare_bind_group(
1257        &mut self,
1258        render_device: &RenderDevice,
1259        pipeline_cache: &PipelineCache,
1260        label: &'static str,
1261        bind_group_layout: &BindGroupLayoutDescriptor,
1262        fallback_bindless_resources: &FallbackBindlessResources,
1263        fallback_buffers: &HashMap<BindlessIndex, Buffer>,
1264        fallback_image: &FallbackImage,
1265        bindless_descriptor: &BindlessDescriptor,
1266        slab_capacity: u32,
1267    ) {
1268        // If the bind group is clean, then do nothing.
1269        if self.bind_group.is_some() {
1270            return;
1271        }
1272
1273        // Determine whether we need to pad out our binding arrays with dummy
1274        // resources.
1275        let required_binding_array_size = if render_device
1276            .features()
1277            .contains(WgpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY)
1278        {
1279            None
1280        } else {
1281            Some(slab_capacity)
1282        };
1283
1284        let binding_resource_arrays = self.create_binding_resource_arrays(
1285            fallback_bindless_resources,
1286            fallback_buffers,
1287            fallback_image,
1288            bindless_descriptor,
1289            required_binding_array_size,
1290        );
1291
1292        let mut bind_group_entries: Vec<_> = self
1293            .bindless_index_tables
1294            .iter()
1295            .map(|bindless_index_table| bindless_index_table.bind_group_entry())
1296            .collect();
1297
1298        for &(&binding, ref binding_resource_array) in binding_resource_arrays.iter() {
1299            bind_group_entries.push(BindGroupEntry {
1300                binding,
1301                resource: match *binding_resource_array {
1302                    BindingResourceArray::Buffers(ref buffer_bindings) => {
1303                        BindingResource::BufferArray(&buffer_bindings[..])
1304                    }
1305                    BindingResourceArray::TextureViews(ref texture_views) => {
1306                        BindingResource::TextureViewArray(&texture_views[..])
1307                    }
1308                    BindingResourceArray::Samplers(ref samplers) => {
1309                        BindingResource::SamplerArray(&samplers[..])
1310                    }
1311                },
1312            });
1313        }
1314
1315        // Create bind group entries for any data buffers we're managing.
1316        for data_buffer in self.data_buffers.values() {
1317            bind_group_entries.push(BindGroupEntry {
1318                binding: *data_buffer.binding_number,
1319                resource: data_buffer
1320                    .buffer
1321                    .buffer()
1322                    .expect("Backing data buffer must have been uploaded by now")
1323                    .as_entire_binding(),
1324            });
1325        }
1326
1327        self.bind_group = Some(render_device.create_bind_group(
1328            Some(label),
1329            &pipeline_cache.get_bind_group_layout(bind_group_layout),
1330            &bind_group_entries,
1331        ));
1332    }
1333
1334    /// Writes any buffers that we're managing to the GPU.
1335    ///
1336    /// Currently, this consists of the bindless index table plus any data
1337    /// buffers we're managing.
1338    fn write_buffer(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
1339        for bindless_index_table in &mut self.bindless_index_tables {
1340            bindless_index_table
1341                .buffer
1342                .write(render_device, render_queue);
1343        }
1344
1345        for data_buffer in self.data_buffers.values_mut() {
1346            data_buffer.buffer.write(render_device, render_queue);
1347        }
1348    }
1349
1350    /// Converts our binding arrays into binding resource arrays suitable for
1351    /// passing to `wgpu`.
1352    fn create_binding_resource_arrays<'a>(
1353        &'a self,
1354        fallback_bindless_resources: &'a FallbackBindlessResources,
1355        fallback_buffers: &'a HashMap<BindlessIndex, Buffer>,
1356        fallback_image: &'a FallbackImage,
1357        bindless_descriptor: &'a BindlessDescriptor,
1358        required_binding_array_size: Option<u32>,
1359    ) -> Vec<(&'a u32, BindingResourceArray<'a>)> {
1360        let mut binding_resource_arrays = vec![];
1361
1362        // Build sampler bindings.
1363        self.create_sampler_binding_resource_arrays(
1364            &mut binding_resource_arrays,
1365            fallback_bindless_resources,
1366            bindless_descriptor,
1367            required_binding_array_size,
1368        );
1369
1370        // Build texture bindings.
1371        self.create_texture_binding_resource_arrays(
1372            &mut binding_resource_arrays,
1373            fallback_image,
1374            bindless_descriptor,
1375            required_binding_array_size,
1376        );
1377
1378        // Build buffer bindings.
1379        self.create_buffer_binding_resource_arrays(
1380            &mut binding_resource_arrays,
1381            fallback_buffers,
1382            bindless_descriptor,
1383            required_binding_array_size,
1384        );
1385
1386        binding_resource_arrays
1387    }
1388
1389    /// Accumulates sampler binding arrays into binding resource arrays suitable
1390    /// for passing to `wgpu`.
1391    fn create_sampler_binding_resource_arrays<'a, 'b>(
1392        &'a self,
1393        binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
1394        fallback_bindless_resources: &'a FallbackBindlessResources,
1395        bindless_descriptor: &'a BindlessDescriptor,
1396        required_binding_array_size: Option<u32>,
1397    ) {
1398        // We have one binding resource array per sampler type.
1399        for (bindless_resource_type, fallback_sampler) in [
1400            (
1401                BindlessResourceType::SamplerFiltering,
1402                &fallback_bindless_resources.filtering_sampler,
1403            ),
1404            (
1405                BindlessResourceType::SamplerNonFiltering,
1406                &fallback_bindless_resources.non_filtering_sampler,
1407            ),
1408            (
1409                BindlessResourceType::SamplerComparison,
1410                &fallback_bindless_resources.comparison_sampler,
1411            ),
1412        ] {
1413            // Skip resource types not used by this material.
1414            if !bindless_descriptor
1415                .resources
1416                .contains(&bindless_resource_type)
1417            {
1418                continue;
1419            }
1420
1421            let mut sampler_bindings = vec![];
1422
1423            match self.samplers.get(&bindless_resource_type) {
1424                Some(sampler_bindless_binding_array) => {
1425                    for maybe_bindless_binding in sampler_bindless_binding_array.bindings.iter() {
1426                        match *maybe_bindless_binding {
1427                            Some(ref bindless_binding) => {
1428                                sampler_bindings.push(&*bindless_binding.resource);
1429                            }
1430                            None => sampler_bindings.push(&**fallback_sampler),
1431                        }
1432                    }
1433                }
1434
1435                None => {
1436                    // Fill with a single fallback sampler.
1437                    sampler_bindings.push(&**fallback_sampler);
1438                }
1439            }
1440
1441            if let Some(required_binding_array_size) = required_binding_array_size {
1442                sampler_bindings.extend(iter::repeat_n(
1443                    &**fallback_sampler,
1444                    required_binding_array_size as usize - sampler_bindings.len(),
1445                ));
1446            }
1447
1448            let binding_number = bindless_resource_type
1449                .binding_number()
1450                .expect("Sampler bindless resource type must have a binding number");
1451
1452            binding_resource_arrays.push((
1453                &**binding_number,
1454                BindingResourceArray::Samplers(sampler_bindings),
1455            ));
1456        }
1457    }
1458
1459    /// Accumulates texture binding arrays into binding resource arrays suitable
1460    /// for passing to `wgpu`.
1461    fn create_texture_binding_resource_arrays<'a, 'b>(
1462        &'a self,
1463        binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
1464        fallback_image: &'a FallbackImage,
1465        bindless_descriptor: &'a BindlessDescriptor,
1466        required_binding_array_size: Option<u32>,
1467    ) {
1468        for (bindless_resource_type, fallback_image) in [
1469            (BindlessResourceType::Texture1d, &fallback_image.d1),
1470            (BindlessResourceType::Texture2d, &fallback_image.d2),
1471            (
1472                BindlessResourceType::Texture2dArray,
1473                &fallback_image.d2_array,
1474            ),
1475            (BindlessResourceType::Texture3d, &fallback_image.d3),
1476            (BindlessResourceType::TextureCube, &fallback_image.cube),
1477            (
1478                BindlessResourceType::TextureCubeArray,
1479                &fallback_image.cube_array,
1480            ),
1481        ] {
1482            // Skip texture types that this material doesn't use.
1483            if !bindless_descriptor
1484                .resources
1485                .contains(&bindless_resource_type)
1486            {
1487                continue;
1488            }
1489
1490            let mut texture_bindings = vec![];
1491
1492            let binding_number = bindless_resource_type
1493                .binding_number()
1494                .expect("Texture bindless resource type must have a binding number");
1495
1496            match self.textures.get(&bindless_resource_type) {
1497                Some(texture_bindless_binding_array) => {
1498                    for maybe_bindless_binding in texture_bindless_binding_array.bindings.iter() {
1499                        match *maybe_bindless_binding {
1500                            Some(ref bindless_binding) => {
1501                                texture_bindings.push(&*bindless_binding.resource);
1502                            }
1503                            None => texture_bindings.push(&*fallback_image.texture_view),
1504                        }
1505                    }
1506                }
1507
1508                None => {
1509                    // Fill with a single fallback image.
1510                    texture_bindings.push(&*fallback_image.texture_view);
1511                }
1512            }
1513
1514            if let Some(required_binding_array_size) = required_binding_array_size {
1515                texture_bindings.extend(iter::repeat_n(
1516                    &*fallback_image.texture_view,
1517                    required_binding_array_size as usize - texture_bindings.len(),
1518                ));
1519            }
1520
1521            binding_resource_arrays.push((
1522                binding_number,
1523                BindingResourceArray::TextureViews(texture_bindings),
1524            ));
1525        }
1526    }
1527
1528    /// Accumulates buffer binding arrays into binding resource arrays suitable
1529    /// for `wgpu`.
1530    fn create_buffer_binding_resource_arrays<'a, 'b>(
1531        &'a self,
1532        binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
1533        fallback_buffers: &'a HashMap<BindlessIndex, Buffer>,
1534        bindless_descriptor: &'a BindlessDescriptor,
1535        required_binding_array_size: Option<u32>,
1536    ) {
1537        for bindless_buffer_descriptor in bindless_descriptor.buffers.iter() {
1538            let Some(buffer_bindless_binding_array) =
1539                self.buffers.get(&bindless_buffer_descriptor.bindless_index)
1540            else {
1541                // This is OK, because index buffers are present in
1542                // `BindlessDescriptor::buffers` but not in
1543                // `BindlessDescriptor::resources`.
1544                continue;
1545            };
1546
1547            let fallback_buffer = fallback_buffers
1548                .get(&bindless_buffer_descriptor.bindless_index)
1549                .expect("Fallback buffer should exist");
1550
1551            let mut buffer_bindings: Vec<_> = buffer_bindless_binding_array
1552                .bindings
1553                .iter()
1554                .map(|maybe_bindless_binding| {
1555                    let buffer = match *maybe_bindless_binding {
1556                        None => fallback_buffer,
1557                        Some(ref bindless_binding) => &bindless_binding.resource,
1558                    };
1559                    BufferBinding {
1560                        buffer,
1561                        offset: 0,
1562                        size: None,
1563                    }
1564                })
1565                .collect();
1566
1567            if let Some(required_binding_array_size) = required_binding_array_size {
1568                buffer_bindings.extend(iter::repeat_n(
1569                    BufferBinding {
1570                        buffer: fallback_buffer,
1571                        offset: 0,
1572                        size: None,
1573                    },
1574                    required_binding_array_size as usize - buffer_bindings.len(),
1575                ));
1576            }
1577
1578            binding_resource_arrays.push((
1579                &*buffer_bindless_binding_array.binding_number,
1580                BindingResourceArray::Buffers(buffer_bindings),
1581            ));
1582        }
1583    }
1584
1585    /// Returns the [`BindGroup`] corresponding to this slab, if it's been
1586    /// prepared.
1587    fn bind_group(&self) -> Option<&BindGroup> {
1588        self.bind_group.as_ref()
1589    }
1590
1591    /// Returns the bindless index table containing the given bindless index.
1592    fn get_bindless_index_table(
1593        &self,
1594        bindless_index: BindlessIndex,
1595    ) -> Option<&MaterialBindlessIndexTable> {
1596        let table_index = self
1597            .bindless_index_tables
1598            .binary_search_by(|bindless_index_table| {
1599                if bindless_index < bindless_index_table.index_range.start {
1600                    Ordering::Less
1601                } else if bindless_index >= bindless_index_table.index_range.end {
1602                    Ordering::Greater
1603                } else {
1604                    Ordering::Equal
1605                }
1606            })
1607            .ok()?;
1608        self.bindless_index_tables.get(table_index)
1609    }
1610}
1611
1612impl<R> MaterialBindlessBindingArray<R>
1613where
1614    R: GetBindingResourceId,
1615{
1616    /// Creates a new [`MaterialBindlessBindingArray`] with the given binding
1617    /// number, managing resources of the given type.
1618    fn new(
1619        binding_number: BindingNumber,
1620        resource_type: BindlessResourceType,
1621    ) -> MaterialBindlessBindingArray<R> {
1622        MaterialBindlessBindingArray {
1623            binding_number,
1624            bindings: vec![],
1625            resource_type,
1626            resource_to_slot: HashMap::default(),
1627            free_slots: vec![],
1628            len: 0,
1629        }
1630    }
1631
1632    /// Returns the slot corresponding to the given resource, if that resource
1633    /// is located in this binding array.
1634    ///
1635    /// If the resource isn't in this binding array, this method returns `None`.
1636    fn find(&self, binding_resource_id: BindingResourceId) -> Option<u32> {
1637        self.resource_to_slot.get(&binding_resource_id).copied()
1638    }
1639
1640    /// Inserts a bindless resource into a binding array and returns the index
1641    /// of the slot it was inserted into.
1642    fn insert(&mut self, binding_resource_id: BindingResourceId, resource: R) -> u32 {
1643        match self.resource_to_slot.entry(binding_resource_id) {
1644            bevy_platform::collections::hash_map::Entry::Occupied(o) => {
1645                let slot = *o.get();
1646
1647                self.bindings[slot as usize]
1648                    .as_mut()
1649                    .expect("A slot in the resource_to_slot map should have a value")
1650                    .ref_count += 1;
1651
1652                slot
1653            }
1654            bevy_platform::collections::hash_map::Entry::Vacant(v) => {
1655                let slot = self.free_slots.pop().unwrap_or(self.len);
1656                v.insert(slot);
1657
1658                if self.bindings.len() < slot as usize + 1 {
1659                    self.bindings.resize_with(slot as usize + 1, || None);
1660                }
1661                debug_assert!(self.bindings[slot as usize].is_none());
1662                self.bindings[slot as usize] = Some(MaterialBindlessBinding::new(resource));
1663
1664                self.len += 1;
1665                slot
1666            }
1667        }
1668    }
1669
1670    /// Removes a reference to an object from the slot.
1671    ///
1672    /// If the reference count dropped to 0 and the object was freed, this
1673    /// method returns true. If the object was still referenced after removing
1674    /// it, returns false.
1675    fn remove(&mut self, slot: u32) -> bool {
1676        let maybe_binding = &mut self.bindings[slot as usize];
1677        let binding = maybe_binding
1678            .as_mut()
1679            .expect("Attempted to free an already-freed binding");
1680
1681        binding.ref_count -= 1;
1682        if binding.ref_count != 0 {
1683            return false;
1684        }
1685
1686        let binding_resource_id = binding.resource.binding_resource_id(self.resource_type);
1687        self.resource_to_slot.remove(&binding_resource_id);
1688
1689        *maybe_binding = None;
1690        self.free_slots.push(slot);
1691        self.len -= 1;
1692        true
1693    }
1694}
1695
1696impl<R> MaterialBindlessBinding<R>
1697where
1698    R: GetBindingResourceId,
1699{
1700    /// Creates a new [`MaterialBindlessBinding`] for a freshly-added resource.
1701    ///
1702    /// The reference count is initialized to 1.
1703    fn new(resource: R) -> MaterialBindlessBinding<R> {
1704        MaterialBindlessBinding {
1705            resource,
1706            ref_count: 1,
1707        }
1708    }
1709}
1710
1711/// Returns true if the material will *actually* use bindless resources or false
1712/// if it won't.
1713///
1714/// This takes the platform support (or lack thereof) for bindless resources
1715/// into account.
1716pub fn material_uses_bindless_resources<M>(render_device: &RenderDevice) -> bool
1717where
1718    M: Material,
1719{
1720    M::bindless_slot_count().is_some_and(|bindless_slot_count| {
1721        M::bindless_supported(render_device) && bindless_slot_count.resolve() > 1
1722    })
1723}
1724
1725impl MaterialBindlessSlab {
1726    /// Creates a new [`MaterialBindlessSlab`] for a material with the given
1727    /// bindless descriptor.
1728    ///
1729    /// We use this when no existing slab could hold a material to be allocated.
1730    fn new(bindless_descriptor: &BindlessDescriptor) -> MaterialBindlessSlab {
1731        let mut buffers = HashMap::default();
1732        let mut samplers = HashMap::default();
1733        let mut textures = HashMap::default();
1734        let mut data_buffers = HashMap::default();
1735
1736        for (bindless_index, bindless_resource_type) in
1737            bindless_descriptor.resources.iter().enumerate()
1738        {
1739            let bindless_index = BindlessIndex(bindless_index as u32);
1740            match *bindless_resource_type {
1741                BindlessResourceType::None => {}
1742                BindlessResourceType::Buffer => {
1743                    let binding_number = bindless_descriptor
1744                        .buffers
1745                        .iter()
1746                        .find(|bindless_buffer_descriptor| {
1747                            bindless_buffer_descriptor.bindless_index == bindless_index
1748                        })
1749                        .expect(
1750                            "Bindless buffer descriptor matching that bindless index should be \
1751                             present",
1752                        )
1753                        .binding_number;
1754                    buffers.insert(
1755                        bindless_index,
1756                        MaterialBindlessBindingArray::new(binding_number, *bindless_resource_type),
1757                    );
1758                }
1759                BindlessResourceType::DataBuffer => {
1760                    // Copy the data in.
1761                    let buffer_descriptor = bindless_descriptor
1762                        .buffers
1763                        .iter()
1764                        .find(|bindless_buffer_descriptor| {
1765                            bindless_buffer_descriptor.bindless_index == bindless_index
1766                        })
1767                        .expect(
1768                            "Bindless buffer descriptor matching that bindless index should be \
1769                             present",
1770                        );
1771                    data_buffers.insert(
1772                        bindless_index,
1773                        MaterialDataBuffer::new(
1774                            buffer_descriptor.binding_number,
1775                            buffer_descriptor
1776                                .size
1777                                .expect("Data buffers should have a size")
1778                                as u32,
1779                        ),
1780                    );
1781                }
1782                BindlessResourceType::SamplerFiltering
1783                | BindlessResourceType::SamplerNonFiltering
1784                | BindlessResourceType::SamplerComparison => {
1785                    samplers.insert(
1786                        *bindless_resource_type,
1787                        MaterialBindlessBindingArray::new(
1788                            *bindless_resource_type.binding_number().unwrap(),
1789                            *bindless_resource_type,
1790                        ),
1791                    );
1792                }
1793                BindlessResourceType::Texture1d
1794                | BindlessResourceType::Texture2d
1795                | BindlessResourceType::Texture2dArray
1796                | BindlessResourceType::Texture3d
1797                | BindlessResourceType::TextureCube
1798                | BindlessResourceType::TextureCubeArray => {
1799                    textures.insert(
1800                        *bindless_resource_type,
1801                        MaterialBindlessBindingArray::new(
1802                            *bindless_resource_type.binding_number().unwrap(),
1803                            *bindless_resource_type,
1804                        ),
1805                    );
1806                }
1807            }
1808        }
1809
1810        let bindless_index_tables = bindless_descriptor
1811            .index_tables
1812            .iter()
1813            .map(MaterialBindlessIndexTable::new)
1814            .collect();
1815
1816        MaterialBindlessSlab {
1817            bind_group: None,
1818            bindless_index_tables,
1819            samplers,
1820            textures,
1821            buffers,
1822            data_buffers,
1823            free_slots: vec![],
1824            live_allocation_count: 0,
1825            allocated_resource_count: 0,
1826        }
1827    }
1828}
1829
1830pub fn init_fallback_bindless_resources(mut commands: Commands, render_device: Res<RenderDevice>) {
1831    commands.insert_resource(FallbackBindlessResources {
1832        filtering_sampler: render_device.create_sampler(&SamplerDescriptor {
1833            label: Some("fallback filtering sampler"),
1834            ..default()
1835        }),
1836        non_filtering_sampler: render_device.create_sampler(&SamplerDescriptor {
1837            label: Some("fallback non-filtering sampler"),
1838            mag_filter: FilterMode::Nearest,
1839            min_filter: FilterMode::Nearest,
1840            mipmap_filter: MipmapFilterMode::Nearest,
1841            ..default()
1842        }),
1843        comparison_sampler: render_device.create_sampler(&SamplerDescriptor {
1844            label: Some("fallback comparison sampler"),
1845            compare: Some(CompareFunction::Always),
1846            ..default()
1847        }),
1848    });
1849}
1850
1851impl MaterialBindGroupNonBindlessAllocator {
1852    /// Creates a new [`MaterialBindGroupNonBindlessAllocator`] managing the
1853    /// bind groups for a single non-bindless material.
1854    fn new(label: &'static str) -> MaterialBindGroupNonBindlessAllocator {
1855        MaterialBindGroupNonBindlessAllocator {
1856            label,
1857            bind_groups: vec![],
1858            to_prepare: HashSet::default(),
1859            free_indices: vec![],
1860        }
1861    }
1862
1863    /// Inserts a bind group, either unprepared or prepared, into this allocator
1864    /// and returns a [`MaterialBindingId`].
1865    ///
1866    /// The returned [`MaterialBindingId`] can later be used to fetch the bind
1867    /// group.
1868    fn allocate(&mut self, bind_group: MaterialNonBindlessAllocatedBindGroup) -> MaterialBindingId {
1869        let group_id = self
1870            .free_indices
1871            .pop()
1872            .unwrap_or(MaterialBindGroupIndex(self.bind_groups.len() as u32));
1873        if self.bind_groups.len() < *group_id as usize + 1 {
1874            self.bind_groups
1875                .resize_with(*group_id as usize + 1, || None);
1876        }
1877
1878        if matches!(
1879            bind_group,
1880            MaterialNonBindlessAllocatedBindGroup::Unprepared { .. }
1881        ) {
1882            self.to_prepare.insert(group_id);
1883        }
1884
1885        self.bind_groups[*group_id as usize] = Some(bind_group);
1886
1887        MaterialBindingId {
1888            group: group_id,
1889            slot: default(),
1890        }
1891    }
1892
1893    /// Inserts an unprepared bind group into this allocator and returns a
1894    /// [`MaterialBindingId`].
1895    fn allocate_unprepared(
1896        &mut self,
1897        unprepared_bind_group: UnpreparedBindGroup,
1898        bind_group_layout: BindGroupLayoutDescriptor,
1899    ) -> MaterialBindingId {
1900        self.allocate(MaterialNonBindlessAllocatedBindGroup::Unprepared {
1901            bind_group: unprepared_bind_group,
1902            layout: bind_group_layout,
1903        })
1904    }
1905
1906    /// Inserts an prepared bind group into this allocator and returns a
1907    /// [`MaterialBindingId`].
1908    fn allocate_prepared(&mut self, prepared_bind_group: PreparedBindGroup) -> MaterialBindingId {
1909        self.allocate(MaterialNonBindlessAllocatedBindGroup::Prepared {
1910            bind_group: prepared_bind_group,
1911            uniform_buffers: vec![],
1912        })
1913    }
1914
1915    /// Deallocates the bind group with the given binding ID.
1916    fn free(&mut self, binding_id: MaterialBindingId) {
1917        debug_assert_eq!(binding_id.slot, MaterialBindGroupSlot(0));
1918        debug_assert!(self.bind_groups[*binding_id.group as usize].is_some());
1919        self.bind_groups[*binding_id.group as usize] = None;
1920        self.to_prepare.remove(&binding_id.group);
1921        self.free_indices.push(binding_id.group);
1922    }
1923
1924    /// Returns a wrapper around the bind group with the given index.
1925    fn get(&self, group: MaterialBindGroupIndex) -> Option<MaterialNonBindlessSlab<'_>> {
1926        self.bind_groups[group.0 as usize]
1927            .as_ref()
1928            .map(|bind_group| match bind_group {
1929                MaterialNonBindlessAllocatedBindGroup::Prepared { bind_group, .. } => {
1930                    MaterialNonBindlessSlab::Prepared(bind_group)
1931                }
1932                MaterialNonBindlessAllocatedBindGroup::Unprepared { .. } => {
1933                    MaterialNonBindlessSlab::Unprepared
1934                }
1935            })
1936    }
1937
1938    /// Prepares any as-yet unprepared bind groups that this allocator is
1939    /// managing.
1940    ///
1941    /// Unprepared bind groups can be added to this allocator with
1942    /// [`Self::allocate_unprepared`]. Such bind groups will defer being
1943    /// prepared until the next time this method is called.
1944    fn prepare_bind_groups(
1945        &mut self,
1946        render_device: &RenderDevice,
1947        pipeline_cache: &PipelineCache,
1948    ) {
1949        for bind_group_index in mem::take(&mut self.to_prepare) {
1950            let Some(MaterialNonBindlessAllocatedBindGroup::Unprepared {
1951                bind_group: unprepared_bind_group,
1952                layout: bind_group_layout,
1953            }) = mem::take(&mut self.bind_groups[*bind_group_index as usize])
1954            else {
1955                panic!("Allocation didn't exist or was already prepared");
1956            };
1957
1958            // Pack any `Data` into uniform buffers.
1959            let mut uniform_buffers = vec![];
1960            for (index, binding) in unprepared_bind_group.bindings.iter() {
1961                let OwnedBindingResource::Data(ref owned_data) = *binding else {
1962                    continue;
1963                };
1964                let label = format!("material uniform data {}", *index);
1965                let uniform_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
1966                    label: Some(&label),
1967                    contents: &owned_data.0,
1968                    usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM,
1969                });
1970                uniform_buffers.push(uniform_buffer);
1971            }
1972
1973            // Create bind group entries.
1974            let mut bind_group_entries = vec![];
1975            let mut uniform_buffers_iter = uniform_buffers.iter();
1976            for (index, binding) in unprepared_bind_group.bindings.iter() {
1977                match *binding {
1978                    OwnedBindingResource::Data(_) => {
1979                        bind_group_entries.push(BindGroupEntry {
1980                            binding: *index,
1981                            resource: uniform_buffers_iter
1982                                .next()
1983                                .expect("We should have created uniform buffers for each `Data`")
1984                                .as_entire_binding(),
1985                        });
1986                    }
1987                    _ => bind_group_entries.push(BindGroupEntry {
1988                        binding: *index,
1989                        resource: binding.get_binding(),
1990                    }),
1991                }
1992            }
1993
1994            // Create the bind group.
1995            let bind_group = render_device.create_bind_group(
1996                self.label,
1997                &pipeline_cache.get_bind_group_layout(&bind_group_layout),
1998                &bind_group_entries,
1999            );
2000
2001            self.bind_groups[*bind_group_index as usize] =
2002                Some(MaterialNonBindlessAllocatedBindGroup::Prepared {
2003                    bind_group: PreparedBindGroup {
2004                        bindings: unprepared_bind_group.bindings,
2005                        bind_group,
2006                    },
2007                    uniform_buffers,
2008                });
2009        }
2010    }
2011}
2012
2013impl<'a> MaterialSlab<'a> {
2014    /// Returns the [`BindGroup`] corresponding to this slab, if it's been
2015    /// prepared.
2016    ///
2017    /// You can prepare bind groups by calling
2018    /// [`MaterialBindGroupAllocator::prepare_bind_groups`]. If the bind group
2019    /// isn't ready, this method returns `None`.
2020    pub fn bind_group(&self) -> Option<&'a BindGroup> {
2021        match self.0 {
2022            MaterialSlabImpl::Bindless(material_bindless_slab) => {
2023                material_bindless_slab.bind_group()
2024            }
2025            MaterialSlabImpl::NonBindless(MaterialNonBindlessSlab::Prepared(
2026                prepared_bind_group,
2027            )) => Some(&prepared_bind_group.bind_group),
2028            MaterialSlabImpl::NonBindless(MaterialNonBindlessSlab::Unprepared) => None,
2029        }
2030    }
2031}
2032
2033impl MaterialDataBuffer {
2034    /// Creates a new [`MaterialDataBuffer`] managing a buffer of elements of
2035    /// size `aligned_element_size` that will be bound to the given binding
2036    /// number.
2037    fn new(binding_number: BindingNumber, aligned_element_size: u32) -> MaterialDataBuffer {
2038        MaterialDataBuffer {
2039            binding_number,
2040            buffer: RetainedRawBufferVec::new(BufferUsages::STORAGE),
2041            aligned_element_size,
2042            free_slots: vec![],
2043            len: 0,
2044        }
2045    }
2046
2047    /// Allocates a slot for a new piece of data, copies the data into that
2048    /// slot, and returns the slot ID.
2049    ///
2050    /// The size of the piece of data supplied to this method must equal the
2051    /// [`Self::aligned_element_size`] provided to [`MaterialDataBuffer::new`].
2052    fn insert(&mut self, data: &[u8]) -> u32 {
2053        // Make sure the data is of the right length.
2054        debug_assert_eq!(data.len(), self.aligned_element_size as usize);
2055
2056        // Grab a slot.
2057        let slot = self.free_slots.pop().unwrap_or(self.len);
2058
2059        // Calculate the range we're going to copy to.
2060        let start = slot as usize * self.aligned_element_size as usize;
2061        let end = (slot as usize + 1) * self.aligned_element_size as usize;
2062
2063        // Resize the buffer if necessary.
2064        if self.buffer.len() < end {
2065            self.buffer.reserve_internal(end);
2066        }
2067        while self.buffer.values().len() < end {
2068            self.buffer.push(0);
2069        }
2070
2071        // Copy in the data.
2072        self.buffer.values_mut()[start..end].copy_from_slice(data);
2073
2074        // Mark the buffer dirty, and finish up.
2075        self.len += 1;
2076        self.buffer.dirty = BufferDirtyState::NeedsReserve;
2077        slot
2078    }
2079
2080    /// Marks the given slot as free.
2081    fn remove(&mut self, slot: u32) {
2082        self.free_slots.push(slot);
2083        self.len -= 1;
2084    }
2085}