1use core::{cmp::Ordering, iter, marker::PhantomData, mem, ops::Range};
8
9use bevy_derive::{Deref, DerefMut};
10use bevy_ecs::{
11 resource::Resource,
12 world::{FromWorld, World},
13};
14use bevy_platform::collections::{HashMap, HashSet};
15use bevy_reflect::{prelude::ReflectDefault, Reflect};
16use bevy_render::{
17 render_resource::{
18 BindGroup, BindGroupEntry, BindGroupLayout, BindingNumber, BindingResource,
19 BindingResources, BindlessDescriptor, BindlessIndex, BindlessIndexTableDescriptor,
20 BindlessResourceType, Buffer, BufferBinding, BufferDescriptor, BufferId,
21 BufferInitDescriptor, BufferUsages, CompareFunction, FilterMode, OwnedBindingResource,
22 PreparedBindGroup, RawBufferVec, Sampler, SamplerDescriptor, SamplerId, TextureView,
23 TextureViewDimension, TextureViewId, UnpreparedBindGroup, WgpuSampler, WgpuTextureView,
24 },
25 renderer::{RenderDevice, RenderQueue},
26 settings::WgpuFeatures,
27 texture::FallbackImage,
28};
29use bevy_utils::default;
30use bytemuck::Pod;
31use tracing::{error, trace};
32
33use crate::Material;
34
35#[derive(Resource)]
42pub enum MaterialBindGroupAllocator<M>
43where
44 M: Material,
45{
46 Bindless(Box<MaterialBindGroupBindlessAllocator<M>>),
48 NonBindless(Box<MaterialBindGroupNonBindlessAllocator<M>>),
50}
51
52pub struct MaterialBindGroupBindlessAllocator<M>
55where
56 M: Material,
57{
58 slabs: Vec<MaterialBindlessSlab<M>>,
60 bind_group_layout: BindGroupLayout,
62 bindless_descriptor: BindlessDescriptor,
66
67 fallback_buffers: HashMap<BindlessIndex, Buffer>,
73
74 slab_capacity: u32,
79}
80
81pub struct MaterialBindlessSlab<M>
83where
84 M: Material,
85{
86 bind_group: Option<BindGroup>,
91
92 bindless_index_tables: Vec<MaterialBindlessIndexTable<M>>,
102
103 samplers: HashMap<BindlessResourceType, MaterialBindlessBindingArray<Sampler>>,
105 textures: HashMap<BindlessResourceType, MaterialBindlessBindingArray<TextureView>>,
107 buffers: HashMap<BindlessIndex, MaterialBindlessBindingArray<Buffer>>,
109 data_buffers: HashMap<BindlessIndex, MaterialDataBuffer>,
112
113 extra_data: Vec<Option<M::Data>>,
118
119 free_slots: Vec<MaterialBindGroupSlot>,
121 live_allocation_count: u32,
123 allocated_resource_count: u32,
125}
126
127struct MaterialBindlessIndexTable<M>
134where
135 M: Material,
136{
137 buffer: RetainedRawBufferVec<u32>,
139 index_range: Range<BindlessIndex>,
147 binding_number: BindingNumber,
149 phantom: PhantomData<M>,
150}
151
152struct MaterialBindlessBindingArray<R>
155where
156 R: GetBindingResourceId,
157{
158 binding_number: BindingNumber,
160 bindings: Vec<Option<MaterialBindlessBinding<R>>>,
163 resource_type: BindlessResourceType,
165 resource_to_slot: HashMap<BindingResourceId, u32>,
169 free_slots: Vec<u32>,
171 len: u32,
173}
174
175struct MaterialBindlessBinding<R>
181where
182 R: GetBindingResourceId,
183{
184 resource: R,
186 ref_count: u32,
189}
190
191pub struct MaterialBindGroupNonBindlessAllocator<M>
193where
194 M: Material,
195{
196 bind_groups: Vec<Option<MaterialNonBindlessAllocatedBindGroup<M>>>,
199 to_prepare: HashSet<MaterialBindGroupIndex>,
204 free_indices: Vec<MaterialBindGroupIndex>,
206 phantom: PhantomData<M>,
207}
208
209enum MaterialNonBindlessAllocatedBindGroup<M>
212where
213 M: Material,
214{
215 Unprepared {
221 bind_group: UnpreparedBindGroup<M::Data>,
223 layout: BindGroupLayout,
225 },
226 Prepared {
228 bind_group: PreparedBindGroup<M::Data>,
229 #[expect(dead_code, reason = "These buffers are only referenced by bind groups")]
230 uniform_buffers: Vec<Buffer>,
231 },
232}
233
234#[derive(Resource)]
237pub struct FallbackBindlessResources {
238 filtering_sampler: Sampler,
240 non_filtering_sampler: Sampler,
242 comparison_sampler: Sampler,
244}
245
246#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
248enum BindingResourceId {
249 Buffer(BufferId),
251 TextureView(TextureViewDimension, TextureViewId),
253 Sampler(SamplerId),
255 DataBuffer,
260}
261
262enum BindingResourceArray<'a> {
268 Buffers(Vec<BufferBinding<'a>>),
270 TextureViews(Vec<&'a WgpuTextureView>),
272 Samplers(Vec<&'a WgpuSampler>),
274}
275
276#[derive(Clone, Copy, Debug, Default, Reflect)]
279#[reflect(Clone, Default)]
280pub struct MaterialBindingId {
281 pub group: MaterialBindGroupIndex,
283 pub slot: MaterialBindGroupSlot,
287}
288
289#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Reflect, Deref, DerefMut)]
294#[reflect(Default, Clone, PartialEq, Hash)]
295pub struct MaterialBindGroupIndex(pub u32);
296
297impl From<u32> for MaterialBindGroupIndex {
298 fn from(value: u32) -> Self {
299 MaterialBindGroupIndex(value)
300 }
301}
302
303#[derive(Clone, Copy, Debug, Default, PartialEq, Reflect, Deref, DerefMut)]
310#[reflect(Default, Clone, PartialEq)]
311pub struct MaterialBindGroupSlot(pub u32);
312
313enum BufferDirtyState {
318 Clean,
320 NeedsReserve,
322 NeedsUpload,
324}
325
326struct BindlessAllocationCandidate {
329 pre_existing_resources: HashMap<BindlessIndex, u32>,
333 needed_free_slots: u32,
336}
337
338trait GetBindingResourceId {
345 fn binding_resource_id(&self, resource_type: BindlessResourceType) -> BindingResourceId;
351}
352
353pub struct MaterialSlab<'a, M>(MaterialSlabImpl<'a, M>)
355where
356 M: Material;
357
358enum MaterialSlabImpl<'a, M>
362where
363 M: Material,
364{
365 Bindless(&'a MaterialBindlessSlab<M>),
368 NonBindless(MaterialNonBindlessSlab<'a, M>),
371}
372
373enum MaterialNonBindlessSlab<'a, M>
376where
377 M: Material,
378{
379 Prepared(&'a PreparedBindGroup<M::Data>),
381 Unprepared(&'a UnpreparedBindGroup<M::Data>),
383}
384
385struct MaterialDataBuffer {
390 binding_number: BindingNumber,
392 buffer: RetainedRawBufferVec<u8>,
397 aligned_element_size: u32,
400 free_slots: Vec<u32>,
402 len: u32,
404}
405
406#[derive(Deref, DerefMut)]
412struct RetainedRawBufferVec<T>
413where
414 T: Pod,
415{
416 #[deref]
418 buffer: RawBufferVec<T>,
419 dirty: BufferDirtyState,
421}
422
423const DEFAULT_BINDLESS_FALLBACK_BUFFER_SIZE: u64 = 16;
428
429impl From<u32> for MaterialBindGroupSlot {
430 fn from(value: u32) -> Self {
431 MaterialBindGroupSlot(value)
432 }
433}
434
435impl From<MaterialBindGroupSlot> for u32 {
436 fn from(value: MaterialBindGroupSlot) -> Self {
437 value.0
438 }
439}
440
441impl<'a> From<&'a OwnedBindingResource> for BindingResourceId {
442 fn from(value: &'a OwnedBindingResource) -> Self {
443 match *value {
444 OwnedBindingResource::Buffer(ref buffer) => BindingResourceId::Buffer(buffer.id()),
445 OwnedBindingResource::Data(_) => BindingResourceId::DataBuffer,
446 OwnedBindingResource::TextureView(ref texture_view_dimension, ref texture_view) => {
447 BindingResourceId::TextureView(*texture_view_dimension, texture_view.id())
448 }
449 OwnedBindingResource::Sampler(_, ref sampler) => {
450 BindingResourceId::Sampler(sampler.id())
451 }
452 }
453 }
454}
455
456impl GetBindingResourceId for Buffer {
457 fn binding_resource_id(&self, _: BindlessResourceType) -> BindingResourceId {
458 BindingResourceId::Buffer(self.id())
459 }
460}
461
462impl GetBindingResourceId for Sampler {
463 fn binding_resource_id(&self, _: BindlessResourceType) -> BindingResourceId {
464 BindingResourceId::Sampler(self.id())
465 }
466}
467
468impl GetBindingResourceId for TextureView {
469 fn binding_resource_id(&self, resource_type: BindlessResourceType) -> BindingResourceId {
470 let texture_view_dimension = match resource_type {
471 BindlessResourceType::Texture1d => TextureViewDimension::D1,
472 BindlessResourceType::Texture2d => TextureViewDimension::D2,
473 BindlessResourceType::Texture2dArray => TextureViewDimension::D2Array,
474 BindlessResourceType::Texture3d => TextureViewDimension::D3,
475 BindlessResourceType::TextureCube => TextureViewDimension::Cube,
476 BindlessResourceType::TextureCubeArray => TextureViewDimension::CubeArray,
477 _ => panic!("Resource type is not a texture"),
478 };
479 BindingResourceId::TextureView(texture_view_dimension, self.id())
480 }
481}
482
483impl<M> MaterialBindGroupAllocator<M>
484where
485 M: Material,
486{
487 fn new(render_device: &RenderDevice) -> MaterialBindGroupAllocator<M> {
490 if material_uses_bindless_resources::<M>(render_device) {
491 MaterialBindGroupAllocator::Bindless(Box::new(MaterialBindGroupBindlessAllocator::new(
492 render_device,
493 )))
494 } else {
495 MaterialBindGroupAllocator::NonBindless(Box::new(
496 MaterialBindGroupNonBindlessAllocator::new(),
497 ))
498 }
499 }
500
501 pub fn get(&self, group: MaterialBindGroupIndex) -> Option<MaterialSlab<M>> {
503 match *self {
504 MaterialBindGroupAllocator::Bindless(ref bindless_allocator) => bindless_allocator
505 .get(group)
506 .map(|bindless_slab| MaterialSlab(MaterialSlabImpl::Bindless(bindless_slab))),
507 MaterialBindGroupAllocator::NonBindless(ref non_bindless_allocator) => {
508 non_bindless_allocator.get(group).map(|non_bindless_slab| {
509 MaterialSlab(MaterialSlabImpl::NonBindless(non_bindless_slab))
510 })
511 }
512 }
513 }
514
515 pub fn allocate_unprepared(
522 &mut self,
523 unprepared_bind_group: UnpreparedBindGroup<M::Data>,
524 bind_group_layout: &BindGroupLayout,
525 ) -> MaterialBindingId {
526 match *self {
527 MaterialBindGroupAllocator::Bindless(
528 ref mut material_bind_group_bindless_allocator,
529 ) => material_bind_group_bindless_allocator.allocate_unprepared(unprepared_bind_group),
530 MaterialBindGroupAllocator::NonBindless(
531 ref mut material_bind_group_non_bindless_allocator,
532 ) => material_bind_group_non_bindless_allocator
533 .allocate_unprepared(unprepared_bind_group, (*bind_group_layout).clone()),
534 }
535 }
536
537 pub fn allocate_prepared(
547 &mut self,
548 prepared_bind_group: PreparedBindGroup<M::Data>,
549 ) -> MaterialBindingId {
550 match *self {
551 MaterialBindGroupAllocator::Bindless(_) => {
552 panic!(
553 "Bindless resources are incompatible with implementing `as_bind_group` \
554 directly; implement `unprepared_bind_group` instead or disable bindless"
555 )
556 }
557 MaterialBindGroupAllocator::NonBindless(ref mut non_bindless_allocator) => {
558 non_bindless_allocator.allocate_prepared(prepared_bind_group)
559 }
560 }
561 }
562
563 pub fn free(&mut self, material_binding_id: MaterialBindingId) {
567 match *self {
568 MaterialBindGroupAllocator::Bindless(
569 ref mut material_bind_group_bindless_allocator,
570 ) => material_bind_group_bindless_allocator.free(material_binding_id),
571 MaterialBindGroupAllocator::NonBindless(
572 ref mut material_bind_group_non_bindless_allocator,
573 ) => material_bind_group_non_bindless_allocator.free(material_binding_id),
574 }
575 }
576
577 pub fn prepare_bind_groups(
580 &mut self,
581 render_device: &RenderDevice,
582 fallback_bindless_resources: &FallbackBindlessResources,
583 fallback_image: &FallbackImage,
584 ) {
585 match *self {
586 MaterialBindGroupAllocator::Bindless(
587 ref mut material_bind_group_bindless_allocator,
588 ) => material_bind_group_bindless_allocator.prepare_bind_groups(
589 render_device,
590 fallback_bindless_resources,
591 fallback_image,
592 ),
593 MaterialBindGroupAllocator::NonBindless(
594 ref mut material_bind_group_non_bindless_allocator,
595 ) => material_bind_group_non_bindless_allocator.prepare_bind_groups(render_device),
596 }
597 }
598
599 pub fn write_buffers(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
605 match *self {
606 MaterialBindGroupAllocator::Bindless(
607 ref mut material_bind_group_bindless_allocator,
608 ) => material_bind_group_bindless_allocator.write_buffers(render_device, render_queue),
609 MaterialBindGroupAllocator::NonBindless(_) => {
610 }
612 }
613 }
614}
615
616impl<M> MaterialBindlessIndexTable<M>
617where
618 M: Material,
619{
620 fn new(
622 bindless_index_table_descriptor: &BindlessIndexTableDescriptor,
623 ) -> MaterialBindlessIndexTable<M> {
624 let mut buffer = RetainedRawBufferVec::new(BufferUsages::STORAGE);
626 for _ in *bindless_index_table_descriptor.indices.start
627 ..*bindless_index_table_descriptor.indices.end
628 {
629 buffer.push(0);
630 }
631
632 MaterialBindlessIndexTable {
633 buffer,
634 index_range: bindless_index_table_descriptor.indices.clone(),
635 binding_number: bindless_index_table_descriptor.binding_number,
636 phantom: PhantomData,
637 }
638 }
639
640 fn get(&self, slot: MaterialBindGroupSlot) -> &[u32] {
646 let struct_size = *self.index_range.end as usize - *self.index_range.start as usize;
647 let start = struct_size * slot.0 as usize;
648 &self.buffer.values()[start..(start + struct_size)]
649 }
650
651 fn get_binding(
653 &self,
654 slot: MaterialBindGroupSlot,
655 bindless_index: BindlessIndex,
656 ) -> Option<u32> {
657 if bindless_index < self.index_range.start || bindless_index >= self.index_range.end {
658 return None;
659 }
660 self.get(slot)
661 .get((*bindless_index - *self.index_range.start) as usize)
662 .copied()
663 }
664
665 fn table_length(&self) -> u32 {
666 self.index_range.end.0 - self.index_range.start.0
667 }
668
669 fn set(
677 &mut self,
678 slot: MaterialBindGroupSlot,
679 allocated_resource_slots: &HashMap<BindlessIndex, u32>,
680 ) {
681 let table_len = self.table_length() as usize;
682 let range = (slot.0 as usize * table_len)..((slot.0 as usize + 1) * table_len);
683 while self.buffer.len() < range.end {
684 self.buffer.push(0);
685 }
686
687 for (&bindless_index, &resource_slot) in allocated_resource_slots {
688 if self.index_range.contains(&bindless_index) {
689 self.buffer.set(
690 *bindless_index + range.start as u32 - *self.index_range.start,
691 resource_slot,
692 );
693 }
694 }
695
696 self.buffer.dirty = BufferDirtyState::NeedsReserve;
698 }
699
700 fn bind_group_entry(&self) -> BindGroupEntry {
702 BindGroupEntry {
703 binding: *self.binding_number,
704 resource: self
705 .buffer
706 .buffer()
707 .expect("Bindings buffer must exist")
708 .as_entire_binding(),
709 }
710 }
711}
712
713impl<T> RetainedRawBufferVec<T>
714where
715 T: Pod,
716{
717 fn new(buffer_usages: BufferUsages) -> RetainedRawBufferVec<T> {
720 RetainedRawBufferVec {
721 buffer: RawBufferVec::new(buffer_usages),
722 dirty: BufferDirtyState::NeedsUpload,
723 }
724 }
725
726 fn prepare(&mut self, render_device: &RenderDevice) {
728 match self.dirty {
729 BufferDirtyState::Clean | BufferDirtyState::NeedsUpload => {}
730 BufferDirtyState::NeedsReserve => {
731 let capacity = self.buffer.len();
732 self.buffer.reserve(capacity, render_device);
733 self.dirty = BufferDirtyState::NeedsUpload;
734 }
735 }
736 }
737
738 fn write(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
740 match self.dirty {
741 BufferDirtyState::Clean => {}
742 BufferDirtyState::NeedsReserve | BufferDirtyState::NeedsUpload => {
743 self.buffer.write_buffer(render_device, render_queue);
744 self.dirty = BufferDirtyState::Clean;
745 }
746 }
747 }
748}
749
750impl<M> MaterialBindGroupBindlessAllocator<M>
751where
752 M: Material,
753{
754 fn new(render_device: &RenderDevice) -> MaterialBindGroupBindlessAllocator<M> {
757 let bindless_descriptor = M::bindless_descriptor()
758 .expect("Non-bindless materials should use the non-bindless allocator");
759 let fallback_buffers = bindless_descriptor
760 .buffers
761 .iter()
762 .map(|bindless_buffer_descriptor| {
763 (
764 bindless_buffer_descriptor.bindless_index,
765 render_device.create_buffer(&BufferDescriptor {
766 label: Some("bindless fallback buffer"),
767 size: match bindless_buffer_descriptor.size {
768 Some(size) => size as u64,
769 None => DEFAULT_BINDLESS_FALLBACK_BUFFER_SIZE,
770 },
771 usage: BufferUsages::STORAGE,
772 mapped_at_creation: false,
773 }),
774 )
775 })
776 .collect();
777
778 MaterialBindGroupBindlessAllocator {
779 slabs: vec![],
780 bind_group_layout: M::bind_group_layout(render_device),
781 bindless_descriptor,
782 fallback_buffers,
783 slab_capacity: M::bindless_slot_count()
784 .expect("Non-bindless materials should use the non-bindless allocator")
785 .resolve(),
786 }
787 }
788
789 fn allocate_unprepared(
798 &mut self,
799 mut unprepared_bind_group: UnpreparedBindGroup<M::Data>,
800 ) -> MaterialBindingId {
801 for (slab_index, slab) in self.slabs.iter_mut().enumerate() {
802 trace!("Trying to allocate in slab {}", slab_index);
803 match slab.try_allocate(unprepared_bind_group, self.slab_capacity) {
804 Ok(slot) => {
805 return MaterialBindingId {
806 group: MaterialBindGroupIndex(slab_index as u32),
807 slot,
808 };
809 }
810 Err(bind_group) => unprepared_bind_group = bind_group,
811 }
812 }
813
814 let group = MaterialBindGroupIndex(self.slabs.len() as u32);
815 self.slabs
816 .push(MaterialBindlessSlab::new(&self.bindless_descriptor));
817
818 let Ok(slot) = self
820 .slabs
821 .last_mut()
822 .expect("We just pushed a slab")
823 .try_allocate(unprepared_bind_group, self.slab_capacity)
824 else {
825 panic!("An allocation into an empty slab should always succeed")
826 };
827
828 MaterialBindingId { group, slot }
829 }
830
831 fn free(&mut self, material_binding_id: MaterialBindingId) {
835 self.slabs
836 .get_mut(material_binding_id.group.0 as usize)
837 .expect("Slab should exist")
838 .free(material_binding_id.slot, &self.bindless_descriptor);
839 }
840
841 fn get(&self, group: MaterialBindGroupIndex) -> Option<&MaterialBindlessSlab<M>> {
846 self.slabs.get(group.0 as usize)
847 }
848
849 fn prepare_bind_groups(
853 &mut self,
854 render_device: &RenderDevice,
855 fallback_bindless_resources: &FallbackBindlessResources,
856 fallback_image: &FallbackImage,
857 ) {
858 for slab in &mut self.slabs {
859 slab.prepare(
860 render_device,
861 &self.bind_group_layout,
862 fallback_bindless_resources,
863 &self.fallback_buffers,
864 fallback_image,
865 &self.bindless_descriptor,
866 self.slab_capacity,
867 );
868 }
869 }
870
871 fn write_buffers(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
875 for slab in &mut self.slabs {
876 slab.write_buffer(render_device, render_queue);
877 }
878 }
879}
880
881impl<M> FromWorld for MaterialBindGroupAllocator<M>
882where
883 M: Material,
884{
885 fn from_world(world: &mut World) -> Self {
886 let render_device = world.resource::<RenderDevice>();
887 MaterialBindGroupAllocator::new(render_device)
888 }
889}
890
891impl<M> MaterialBindlessSlab<M>
892where
893 M: Material,
894{
895 fn try_allocate(
902 &mut self,
903 unprepared_bind_group: UnpreparedBindGroup<M::Data>,
904 slot_capacity: u32,
905 ) -> Result<MaterialBindGroupSlot, UnpreparedBindGroup<M::Data>> {
906 let Some(allocation_candidate) = self.check_allocation(&unprepared_bind_group) else {
908 return Err(unprepared_bind_group);
909 };
910
911 if self.allocated_resource_count > 0
920 && self.allocated_resource_count + allocation_candidate.needed_free_slots
921 > slot_capacity
922 {
923 trace!("Slab is full, can't allocate");
924 return Err(unprepared_bind_group);
925 }
926
927 let slot = self
929 .free_slots
930 .pop()
931 .unwrap_or(MaterialBindGroupSlot(self.live_allocation_count));
932
933 self.live_allocation_count += 1;
935
936 let allocated_resource_slots =
938 self.insert_resources(unprepared_bind_group.bindings, allocation_candidate);
939
940 for bindless_index_table in &mut self.bindless_index_tables {
942 bindless_index_table.set(slot, &allocated_resource_slots);
943 }
944
945 if self.extra_data.len() < (*slot as usize + 1) {
947 self.extra_data.resize_with(*slot as usize + 1, || None);
948 }
949 self.extra_data[*slot as usize] = Some(unprepared_bind_group.data);
950
951 self.bind_group = None;
953
954 Ok(slot)
955 }
956
957 fn check_allocation(
960 &self,
961 unprepared_bind_group: &UnpreparedBindGroup<M::Data>,
962 ) -> Option<BindlessAllocationCandidate> {
963 let mut allocation_candidate = BindlessAllocationCandidate {
964 pre_existing_resources: HashMap::default(),
965 needed_free_slots: 0,
966 };
967
968 for &(bindless_index, ref owned_binding_resource) in unprepared_bind_group.bindings.iter() {
969 let bindless_index = BindlessIndex(bindless_index);
970 match *owned_binding_resource {
971 OwnedBindingResource::Buffer(ref buffer) => {
972 let Some(binding_array) = self.buffers.get(&bindless_index) else {
973 error!(
974 "Binding array wasn't present for buffer at index {:?}",
975 bindless_index
976 );
977 return None;
978 };
979 match binding_array.find(BindingResourceId::Buffer(buffer.id())) {
980 Some(slot) => {
981 allocation_candidate
982 .pre_existing_resources
983 .insert(bindless_index, slot);
984 }
985 None => allocation_candidate.needed_free_slots += 1,
986 }
987 }
988
989 OwnedBindingResource::Data(_) => {
990 }
992
993 OwnedBindingResource::TextureView(texture_view_dimension, ref texture_view) => {
994 let bindless_resource_type = BindlessResourceType::from(texture_view_dimension);
995 match self
996 .textures
997 .get(&bindless_resource_type)
998 .expect("Missing binding array for texture")
999 .find(BindingResourceId::TextureView(
1000 texture_view_dimension,
1001 texture_view.id(),
1002 )) {
1003 Some(slot) => {
1004 allocation_candidate
1005 .pre_existing_resources
1006 .insert(bindless_index, slot);
1007 }
1008 None => {
1009 allocation_candidate.needed_free_slots += 1;
1010 }
1011 }
1012 }
1013
1014 OwnedBindingResource::Sampler(sampler_binding_type, ref sampler) => {
1015 let bindless_resource_type = BindlessResourceType::from(sampler_binding_type);
1016 match self
1017 .samplers
1018 .get(&bindless_resource_type)
1019 .expect("Missing binding array for sampler")
1020 .find(BindingResourceId::Sampler(sampler.id()))
1021 {
1022 Some(slot) => {
1023 allocation_candidate
1024 .pre_existing_resources
1025 .insert(bindless_index, slot);
1026 }
1027 None => {
1028 allocation_candidate.needed_free_slots += 1;
1029 }
1030 }
1031 }
1032 }
1033 }
1034
1035 Some(allocation_candidate)
1036 }
1037
1038 fn insert_resources(
1043 &mut self,
1044 mut binding_resources: BindingResources,
1045 allocation_candidate: BindlessAllocationCandidate,
1046 ) -> HashMap<BindlessIndex, u32> {
1047 let mut allocated_resource_slots = HashMap::default();
1048
1049 for (bindless_index, owned_binding_resource) in binding_resources.drain(..) {
1050 let bindless_index = BindlessIndex(bindless_index);
1051
1052 let pre_existing_slot = allocation_candidate
1053 .pre_existing_resources
1054 .get(&bindless_index);
1055
1056 let binding_resource_id = BindingResourceId::from(&owned_binding_resource);
1058 let increment_allocated_resource_count = match owned_binding_resource {
1059 OwnedBindingResource::Buffer(buffer) => {
1060 let slot = self
1061 .buffers
1062 .get_mut(&bindless_index)
1063 .expect("Buffer binding array should exist")
1064 .insert(binding_resource_id, buffer);
1065 allocated_resource_slots.insert(bindless_index, slot);
1066
1067 if let Some(pre_existing_slot) = pre_existing_slot {
1068 assert_eq!(*pre_existing_slot, slot);
1069
1070 false
1071 } else {
1072 true
1073 }
1074 }
1075 OwnedBindingResource::Data(data) => {
1076 if pre_existing_slot.is_some() {
1077 panic!("Data buffers can't be deduplicated")
1078 }
1079
1080 let slot = self
1081 .data_buffers
1082 .get_mut(&bindless_index)
1083 .expect("Data buffer binding array should exist")
1084 .insert(&data);
1085 allocated_resource_slots.insert(bindless_index, slot);
1086 false
1087 }
1088 OwnedBindingResource::TextureView(texture_view_dimension, texture_view) => {
1089 let bindless_resource_type = BindlessResourceType::from(texture_view_dimension);
1090 let slot = self
1091 .textures
1092 .get_mut(&bindless_resource_type)
1093 .expect("Texture array should exist")
1094 .insert(binding_resource_id, texture_view);
1095 allocated_resource_slots.insert(bindless_index, slot);
1096
1097 if let Some(pre_existing_slot) = pre_existing_slot {
1098 assert_eq!(*pre_existing_slot, slot);
1099
1100 false
1101 } else {
1102 true
1103 }
1104 }
1105 OwnedBindingResource::Sampler(sampler_binding_type, sampler) => {
1106 let bindless_resource_type = BindlessResourceType::from(sampler_binding_type);
1107 let slot = self
1108 .samplers
1109 .get_mut(&bindless_resource_type)
1110 .expect("Sampler should exist")
1111 .insert(binding_resource_id, sampler);
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 };
1123
1124 if increment_allocated_resource_count {
1126 self.allocated_resource_count += 1;
1127 }
1128 }
1129
1130 allocated_resource_slots
1131 }
1132
1133 fn free(&mut self, slot: MaterialBindGroupSlot, bindless_descriptor: &BindlessDescriptor) {
1136 for (bindless_index, bindless_resource_type) in
1138 bindless_descriptor.resources.iter().enumerate()
1139 {
1140 let bindless_index = BindlessIndex::from(bindless_index as u32);
1141 let Some(bindless_index_table) = self.get_bindless_index_table(bindless_index) else {
1142 continue;
1143 };
1144 let Some(bindless_binding) = bindless_index_table.get_binding(slot, bindless_index)
1145 else {
1146 continue;
1147 };
1148
1149 let decrement_allocated_resource_count = match *bindless_resource_type {
1153 BindlessResourceType::None => false,
1154 BindlessResourceType::Buffer => self
1155 .buffers
1156 .get_mut(&bindless_index)
1157 .expect("Buffer should exist with that bindless index")
1158 .remove(bindless_binding),
1159 BindlessResourceType::DataBuffer => {
1160 self.data_buffers
1161 .get_mut(&bindless_index)
1162 .expect("Data buffer should exist with that bindless index")
1163 .remove(bindless_binding);
1164 false
1165 }
1166 BindlessResourceType::SamplerFiltering
1167 | BindlessResourceType::SamplerNonFiltering
1168 | BindlessResourceType::SamplerComparison => self
1169 .samplers
1170 .get_mut(bindless_resource_type)
1171 .expect("Sampler array should exist")
1172 .remove(bindless_binding),
1173 BindlessResourceType::Texture1d
1174 | BindlessResourceType::Texture2d
1175 | BindlessResourceType::Texture2dArray
1176 | BindlessResourceType::Texture3d
1177 | BindlessResourceType::TextureCube
1178 | BindlessResourceType::TextureCubeArray => self
1179 .textures
1180 .get_mut(bindless_resource_type)
1181 .expect("Texture array should exist")
1182 .remove(bindless_binding),
1183 };
1184
1185 if decrement_allocated_resource_count {
1188 self.allocated_resource_count -= 1;
1189 }
1190 }
1191
1192 self.extra_data[slot.0 as usize] = None;
1194
1195 self.bind_group = None;
1197
1198 self.free_slots.push(slot);
1200 self.live_allocation_count -= 1;
1201 }
1202
1203 fn prepare(
1205 &mut self,
1206 render_device: &RenderDevice,
1207 bind_group_layout: &BindGroupLayout,
1208 fallback_bindless_resources: &FallbackBindlessResources,
1209 fallback_buffers: &HashMap<BindlessIndex, Buffer>,
1210 fallback_image: &FallbackImage,
1211 bindless_descriptor: &BindlessDescriptor,
1212 slab_capacity: u32,
1213 ) {
1214 for bindless_index_table in &mut self.bindless_index_tables {
1216 bindless_index_table.buffer.prepare(render_device);
1217 }
1218
1219 for data_buffer in self.data_buffers.values_mut() {
1221 data_buffer.buffer.prepare(render_device);
1222 }
1223
1224 self.prepare_bind_group(
1226 render_device,
1227 bind_group_layout,
1228 fallback_bindless_resources,
1229 fallback_buffers,
1230 fallback_image,
1231 bindless_descriptor,
1232 slab_capacity,
1233 );
1234 }
1235
1236 fn prepare_bind_group(
1239 &mut self,
1240 render_device: &RenderDevice,
1241 bind_group_layout: &BindGroupLayout,
1242 fallback_bindless_resources: &FallbackBindlessResources,
1243 fallback_buffers: &HashMap<BindlessIndex, Buffer>,
1244 fallback_image: &FallbackImage,
1245 bindless_descriptor: &BindlessDescriptor,
1246 slab_capacity: u32,
1247 ) {
1248 if self.bind_group.is_some() {
1250 return;
1251 }
1252
1253 let required_binding_array_size = if render_device
1256 .features()
1257 .contains(WgpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY)
1258 {
1259 None
1260 } else {
1261 Some(slab_capacity)
1262 };
1263
1264 let binding_resource_arrays = self.create_binding_resource_arrays(
1265 fallback_bindless_resources,
1266 fallback_buffers,
1267 fallback_image,
1268 bindless_descriptor,
1269 required_binding_array_size,
1270 );
1271
1272 let mut bind_group_entries: Vec<_> = self
1273 .bindless_index_tables
1274 .iter()
1275 .map(|bindless_index_table| bindless_index_table.bind_group_entry())
1276 .collect();
1277
1278 for &(&binding, ref binding_resource_array) in binding_resource_arrays.iter() {
1279 bind_group_entries.push(BindGroupEntry {
1280 binding,
1281 resource: match *binding_resource_array {
1282 BindingResourceArray::Buffers(ref buffer_bindings) => {
1283 BindingResource::BufferArray(&buffer_bindings[..])
1284 }
1285 BindingResourceArray::TextureViews(ref texture_views) => {
1286 BindingResource::TextureViewArray(&texture_views[..])
1287 }
1288 BindingResourceArray::Samplers(ref samplers) => {
1289 BindingResource::SamplerArray(&samplers[..])
1290 }
1291 },
1292 });
1293 }
1294
1295 for data_buffer in self.data_buffers.values() {
1297 bind_group_entries.push(BindGroupEntry {
1298 binding: *data_buffer.binding_number,
1299 resource: data_buffer
1300 .buffer
1301 .buffer()
1302 .expect("Backing data buffer must have been uploaded by now")
1303 .as_entire_binding(),
1304 });
1305 }
1306
1307 self.bind_group = Some(render_device.create_bind_group(
1308 M::label(),
1309 bind_group_layout,
1310 &bind_group_entries,
1311 ));
1312 }
1313
1314 fn write_buffer(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
1319 for bindless_index_table in &mut self.bindless_index_tables {
1320 bindless_index_table
1321 .buffer
1322 .write(render_device, render_queue);
1323 }
1324
1325 for data_buffer in self.data_buffers.values_mut() {
1326 data_buffer.buffer.write(render_device, render_queue);
1327 }
1328 }
1329
1330 fn create_binding_resource_arrays<'a>(
1333 &'a self,
1334 fallback_bindless_resources: &'a FallbackBindlessResources,
1335 fallback_buffers: &'a HashMap<BindlessIndex, Buffer>,
1336 fallback_image: &'a FallbackImage,
1337 bindless_descriptor: &'a BindlessDescriptor,
1338 required_binding_array_size: Option<u32>,
1339 ) -> Vec<(&'a u32, BindingResourceArray<'a>)> {
1340 let mut binding_resource_arrays = vec![];
1341
1342 self.create_sampler_binding_resource_arrays(
1344 &mut binding_resource_arrays,
1345 fallback_bindless_resources,
1346 required_binding_array_size,
1347 );
1348
1349 self.create_texture_binding_resource_arrays(
1351 &mut binding_resource_arrays,
1352 fallback_image,
1353 required_binding_array_size,
1354 );
1355
1356 self.create_buffer_binding_resource_arrays(
1358 &mut binding_resource_arrays,
1359 fallback_buffers,
1360 bindless_descriptor,
1361 required_binding_array_size,
1362 );
1363
1364 binding_resource_arrays
1365 }
1366
1367 fn create_sampler_binding_resource_arrays<'a, 'b>(
1370 &'a self,
1371 binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
1372 fallback_bindless_resources: &'a FallbackBindlessResources,
1373 required_binding_array_size: Option<u32>,
1374 ) {
1375 for (bindless_resource_type, fallback_sampler) in [
1377 (
1378 BindlessResourceType::SamplerFiltering,
1379 &fallback_bindless_resources.filtering_sampler,
1380 ),
1381 (
1382 BindlessResourceType::SamplerNonFiltering,
1383 &fallback_bindless_resources.non_filtering_sampler,
1384 ),
1385 (
1386 BindlessResourceType::SamplerComparison,
1387 &fallback_bindless_resources.comparison_sampler,
1388 ),
1389 ] {
1390 let mut sampler_bindings = vec![];
1391
1392 match self.samplers.get(&bindless_resource_type) {
1393 Some(sampler_bindless_binding_array) => {
1394 for maybe_bindless_binding in sampler_bindless_binding_array.bindings.iter() {
1395 match *maybe_bindless_binding {
1396 Some(ref bindless_binding) => {
1397 sampler_bindings.push(&*bindless_binding.resource);
1398 }
1399 None => sampler_bindings.push(&**fallback_sampler),
1400 }
1401 }
1402 }
1403
1404 None => {
1405 sampler_bindings.push(&**fallback_sampler);
1407 }
1408 }
1409
1410 if let Some(required_binding_array_size) = required_binding_array_size {
1411 sampler_bindings.extend(iter::repeat_n(
1412 &**fallback_sampler,
1413 required_binding_array_size as usize - sampler_bindings.len(),
1414 ));
1415 }
1416
1417 let binding_number = bindless_resource_type
1418 .binding_number()
1419 .expect("Sampler bindless resource type must have a binding number");
1420
1421 binding_resource_arrays.push((
1422 &**binding_number,
1423 BindingResourceArray::Samplers(sampler_bindings),
1424 ));
1425 }
1426 }
1427
1428 fn create_texture_binding_resource_arrays<'a, 'b>(
1431 &'a self,
1432 binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
1433 fallback_image: &'a FallbackImage,
1434 required_binding_array_size: Option<u32>,
1435 ) {
1436 for (bindless_resource_type, fallback_image) in [
1437 (BindlessResourceType::Texture1d, &fallback_image.d1),
1438 (BindlessResourceType::Texture2d, &fallback_image.d2),
1439 (
1440 BindlessResourceType::Texture2dArray,
1441 &fallback_image.d2_array,
1442 ),
1443 (BindlessResourceType::Texture3d, &fallback_image.d3),
1444 (BindlessResourceType::TextureCube, &fallback_image.cube),
1445 (
1446 BindlessResourceType::TextureCubeArray,
1447 &fallback_image.cube_array,
1448 ),
1449 ] {
1450 let mut texture_bindings = vec![];
1451
1452 let binding_number = bindless_resource_type
1453 .binding_number()
1454 .expect("Texture bindless resource type must have a binding number");
1455
1456 match self.textures.get(&bindless_resource_type) {
1457 Some(texture_bindless_binding_array) => {
1458 for maybe_bindless_binding in texture_bindless_binding_array.bindings.iter() {
1459 match *maybe_bindless_binding {
1460 Some(ref bindless_binding) => {
1461 texture_bindings.push(&*bindless_binding.resource);
1462 }
1463 None => texture_bindings.push(&*fallback_image.texture_view),
1464 }
1465 }
1466 }
1467
1468 None => {
1469 texture_bindings.push(&*fallback_image.texture_view);
1471 }
1472 }
1473
1474 if let Some(required_binding_array_size) = required_binding_array_size {
1475 texture_bindings.extend(iter::repeat_n(
1476 &*fallback_image.texture_view,
1477 required_binding_array_size as usize - texture_bindings.len(),
1478 ));
1479 }
1480
1481 binding_resource_arrays.push((
1482 binding_number,
1483 BindingResourceArray::TextureViews(texture_bindings),
1484 ));
1485 }
1486 }
1487
1488 fn create_buffer_binding_resource_arrays<'a, 'b>(
1491 &'a self,
1492 binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
1493 fallback_buffers: &'a HashMap<BindlessIndex, Buffer>,
1494 bindless_descriptor: &'a BindlessDescriptor,
1495 required_binding_array_size: Option<u32>,
1496 ) {
1497 for bindless_buffer_descriptor in bindless_descriptor.buffers.iter() {
1498 let Some(buffer_bindless_binding_array) =
1499 self.buffers.get(&bindless_buffer_descriptor.bindless_index)
1500 else {
1501 continue;
1505 };
1506
1507 let fallback_buffer = fallback_buffers
1508 .get(&bindless_buffer_descriptor.bindless_index)
1509 .expect("Fallback buffer should exist");
1510
1511 let mut buffer_bindings: Vec<_> = buffer_bindless_binding_array
1512 .bindings
1513 .iter()
1514 .map(|maybe_bindless_binding| {
1515 let buffer = match *maybe_bindless_binding {
1516 None => fallback_buffer,
1517 Some(ref bindless_binding) => &bindless_binding.resource,
1518 };
1519 BufferBinding {
1520 buffer,
1521 offset: 0,
1522 size: None,
1523 }
1524 })
1525 .collect();
1526
1527 if let Some(required_binding_array_size) = required_binding_array_size {
1528 buffer_bindings.extend(iter::repeat_n(
1529 BufferBinding {
1530 buffer: fallback_buffer,
1531 offset: 0,
1532 size: None,
1533 },
1534 required_binding_array_size as usize - buffer_bindings.len(),
1535 ));
1536 }
1537
1538 binding_resource_arrays.push((
1539 &*buffer_bindless_binding_array.binding_number,
1540 BindingResourceArray::Buffers(buffer_bindings),
1541 ));
1542 }
1543 }
1544
1545 fn bind_group(&self) -> Option<&BindGroup> {
1548 self.bind_group.as_ref()
1549 }
1550
1551 fn get_extra_data(&self, slot: MaterialBindGroupSlot) -> &M::Data {
1553 self.extra_data
1554 .get(slot.0 as usize)
1555 .and_then(|data| data.as_ref())
1556 .expect("Extra data not present")
1557 }
1558
1559 fn get_bindless_index_table(
1561 &self,
1562 bindless_index: BindlessIndex,
1563 ) -> Option<&MaterialBindlessIndexTable<M>> {
1564 let table_index = self
1565 .bindless_index_tables
1566 .binary_search_by(|bindless_index_table| {
1567 if bindless_index < bindless_index_table.index_range.start {
1568 Ordering::Less
1569 } else if bindless_index >= bindless_index_table.index_range.end {
1570 Ordering::Greater
1571 } else {
1572 Ordering::Equal
1573 }
1574 })
1575 .ok()?;
1576 self.bindless_index_tables.get(table_index)
1577 }
1578}
1579
1580impl<R> MaterialBindlessBindingArray<R>
1581where
1582 R: GetBindingResourceId,
1583{
1584 fn new(
1587 binding_number: BindingNumber,
1588 resource_type: BindlessResourceType,
1589 ) -> MaterialBindlessBindingArray<R> {
1590 MaterialBindlessBindingArray {
1591 binding_number,
1592 bindings: vec![],
1593 resource_type,
1594 resource_to_slot: HashMap::default(),
1595 free_slots: vec![],
1596 len: 0,
1597 }
1598 }
1599
1600 fn find(&self, binding_resource_id: BindingResourceId) -> Option<u32> {
1605 self.resource_to_slot.get(&binding_resource_id).copied()
1606 }
1607
1608 fn insert(&mut self, binding_resource_id: BindingResourceId, resource: R) -> u32 {
1611 match self.resource_to_slot.entry(binding_resource_id) {
1612 bevy_platform::collections::hash_map::Entry::Occupied(o) => {
1613 let slot = *o.get();
1614
1615 self.bindings[slot as usize]
1616 .as_mut()
1617 .expect("A slot in the resource_to_slot map should have a value")
1618 .ref_count += 1;
1619
1620 slot
1621 }
1622 bevy_platform::collections::hash_map::Entry::Vacant(v) => {
1623 let slot = self.free_slots.pop().unwrap_or(self.len);
1624 v.insert(slot);
1625
1626 if self.bindings.len() < slot as usize + 1 {
1627 self.bindings.resize_with(slot as usize + 1, || None);
1628 }
1629 self.bindings[slot as usize] = Some(MaterialBindlessBinding::new(resource));
1630
1631 self.len += 1;
1632 slot
1633 }
1634 }
1635 }
1636
1637 fn remove(&mut self, slot: u32) -> bool {
1643 let maybe_binding = &mut self.bindings[slot as usize];
1644 let binding = maybe_binding
1645 .as_mut()
1646 .expect("Attempted to free an already-freed binding");
1647
1648 binding.ref_count -= 1;
1649 if binding.ref_count != 0 {
1650 return false;
1651 }
1652
1653 let binding_resource_id = binding.resource.binding_resource_id(self.resource_type);
1654 self.resource_to_slot.remove(&binding_resource_id);
1655
1656 *maybe_binding = None;
1657 self.free_slots.push(slot);
1658 self.len -= 1;
1659 true
1660 }
1661}
1662
1663impl<R> MaterialBindlessBinding<R>
1664where
1665 R: GetBindingResourceId,
1666{
1667 fn new(resource: R) -> MaterialBindlessBinding<R> {
1671 MaterialBindlessBinding {
1672 resource,
1673 ref_count: 1,
1674 }
1675 }
1676}
1677
1678pub fn material_uses_bindless_resources<M>(render_device: &RenderDevice) -> bool
1684where
1685 M: Material,
1686{
1687 M::bindless_slot_count().is_some_and(|bindless_slot_count| {
1688 M::bindless_supported(render_device) && bindless_slot_count.resolve() > 1
1689 })
1690}
1691
1692impl<M> MaterialBindlessSlab<M>
1693where
1694 M: Material,
1695{
1696 fn new(bindless_descriptor: &BindlessDescriptor) -> MaterialBindlessSlab<M> {
1701 let mut buffers = HashMap::default();
1702 let mut samplers = HashMap::default();
1703 let mut textures = HashMap::default();
1704 let mut data_buffers = HashMap::default();
1705
1706 for (bindless_index, bindless_resource_type) in
1707 bindless_descriptor.resources.iter().enumerate()
1708 {
1709 let bindless_index = BindlessIndex(bindless_index as u32);
1710 match *bindless_resource_type {
1711 BindlessResourceType::None => {}
1712 BindlessResourceType::Buffer => {
1713 let binding_number = bindless_descriptor
1714 .buffers
1715 .iter()
1716 .find(|bindless_buffer_descriptor| {
1717 bindless_buffer_descriptor.bindless_index == bindless_index
1718 })
1719 .expect(
1720 "Bindless buffer descriptor matching that bindless index should be \
1721 present",
1722 )
1723 .binding_number;
1724 buffers.insert(
1725 bindless_index,
1726 MaterialBindlessBindingArray::new(binding_number, *bindless_resource_type),
1727 );
1728 }
1729 BindlessResourceType::DataBuffer => {
1730 let buffer_descriptor = bindless_descriptor
1732 .buffers
1733 .iter()
1734 .find(|bindless_buffer_descriptor| {
1735 bindless_buffer_descriptor.bindless_index == bindless_index
1736 })
1737 .expect(
1738 "Bindless buffer descriptor matching that bindless index should be \
1739 present",
1740 );
1741 data_buffers.insert(
1742 bindless_index,
1743 MaterialDataBuffer::new(
1744 buffer_descriptor.binding_number,
1745 buffer_descriptor
1746 .size
1747 .expect("Data buffers should have a size")
1748 as u32,
1749 ),
1750 );
1751 }
1752 BindlessResourceType::SamplerFiltering
1753 | BindlessResourceType::SamplerNonFiltering
1754 | BindlessResourceType::SamplerComparison => {
1755 samplers.insert(
1756 *bindless_resource_type,
1757 MaterialBindlessBindingArray::new(
1758 *bindless_resource_type.binding_number().unwrap(),
1759 *bindless_resource_type,
1760 ),
1761 );
1762 }
1763 BindlessResourceType::Texture1d
1764 | BindlessResourceType::Texture2d
1765 | BindlessResourceType::Texture2dArray
1766 | BindlessResourceType::Texture3d
1767 | BindlessResourceType::TextureCube
1768 | BindlessResourceType::TextureCubeArray => {
1769 textures.insert(
1770 *bindless_resource_type,
1771 MaterialBindlessBindingArray::new(
1772 *bindless_resource_type.binding_number().unwrap(),
1773 *bindless_resource_type,
1774 ),
1775 );
1776 }
1777 }
1778 }
1779
1780 let bindless_index_tables = bindless_descriptor
1781 .index_tables
1782 .iter()
1783 .map(|bindless_index_table| MaterialBindlessIndexTable::new(bindless_index_table))
1784 .collect();
1785
1786 MaterialBindlessSlab {
1787 bind_group: None,
1788 bindless_index_tables,
1789 samplers,
1790 textures,
1791 buffers,
1792 data_buffers,
1793 extra_data: vec![],
1794 free_slots: vec![],
1795 live_allocation_count: 0,
1796 allocated_resource_count: 0,
1797 }
1798 }
1799}
1800
1801impl FromWorld for FallbackBindlessResources {
1802 fn from_world(world: &mut World) -> Self {
1803 let render_device = world.resource::<RenderDevice>();
1804 FallbackBindlessResources {
1805 filtering_sampler: render_device.create_sampler(&SamplerDescriptor {
1806 label: Some("fallback filtering sampler"),
1807 ..default()
1808 }),
1809 non_filtering_sampler: render_device.create_sampler(&SamplerDescriptor {
1810 label: Some("fallback non-filtering sampler"),
1811 mag_filter: FilterMode::Nearest,
1812 min_filter: FilterMode::Nearest,
1813 mipmap_filter: FilterMode::Nearest,
1814 ..default()
1815 }),
1816 comparison_sampler: render_device.create_sampler(&SamplerDescriptor {
1817 label: Some("fallback comparison sampler"),
1818 compare: Some(CompareFunction::Always),
1819 ..default()
1820 }),
1821 }
1822 }
1823}
1824
1825impl<M> MaterialBindGroupNonBindlessAllocator<M>
1826where
1827 M: Material,
1828{
1829 fn new() -> MaterialBindGroupNonBindlessAllocator<M> {
1832 MaterialBindGroupNonBindlessAllocator {
1833 bind_groups: vec![],
1834 to_prepare: HashSet::default(),
1835 free_indices: vec![],
1836 phantom: PhantomData,
1837 }
1838 }
1839
1840 fn allocate(
1846 &mut self,
1847 bind_group: MaterialNonBindlessAllocatedBindGroup<M>,
1848 ) -> MaterialBindingId {
1849 let group_id = self
1850 .free_indices
1851 .pop()
1852 .unwrap_or(MaterialBindGroupIndex(self.bind_groups.len() as u32));
1853 if self.bind_groups.len() < *group_id as usize + 1 {
1854 self.bind_groups
1855 .resize_with(*group_id as usize + 1, || None);
1856 }
1857
1858 if matches!(
1859 bind_group,
1860 MaterialNonBindlessAllocatedBindGroup::Unprepared { .. }
1861 ) {
1862 self.to_prepare.insert(group_id);
1863 }
1864
1865 self.bind_groups[*group_id as usize] = Some(bind_group);
1866
1867 MaterialBindingId {
1868 group: group_id,
1869 slot: default(),
1870 }
1871 }
1872
1873 fn allocate_unprepared(
1876 &mut self,
1877 unprepared_bind_group: UnpreparedBindGroup<M::Data>,
1878 bind_group_layout: BindGroupLayout,
1879 ) -> MaterialBindingId {
1880 self.allocate(MaterialNonBindlessAllocatedBindGroup::Unprepared {
1881 bind_group: unprepared_bind_group,
1882 layout: bind_group_layout,
1883 })
1884 }
1885
1886 fn allocate_prepared(
1889 &mut self,
1890 prepared_bind_group: PreparedBindGroup<M::Data>,
1891 ) -> MaterialBindingId {
1892 self.allocate(MaterialNonBindlessAllocatedBindGroup::Prepared {
1893 bind_group: prepared_bind_group,
1894 uniform_buffers: vec![],
1895 })
1896 }
1897
1898 fn free(&mut self, binding_id: MaterialBindingId) {
1900 debug_assert_eq!(binding_id.slot, MaterialBindGroupSlot(0));
1901 debug_assert!(self.bind_groups[*binding_id.group as usize].is_some());
1902 self.bind_groups[*binding_id.group as usize] = None;
1903 self.to_prepare.remove(&binding_id.group);
1904 self.free_indices.push(binding_id.group);
1905 }
1906
1907 fn get(&self, group: MaterialBindGroupIndex) -> Option<MaterialNonBindlessSlab<M>> {
1909 self.bind_groups[group.0 as usize]
1910 .as_ref()
1911 .map(|bind_group| match bind_group {
1912 MaterialNonBindlessAllocatedBindGroup::Prepared { bind_group, .. } => {
1913 MaterialNonBindlessSlab::Prepared(bind_group)
1914 }
1915 MaterialNonBindlessAllocatedBindGroup::Unprepared { bind_group, .. } => {
1916 MaterialNonBindlessSlab::Unprepared(bind_group)
1917 }
1918 })
1919 }
1920
1921 fn prepare_bind_groups(&mut self, render_device: &RenderDevice) {
1928 for bind_group_index in mem::take(&mut self.to_prepare) {
1929 let Some(MaterialNonBindlessAllocatedBindGroup::Unprepared {
1930 bind_group: unprepared_bind_group,
1931 layout: bind_group_layout,
1932 }) = mem::take(&mut self.bind_groups[*bind_group_index as usize])
1933 else {
1934 panic!("Allocation didn't exist or was already prepared");
1935 };
1936
1937 let mut uniform_buffers = vec![];
1939 for (index, binding) in unprepared_bind_group.bindings.iter() {
1940 let OwnedBindingResource::Data(ref owned_data) = *binding else {
1941 continue;
1942 };
1943 let label = format!("material uniform data {}", *index);
1944 let uniform_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
1945 label: Some(&label),
1946 contents: &owned_data.0,
1947 usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM,
1948 });
1949 uniform_buffers.push(uniform_buffer);
1950 }
1951
1952 let mut bind_group_entries = vec![];
1954 let mut uniform_buffers_iter = uniform_buffers.iter();
1955 for (index, binding) in unprepared_bind_group.bindings.iter() {
1956 match *binding {
1957 OwnedBindingResource::Data(_) => {
1958 bind_group_entries.push(BindGroupEntry {
1959 binding: *index,
1960 resource: uniform_buffers_iter
1961 .next()
1962 .expect("We should have created uniform buffers for each `Data`")
1963 .as_entire_binding(),
1964 });
1965 }
1966 _ => bind_group_entries.push(BindGroupEntry {
1967 binding: *index,
1968 resource: binding.get_binding(),
1969 }),
1970 }
1971 }
1972
1973 let bind_group = render_device.create_bind_group(
1975 M::label(),
1976 &bind_group_layout,
1977 &bind_group_entries,
1978 );
1979
1980 self.bind_groups[*bind_group_index as usize] =
1981 Some(MaterialNonBindlessAllocatedBindGroup::Prepared {
1982 bind_group: PreparedBindGroup {
1983 bindings: unprepared_bind_group.bindings,
1984 bind_group,
1985 data: unprepared_bind_group.data,
1986 },
1987 uniform_buffers,
1988 });
1989 }
1990 }
1991}
1992
1993impl<'a, M> MaterialSlab<'a, M>
1994where
1995 M: Material,
1996{
1997 pub fn get_extra_data(&self, slot: MaterialBindGroupSlot) -> &M::Data {
2002 match self.0 {
2003 MaterialSlabImpl::Bindless(material_bindless_slab) => {
2004 material_bindless_slab.get_extra_data(slot)
2005 }
2006 MaterialSlabImpl::NonBindless(MaterialNonBindlessSlab::Prepared(
2007 prepared_bind_group,
2008 )) => &prepared_bind_group.data,
2009 MaterialSlabImpl::NonBindless(MaterialNonBindlessSlab::Unprepared(
2010 unprepared_bind_group,
2011 )) => &unprepared_bind_group.data,
2012 }
2013 }
2014
2015 pub fn bind_group(&self) -> Option<&'a BindGroup> {
2022 match self.0 {
2023 MaterialSlabImpl::Bindless(material_bindless_slab) => {
2024 material_bindless_slab.bind_group()
2025 }
2026 MaterialSlabImpl::NonBindless(MaterialNonBindlessSlab::Prepared(
2027 prepared_bind_group,
2028 )) => Some(&prepared_bind_group.bind_group),
2029 MaterialSlabImpl::NonBindless(MaterialNonBindlessSlab::Unprepared(_)) => None,
2030 }
2031 }
2032}
2033
2034impl MaterialDataBuffer {
2035 fn new(binding_number: BindingNumber, aligned_element_size: u32) -> MaterialDataBuffer {
2039 MaterialDataBuffer {
2040 binding_number,
2041 buffer: RetainedRawBufferVec::new(BufferUsages::STORAGE),
2042 aligned_element_size,
2043 free_slots: vec![],
2044 len: 0,
2045 }
2046 }
2047
2048 fn insert(&mut self, data: &[u8]) -> u32 {
2054 debug_assert_eq!(data.len(), self.aligned_element_size as usize);
2056
2057 let slot = self.free_slots.pop().unwrap_or(self.len);
2059
2060 let start = slot as usize * self.aligned_element_size as usize;
2062 let end = (slot as usize + 1) * self.aligned_element_size as usize;
2063
2064 if self.buffer.len() < end {
2066 self.buffer.reserve_internal(end);
2067 }
2068 while self.buffer.values().len() < end {
2069 self.buffer.push(0);
2070 }
2071
2072 self.buffer.values_mut()[start..end].copy_from_slice(data);
2074
2075 self.len += 1;
2077 self.buffer.dirty = BufferDirtyState::NeedsReserve;
2078 slot
2079 }
2080
2081 fn remove(&mut self, slot: u32) {
2083 self.free_slots.push(slot);
2084 self.len -= 1;
2085 }
2086}