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, PipelineCache};
16use bevy_render::{
17 render_resource::{
18 BindGroup, BindGroupEntry, BindGroupLayoutDescriptor, BindingNumber, BindingResource,
19 BindingResources, BindlessDescriptor, BindlessIndex, BindlessIndexTableDescriptor,
20 BindlessResourceType, Buffer, BufferBinding, BufferDescriptor, BufferId,
21 BufferInitDescriptor, BufferUsages, CompareFunction, FilterMode, MipmapFilterMode,
22 OwnedBindingResource, PreparedBindGroup, RawBufferVec, Sampler, SamplerDescriptor,
23 SamplerId, TextureView, TextureViewDimension, TextureViewId, UnpreparedBindGroup,
24 WgpuSampler, WgpuTextureView,
25 },
26 renderer::{RenderDevice, RenderQueue},
27 settings::WgpuFeatures,
28 texture::FallbackImage,
29};
30use bevy_utils::{default, TypeIdMap};
31use bytemuck::{Pod, Zeroable};
32use core::hash::Hash;
33use core::{cmp::Ordering, iter, mem, ops::Range};
34use tracing::{error, trace};
35
36#[derive(Resource, Deref, DerefMut, Default)]
37pub struct MaterialBindGroupAllocators(TypeIdMap<MaterialBindGroupAllocator>);
38
39pub enum MaterialBindGroupAllocator {
46 Bindless(Box<MaterialBindGroupBindlessAllocator>),
48 NonBindless(Box<MaterialBindGroupNonBindlessAllocator>),
50}
51
52pub struct MaterialBindGroupBindlessAllocator {
55 label: &'static str,
57 slabs: Vec<MaterialBindlessSlab>,
59 bind_group_layout: BindGroupLayoutDescriptor,
61 bindless_descriptor: BindlessDescriptor,
65
66 fallback_buffers: HashMap<BindlessIndex, Buffer>,
72
73 slab_capacity: u32,
78}
79
80pub struct MaterialBindlessSlab {
82 bind_group: Option<BindGroup>,
87
88 bindless_index_tables: Vec<MaterialBindlessIndexTable>,
98
99 samplers: HashMap<BindlessResourceType, MaterialBindlessBindingArray<Sampler>>,
101 textures: HashMap<BindlessResourceType, MaterialBindlessBindingArray<TextureView>>,
103 buffers: HashMap<BindlessIndex, MaterialBindlessBindingArray<Buffer>>,
105 data_buffers: HashMap<BindlessIndex, MaterialDataBuffer>,
108
109 free_slots: Vec<MaterialBindGroupSlot>,
111 live_allocation_count: u32,
113 allocated_resource_count: u32,
115}
116
117struct MaterialBindlessIndexTable {
124 buffer: RetainedRawBufferVec<u32>,
126 index_range: Range<BindlessIndex>,
134 binding_number: BindingNumber,
136}
137
138struct MaterialBindlessBindingArray<R>
141where
142 R: GetBindingResourceId,
143{
144 binding_number: BindingNumber,
146 bindings: Vec<Option<MaterialBindlessBinding<R>>>,
149 resource_type: BindlessResourceType,
151 resource_to_slot: HashMap<BindingResourceId, u32>,
155 free_slots: Vec<u32>,
157 len: u32,
159}
160
161struct MaterialBindlessBinding<R>
167where
168 R: GetBindingResourceId,
169{
170 resource: R,
172 ref_count: u32,
175}
176
177pub struct MaterialBindGroupNonBindlessAllocator {
179 label: &'static str,
181 bind_groups: Vec<Option<MaterialNonBindlessAllocatedBindGroup>>,
184 to_prepare: HashSet<MaterialBindGroupIndex>,
189 free_indices: Vec<MaterialBindGroupIndex>,
191}
192
193enum MaterialNonBindlessAllocatedBindGroup {
196 Unprepared {
202 bind_group: UnpreparedBindGroup,
204 layout: BindGroupLayoutDescriptor,
206 },
207 Prepared {
209 bind_group: PreparedBindGroup,
210 #[expect(dead_code, reason = "These buffers are only referenced by bind groups")]
211 uniform_buffers: Vec<Buffer>,
212 },
213}
214
215#[derive(Resource)]
218pub struct FallbackBindlessResources {
219 filtering_sampler: Sampler,
221 non_filtering_sampler: Sampler,
223 comparison_sampler: Sampler,
225}
226
227#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
229enum BindingResourceId {
230 Buffer(BufferId),
232 TextureView(TextureViewDimension, TextureViewId),
234 Sampler(SamplerId),
236 DataBuffer,
241}
242
243enum BindingResourceArray<'a> {
249 Buffers(Vec<BufferBinding<'a>>),
251 TextureViews(Vec<&'a WgpuTextureView>),
253 Samplers(Vec<&'a WgpuSampler>),
255}
256
257#[derive(Clone, Copy, Debug, Default, Pod, Zeroable, Reflect)]
260#[reflect(Clone, Default)]
261#[repr(C)]
262pub struct MaterialBindingId {
263 pub group: MaterialBindGroupIndex,
265 pub slot: MaterialBindGroupSlot,
269}
270
271#[derive(
276 Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Pod, Zeroable, Reflect, Deref, DerefMut,
277)]
278#[reflect(Default, Clone, PartialEq, Hash)]
279#[repr(C)]
280pub struct MaterialBindGroupIndex(pub u32);
281
282impl From<u32> for MaterialBindGroupIndex {
283 fn from(value: u32) -> Self {
284 MaterialBindGroupIndex(value)
285 }
286}
287
288#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable, Reflect, Deref, DerefMut)]
295#[reflect(Default, Clone, PartialEq)]
296#[repr(C)]
297pub struct MaterialBindGroupSlot(pub u32);
298
299enum BufferDirtyState {
304 Clean,
306 NeedsReserve,
308 NeedsUpload,
310}
311
312struct BindlessAllocationCandidate {
315 pre_existing_resources: HashMap<BindlessIndex, u32>,
319 needed_free_slots: u32,
322}
323
324trait GetBindingResourceId {
331 fn binding_resource_id(&self, resource_type: BindlessResourceType) -> BindingResourceId;
337}
338
339pub struct MaterialSlab<'a>(MaterialSlabImpl<'a>);
341
342enum MaterialSlabImpl<'a> {
346 Bindless(&'a MaterialBindlessSlab),
349 NonBindless(MaterialNonBindlessSlab<'a>),
352}
353
354enum MaterialNonBindlessSlab<'a> {
357 Prepared(&'a PreparedBindGroup),
359 Unprepared,
361}
362
363struct MaterialDataBuffer {
368 binding_number: BindingNumber,
370 buffer: RetainedRawBufferVec<u8>,
375 aligned_element_size: u32,
378 free_slots: Vec<u32>,
380 len: u32,
382}
383
384#[derive(Deref, DerefMut)]
390struct RetainedRawBufferVec<T>
391where
392 T: Pod,
393{
394 #[deref]
396 buffer: RawBufferVec<T>,
397 dirty: BufferDirtyState,
399}
400
401const DEFAULT_BINDLESS_FALLBACK_BUFFER_SIZE: u64 = 16;
406
407impl From<u32> for MaterialBindGroupSlot {
408 fn from(value: u32) -> Self {
409 MaterialBindGroupSlot(value)
410 }
411}
412
413impl From<MaterialBindGroupSlot> for u32 {
414 fn from(value: MaterialBindGroupSlot) -> Self {
415 value.0
416 }
417}
418
419impl<'a> From<&'a OwnedBindingResource> for BindingResourceId {
420 fn from(value: &'a OwnedBindingResource) -> Self {
421 match *value {
422 OwnedBindingResource::Buffer(ref buffer) => BindingResourceId::Buffer(buffer.id()),
423 OwnedBindingResource::Data(_) => BindingResourceId::DataBuffer,
424 OwnedBindingResource::TextureView(ref texture_view_dimension, ref texture_view) => {
425 BindingResourceId::TextureView(*texture_view_dimension, texture_view.id())
426 }
427 OwnedBindingResource::Sampler(_, ref sampler) => {
428 BindingResourceId::Sampler(sampler.id())
429 }
430 }
431 }
432}
433
434impl GetBindingResourceId for Buffer {
435 fn binding_resource_id(&self, _: BindlessResourceType) -> BindingResourceId {
436 BindingResourceId::Buffer(self.id())
437 }
438}
439
440impl GetBindingResourceId for Sampler {
441 fn binding_resource_id(&self, _: BindlessResourceType) -> BindingResourceId {
442 BindingResourceId::Sampler(self.id())
443 }
444}
445
446impl GetBindingResourceId for TextureView {
447 fn binding_resource_id(&self, resource_type: BindlessResourceType) -> BindingResourceId {
448 let texture_view_dimension = match resource_type {
449 BindlessResourceType::Texture1d => TextureViewDimension::D1,
450 BindlessResourceType::Texture2d => TextureViewDimension::D2,
451 BindlessResourceType::Texture2dArray => TextureViewDimension::D2Array,
452 BindlessResourceType::Texture3d => TextureViewDimension::D3,
453 BindlessResourceType::TextureCube => TextureViewDimension::Cube,
454 BindlessResourceType::TextureCubeArray => TextureViewDimension::CubeArray,
455 _ => panic!("Resource type is not a texture"),
456 };
457 BindingResourceId::TextureView(texture_view_dimension, self.id())
458 }
459}
460
461impl MaterialBindGroupAllocator {
462 pub fn new(
465 render_device: &RenderDevice,
466 label: &'static str,
467 bindless_descriptor: Option<BindlessDescriptor>,
468 bind_group_layout: BindGroupLayoutDescriptor,
469 slab_capacity: Option<BindlessSlabResourceLimit>,
470 ) -> MaterialBindGroupAllocator {
471 if let Some(bindless_descriptor) = bindless_descriptor {
472 MaterialBindGroupAllocator::Bindless(Box::new(MaterialBindGroupBindlessAllocator::new(
473 render_device,
474 label,
475 bindless_descriptor,
476 bind_group_layout,
477 slab_capacity,
478 )))
479 } else {
480 MaterialBindGroupAllocator::NonBindless(Box::new(
481 MaterialBindGroupNonBindlessAllocator::new(label),
482 ))
483 }
484 }
485
486 pub fn get(&self, group: MaterialBindGroupIndex) -> Option<MaterialSlab<'_>> {
488 match *self {
489 MaterialBindGroupAllocator::Bindless(ref bindless_allocator) => bindless_allocator
490 .get(group)
491 .map(|bindless_slab| MaterialSlab(MaterialSlabImpl::Bindless(bindless_slab))),
492 MaterialBindGroupAllocator::NonBindless(ref non_bindless_allocator) => {
493 non_bindless_allocator.get(group).map(|non_bindless_slab| {
494 MaterialSlab(MaterialSlabImpl::NonBindless(non_bindless_slab))
495 })
496 }
497 }
498 }
499
500 pub fn allocate_unprepared(
507 &mut self,
508 unprepared_bind_group: UnpreparedBindGroup,
509 bind_group_layout: &BindGroupLayoutDescriptor,
510 ) -> MaterialBindingId {
511 match *self {
512 MaterialBindGroupAllocator::Bindless(
513 ref mut material_bind_group_bindless_allocator,
514 ) => material_bind_group_bindless_allocator.allocate_unprepared(unprepared_bind_group),
515 MaterialBindGroupAllocator::NonBindless(
516 ref mut material_bind_group_non_bindless_allocator,
517 ) => material_bind_group_non_bindless_allocator
518 .allocate_unprepared(unprepared_bind_group, (*bind_group_layout).clone()),
519 }
520 }
521
522 pub fn allocate_prepared(
532 &mut self,
533 prepared_bind_group: PreparedBindGroup,
534 ) -> MaterialBindingId {
535 match *self {
536 MaterialBindGroupAllocator::Bindless(_) => {
537 panic!(
538 "Bindless resources are incompatible with implementing `as_bind_group` \
539 directly; implement `unprepared_bind_group` instead or disable bindless"
540 )
541 }
542 MaterialBindGroupAllocator::NonBindless(ref mut non_bindless_allocator) => {
543 non_bindless_allocator.allocate_prepared(prepared_bind_group)
544 }
545 }
546 }
547
548 pub fn free(&mut self, material_binding_id: MaterialBindingId) {
552 match *self {
553 MaterialBindGroupAllocator::Bindless(
554 ref mut material_bind_group_bindless_allocator,
555 ) => material_bind_group_bindless_allocator.free(material_binding_id),
556 MaterialBindGroupAllocator::NonBindless(
557 ref mut material_bind_group_non_bindless_allocator,
558 ) => material_bind_group_non_bindless_allocator.free(material_binding_id),
559 }
560 }
561
562 pub fn prepare_bind_groups(
565 &mut self,
566 render_device: &RenderDevice,
567 pipeline_cache: &PipelineCache,
568 fallback_bindless_resources: &FallbackBindlessResources,
569 fallback_image: &FallbackImage,
570 ) {
571 match *self {
572 MaterialBindGroupAllocator::Bindless(
573 ref mut material_bind_group_bindless_allocator,
574 ) => material_bind_group_bindless_allocator.prepare_bind_groups(
575 render_device,
576 pipeline_cache,
577 fallback_bindless_resources,
578 fallback_image,
579 ),
580 MaterialBindGroupAllocator::NonBindless(
581 ref mut material_bind_group_non_bindless_allocator,
582 ) => material_bind_group_non_bindless_allocator
583 .prepare_bind_groups(render_device, pipeline_cache),
584 }
585 }
586
587 pub fn write_buffers(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
593 match *self {
594 MaterialBindGroupAllocator::Bindless(
595 ref mut material_bind_group_bindless_allocator,
596 ) => material_bind_group_bindless_allocator.write_buffers(render_device, render_queue),
597 MaterialBindGroupAllocator::NonBindless(_) => {
598 }
600 }
601 }
602
603 pub fn slab_count(&self) -> usize {
606 match self {
607 Self::Bindless(bless) => bless.slabs.len(),
608 Self::NonBindless(_) => 0,
609 }
610 }
611
612 pub fn slabs_size(&self) -> usize {
615 match self {
616 Self::Bindless(bless) => bless
617 .slabs
618 .iter()
619 .flat_map(|slab| {
620 slab.data_buffers
621 .iter()
622 .map(|(_, buffer)| buffer.buffer.len())
623 })
624 .sum(),
625 Self::NonBindless(_) => 0,
626 }
627 }
628
629 pub fn allocations(&self) -> u64 {
632 match self {
633 Self::Bindless(bless) => bless
634 .slabs
635 .iter()
636 .map(|slab| u64::from(slab.allocated_resource_count))
637 .sum(),
638 Self::NonBindless(_) => 0,
639 }
640 }
641}
642
643impl MaterialBindlessIndexTable {
644 fn new(
646 bindless_index_table_descriptor: &BindlessIndexTableDescriptor,
647 ) -> MaterialBindlessIndexTable {
648 let mut buffer = RetainedRawBufferVec::new(BufferUsages::STORAGE);
650 for _ in *bindless_index_table_descriptor.indices.start
651 ..*bindless_index_table_descriptor.indices.end
652 {
653 buffer.push(0);
654 }
655
656 MaterialBindlessIndexTable {
657 buffer,
658 index_range: bindless_index_table_descriptor.indices.clone(),
659 binding_number: bindless_index_table_descriptor.binding_number,
660 }
661 }
662
663 fn get(&self, slot: MaterialBindGroupSlot) -> &[u32] {
669 let struct_size = *self.index_range.end as usize - *self.index_range.start as usize;
670 let start = struct_size * slot.0 as usize;
671 &self.buffer.values()[start..(start + struct_size)]
672 }
673
674 fn get_binding(
676 &self,
677 slot: MaterialBindGroupSlot,
678 bindless_index: BindlessIndex,
679 ) -> Option<u32> {
680 if bindless_index < self.index_range.start || bindless_index >= self.index_range.end {
681 return None;
682 }
683 self.get(slot)
684 .get((*bindless_index - *self.index_range.start) as usize)
685 .copied()
686 }
687
688 fn table_length(&self) -> u32 {
689 self.index_range.end.0 - self.index_range.start.0
690 }
691
692 fn set(
700 &mut self,
701 slot: MaterialBindGroupSlot,
702 allocated_resource_slots: &HashMap<BindlessIndex, u32>,
703 ) {
704 let table_len = self.table_length() as usize;
705 let range = (slot.0 as usize * table_len)..((slot.0 as usize + 1) * table_len);
706 while self.buffer.len() < range.end {
707 self.buffer.push(0);
708 }
709
710 for (&bindless_index, &resource_slot) in allocated_resource_slots {
711 if self.index_range.contains(&bindless_index) {
712 self.buffer.set(
713 *bindless_index + range.start as u32 - *self.index_range.start,
714 resource_slot,
715 );
716 }
717 }
718
719 self.buffer.dirty = BufferDirtyState::NeedsReserve;
721 }
722
723 fn bind_group_entry(&self) -> BindGroupEntry<'_> {
725 BindGroupEntry {
726 binding: *self.binding_number,
727 resource: self
728 .buffer
729 .buffer()
730 .expect("Bindings buffer must exist")
731 .as_entire_binding(),
732 }
733 }
734}
735
736impl<T> RetainedRawBufferVec<T>
737where
738 T: Pod,
739{
740 fn new(buffer_usages: BufferUsages) -> RetainedRawBufferVec<T> {
743 RetainedRawBufferVec {
744 buffer: RawBufferVec::new(buffer_usages),
745 dirty: BufferDirtyState::NeedsUpload,
746 }
747 }
748
749 fn prepare(&mut self, render_device: &RenderDevice) {
751 match self.dirty {
752 BufferDirtyState::Clean | BufferDirtyState::NeedsUpload => {}
753 BufferDirtyState::NeedsReserve => {
754 let capacity = self.buffer.len();
755 self.buffer.reserve(capacity, render_device);
756 self.dirty = BufferDirtyState::NeedsUpload;
757 }
758 }
759 }
760
761 fn write(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
763 match self.dirty {
764 BufferDirtyState::Clean => {}
765 BufferDirtyState::NeedsReserve | BufferDirtyState::NeedsUpload => {
766 self.buffer.write_buffer(render_device, render_queue);
767 self.dirty = BufferDirtyState::Clean;
768 }
769 }
770 }
771}
772
773impl MaterialBindGroupBindlessAllocator {
774 fn new(
777 render_device: &RenderDevice,
778 label: &'static str,
779 bindless_descriptor: BindlessDescriptor,
780 bind_group_layout: BindGroupLayoutDescriptor,
781 slab_capacity: Option<BindlessSlabResourceLimit>,
782 ) -> MaterialBindGroupBindlessAllocator {
783 let fallback_buffers = bindless_descriptor
784 .buffers
785 .iter()
786 .map(|bindless_buffer_descriptor| {
787 (
788 bindless_buffer_descriptor.bindless_index,
789 render_device.create_buffer(&BufferDescriptor {
790 label: Some("bindless fallback buffer"),
791 size: match bindless_buffer_descriptor.size {
792 Some(size) => size as u64,
793 None => DEFAULT_BINDLESS_FALLBACK_BUFFER_SIZE,
794 },
795 usage: BufferUsages::STORAGE,
796 mapped_at_creation: false,
797 }),
798 )
799 })
800 .collect();
801
802 MaterialBindGroupBindlessAllocator {
803 label,
804 slabs: vec![],
805 bind_group_layout,
806 bindless_descriptor,
807 fallback_buffers,
808 slab_capacity: slab_capacity
809 .expect("Non-bindless materials should use the non-bindless allocator")
810 .resolve(),
811 }
812 }
813
814 fn allocate_unprepared(
823 &mut self,
824 mut unprepared_bind_group: UnpreparedBindGroup,
825 ) -> MaterialBindingId {
826 for (slab_index, slab) in self.slabs.iter_mut().enumerate() {
827 trace!("Trying to allocate in slab {}", slab_index);
828 match slab.try_allocate(unprepared_bind_group, self.slab_capacity) {
829 Ok(slot) => {
830 return MaterialBindingId {
831 group: MaterialBindGroupIndex(slab_index as u32),
832 slot,
833 };
834 }
835 Err(bind_group) => unprepared_bind_group = bind_group,
836 }
837 }
838
839 let group = MaterialBindGroupIndex(self.slabs.len() as u32);
840 self.slabs
841 .push(MaterialBindlessSlab::new(&self.bindless_descriptor));
842
843 let Ok(slot) = self
845 .slabs
846 .last_mut()
847 .expect("We just pushed a slab")
848 .try_allocate(unprepared_bind_group, self.slab_capacity)
849 else {
850 panic!("An allocation into an empty slab should always succeed")
851 };
852
853 MaterialBindingId { group, slot }
854 }
855
856 fn free(&mut self, material_binding_id: MaterialBindingId) {
860 self.slabs
861 .get_mut(material_binding_id.group.0 as usize)
862 .expect("Slab should exist")
863 .free(material_binding_id.slot, &self.bindless_descriptor);
864 }
865
866 fn get(&self, group: MaterialBindGroupIndex) -> Option<&MaterialBindlessSlab> {
871 self.slabs.get(group.0 as usize)
872 }
873
874 fn prepare_bind_groups(
878 &mut self,
879 render_device: &RenderDevice,
880 pipeline_cache: &PipelineCache,
881 fallback_bindless_resources: &FallbackBindlessResources,
882 fallback_image: &FallbackImage,
883 ) {
884 for slab in &mut self.slabs {
885 slab.prepare(
886 render_device,
887 pipeline_cache,
888 self.label,
889 &self.bind_group_layout,
890 fallback_bindless_resources,
891 &self.fallback_buffers,
892 fallback_image,
893 &self.bindless_descriptor,
894 self.slab_capacity,
895 );
896 }
897 }
898
899 fn write_buffers(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
903 for slab in &mut self.slabs {
904 slab.write_buffer(render_device, render_queue);
905 }
906 }
907}
908
909impl MaterialBindlessSlab {
910 fn try_allocate(
917 &mut self,
918 unprepared_bind_group: UnpreparedBindGroup,
919 slot_capacity: u32,
920 ) -> Result<MaterialBindGroupSlot, UnpreparedBindGroup> {
921 let Some(allocation_candidate) = self.check_allocation(&unprepared_bind_group) else {
923 return Err(unprepared_bind_group);
924 };
925
926 if self.allocated_resource_count > 0
935 && self.allocated_resource_count + allocation_candidate.needed_free_slots
936 > slot_capacity
937 {
938 trace!("Slab is full, can't allocate");
939 return Err(unprepared_bind_group);
940 }
941
942 let slot = match self.free_slots.pop() {
944 Some(slot) => slot,
945 None => {
946 if self.live_allocation_count > 0xFFFF {
949 trace!("Slab material bind group slot would overflow, can't allocate");
950 return Err(unprepared_bind_group);
951 }
952 MaterialBindGroupSlot(self.live_allocation_count)
953 }
954 };
955
956 self.live_allocation_count += 1;
958
959 let allocated_resource_slots =
961 self.insert_resources(unprepared_bind_group.bindings, allocation_candidate);
962
963 for bindless_index_table in &mut self.bindless_index_tables {
965 bindless_index_table.set(slot, &allocated_resource_slots);
966 }
967
968 self.bind_group = None;
970
971 Ok(slot)
972 }
973
974 fn check_allocation(
977 &self,
978 unprepared_bind_group: &UnpreparedBindGroup,
979 ) -> Option<BindlessAllocationCandidate> {
980 let mut allocation_candidate = BindlessAllocationCandidate {
981 pre_existing_resources: HashMap::default(),
982 needed_free_slots: 0,
983 };
984
985 for &(bindless_index, ref owned_binding_resource) in unprepared_bind_group.bindings.iter() {
986 let bindless_index = BindlessIndex(bindless_index);
987 match *owned_binding_resource {
988 OwnedBindingResource::Buffer(ref buffer) => {
989 let Some(binding_array) = self.buffers.get(&bindless_index) else {
990 error!(
991 "Binding array wasn't present for buffer at index {:?}",
992 bindless_index
993 );
994 return None;
995 };
996 match binding_array.find(BindingResourceId::Buffer(buffer.id())) {
997 Some(slot) => {
998 allocation_candidate
999 .pre_existing_resources
1000 .insert(bindless_index, slot);
1001 }
1002 None => allocation_candidate.needed_free_slots += 1,
1003 }
1004 }
1005
1006 OwnedBindingResource::Data(_) => {
1007 }
1009
1010 OwnedBindingResource::TextureView(texture_view_dimension, ref texture_view) => {
1011 let bindless_resource_type = BindlessResourceType::from(texture_view_dimension);
1012 match self
1013 .textures
1014 .get(&bindless_resource_type)
1015 .expect("Missing binding array for texture")
1016 .find(BindingResourceId::TextureView(
1017 texture_view_dimension,
1018 texture_view.id(),
1019 )) {
1020 Some(slot) => {
1021 allocation_candidate
1022 .pre_existing_resources
1023 .insert(bindless_index, slot);
1024 }
1025 None => {
1026 allocation_candidate.needed_free_slots += 1;
1027 }
1028 }
1029 }
1030
1031 OwnedBindingResource::Sampler(sampler_binding_type, ref sampler) => {
1032 let bindless_resource_type = BindlessResourceType::from(sampler_binding_type);
1033 match self
1034 .samplers
1035 .get(&bindless_resource_type)
1036 .expect("Missing binding array for sampler")
1037 .find(BindingResourceId::Sampler(sampler.id()))
1038 {
1039 Some(slot) => {
1040 allocation_candidate
1041 .pre_existing_resources
1042 .insert(bindless_index, slot);
1043 }
1044 None => {
1045 allocation_candidate.needed_free_slots += 1;
1046 }
1047 }
1048 }
1049 }
1050 }
1051
1052 Some(allocation_candidate)
1053 }
1054
1055 fn insert_resources(
1060 &mut self,
1061 mut binding_resources: BindingResources,
1062 allocation_candidate: BindlessAllocationCandidate,
1063 ) -> HashMap<BindlessIndex, u32> {
1064 let mut allocated_resource_slots = HashMap::default();
1065
1066 for (bindless_index, owned_binding_resource) in binding_resources.drain(..) {
1067 let bindless_index = BindlessIndex(bindless_index);
1068
1069 let pre_existing_slot = allocation_candidate
1070 .pre_existing_resources
1071 .get(&bindless_index);
1072
1073 let binding_resource_id = BindingResourceId::from(&owned_binding_resource);
1075 let increment_allocated_resource_count = match owned_binding_resource {
1076 OwnedBindingResource::Buffer(buffer) => {
1077 let slot = self
1078 .buffers
1079 .get_mut(&bindless_index)
1080 .expect("Buffer binding array should exist")
1081 .insert(binding_resource_id, buffer);
1082 allocated_resource_slots.insert(bindless_index, slot);
1083
1084 if let Some(pre_existing_slot) = pre_existing_slot {
1085 assert_eq!(*pre_existing_slot, slot);
1086
1087 false
1088 } else {
1089 true
1090 }
1091 }
1092 OwnedBindingResource::Data(data) => {
1093 if pre_existing_slot.is_some() {
1094 panic!("Data buffers can't be deduplicated")
1095 }
1096
1097 let slot = self
1098 .data_buffers
1099 .get_mut(&bindless_index)
1100 .expect("Data buffer binding array should exist")
1101 .insert(&data);
1102 allocated_resource_slots.insert(bindless_index, slot);
1103 false
1104 }
1105 OwnedBindingResource::TextureView(texture_view_dimension, texture_view) => {
1106 let bindless_resource_type = BindlessResourceType::from(texture_view_dimension);
1107 let slot = self
1108 .textures
1109 .get_mut(&bindless_resource_type)
1110 .expect("Texture array should exist")
1111 .insert(binding_resource_id, texture_view);
1112 allocated_resource_slots.insert(bindless_index, slot);
1113
1114 if let Some(pre_existing_slot) = pre_existing_slot {
1115 assert_eq!(*pre_existing_slot, slot);
1116
1117 false
1118 } else {
1119 true
1120 }
1121 }
1122 OwnedBindingResource::Sampler(sampler_binding_type, sampler) => {
1123 let bindless_resource_type = BindlessResourceType::from(sampler_binding_type);
1124 let slot = self
1125 .samplers
1126 .get_mut(&bindless_resource_type)
1127 .expect("Sampler should exist")
1128 .insert(binding_resource_id, sampler);
1129 allocated_resource_slots.insert(bindless_index, slot);
1130
1131 if let Some(pre_existing_slot) = pre_existing_slot {
1132 assert_eq!(*pre_existing_slot, slot);
1133
1134 false
1135 } else {
1136 true
1137 }
1138 }
1139 };
1140
1141 if increment_allocated_resource_count {
1143 self.allocated_resource_count += 1;
1144 }
1145 }
1146
1147 allocated_resource_slots
1148 }
1149
1150 fn free(&mut self, slot: MaterialBindGroupSlot, bindless_descriptor: &BindlessDescriptor) {
1153 for (bindless_index, bindless_resource_type) in
1155 bindless_descriptor.resources.iter().enumerate()
1156 {
1157 let bindless_index = BindlessIndex::from(bindless_index as u32);
1158 let Some(bindless_index_table) = self.get_bindless_index_table(bindless_index) else {
1159 continue;
1160 };
1161 let Some(bindless_binding) = bindless_index_table.get_binding(slot, bindless_index)
1162 else {
1163 continue;
1164 };
1165
1166 let decrement_allocated_resource_count = match *bindless_resource_type {
1170 BindlessResourceType::None => false,
1171 BindlessResourceType::Buffer => self
1172 .buffers
1173 .get_mut(&bindless_index)
1174 .expect("Buffer should exist with that bindless index")
1175 .remove(bindless_binding),
1176 BindlessResourceType::DataBuffer => {
1177 self.data_buffers
1178 .get_mut(&bindless_index)
1179 .expect("Data buffer should exist with that bindless index")
1180 .remove(bindless_binding);
1181 false
1182 }
1183 BindlessResourceType::SamplerFiltering
1184 | BindlessResourceType::SamplerNonFiltering
1185 | BindlessResourceType::SamplerComparison => self
1186 .samplers
1187 .get_mut(bindless_resource_type)
1188 .expect("Sampler array should exist")
1189 .remove(bindless_binding),
1190 BindlessResourceType::Texture1d
1191 | BindlessResourceType::Texture2d
1192 | BindlessResourceType::Texture2dArray
1193 | BindlessResourceType::Texture3d
1194 | BindlessResourceType::TextureCube
1195 | BindlessResourceType::TextureCubeArray => self
1196 .textures
1197 .get_mut(bindless_resource_type)
1198 .expect("Texture array should exist")
1199 .remove(bindless_binding),
1200 };
1201
1202 if decrement_allocated_resource_count {
1205 self.allocated_resource_count -= 1;
1206 }
1207 }
1208
1209 self.bind_group = None;
1211
1212 self.free_slots.push(slot);
1214 self.live_allocation_count -= 1;
1215 }
1216
1217 fn prepare(
1219 &mut self,
1220 render_device: &RenderDevice,
1221 pipeline_cache: &PipelineCache,
1222 label: &'static str,
1223 bind_group_layout: &BindGroupLayoutDescriptor,
1224 fallback_bindless_resources: &FallbackBindlessResources,
1225 fallback_buffers: &HashMap<BindlessIndex, Buffer>,
1226 fallback_image: &FallbackImage,
1227 bindless_descriptor: &BindlessDescriptor,
1228 slab_capacity: u32,
1229 ) {
1230 for bindless_index_table in &mut self.bindless_index_tables {
1232 bindless_index_table.buffer.prepare(render_device);
1233 }
1234
1235 for data_buffer in self.data_buffers.values_mut() {
1237 data_buffer.buffer.prepare(render_device);
1238 }
1239
1240 self.prepare_bind_group(
1242 render_device,
1243 pipeline_cache,
1244 label,
1245 bind_group_layout,
1246 fallback_bindless_resources,
1247 fallback_buffers,
1248 fallback_image,
1249 bindless_descriptor,
1250 slab_capacity,
1251 );
1252 }
1253
1254 fn prepare_bind_group(
1257 &mut self,
1258 render_device: &RenderDevice,
1259 pipeline_cache: &PipelineCache,
1260 label: &'static str,
1261 bind_group_layout: &BindGroupLayoutDescriptor,
1262 fallback_bindless_resources: &FallbackBindlessResources,
1263 fallback_buffers: &HashMap<BindlessIndex, Buffer>,
1264 fallback_image: &FallbackImage,
1265 bindless_descriptor: &BindlessDescriptor,
1266 slab_capacity: u32,
1267 ) {
1268 if self.bind_group.is_some() {
1270 return;
1271 }
1272
1273 let required_binding_array_size = if render_device
1276 .features()
1277 .contains(WgpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY)
1278 {
1279 None
1280 } else {
1281 Some(slab_capacity)
1282 };
1283
1284 let binding_resource_arrays = self.create_binding_resource_arrays(
1285 fallback_bindless_resources,
1286 fallback_buffers,
1287 fallback_image,
1288 bindless_descriptor,
1289 required_binding_array_size,
1290 );
1291
1292 let mut bind_group_entries: Vec<_> = self
1293 .bindless_index_tables
1294 .iter()
1295 .map(|bindless_index_table| bindless_index_table.bind_group_entry())
1296 .collect();
1297
1298 for &(&binding, ref binding_resource_array) in binding_resource_arrays.iter() {
1299 bind_group_entries.push(BindGroupEntry {
1300 binding,
1301 resource: match *binding_resource_array {
1302 BindingResourceArray::Buffers(ref buffer_bindings) => {
1303 BindingResource::BufferArray(&buffer_bindings[..])
1304 }
1305 BindingResourceArray::TextureViews(ref texture_views) => {
1306 BindingResource::TextureViewArray(&texture_views[..])
1307 }
1308 BindingResourceArray::Samplers(ref samplers) => {
1309 BindingResource::SamplerArray(&samplers[..])
1310 }
1311 },
1312 });
1313 }
1314
1315 for data_buffer in self.data_buffers.values() {
1317 bind_group_entries.push(BindGroupEntry {
1318 binding: *data_buffer.binding_number,
1319 resource: data_buffer
1320 .buffer
1321 .buffer()
1322 .expect("Backing data buffer must have been uploaded by now")
1323 .as_entire_binding(),
1324 });
1325 }
1326
1327 self.bind_group = Some(render_device.create_bind_group(
1328 Some(label),
1329 &pipeline_cache.get_bind_group_layout(bind_group_layout),
1330 &bind_group_entries,
1331 ));
1332 }
1333
1334 fn write_buffer(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
1339 for bindless_index_table in &mut self.bindless_index_tables {
1340 bindless_index_table
1341 .buffer
1342 .write(render_device, render_queue);
1343 }
1344
1345 for data_buffer in self.data_buffers.values_mut() {
1346 data_buffer.buffer.write(render_device, render_queue);
1347 }
1348 }
1349
1350 fn create_binding_resource_arrays<'a>(
1353 &'a self,
1354 fallback_bindless_resources: &'a FallbackBindlessResources,
1355 fallback_buffers: &'a HashMap<BindlessIndex, Buffer>,
1356 fallback_image: &'a FallbackImage,
1357 bindless_descriptor: &'a BindlessDescriptor,
1358 required_binding_array_size: Option<u32>,
1359 ) -> Vec<(&'a u32, BindingResourceArray<'a>)> {
1360 let mut binding_resource_arrays = vec![];
1361
1362 self.create_sampler_binding_resource_arrays(
1364 &mut binding_resource_arrays,
1365 fallback_bindless_resources,
1366 bindless_descriptor,
1367 required_binding_array_size,
1368 );
1369
1370 self.create_texture_binding_resource_arrays(
1372 &mut binding_resource_arrays,
1373 fallback_image,
1374 bindless_descriptor,
1375 required_binding_array_size,
1376 );
1377
1378 self.create_buffer_binding_resource_arrays(
1380 &mut binding_resource_arrays,
1381 fallback_buffers,
1382 bindless_descriptor,
1383 required_binding_array_size,
1384 );
1385
1386 binding_resource_arrays
1387 }
1388
1389 fn create_sampler_binding_resource_arrays<'a, 'b>(
1392 &'a self,
1393 binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
1394 fallback_bindless_resources: &'a FallbackBindlessResources,
1395 bindless_descriptor: &'a BindlessDescriptor,
1396 required_binding_array_size: Option<u32>,
1397 ) {
1398 for (bindless_resource_type, fallback_sampler) in [
1400 (
1401 BindlessResourceType::SamplerFiltering,
1402 &fallback_bindless_resources.filtering_sampler,
1403 ),
1404 (
1405 BindlessResourceType::SamplerNonFiltering,
1406 &fallback_bindless_resources.non_filtering_sampler,
1407 ),
1408 (
1409 BindlessResourceType::SamplerComparison,
1410 &fallback_bindless_resources.comparison_sampler,
1411 ),
1412 ] {
1413 if !bindless_descriptor
1415 .resources
1416 .contains(&bindless_resource_type)
1417 {
1418 continue;
1419 }
1420
1421 let mut sampler_bindings = vec![];
1422
1423 match self.samplers.get(&bindless_resource_type) {
1424 Some(sampler_bindless_binding_array) => {
1425 for maybe_bindless_binding in sampler_bindless_binding_array.bindings.iter() {
1426 match *maybe_bindless_binding {
1427 Some(ref bindless_binding) => {
1428 sampler_bindings.push(&*bindless_binding.resource);
1429 }
1430 None => sampler_bindings.push(&**fallback_sampler),
1431 }
1432 }
1433 }
1434
1435 None => {
1436 sampler_bindings.push(&**fallback_sampler);
1438 }
1439 }
1440
1441 if let Some(required_binding_array_size) = required_binding_array_size {
1442 sampler_bindings.extend(iter::repeat_n(
1443 &**fallback_sampler,
1444 required_binding_array_size as usize - sampler_bindings.len(),
1445 ));
1446 }
1447
1448 let binding_number = bindless_resource_type
1449 .binding_number()
1450 .expect("Sampler bindless resource type must have a binding number");
1451
1452 binding_resource_arrays.push((
1453 &**binding_number,
1454 BindingResourceArray::Samplers(sampler_bindings),
1455 ));
1456 }
1457 }
1458
1459 fn create_texture_binding_resource_arrays<'a, 'b>(
1462 &'a self,
1463 binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
1464 fallback_image: &'a FallbackImage,
1465 bindless_descriptor: &'a BindlessDescriptor,
1466 required_binding_array_size: Option<u32>,
1467 ) {
1468 for (bindless_resource_type, fallback_image) in [
1469 (BindlessResourceType::Texture1d, &fallback_image.d1),
1470 (BindlessResourceType::Texture2d, &fallback_image.d2),
1471 (
1472 BindlessResourceType::Texture2dArray,
1473 &fallback_image.d2_array,
1474 ),
1475 (BindlessResourceType::Texture3d, &fallback_image.d3),
1476 (BindlessResourceType::TextureCube, &fallback_image.cube),
1477 (
1478 BindlessResourceType::TextureCubeArray,
1479 &fallback_image.cube_array,
1480 ),
1481 ] {
1482 if !bindless_descriptor
1484 .resources
1485 .contains(&bindless_resource_type)
1486 {
1487 continue;
1488 }
1489
1490 let mut texture_bindings = vec![];
1491
1492 let binding_number = bindless_resource_type
1493 .binding_number()
1494 .expect("Texture bindless resource type must have a binding number");
1495
1496 match self.textures.get(&bindless_resource_type) {
1497 Some(texture_bindless_binding_array) => {
1498 for maybe_bindless_binding in texture_bindless_binding_array.bindings.iter() {
1499 match *maybe_bindless_binding {
1500 Some(ref bindless_binding) => {
1501 texture_bindings.push(&*bindless_binding.resource);
1502 }
1503 None => texture_bindings.push(&*fallback_image.texture_view),
1504 }
1505 }
1506 }
1507
1508 None => {
1509 texture_bindings.push(&*fallback_image.texture_view);
1511 }
1512 }
1513
1514 if let Some(required_binding_array_size) = required_binding_array_size {
1515 texture_bindings.extend(iter::repeat_n(
1516 &*fallback_image.texture_view,
1517 required_binding_array_size as usize - texture_bindings.len(),
1518 ));
1519 }
1520
1521 binding_resource_arrays.push((
1522 binding_number,
1523 BindingResourceArray::TextureViews(texture_bindings),
1524 ));
1525 }
1526 }
1527
1528 fn create_buffer_binding_resource_arrays<'a, 'b>(
1531 &'a self,
1532 binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
1533 fallback_buffers: &'a HashMap<BindlessIndex, Buffer>,
1534 bindless_descriptor: &'a BindlessDescriptor,
1535 required_binding_array_size: Option<u32>,
1536 ) {
1537 for bindless_buffer_descriptor in bindless_descriptor.buffers.iter() {
1538 let Some(buffer_bindless_binding_array) =
1539 self.buffers.get(&bindless_buffer_descriptor.bindless_index)
1540 else {
1541 continue;
1545 };
1546
1547 let fallback_buffer = fallback_buffers
1548 .get(&bindless_buffer_descriptor.bindless_index)
1549 .expect("Fallback buffer should exist");
1550
1551 let mut buffer_bindings: Vec<_> = buffer_bindless_binding_array
1552 .bindings
1553 .iter()
1554 .map(|maybe_bindless_binding| {
1555 let buffer = match *maybe_bindless_binding {
1556 None => fallback_buffer,
1557 Some(ref bindless_binding) => &bindless_binding.resource,
1558 };
1559 BufferBinding {
1560 buffer,
1561 offset: 0,
1562 size: None,
1563 }
1564 })
1565 .collect();
1566
1567 if let Some(required_binding_array_size) = required_binding_array_size {
1568 buffer_bindings.extend(iter::repeat_n(
1569 BufferBinding {
1570 buffer: fallback_buffer,
1571 offset: 0,
1572 size: None,
1573 },
1574 required_binding_array_size as usize - buffer_bindings.len(),
1575 ));
1576 }
1577
1578 binding_resource_arrays.push((
1579 &*buffer_bindless_binding_array.binding_number,
1580 BindingResourceArray::Buffers(buffer_bindings),
1581 ));
1582 }
1583 }
1584
1585 fn bind_group(&self) -> Option<&BindGroup> {
1588 self.bind_group.as_ref()
1589 }
1590
1591 fn get_bindless_index_table(
1593 &self,
1594 bindless_index: BindlessIndex,
1595 ) -> Option<&MaterialBindlessIndexTable> {
1596 let table_index = self
1597 .bindless_index_tables
1598 .binary_search_by(|bindless_index_table| {
1599 if bindless_index < bindless_index_table.index_range.start {
1600 Ordering::Less
1601 } else if bindless_index >= bindless_index_table.index_range.end {
1602 Ordering::Greater
1603 } else {
1604 Ordering::Equal
1605 }
1606 })
1607 .ok()?;
1608 self.bindless_index_tables.get(table_index)
1609 }
1610}
1611
1612impl<R> MaterialBindlessBindingArray<R>
1613where
1614 R: GetBindingResourceId,
1615{
1616 fn new(
1619 binding_number: BindingNumber,
1620 resource_type: BindlessResourceType,
1621 ) -> MaterialBindlessBindingArray<R> {
1622 MaterialBindlessBindingArray {
1623 binding_number,
1624 bindings: vec![],
1625 resource_type,
1626 resource_to_slot: HashMap::default(),
1627 free_slots: vec![],
1628 len: 0,
1629 }
1630 }
1631
1632 fn find(&self, binding_resource_id: BindingResourceId) -> Option<u32> {
1637 self.resource_to_slot.get(&binding_resource_id).copied()
1638 }
1639
1640 fn insert(&mut self, binding_resource_id: BindingResourceId, resource: R) -> u32 {
1643 match self.resource_to_slot.entry(binding_resource_id) {
1644 bevy_platform::collections::hash_map::Entry::Occupied(o) => {
1645 let slot = *o.get();
1646
1647 self.bindings[slot as usize]
1648 .as_mut()
1649 .expect("A slot in the resource_to_slot map should have a value")
1650 .ref_count += 1;
1651
1652 slot
1653 }
1654 bevy_platform::collections::hash_map::Entry::Vacant(v) => {
1655 let slot = self.free_slots.pop().unwrap_or(self.len);
1656 v.insert(slot);
1657
1658 if self.bindings.len() < slot as usize + 1 {
1659 self.bindings.resize_with(slot as usize + 1, || None);
1660 }
1661 debug_assert!(self.bindings[slot as usize].is_none());
1662 self.bindings[slot as usize] = Some(MaterialBindlessBinding::new(resource));
1663
1664 self.len += 1;
1665 slot
1666 }
1667 }
1668 }
1669
1670 fn remove(&mut self, slot: u32) -> bool {
1676 let maybe_binding = &mut self.bindings[slot as usize];
1677 let binding = maybe_binding
1678 .as_mut()
1679 .expect("Attempted to free an already-freed binding");
1680
1681 binding.ref_count -= 1;
1682 if binding.ref_count != 0 {
1683 return false;
1684 }
1685
1686 let binding_resource_id = binding.resource.binding_resource_id(self.resource_type);
1687 self.resource_to_slot.remove(&binding_resource_id);
1688
1689 *maybe_binding = None;
1690 self.free_slots.push(slot);
1691 self.len -= 1;
1692 true
1693 }
1694}
1695
1696impl<R> MaterialBindlessBinding<R>
1697where
1698 R: GetBindingResourceId,
1699{
1700 fn new(resource: R) -> MaterialBindlessBinding<R> {
1704 MaterialBindlessBinding {
1705 resource,
1706 ref_count: 1,
1707 }
1708 }
1709}
1710
1711pub fn material_uses_bindless_resources<M>(render_device: &RenderDevice) -> bool
1717where
1718 M: Material,
1719{
1720 M::bindless_slot_count().is_some_and(|bindless_slot_count| {
1721 M::bindless_supported(render_device) && bindless_slot_count.resolve() > 1
1722 })
1723}
1724
1725impl MaterialBindlessSlab {
1726 fn new(bindless_descriptor: &BindlessDescriptor) -> MaterialBindlessSlab {
1731 let mut buffers = HashMap::default();
1732 let mut samplers = HashMap::default();
1733 let mut textures = HashMap::default();
1734 let mut data_buffers = HashMap::default();
1735
1736 for (bindless_index, bindless_resource_type) in
1737 bindless_descriptor.resources.iter().enumerate()
1738 {
1739 let bindless_index = BindlessIndex(bindless_index as u32);
1740 match *bindless_resource_type {
1741 BindlessResourceType::None => {}
1742 BindlessResourceType::Buffer => {
1743 let binding_number = bindless_descriptor
1744 .buffers
1745 .iter()
1746 .find(|bindless_buffer_descriptor| {
1747 bindless_buffer_descriptor.bindless_index == bindless_index
1748 })
1749 .expect(
1750 "Bindless buffer descriptor matching that bindless index should be \
1751 present",
1752 )
1753 .binding_number;
1754 buffers.insert(
1755 bindless_index,
1756 MaterialBindlessBindingArray::new(binding_number, *bindless_resource_type),
1757 );
1758 }
1759 BindlessResourceType::DataBuffer => {
1760 let buffer_descriptor = bindless_descriptor
1762 .buffers
1763 .iter()
1764 .find(|bindless_buffer_descriptor| {
1765 bindless_buffer_descriptor.bindless_index == bindless_index
1766 })
1767 .expect(
1768 "Bindless buffer descriptor matching that bindless index should be \
1769 present",
1770 );
1771 data_buffers.insert(
1772 bindless_index,
1773 MaterialDataBuffer::new(
1774 buffer_descriptor.binding_number,
1775 buffer_descriptor
1776 .size
1777 .expect("Data buffers should have a size")
1778 as u32,
1779 ),
1780 );
1781 }
1782 BindlessResourceType::SamplerFiltering
1783 | BindlessResourceType::SamplerNonFiltering
1784 | BindlessResourceType::SamplerComparison => {
1785 samplers.insert(
1786 *bindless_resource_type,
1787 MaterialBindlessBindingArray::new(
1788 *bindless_resource_type.binding_number().unwrap(),
1789 *bindless_resource_type,
1790 ),
1791 );
1792 }
1793 BindlessResourceType::Texture1d
1794 | BindlessResourceType::Texture2d
1795 | BindlessResourceType::Texture2dArray
1796 | BindlessResourceType::Texture3d
1797 | BindlessResourceType::TextureCube
1798 | BindlessResourceType::TextureCubeArray => {
1799 textures.insert(
1800 *bindless_resource_type,
1801 MaterialBindlessBindingArray::new(
1802 *bindless_resource_type.binding_number().unwrap(),
1803 *bindless_resource_type,
1804 ),
1805 );
1806 }
1807 }
1808 }
1809
1810 let bindless_index_tables = bindless_descriptor
1811 .index_tables
1812 .iter()
1813 .map(MaterialBindlessIndexTable::new)
1814 .collect();
1815
1816 MaterialBindlessSlab {
1817 bind_group: None,
1818 bindless_index_tables,
1819 samplers,
1820 textures,
1821 buffers,
1822 data_buffers,
1823 free_slots: vec![],
1824 live_allocation_count: 0,
1825 allocated_resource_count: 0,
1826 }
1827 }
1828}
1829
1830pub fn init_fallback_bindless_resources(mut commands: Commands, render_device: Res<RenderDevice>) {
1831 commands.insert_resource(FallbackBindlessResources {
1832 filtering_sampler: render_device.create_sampler(&SamplerDescriptor {
1833 label: Some("fallback filtering sampler"),
1834 ..default()
1835 }),
1836 non_filtering_sampler: render_device.create_sampler(&SamplerDescriptor {
1837 label: Some("fallback non-filtering sampler"),
1838 mag_filter: FilterMode::Nearest,
1839 min_filter: FilterMode::Nearest,
1840 mipmap_filter: MipmapFilterMode::Nearest,
1841 ..default()
1842 }),
1843 comparison_sampler: render_device.create_sampler(&SamplerDescriptor {
1844 label: Some("fallback comparison sampler"),
1845 compare: Some(CompareFunction::Always),
1846 ..default()
1847 }),
1848 });
1849}
1850
1851impl MaterialBindGroupNonBindlessAllocator {
1852 fn new(label: &'static str) -> MaterialBindGroupNonBindlessAllocator {
1855 MaterialBindGroupNonBindlessAllocator {
1856 label,
1857 bind_groups: vec![],
1858 to_prepare: HashSet::default(),
1859 free_indices: vec![],
1860 }
1861 }
1862
1863 fn allocate(&mut self, bind_group: MaterialNonBindlessAllocatedBindGroup) -> MaterialBindingId {
1869 let group_id = self
1870 .free_indices
1871 .pop()
1872 .unwrap_or(MaterialBindGroupIndex(self.bind_groups.len() as u32));
1873 if self.bind_groups.len() < *group_id as usize + 1 {
1874 self.bind_groups
1875 .resize_with(*group_id as usize + 1, || None);
1876 }
1877
1878 if matches!(
1879 bind_group,
1880 MaterialNonBindlessAllocatedBindGroup::Unprepared { .. }
1881 ) {
1882 self.to_prepare.insert(group_id);
1883 }
1884
1885 self.bind_groups[*group_id as usize] = Some(bind_group);
1886
1887 MaterialBindingId {
1888 group: group_id,
1889 slot: default(),
1890 }
1891 }
1892
1893 fn allocate_unprepared(
1896 &mut self,
1897 unprepared_bind_group: UnpreparedBindGroup,
1898 bind_group_layout: BindGroupLayoutDescriptor,
1899 ) -> MaterialBindingId {
1900 self.allocate(MaterialNonBindlessAllocatedBindGroup::Unprepared {
1901 bind_group: unprepared_bind_group,
1902 layout: bind_group_layout,
1903 })
1904 }
1905
1906 fn allocate_prepared(&mut self, prepared_bind_group: PreparedBindGroup) -> MaterialBindingId {
1909 self.allocate(MaterialNonBindlessAllocatedBindGroup::Prepared {
1910 bind_group: prepared_bind_group,
1911 uniform_buffers: vec![],
1912 })
1913 }
1914
1915 fn free(&mut self, binding_id: MaterialBindingId) {
1917 debug_assert_eq!(binding_id.slot, MaterialBindGroupSlot(0));
1918 debug_assert!(self.bind_groups[*binding_id.group as usize].is_some());
1919 self.bind_groups[*binding_id.group as usize] = None;
1920 self.to_prepare.remove(&binding_id.group);
1921 self.free_indices.push(binding_id.group);
1922 }
1923
1924 fn get(&self, group: MaterialBindGroupIndex) -> Option<MaterialNonBindlessSlab<'_>> {
1926 self.bind_groups[group.0 as usize]
1927 .as_ref()
1928 .map(|bind_group| match bind_group {
1929 MaterialNonBindlessAllocatedBindGroup::Prepared { bind_group, .. } => {
1930 MaterialNonBindlessSlab::Prepared(bind_group)
1931 }
1932 MaterialNonBindlessAllocatedBindGroup::Unprepared { .. } => {
1933 MaterialNonBindlessSlab::Unprepared
1934 }
1935 })
1936 }
1937
1938 fn prepare_bind_groups(
1945 &mut self,
1946 render_device: &RenderDevice,
1947 pipeline_cache: &PipelineCache,
1948 ) {
1949 for bind_group_index in mem::take(&mut self.to_prepare) {
1950 let Some(MaterialNonBindlessAllocatedBindGroup::Unprepared {
1951 bind_group: unprepared_bind_group,
1952 layout: bind_group_layout,
1953 }) = mem::take(&mut self.bind_groups[*bind_group_index as usize])
1954 else {
1955 panic!("Allocation didn't exist or was already prepared");
1956 };
1957
1958 let mut uniform_buffers = vec![];
1960 for (index, binding) in unprepared_bind_group.bindings.iter() {
1961 let OwnedBindingResource::Data(ref owned_data) = *binding else {
1962 continue;
1963 };
1964 let label = format!("material uniform data {}", *index);
1965 let uniform_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
1966 label: Some(&label),
1967 contents: &owned_data.0,
1968 usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM,
1969 });
1970 uniform_buffers.push(uniform_buffer);
1971 }
1972
1973 let mut bind_group_entries = vec![];
1975 let mut uniform_buffers_iter = uniform_buffers.iter();
1976 for (index, binding) in unprepared_bind_group.bindings.iter() {
1977 match *binding {
1978 OwnedBindingResource::Data(_) => {
1979 bind_group_entries.push(BindGroupEntry {
1980 binding: *index,
1981 resource: uniform_buffers_iter
1982 .next()
1983 .expect("We should have created uniform buffers for each `Data`")
1984 .as_entire_binding(),
1985 });
1986 }
1987 _ => bind_group_entries.push(BindGroupEntry {
1988 binding: *index,
1989 resource: binding.get_binding(),
1990 }),
1991 }
1992 }
1993
1994 let bind_group = render_device.create_bind_group(
1996 self.label,
1997 &pipeline_cache.get_bind_group_layout(&bind_group_layout),
1998 &bind_group_entries,
1999 );
2000
2001 self.bind_groups[*bind_group_index as usize] =
2002 Some(MaterialNonBindlessAllocatedBindGroup::Prepared {
2003 bind_group: PreparedBindGroup {
2004 bindings: unprepared_bind_group.bindings,
2005 bind_group,
2006 },
2007 uniform_buffers,
2008 });
2009 }
2010 }
2011}
2012
2013impl<'a> MaterialSlab<'a> {
2014 pub fn bind_group(&self) -> Option<&'a BindGroup> {
2021 match self.0 {
2022 MaterialSlabImpl::Bindless(material_bindless_slab) => {
2023 material_bindless_slab.bind_group()
2024 }
2025 MaterialSlabImpl::NonBindless(MaterialNonBindlessSlab::Prepared(
2026 prepared_bind_group,
2027 )) => Some(&prepared_bind_group.bind_group),
2028 MaterialSlabImpl::NonBindless(MaterialNonBindlessSlab::Unprepared) => None,
2029 }
2030 }
2031}
2032
2033impl MaterialDataBuffer {
2034 fn new(binding_number: BindingNumber, aligned_element_size: u32) -> MaterialDataBuffer {
2038 MaterialDataBuffer {
2039 binding_number,
2040 buffer: RetainedRawBufferVec::new(BufferUsages::STORAGE),
2041 aligned_element_size,
2042 free_slots: vec![],
2043 len: 0,
2044 }
2045 }
2046
2047 fn insert(&mut self, data: &[u8]) -> u32 {
2053 debug_assert_eq!(data.len(), self.aligned_element_size as usize);
2055
2056 let slot = self.free_slots.pop().unwrap_or(self.len);
2058
2059 let start = slot as usize * self.aligned_element_size as usize;
2061 let end = (slot as usize + 1) * self.aligned_element_size as usize;
2062
2063 if self.buffer.len() < end {
2065 self.buffer.reserve_internal(end);
2066 }
2067 while self.buffer.values().len() < end {
2068 self.buffer.push(0);
2069 }
2070
2071 self.buffer.values_mut()[start..end].copy_from_slice(data);
2073
2074 self.len += 1;
2076 self.buffer.dirty = BufferDirtyState::NeedsReserve;
2077 slot
2078 }
2079
2080 fn remove(&mut self, slot: u32) {
2082 self.free_slots.push(slot);
2083 self.len -= 1;
2084 }
2085}