1use 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;
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, TypeIdMap};
30use bytemuck::Pod;
31use core::hash::Hash;
32use core::{cmp::Ordering, iter, mem, ops::Range};
33use tracing::{error, trace};
34
35#[derive(Resource, Deref, DerefMut, Default)]
36pub struct MaterialBindGroupAllocators(TypeIdMap<MaterialBindGroupAllocator>);
37
38pub enum MaterialBindGroupAllocator {
45 Bindless(Box<MaterialBindGroupBindlessAllocator>),
47 NonBindless(Box<MaterialBindGroupNonBindlessAllocator>),
49}
50
51pub struct MaterialBindGroupBindlessAllocator {
54 label: Option<&'static str>,
56 slabs: Vec<MaterialBindlessSlab>,
58 bind_group_layout: BindGroupLayout,
60 bindless_descriptor: BindlessDescriptor,
64
65 fallback_buffers: HashMap<BindlessIndex, Buffer>,
71
72 slab_capacity: u32,
77}
78
79pub struct MaterialBindlessSlab {
81 bind_group: Option<BindGroup>,
86
87 bindless_index_tables: Vec<MaterialBindlessIndexTable>,
97
98 samplers: HashMap<BindlessResourceType, MaterialBindlessBindingArray<Sampler>>,
100 textures: HashMap<BindlessResourceType, MaterialBindlessBindingArray<TextureView>>,
102 buffers: HashMap<BindlessIndex, MaterialBindlessBindingArray<Buffer>>,
104 data_buffers: HashMap<BindlessIndex, MaterialDataBuffer>,
107
108 free_slots: Vec<MaterialBindGroupSlot>,
110 live_allocation_count: u32,
112 allocated_resource_count: u32,
114}
115
116struct MaterialBindlessIndexTable {
123 buffer: RetainedRawBufferVec<u32>,
125 index_range: Range<BindlessIndex>,
133 binding_number: BindingNumber,
135}
136
137struct MaterialBindlessBindingArray<R>
140where
141 R: GetBindingResourceId,
142{
143 binding_number: BindingNumber,
145 bindings: Vec<Option<MaterialBindlessBinding<R>>>,
148 resource_type: BindlessResourceType,
150 resource_to_slot: HashMap<BindingResourceId, u32>,
154 free_slots: Vec<u32>,
156 len: u32,
158}
159
160struct MaterialBindlessBinding<R>
166where
167 R: GetBindingResourceId,
168{
169 resource: R,
171 ref_count: u32,
174}
175
176pub struct MaterialBindGroupNonBindlessAllocator {
178 label: Option<&'static str>,
180 bind_groups: Vec<Option<MaterialNonBindlessAllocatedBindGroup>>,
183 to_prepare: HashSet<MaterialBindGroupIndex>,
188 free_indices: Vec<MaterialBindGroupIndex>,
190}
191
192enum MaterialNonBindlessAllocatedBindGroup {
195 Unprepared {
201 bind_group: UnpreparedBindGroup,
203 layout: BindGroupLayout,
205 },
206 Prepared {
208 bind_group: PreparedBindGroup,
209 #[expect(dead_code, reason = "These buffers are only referenced by bind groups")]
210 uniform_buffers: Vec<Buffer>,
211 },
212}
213
214#[derive(Resource)]
217pub struct FallbackBindlessResources {
218 filtering_sampler: Sampler,
220 non_filtering_sampler: Sampler,
222 comparison_sampler: Sampler,
224}
225
226#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
228enum BindingResourceId {
229 Buffer(BufferId),
231 TextureView(TextureViewDimension, TextureViewId),
233 Sampler(SamplerId),
235 DataBuffer,
240}
241
242enum BindingResourceArray<'a> {
248 Buffers(Vec<BufferBinding<'a>>),
250 TextureViews(Vec<&'a WgpuTextureView>),
252 Samplers(Vec<&'a WgpuSampler>),
254}
255
256#[derive(Clone, Copy, Debug, Default, Reflect)]
259#[reflect(Clone, Default)]
260pub struct MaterialBindingId {
261 pub group: MaterialBindGroupIndex,
263 pub slot: MaterialBindGroupSlot,
267}
268
269#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Reflect, Deref, DerefMut)]
274#[reflect(Default, Clone, PartialEq, Hash)]
275pub struct MaterialBindGroupIndex(pub u32);
276
277impl From<u32> for MaterialBindGroupIndex {
278 fn from(value: u32) -> Self {
279 MaterialBindGroupIndex(value)
280 }
281}
282
283#[derive(Clone, Copy, Debug, Default, PartialEq, Reflect, Deref, DerefMut)]
290#[reflect(Default, Clone, PartialEq)]
291pub struct MaterialBindGroupSlot(pub u32);
292
293enum BufferDirtyState {
298 Clean,
300 NeedsReserve,
302 NeedsUpload,
304}
305
306struct BindlessAllocationCandidate {
309 pre_existing_resources: HashMap<BindlessIndex, u32>,
313 needed_free_slots: u32,
316}
317
318trait GetBindingResourceId {
325 fn binding_resource_id(&self, resource_type: BindlessResourceType) -> BindingResourceId;
331}
332
333pub struct MaterialSlab<'a>(MaterialSlabImpl<'a>);
335
336enum MaterialSlabImpl<'a> {
340 Bindless(&'a MaterialBindlessSlab),
343 NonBindless(MaterialNonBindlessSlab<'a>),
346}
347
348enum MaterialNonBindlessSlab<'a> {
351 Prepared(&'a PreparedBindGroup),
353 Unprepared,
355}
356
357struct MaterialDataBuffer {
362 binding_number: BindingNumber,
364 buffer: RetainedRawBufferVec<u8>,
369 aligned_element_size: u32,
372 free_slots: Vec<u32>,
374 len: u32,
376}
377
378#[derive(Deref, DerefMut)]
384struct RetainedRawBufferVec<T>
385where
386 T: Pod,
387{
388 #[deref]
390 buffer: RawBufferVec<T>,
391 dirty: BufferDirtyState,
393}
394
395const DEFAULT_BINDLESS_FALLBACK_BUFFER_SIZE: u64 = 16;
400
401impl From<u32> for MaterialBindGroupSlot {
402 fn from(value: u32) -> Self {
403 MaterialBindGroupSlot(value)
404 }
405}
406
407impl From<MaterialBindGroupSlot> for u32 {
408 fn from(value: MaterialBindGroupSlot) -> Self {
409 value.0
410 }
411}
412
413impl<'a> From<&'a OwnedBindingResource> for BindingResourceId {
414 fn from(value: &'a OwnedBindingResource) -> Self {
415 match *value {
416 OwnedBindingResource::Buffer(ref buffer) => BindingResourceId::Buffer(buffer.id()),
417 OwnedBindingResource::Data(_) => BindingResourceId::DataBuffer,
418 OwnedBindingResource::TextureView(ref texture_view_dimension, ref texture_view) => {
419 BindingResourceId::TextureView(*texture_view_dimension, texture_view.id())
420 }
421 OwnedBindingResource::Sampler(_, ref sampler) => {
422 BindingResourceId::Sampler(sampler.id())
423 }
424 }
425 }
426}
427
428impl GetBindingResourceId for Buffer {
429 fn binding_resource_id(&self, _: BindlessResourceType) -> BindingResourceId {
430 BindingResourceId::Buffer(self.id())
431 }
432}
433
434impl GetBindingResourceId for Sampler {
435 fn binding_resource_id(&self, _: BindlessResourceType) -> BindingResourceId {
436 BindingResourceId::Sampler(self.id())
437 }
438}
439
440impl GetBindingResourceId for TextureView {
441 fn binding_resource_id(&self, resource_type: BindlessResourceType) -> BindingResourceId {
442 let texture_view_dimension = match resource_type {
443 BindlessResourceType::Texture1d => TextureViewDimension::D1,
444 BindlessResourceType::Texture2d => TextureViewDimension::D2,
445 BindlessResourceType::Texture2dArray => TextureViewDimension::D2Array,
446 BindlessResourceType::Texture3d => TextureViewDimension::D3,
447 BindlessResourceType::TextureCube => TextureViewDimension::Cube,
448 BindlessResourceType::TextureCubeArray => TextureViewDimension::CubeArray,
449 _ => panic!("Resource type is not a texture"),
450 };
451 BindingResourceId::TextureView(texture_view_dimension, self.id())
452 }
453}
454
455impl MaterialBindGroupAllocator {
456 pub fn new(
459 render_device: &RenderDevice,
460 label: Option<&'static str>,
461 bindless_descriptor: Option<BindlessDescriptor>,
462 bind_group_layout: BindGroupLayout,
463 slab_capacity: Option<BindlessSlabResourceLimit>,
464 ) -> MaterialBindGroupAllocator {
465 if let Some(bindless_descriptor) = bindless_descriptor {
466 MaterialBindGroupAllocator::Bindless(Box::new(MaterialBindGroupBindlessAllocator::new(
467 render_device,
468 label,
469 bindless_descriptor,
470 bind_group_layout,
471 slab_capacity,
472 )))
473 } else {
474 MaterialBindGroupAllocator::NonBindless(Box::new(
475 MaterialBindGroupNonBindlessAllocator::new(label),
476 ))
477 }
478 }
479
480 pub fn get(&self, group: MaterialBindGroupIndex) -> Option<MaterialSlab<'_>> {
482 match *self {
483 MaterialBindGroupAllocator::Bindless(ref bindless_allocator) => bindless_allocator
484 .get(group)
485 .map(|bindless_slab| MaterialSlab(MaterialSlabImpl::Bindless(bindless_slab))),
486 MaterialBindGroupAllocator::NonBindless(ref non_bindless_allocator) => {
487 non_bindless_allocator.get(group).map(|non_bindless_slab| {
488 MaterialSlab(MaterialSlabImpl::NonBindless(non_bindless_slab))
489 })
490 }
491 }
492 }
493
494 pub fn allocate_unprepared(
501 &mut self,
502 unprepared_bind_group: UnpreparedBindGroup,
503 bind_group_layout: &BindGroupLayout,
504 ) -> MaterialBindingId {
505 match *self {
506 MaterialBindGroupAllocator::Bindless(
507 ref mut material_bind_group_bindless_allocator,
508 ) => material_bind_group_bindless_allocator.allocate_unprepared(unprepared_bind_group),
509 MaterialBindGroupAllocator::NonBindless(
510 ref mut material_bind_group_non_bindless_allocator,
511 ) => material_bind_group_non_bindless_allocator
512 .allocate_unprepared(unprepared_bind_group, (*bind_group_layout).clone()),
513 }
514 }
515
516 pub fn allocate_prepared(
526 &mut self,
527 prepared_bind_group: PreparedBindGroup,
528 ) -> MaterialBindingId {
529 match *self {
530 MaterialBindGroupAllocator::Bindless(_) => {
531 panic!(
532 "Bindless resources are incompatible with implementing `as_bind_group` \
533 directly; implement `unprepared_bind_group` instead or disable bindless"
534 )
535 }
536 MaterialBindGroupAllocator::NonBindless(ref mut non_bindless_allocator) => {
537 non_bindless_allocator.allocate_prepared(prepared_bind_group)
538 }
539 }
540 }
541
542 pub fn free(&mut self, material_binding_id: MaterialBindingId) {
546 match *self {
547 MaterialBindGroupAllocator::Bindless(
548 ref mut material_bind_group_bindless_allocator,
549 ) => material_bind_group_bindless_allocator.free(material_binding_id),
550 MaterialBindGroupAllocator::NonBindless(
551 ref mut material_bind_group_non_bindless_allocator,
552 ) => material_bind_group_non_bindless_allocator.free(material_binding_id),
553 }
554 }
555
556 pub fn prepare_bind_groups(
559 &mut self,
560 render_device: &RenderDevice,
561 fallback_bindless_resources: &FallbackBindlessResources,
562 fallback_image: &FallbackImage,
563 ) {
564 match *self {
565 MaterialBindGroupAllocator::Bindless(
566 ref mut material_bind_group_bindless_allocator,
567 ) => material_bind_group_bindless_allocator.prepare_bind_groups(
568 render_device,
569 fallback_bindless_resources,
570 fallback_image,
571 ),
572 MaterialBindGroupAllocator::NonBindless(
573 ref mut material_bind_group_non_bindless_allocator,
574 ) => material_bind_group_non_bindless_allocator.prepare_bind_groups(render_device),
575 }
576 }
577
578 pub fn write_buffers(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
584 match *self {
585 MaterialBindGroupAllocator::Bindless(
586 ref mut material_bind_group_bindless_allocator,
587 ) => material_bind_group_bindless_allocator.write_buffers(render_device, render_queue),
588 MaterialBindGroupAllocator::NonBindless(_) => {
589 }
591 }
592 }
593}
594
595impl MaterialBindlessIndexTable {
596 fn new(
598 bindless_index_table_descriptor: &BindlessIndexTableDescriptor,
599 ) -> MaterialBindlessIndexTable {
600 let mut buffer = RetainedRawBufferVec::new(BufferUsages::STORAGE);
602 for _ in *bindless_index_table_descriptor.indices.start
603 ..*bindless_index_table_descriptor.indices.end
604 {
605 buffer.push(0);
606 }
607
608 MaterialBindlessIndexTable {
609 buffer,
610 index_range: bindless_index_table_descriptor.indices.clone(),
611 binding_number: bindless_index_table_descriptor.binding_number,
612 }
613 }
614
615 fn get(&self, slot: MaterialBindGroupSlot) -> &[u32] {
621 let struct_size = *self.index_range.end as usize - *self.index_range.start as usize;
622 let start = struct_size * slot.0 as usize;
623 &self.buffer.values()[start..(start + struct_size)]
624 }
625
626 fn get_binding(
628 &self,
629 slot: MaterialBindGroupSlot,
630 bindless_index: BindlessIndex,
631 ) -> Option<u32> {
632 if bindless_index < self.index_range.start || bindless_index >= self.index_range.end {
633 return None;
634 }
635 self.get(slot)
636 .get((*bindless_index - *self.index_range.start) as usize)
637 .copied()
638 }
639
640 fn table_length(&self) -> u32 {
641 self.index_range.end.0 - self.index_range.start.0
642 }
643
644 fn set(
652 &mut self,
653 slot: MaterialBindGroupSlot,
654 allocated_resource_slots: &HashMap<BindlessIndex, u32>,
655 ) {
656 let table_len = self.table_length() as usize;
657 let range = (slot.0 as usize * table_len)..((slot.0 as usize + 1) * table_len);
658 while self.buffer.len() < range.end {
659 self.buffer.push(0);
660 }
661
662 for (&bindless_index, &resource_slot) in allocated_resource_slots {
663 if self.index_range.contains(&bindless_index) {
664 self.buffer.set(
665 *bindless_index + range.start as u32 - *self.index_range.start,
666 resource_slot,
667 );
668 }
669 }
670
671 self.buffer.dirty = BufferDirtyState::NeedsReserve;
673 }
674
675 fn bind_group_entry(&self) -> BindGroupEntry<'_> {
677 BindGroupEntry {
678 binding: *self.binding_number,
679 resource: self
680 .buffer
681 .buffer()
682 .expect("Bindings buffer must exist")
683 .as_entire_binding(),
684 }
685 }
686}
687
688impl<T> RetainedRawBufferVec<T>
689where
690 T: Pod,
691{
692 fn new(buffer_usages: BufferUsages) -> RetainedRawBufferVec<T> {
695 RetainedRawBufferVec {
696 buffer: RawBufferVec::new(buffer_usages),
697 dirty: BufferDirtyState::NeedsUpload,
698 }
699 }
700
701 fn prepare(&mut self, render_device: &RenderDevice) {
703 match self.dirty {
704 BufferDirtyState::Clean | BufferDirtyState::NeedsUpload => {}
705 BufferDirtyState::NeedsReserve => {
706 let capacity = self.buffer.len();
707 self.buffer.reserve(capacity, render_device);
708 self.dirty = BufferDirtyState::NeedsUpload;
709 }
710 }
711 }
712
713 fn write(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
715 match self.dirty {
716 BufferDirtyState::Clean => {}
717 BufferDirtyState::NeedsReserve | BufferDirtyState::NeedsUpload => {
718 self.buffer.write_buffer(render_device, render_queue);
719 self.dirty = BufferDirtyState::Clean;
720 }
721 }
722 }
723}
724
725impl MaterialBindGroupBindlessAllocator {
726 fn new(
729 render_device: &RenderDevice,
730 label: Option<&'static str>,
731 bindless_descriptor: BindlessDescriptor,
732 bind_group_layout: BindGroupLayout,
733 slab_capacity: Option<BindlessSlabResourceLimit>,
734 ) -> MaterialBindGroupBindlessAllocator {
735 let fallback_buffers = bindless_descriptor
736 .buffers
737 .iter()
738 .map(|bindless_buffer_descriptor| {
739 (
740 bindless_buffer_descriptor.bindless_index,
741 render_device.create_buffer(&BufferDescriptor {
742 label: Some("bindless fallback buffer"),
743 size: match bindless_buffer_descriptor.size {
744 Some(size) => size as u64,
745 None => DEFAULT_BINDLESS_FALLBACK_BUFFER_SIZE,
746 },
747 usage: BufferUsages::STORAGE,
748 mapped_at_creation: false,
749 }),
750 )
751 })
752 .collect();
753
754 MaterialBindGroupBindlessAllocator {
755 label,
756 slabs: vec![],
757 bind_group_layout,
758 bindless_descriptor,
759 fallback_buffers,
760 slab_capacity: slab_capacity
761 .expect("Non-bindless materials should use the non-bindless allocator")
762 .resolve(),
763 }
764 }
765
766 fn allocate_unprepared(
775 &mut self,
776 mut unprepared_bind_group: UnpreparedBindGroup,
777 ) -> MaterialBindingId {
778 for (slab_index, slab) in self.slabs.iter_mut().enumerate() {
779 trace!("Trying to allocate in slab {}", slab_index);
780 match slab.try_allocate(unprepared_bind_group, self.slab_capacity) {
781 Ok(slot) => {
782 return MaterialBindingId {
783 group: MaterialBindGroupIndex(slab_index as u32),
784 slot,
785 };
786 }
787 Err(bind_group) => unprepared_bind_group = bind_group,
788 }
789 }
790
791 let group = MaterialBindGroupIndex(self.slabs.len() as u32);
792 self.slabs
793 .push(MaterialBindlessSlab::new(&self.bindless_descriptor));
794
795 let Ok(slot) = self
797 .slabs
798 .last_mut()
799 .expect("We just pushed a slab")
800 .try_allocate(unprepared_bind_group, self.slab_capacity)
801 else {
802 panic!("An allocation into an empty slab should always succeed")
803 };
804
805 MaterialBindingId { group, slot }
806 }
807
808 fn free(&mut self, material_binding_id: MaterialBindingId) {
812 self.slabs
813 .get_mut(material_binding_id.group.0 as usize)
814 .expect("Slab should exist")
815 .free(material_binding_id.slot, &self.bindless_descriptor);
816 }
817
818 fn get(&self, group: MaterialBindGroupIndex) -> Option<&MaterialBindlessSlab> {
823 self.slabs.get(group.0 as usize)
824 }
825
826 fn prepare_bind_groups(
830 &mut self,
831 render_device: &RenderDevice,
832 fallback_bindless_resources: &FallbackBindlessResources,
833 fallback_image: &FallbackImage,
834 ) {
835 for slab in &mut self.slabs {
836 slab.prepare(
837 render_device,
838 self.label,
839 &self.bind_group_layout,
840 fallback_bindless_resources,
841 &self.fallback_buffers,
842 fallback_image,
843 &self.bindless_descriptor,
844 self.slab_capacity,
845 );
846 }
847 }
848
849 fn write_buffers(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
853 for slab in &mut self.slabs {
854 slab.write_buffer(render_device, render_queue);
855 }
856 }
857}
858
859impl MaterialBindlessSlab {
860 fn try_allocate(
867 &mut self,
868 unprepared_bind_group: UnpreparedBindGroup,
869 slot_capacity: u32,
870 ) -> Result<MaterialBindGroupSlot, UnpreparedBindGroup> {
871 let Some(allocation_candidate) = self.check_allocation(&unprepared_bind_group) else {
873 return Err(unprepared_bind_group);
874 };
875
876 if self.allocated_resource_count > 0
885 && self.allocated_resource_count + allocation_candidate.needed_free_slots
886 > slot_capacity
887 {
888 trace!("Slab is full, can't allocate");
889 return Err(unprepared_bind_group);
890 }
891
892 let slot = self
894 .free_slots
895 .pop()
896 .unwrap_or(MaterialBindGroupSlot(self.live_allocation_count));
897
898 self.live_allocation_count += 1;
900
901 let allocated_resource_slots =
903 self.insert_resources(unprepared_bind_group.bindings, allocation_candidate);
904
905 for bindless_index_table in &mut self.bindless_index_tables {
907 bindless_index_table.set(slot, &allocated_resource_slots);
908 }
909
910 self.bind_group = None;
912
913 Ok(slot)
914 }
915
916 fn check_allocation(
919 &self,
920 unprepared_bind_group: &UnpreparedBindGroup,
921 ) -> Option<BindlessAllocationCandidate> {
922 let mut allocation_candidate = BindlessAllocationCandidate {
923 pre_existing_resources: HashMap::default(),
924 needed_free_slots: 0,
925 };
926
927 for &(bindless_index, ref owned_binding_resource) in unprepared_bind_group.bindings.iter() {
928 let bindless_index = BindlessIndex(bindless_index);
929 match *owned_binding_resource {
930 OwnedBindingResource::Buffer(ref buffer) => {
931 let Some(binding_array) = self.buffers.get(&bindless_index) else {
932 error!(
933 "Binding array wasn't present for buffer at index {:?}",
934 bindless_index
935 );
936 return None;
937 };
938 match binding_array.find(BindingResourceId::Buffer(buffer.id())) {
939 Some(slot) => {
940 allocation_candidate
941 .pre_existing_resources
942 .insert(bindless_index, slot);
943 }
944 None => allocation_candidate.needed_free_slots += 1,
945 }
946 }
947
948 OwnedBindingResource::Data(_) => {
949 }
951
952 OwnedBindingResource::TextureView(texture_view_dimension, ref texture_view) => {
953 let bindless_resource_type = BindlessResourceType::from(texture_view_dimension);
954 match self
955 .textures
956 .get(&bindless_resource_type)
957 .expect("Missing binding array for texture")
958 .find(BindingResourceId::TextureView(
959 texture_view_dimension,
960 texture_view.id(),
961 )) {
962 Some(slot) => {
963 allocation_candidate
964 .pre_existing_resources
965 .insert(bindless_index, slot);
966 }
967 None => {
968 allocation_candidate.needed_free_slots += 1;
969 }
970 }
971 }
972
973 OwnedBindingResource::Sampler(sampler_binding_type, ref sampler) => {
974 let bindless_resource_type = BindlessResourceType::from(sampler_binding_type);
975 match self
976 .samplers
977 .get(&bindless_resource_type)
978 .expect("Missing binding array for sampler")
979 .find(BindingResourceId::Sampler(sampler.id()))
980 {
981 Some(slot) => {
982 allocation_candidate
983 .pre_existing_resources
984 .insert(bindless_index, slot);
985 }
986 None => {
987 allocation_candidate.needed_free_slots += 1;
988 }
989 }
990 }
991 }
992 }
993
994 Some(allocation_candidate)
995 }
996
997 fn insert_resources(
1002 &mut self,
1003 mut binding_resources: BindingResources,
1004 allocation_candidate: BindlessAllocationCandidate,
1005 ) -> HashMap<BindlessIndex, u32> {
1006 let mut allocated_resource_slots = HashMap::default();
1007
1008 for (bindless_index, owned_binding_resource) in binding_resources.drain(..) {
1009 let bindless_index = BindlessIndex(bindless_index);
1010
1011 let pre_existing_slot = allocation_candidate
1012 .pre_existing_resources
1013 .get(&bindless_index);
1014
1015 let binding_resource_id = BindingResourceId::from(&owned_binding_resource);
1017 let increment_allocated_resource_count = match owned_binding_resource {
1018 OwnedBindingResource::Buffer(buffer) => {
1019 let slot = self
1020 .buffers
1021 .get_mut(&bindless_index)
1022 .expect("Buffer binding array should exist")
1023 .insert(binding_resource_id, buffer);
1024 allocated_resource_slots.insert(bindless_index, slot);
1025
1026 if let Some(pre_existing_slot) = pre_existing_slot {
1027 assert_eq!(*pre_existing_slot, slot);
1028
1029 false
1030 } else {
1031 true
1032 }
1033 }
1034 OwnedBindingResource::Data(data) => {
1035 if pre_existing_slot.is_some() {
1036 panic!("Data buffers can't be deduplicated")
1037 }
1038
1039 let slot = self
1040 .data_buffers
1041 .get_mut(&bindless_index)
1042 .expect("Data buffer binding array should exist")
1043 .insert(&data);
1044 allocated_resource_slots.insert(bindless_index, slot);
1045 false
1046 }
1047 OwnedBindingResource::TextureView(texture_view_dimension, texture_view) => {
1048 let bindless_resource_type = BindlessResourceType::from(texture_view_dimension);
1049 let slot = self
1050 .textures
1051 .get_mut(&bindless_resource_type)
1052 .expect("Texture array should exist")
1053 .insert(binding_resource_id, texture_view);
1054 allocated_resource_slots.insert(bindless_index, slot);
1055
1056 if let Some(pre_existing_slot) = pre_existing_slot {
1057 assert_eq!(*pre_existing_slot, slot);
1058
1059 false
1060 } else {
1061 true
1062 }
1063 }
1064 OwnedBindingResource::Sampler(sampler_binding_type, sampler) => {
1065 let bindless_resource_type = BindlessResourceType::from(sampler_binding_type);
1066 let slot = self
1067 .samplers
1068 .get_mut(&bindless_resource_type)
1069 .expect("Sampler should exist")
1070 .insert(binding_resource_id, sampler);
1071 allocated_resource_slots.insert(bindless_index, slot);
1072
1073 if let Some(pre_existing_slot) = pre_existing_slot {
1074 assert_eq!(*pre_existing_slot, slot);
1075
1076 false
1077 } else {
1078 true
1079 }
1080 }
1081 };
1082
1083 if increment_allocated_resource_count {
1085 self.allocated_resource_count += 1;
1086 }
1087 }
1088
1089 allocated_resource_slots
1090 }
1091
1092 fn free(&mut self, slot: MaterialBindGroupSlot, bindless_descriptor: &BindlessDescriptor) {
1095 for (bindless_index, bindless_resource_type) in
1097 bindless_descriptor.resources.iter().enumerate()
1098 {
1099 let bindless_index = BindlessIndex::from(bindless_index as u32);
1100 let Some(bindless_index_table) = self.get_bindless_index_table(bindless_index) else {
1101 continue;
1102 };
1103 let Some(bindless_binding) = bindless_index_table.get_binding(slot, bindless_index)
1104 else {
1105 continue;
1106 };
1107
1108 let decrement_allocated_resource_count = match *bindless_resource_type {
1112 BindlessResourceType::None => false,
1113 BindlessResourceType::Buffer => self
1114 .buffers
1115 .get_mut(&bindless_index)
1116 .expect("Buffer should exist with that bindless index")
1117 .remove(bindless_binding),
1118 BindlessResourceType::DataBuffer => {
1119 self.data_buffers
1120 .get_mut(&bindless_index)
1121 .expect("Data buffer should exist with that bindless index")
1122 .remove(bindless_binding);
1123 false
1124 }
1125 BindlessResourceType::SamplerFiltering
1126 | BindlessResourceType::SamplerNonFiltering
1127 | BindlessResourceType::SamplerComparison => self
1128 .samplers
1129 .get_mut(bindless_resource_type)
1130 .expect("Sampler array should exist")
1131 .remove(bindless_binding),
1132 BindlessResourceType::Texture1d
1133 | BindlessResourceType::Texture2d
1134 | BindlessResourceType::Texture2dArray
1135 | BindlessResourceType::Texture3d
1136 | BindlessResourceType::TextureCube
1137 | BindlessResourceType::TextureCubeArray => self
1138 .textures
1139 .get_mut(bindless_resource_type)
1140 .expect("Texture array should exist")
1141 .remove(bindless_binding),
1142 };
1143
1144 if decrement_allocated_resource_count {
1147 self.allocated_resource_count -= 1;
1148 }
1149 }
1150
1151 self.bind_group = None;
1153
1154 self.free_slots.push(slot);
1156 self.live_allocation_count -= 1;
1157 }
1158
1159 fn prepare(
1161 &mut self,
1162 render_device: &RenderDevice,
1163 label: Option<&'static str>,
1164 bind_group_layout: &BindGroupLayout,
1165 fallback_bindless_resources: &FallbackBindlessResources,
1166 fallback_buffers: &HashMap<BindlessIndex, Buffer>,
1167 fallback_image: &FallbackImage,
1168 bindless_descriptor: &BindlessDescriptor,
1169 slab_capacity: u32,
1170 ) {
1171 for bindless_index_table in &mut self.bindless_index_tables {
1173 bindless_index_table.buffer.prepare(render_device);
1174 }
1175
1176 for data_buffer in self.data_buffers.values_mut() {
1178 data_buffer.buffer.prepare(render_device);
1179 }
1180
1181 self.prepare_bind_group(
1183 render_device,
1184 label,
1185 bind_group_layout,
1186 fallback_bindless_resources,
1187 fallback_buffers,
1188 fallback_image,
1189 bindless_descriptor,
1190 slab_capacity,
1191 );
1192 }
1193
1194 fn prepare_bind_group(
1197 &mut self,
1198 render_device: &RenderDevice,
1199 label: Option<&'static str>,
1200 bind_group_layout: &BindGroupLayout,
1201 fallback_bindless_resources: &FallbackBindlessResources,
1202 fallback_buffers: &HashMap<BindlessIndex, Buffer>,
1203 fallback_image: &FallbackImage,
1204 bindless_descriptor: &BindlessDescriptor,
1205 slab_capacity: u32,
1206 ) {
1207 if self.bind_group.is_some() {
1209 return;
1210 }
1211
1212 let required_binding_array_size = if render_device
1215 .features()
1216 .contains(WgpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY)
1217 {
1218 None
1219 } else {
1220 Some(slab_capacity)
1221 };
1222
1223 let binding_resource_arrays = self.create_binding_resource_arrays(
1224 fallback_bindless_resources,
1225 fallback_buffers,
1226 fallback_image,
1227 bindless_descriptor,
1228 required_binding_array_size,
1229 );
1230
1231 let mut bind_group_entries: Vec<_> = self
1232 .bindless_index_tables
1233 .iter()
1234 .map(|bindless_index_table| bindless_index_table.bind_group_entry())
1235 .collect();
1236
1237 for &(&binding, ref binding_resource_array) in binding_resource_arrays.iter() {
1238 bind_group_entries.push(BindGroupEntry {
1239 binding,
1240 resource: match *binding_resource_array {
1241 BindingResourceArray::Buffers(ref buffer_bindings) => {
1242 BindingResource::BufferArray(&buffer_bindings[..])
1243 }
1244 BindingResourceArray::TextureViews(ref texture_views) => {
1245 BindingResource::TextureViewArray(&texture_views[..])
1246 }
1247 BindingResourceArray::Samplers(ref samplers) => {
1248 BindingResource::SamplerArray(&samplers[..])
1249 }
1250 },
1251 });
1252 }
1253
1254 for data_buffer in self.data_buffers.values() {
1256 bind_group_entries.push(BindGroupEntry {
1257 binding: *data_buffer.binding_number,
1258 resource: data_buffer
1259 .buffer
1260 .buffer()
1261 .expect("Backing data buffer must have been uploaded by now")
1262 .as_entire_binding(),
1263 });
1264 }
1265
1266 self.bind_group =
1267 Some(render_device.create_bind_group(label, bind_group_layout, &bind_group_entries));
1268 }
1269
1270 fn write_buffer(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
1275 for bindless_index_table in &mut self.bindless_index_tables {
1276 bindless_index_table
1277 .buffer
1278 .write(render_device, render_queue);
1279 }
1280
1281 for data_buffer in self.data_buffers.values_mut() {
1282 data_buffer.buffer.write(render_device, render_queue);
1283 }
1284 }
1285
1286 fn create_binding_resource_arrays<'a>(
1289 &'a self,
1290 fallback_bindless_resources: &'a FallbackBindlessResources,
1291 fallback_buffers: &'a HashMap<BindlessIndex, Buffer>,
1292 fallback_image: &'a FallbackImage,
1293 bindless_descriptor: &'a BindlessDescriptor,
1294 required_binding_array_size: Option<u32>,
1295 ) -> Vec<(&'a u32, BindingResourceArray<'a>)> {
1296 let mut binding_resource_arrays = vec![];
1297
1298 self.create_sampler_binding_resource_arrays(
1300 &mut binding_resource_arrays,
1301 fallback_bindless_resources,
1302 required_binding_array_size,
1303 );
1304
1305 self.create_texture_binding_resource_arrays(
1307 &mut binding_resource_arrays,
1308 fallback_image,
1309 required_binding_array_size,
1310 );
1311
1312 self.create_buffer_binding_resource_arrays(
1314 &mut binding_resource_arrays,
1315 fallback_buffers,
1316 bindless_descriptor,
1317 required_binding_array_size,
1318 );
1319
1320 binding_resource_arrays
1321 }
1322
1323 fn create_sampler_binding_resource_arrays<'a, 'b>(
1326 &'a self,
1327 binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
1328 fallback_bindless_resources: &'a FallbackBindlessResources,
1329 required_binding_array_size: Option<u32>,
1330 ) {
1331 for (bindless_resource_type, fallback_sampler) in [
1333 (
1334 BindlessResourceType::SamplerFiltering,
1335 &fallback_bindless_resources.filtering_sampler,
1336 ),
1337 (
1338 BindlessResourceType::SamplerNonFiltering,
1339 &fallback_bindless_resources.non_filtering_sampler,
1340 ),
1341 (
1342 BindlessResourceType::SamplerComparison,
1343 &fallback_bindless_resources.comparison_sampler,
1344 ),
1345 ] {
1346 let mut sampler_bindings = vec![];
1347
1348 match self.samplers.get(&bindless_resource_type) {
1349 Some(sampler_bindless_binding_array) => {
1350 for maybe_bindless_binding in sampler_bindless_binding_array.bindings.iter() {
1351 match *maybe_bindless_binding {
1352 Some(ref bindless_binding) => {
1353 sampler_bindings.push(&*bindless_binding.resource);
1354 }
1355 None => sampler_bindings.push(&**fallback_sampler),
1356 }
1357 }
1358 }
1359
1360 None => {
1361 sampler_bindings.push(&**fallback_sampler);
1363 }
1364 }
1365
1366 if let Some(required_binding_array_size) = required_binding_array_size {
1367 sampler_bindings.extend(iter::repeat_n(
1368 &**fallback_sampler,
1369 required_binding_array_size as usize - sampler_bindings.len(),
1370 ));
1371 }
1372
1373 let binding_number = bindless_resource_type
1374 .binding_number()
1375 .expect("Sampler bindless resource type must have a binding number");
1376
1377 binding_resource_arrays.push((
1378 &**binding_number,
1379 BindingResourceArray::Samplers(sampler_bindings),
1380 ));
1381 }
1382 }
1383
1384 fn create_texture_binding_resource_arrays<'a, 'b>(
1387 &'a self,
1388 binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
1389 fallback_image: &'a FallbackImage,
1390 required_binding_array_size: Option<u32>,
1391 ) {
1392 for (bindless_resource_type, fallback_image) in [
1393 (BindlessResourceType::Texture1d, &fallback_image.d1),
1394 (BindlessResourceType::Texture2d, &fallback_image.d2),
1395 (
1396 BindlessResourceType::Texture2dArray,
1397 &fallback_image.d2_array,
1398 ),
1399 (BindlessResourceType::Texture3d, &fallback_image.d3),
1400 (BindlessResourceType::TextureCube, &fallback_image.cube),
1401 (
1402 BindlessResourceType::TextureCubeArray,
1403 &fallback_image.cube_array,
1404 ),
1405 ] {
1406 let mut texture_bindings = vec![];
1407
1408 let binding_number = bindless_resource_type
1409 .binding_number()
1410 .expect("Texture bindless resource type must have a binding number");
1411
1412 match self.textures.get(&bindless_resource_type) {
1413 Some(texture_bindless_binding_array) => {
1414 for maybe_bindless_binding in texture_bindless_binding_array.bindings.iter() {
1415 match *maybe_bindless_binding {
1416 Some(ref bindless_binding) => {
1417 texture_bindings.push(&*bindless_binding.resource);
1418 }
1419 None => texture_bindings.push(&*fallback_image.texture_view),
1420 }
1421 }
1422 }
1423
1424 None => {
1425 texture_bindings.push(&*fallback_image.texture_view);
1427 }
1428 }
1429
1430 if let Some(required_binding_array_size) = required_binding_array_size {
1431 texture_bindings.extend(iter::repeat_n(
1432 &*fallback_image.texture_view,
1433 required_binding_array_size as usize - texture_bindings.len(),
1434 ));
1435 }
1436
1437 binding_resource_arrays.push((
1438 binding_number,
1439 BindingResourceArray::TextureViews(texture_bindings),
1440 ));
1441 }
1442 }
1443
1444 fn create_buffer_binding_resource_arrays<'a, 'b>(
1447 &'a self,
1448 binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
1449 fallback_buffers: &'a HashMap<BindlessIndex, Buffer>,
1450 bindless_descriptor: &'a BindlessDescriptor,
1451 required_binding_array_size: Option<u32>,
1452 ) {
1453 for bindless_buffer_descriptor in bindless_descriptor.buffers.iter() {
1454 let Some(buffer_bindless_binding_array) =
1455 self.buffers.get(&bindless_buffer_descriptor.bindless_index)
1456 else {
1457 continue;
1461 };
1462
1463 let fallback_buffer = fallback_buffers
1464 .get(&bindless_buffer_descriptor.bindless_index)
1465 .expect("Fallback buffer should exist");
1466
1467 let mut buffer_bindings: Vec<_> = buffer_bindless_binding_array
1468 .bindings
1469 .iter()
1470 .map(|maybe_bindless_binding| {
1471 let buffer = match *maybe_bindless_binding {
1472 None => fallback_buffer,
1473 Some(ref bindless_binding) => &bindless_binding.resource,
1474 };
1475 BufferBinding {
1476 buffer,
1477 offset: 0,
1478 size: None,
1479 }
1480 })
1481 .collect();
1482
1483 if let Some(required_binding_array_size) = required_binding_array_size {
1484 buffer_bindings.extend(iter::repeat_n(
1485 BufferBinding {
1486 buffer: fallback_buffer,
1487 offset: 0,
1488 size: None,
1489 },
1490 required_binding_array_size as usize - buffer_bindings.len(),
1491 ));
1492 }
1493
1494 binding_resource_arrays.push((
1495 &*buffer_bindless_binding_array.binding_number,
1496 BindingResourceArray::Buffers(buffer_bindings),
1497 ));
1498 }
1499 }
1500
1501 fn bind_group(&self) -> Option<&BindGroup> {
1504 self.bind_group.as_ref()
1505 }
1506
1507 fn get_bindless_index_table(
1509 &self,
1510 bindless_index: BindlessIndex,
1511 ) -> Option<&MaterialBindlessIndexTable> {
1512 let table_index = self
1513 .bindless_index_tables
1514 .binary_search_by(|bindless_index_table| {
1515 if bindless_index < bindless_index_table.index_range.start {
1516 Ordering::Less
1517 } else if bindless_index >= bindless_index_table.index_range.end {
1518 Ordering::Greater
1519 } else {
1520 Ordering::Equal
1521 }
1522 })
1523 .ok()?;
1524 self.bindless_index_tables.get(table_index)
1525 }
1526}
1527
1528impl<R> MaterialBindlessBindingArray<R>
1529where
1530 R: GetBindingResourceId,
1531{
1532 fn new(
1535 binding_number: BindingNumber,
1536 resource_type: BindlessResourceType,
1537 ) -> MaterialBindlessBindingArray<R> {
1538 MaterialBindlessBindingArray {
1539 binding_number,
1540 bindings: vec![],
1541 resource_type,
1542 resource_to_slot: HashMap::default(),
1543 free_slots: vec![],
1544 len: 0,
1545 }
1546 }
1547
1548 fn find(&self, binding_resource_id: BindingResourceId) -> Option<u32> {
1553 self.resource_to_slot.get(&binding_resource_id).copied()
1554 }
1555
1556 fn insert(&mut self, binding_resource_id: BindingResourceId, resource: R) -> u32 {
1559 match self.resource_to_slot.entry(binding_resource_id) {
1560 bevy_platform::collections::hash_map::Entry::Occupied(o) => {
1561 let slot = *o.get();
1562
1563 self.bindings[slot as usize]
1564 .as_mut()
1565 .expect("A slot in the resource_to_slot map should have a value")
1566 .ref_count += 1;
1567
1568 slot
1569 }
1570 bevy_platform::collections::hash_map::Entry::Vacant(v) => {
1571 let slot = self.free_slots.pop().unwrap_or(self.len);
1572 v.insert(slot);
1573
1574 if self.bindings.len() < slot as usize + 1 {
1575 self.bindings.resize_with(slot as usize + 1, || None);
1576 }
1577 self.bindings[slot as usize] = Some(MaterialBindlessBinding::new(resource));
1578
1579 self.len += 1;
1580 slot
1581 }
1582 }
1583 }
1584
1585 fn remove(&mut self, slot: u32) -> bool {
1591 let maybe_binding = &mut self.bindings[slot as usize];
1592 let binding = maybe_binding
1593 .as_mut()
1594 .expect("Attempted to free an already-freed binding");
1595
1596 binding.ref_count -= 1;
1597 if binding.ref_count != 0 {
1598 return false;
1599 }
1600
1601 let binding_resource_id = binding.resource.binding_resource_id(self.resource_type);
1602 self.resource_to_slot.remove(&binding_resource_id);
1603
1604 *maybe_binding = None;
1605 self.free_slots.push(slot);
1606 self.len -= 1;
1607 true
1608 }
1609}
1610
1611impl<R> MaterialBindlessBinding<R>
1612where
1613 R: GetBindingResourceId,
1614{
1615 fn new(resource: R) -> MaterialBindlessBinding<R> {
1619 MaterialBindlessBinding {
1620 resource,
1621 ref_count: 1,
1622 }
1623 }
1624}
1625
1626pub fn material_uses_bindless_resources<M>(render_device: &RenderDevice) -> bool
1632where
1633 M: Material,
1634{
1635 M::bindless_slot_count().is_some_and(|bindless_slot_count| {
1636 M::bindless_supported(render_device) && bindless_slot_count.resolve() > 1
1637 })
1638}
1639
1640impl MaterialBindlessSlab {
1641 fn new(bindless_descriptor: &BindlessDescriptor) -> MaterialBindlessSlab {
1646 let mut buffers = HashMap::default();
1647 let mut samplers = HashMap::default();
1648 let mut textures = HashMap::default();
1649 let mut data_buffers = HashMap::default();
1650
1651 for (bindless_index, bindless_resource_type) in
1652 bindless_descriptor.resources.iter().enumerate()
1653 {
1654 let bindless_index = BindlessIndex(bindless_index as u32);
1655 match *bindless_resource_type {
1656 BindlessResourceType::None => {}
1657 BindlessResourceType::Buffer => {
1658 let binding_number = bindless_descriptor
1659 .buffers
1660 .iter()
1661 .find(|bindless_buffer_descriptor| {
1662 bindless_buffer_descriptor.bindless_index == bindless_index
1663 })
1664 .expect(
1665 "Bindless buffer descriptor matching that bindless index should be \
1666 present",
1667 )
1668 .binding_number;
1669 buffers.insert(
1670 bindless_index,
1671 MaterialBindlessBindingArray::new(binding_number, *bindless_resource_type),
1672 );
1673 }
1674 BindlessResourceType::DataBuffer => {
1675 let buffer_descriptor = bindless_descriptor
1677 .buffers
1678 .iter()
1679 .find(|bindless_buffer_descriptor| {
1680 bindless_buffer_descriptor.bindless_index == bindless_index
1681 })
1682 .expect(
1683 "Bindless buffer descriptor matching that bindless index should be \
1684 present",
1685 );
1686 data_buffers.insert(
1687 bindless_index,
1688 MaterialDataBuffer::new(
1689 buffer_descriptor.binding_number,
1690 buffer_descriptor
1691 .size
1692 .expect("Data buffers should have a size")
1693 as u32,
1694 ),
1695 );
1696 }
1697 BindlessResourceType::SamplerFiltering
1698 | BindlessResourceType::SamplerNonFiltering
1699 | BindlessResourceType::SamplerComparison => {
1700 samplers.insert(
1701 *bindless_resource_type,
1702 MaterialBindlessBindingArray::new(
1703 *bindless_resource_type.binding_number().unwrap(),
1704 *bindless_resource_type,
1705 ),
1706 );
1707 }
1708 BindlessResourceType::Texture1d
1709 | BindlessResourceType::Texture2d
1710 | BindlessResourceType::Texture2dArray
1711 | BindlessResourceType::Texture3d
1712 | BindlessResourceType::TextureCube
1713 | BindlessResourceType::TextureCubeArray => {
1714 textures.insert(
1715 *bindless_resource_type,
1716 MaterialBindlessBindingArray::new(
1717 *bindless_resource_type.binding_number().unwrap(),
1718 *bindless_resource_type,
1719 ),
1720 );
1721 }
1722 }
1723 }
1724
1725 let bindless_index_tables = bindless_descriptor
1726 .index_tables
1727 .iter()
1728 .map(MaterialBindlessIndexTable::new)
1729 .collect();
1730
1731 MaterialBindlessSlab {
1732 bind_group: None,
1733 bindless_index_tables,
1734 samplers,
1735 textures,
1736 buffers,
1737 data_buffers,
1738 free_slots: vec![],
1739 live_allocation_count: 0,
1740 allocated_resource_count: 0,
1741 }
1742 }
1743}
1744
1745pub fn init_fallback_bindless_resources(mut commands: Commands, render_device: Res<RenderDevice>) {
1746 commands.insert_resource(FallbackBindlessResources {
1747 filtering_sampler: render_device.create_sampler(&SamplerDescriptor {
1748 label: Some("fallback filtering sampler"),
1749 ..default()
1750 }),
1751 non_filtering_sampler: render_device.create_sampler(&SamplerDescriptor {
1752 label: Some("fallback non-filtering sampler"),
1753 mag_filter: FilterMode::Nearest,
1754 min_filter: FilterMode::Nearest,
1755 mipmap_filter: FilterMode::Nearest,
1756 ..default()
1757 }),
1758 comparison_sampler: render_device.create_sampler(&SamplerDescriptor {
1759 label: Some("fallback comparison sampler"),
1760 compare: Some(CompareFunction::Always),
1761 ..default()
1762 }),
1763 });
1764}
1765
1766impl MaterialBindGroupNonBindlessAllocator {
1767 fn new(label: Option<&'static str>) -> MaterialBindGroupNonBindlessAllocator {
1770 MaterialBindGroupNonBindlessAllocator {
1771 label,
1772 bind_groups: vec![],
1773 to_prepare: HashSet::default(),
1774 free_indices: vec![],
1775 }
1776 }
1777
1778 fn allocate(&mut self, bind_group: MaterialNonBindlessAllocatedBindGroup) -> MaterialBindingId {
1784 let group_id = self
1785 .free_indices
1786 .pop()
1787 .unwrap_or(MaterialBindGroupIndex(self.bind_groups.len() as u32));
1788 if self.bind_groups.len() < *group_id as usize + 1 {
1789 self.bind_groups
1790 .resize_with(*group_id as usize + 1, || None);
1791 }
1792
1793 if matches!(
1794 bind_group,
1795 MaterialNonBindlessAllocatedBindGroup::Unprepared { .. }
1796 ) {
1797 self.to_prepare.insert(group_id);
1798 }
1799
1800 self.bind_groups[*group_id as usize] = Some(bind_group);
1801
1802 MaterialBindingId {
1803 group: group_id,
1804 slot: default(),
1805 }
1806 }
1807
1808 fn allocate_unprepared(
1811 &mut self,
1812 unprepared_bind_group: UnpreparedBindGroup,
1813 bind_group_layout: BindGroupLayout,
1814 ) -> MaterialBindingId {
1815 self.allocate(MaterialNonBindlessAllocatedBindGroup::Unprepared {
1816 bind_group: unprepared_bind_group,
1817 layout: bind_group_layout,
1818 })
1819 }
1820
1821 fn allocate_prepared(&mut self, prepared_bind_group: PreparedBindGroup) -> MaterialBindingId {
1824 self.allocate(MaterialNonBindlessAllocatedBindGroup::Prepared {
1825 bind_group: prepared_bind_group,
1826 uniform_buffers: vec![],
1827 })
1828 }
1829
1830 fn free(&mut self, binding_id: MaterialBindingId) {
1832 debug_assert_eq!(binding_id.slot, MaterialBindGroupSlot(0));
1833 debug_assert!(self.bind_groups[*binding_id.group as usize].is_some());
1834 self.bind_groups[*binding_id.group as usize] = None;
1835 self.to_prepare.remove(&binding_id.group);
1836 self.free_indices.push(binding_id.group);
1837 }
1838
1839 fn get(&self, group: MaterialBindGroupIndex) -> Option<MaterialNonBindlessSlab<'_>> {
1841 self.bind_groups[group.0 as usize]
1842 .as_ref()
1843 .map(|bind_group| match bind_group {
1844 MaterialNonBindlessAllocatedBindGroup::Prepared { bind_group, .. } => {
1845 MaterialNonBindlessSlab::Prepared(bind_group)
1846 }
1847 MaterialNonBindlessAllocatedBindGroup::Unprepared { .. } => {
1848 MaterialNonBindlessSlab::Unprepared
1849 }
1850 })
1851 }
1852
1853 fn prepare_bind_groups(&mut self, render_device: &RenderDevice) {
1860 for bind_group_index in mem::take(&mut self.to_prepare) {
1861 let Some(MaterialNonBindlessAllocatedBindGroup::Unprepared {
1862 bind_group: unprepared_bind_group,
1863 layout: bind_group_layout,
1864 }) = mem::take(&mut self.bind_groups[*bind_group_index as usize])
1865 else {
1866 panic!("Allocation didn't exist or was already prepared");
1867 };
1868
1869 let mut uniform_buffers = vec![];
1871 for (index, binding) in unprepared_bind_group.bindings.iter() {
1872 let OwnedBindingResource::Data(ref owned_data) = *binding else {
1873 continue;
1874 };
1875 let label = format!("material uniform data {}", *index);
1876 let uniform_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
1877 label: Some(&label),
1878 contents: &owned_data.0,
1879 usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM,
1880 });
1881 uniform_buffers.push(uniform_buffer);
1882 }
1883
1884 let mut bind_group_entries = vec![];
1886 let mut uniform_buffers_iter = uniform_buffers.iter();
1887 for (index, binding) in unprepared_bind_group.bindings.iter() {
1888 match *binding {
1889 OwnedBindingResource::Data(_) => {
1890 bind_group_entries.push(BindGroupEntry {
1891 binding: *index,
1892 resource: uniform_buffers_iter
1893 .next()
1894 .expect("We should have created uniform buffers for each `Data`")
1895 .as_entire_binding(),
1896 });
1897 }
1898 _ => bind_group_entries.push(BindGroupEntry {
1899 binding: *index,
1900 resource: binding.get_binding(),
1901 }),
1902 }
1903 }
1904
1905 let bind_group = render_device.create_bind_group(
1907 self.label,
1908 &bind_group_layout,
1909 &bind_group_entries,
1910 );
1911
1912 self.bind_groups[*bind_group_index as usize] =
1913 Some(MaterialNonBindlessAllocatedBindGroup::Prepared {
1914 bind_group: PreparedBindGroup {
1915 bindings: unprepared_bind_group.bindings,
1916 bind_group,
1917 },
1918 uniform_buffers,
1919 });
1920 }
1921 }
1922}
1923
1924impl<'a> MaterialSlab<'a> {
1925 pub fn bind_group(&self) -> Option<&'a BindGroup> {
1932 match self.0 {
1933 MaterialSlabImpl::Bindless(material_bindless_slab) => {
1934 material_bindless_slab.bind_group()
1935 }
1936 MaterialSlabImpl::NonBindless(MaterialNonBindlessSlab::Prepared(
1937 prepared_bind_group,
1938 )) => Some(&prepared_bind_group.bind_group),
1939 MaterialSlabImpl::NonBindless(MaterialNonBindlessSlab::Unprepared) => None,
1940 }
1941 }
1942}
1943
1944impl MaterialDataBuffer {
1945 fn new(binding_number: BindingNumber, aligned_element_size: u32) -> MaterialDataBuffer {
1949 MaterialDataBuffer {
1950 binding_number,
1951 buffer: RetainedRawBufferVec::new(BufferUsages::STORAGE),
1952 aligned_element_size,
1953 free_slots: vec![],
1954 len: 0,
1955 }
1956 }
1957
1958 fn insert(&mut self, data: &[u8]) -> u32 {
1964 debug_assert_eq!(data.len(), self.aligned_element_size as usize);
1966
1967 let slot = self.free_slots.pop().unwrap_or(self.len);
1969
1970 let start = slot as usize * self.aligned_element_size as usize;
1972 let end = (slot as usize + 1) * self.aligned_element_size as usize;
1973
1974 if self.buffer.len() < end {
1976 self.buffer.reserve_internal(end);
1977 }
1978 while self.buffer.values().len() < end {
1979 self.buffer.push(0);
1980 }
1981
1982 self.buffer.values_mut()[start..end].copy_from_slice(data);
1984
1985 self.len += 1;
1987 self.buffer.dirty = BufferDirtyState::NeedsReserve;
1988 slot
1989 }
1990
1991 fn remove(&mut self, slot: u32) {
1993 self.free_slots.push(slot);
1994 self.len -= 1;
1995 }
1996}