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