1use core::{iter, num::NonZero};
2
3use bevy_camera::Camera;
4use bevy_ecs::{entity::EntityHashMap, prelude::*};
5use bevy_light::{
6 cluster::{
7 ClusterableObjectCounts, ClusterableObjects, Clusters, GlobalClusterGpuSettings,
8 GlobalClusterSettings,
9 },
10 ClusteredDecal, EnvironmentMapLight, IrradianceVolume, PointLight, RectLight, SpotLight,
11};
12use bevy_math::{uvec4, UVec3, UVec4, Vec4};
13use bevy_render::{
14 render_resource::{
15 BindingResource, BufferBindingType, BufferUsages, DownlevelFlags, RawBufferVec, ShaderSize,
16 ShaderType, StorageBuffer, UniformBuffer,
17 },
18 renderer::{RenderAdapter, RenderDevice, RenderQueue},
19 sync_world::{MainEntity, RenderEntity},
20 Extract,
21};
22use bytemuck::{Pod, Zeroable};
23use tracing::{error, info, trace, warn};
24
25use crate::{MeshPipeline, RenderViewLightProbes};
26
27pub(crate) mod gpu;
28
29pub const MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS: usize = 204;
32const _: () =
35 assert!(size_of::<GpuClusteredLight>() * MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS <= 16384);
36
37pub const CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT: u32 = 3;
40
41const CLUSTER_COUNT_SIZE: u32 = 9;
44
45const CLUSTER_OFFSET_MASK: u32 = (1 << (32 - (CLUSTER_COUNT_SIZE * 2))) - 1;
46const CLUSTER_COUNT_MASK: u32 = (1 << CLUSTER_COUNT_SIZE) - 1;
47
48pub const GPU_CLUSTERING_INITIAL_Z_SLICE_LIST_CAPACITY: usize = 1024;
53
54pub const GPU_CLUSTERING_INITIAL_INDEX_LIST_CAPACITY: usize = 65536;
59
60pub(crate) fn make_global_cluster_settings(world: &World) -> GlobalClusterSettings {
62 let device = world.resource::<RenderDevice>();
63 let adapter = world.resource::<RenderAdapter>();
64 let clustered_decals_are_usable =
65 crate::decal::clustered::clustered_decals_are_usable(device, adapter);
66 let supports_storage_buffers = matches!(
67 device.get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT),
68 BufferBindingType::Storage { .. }
69 );
70
71 let gpu_clustering_supported = !(cfg!(target_os = "android") || cfg!(target_abi = "sim"))
80 && adapter
81 .get_downlevel_capabilities()
82 .flags
83 .contains(DownlevelFlags::COMPUTE_SHADERS)
84 && device.limits().max_storage_buffers_per_shader_stage > 0;
85
86 let gpu_clustering = if gpu_clustering_supported {
87 info!("GPU clustering is supported on this device.");
88 Some(GlobalClusterGpuSettings {
89 initial_z_slice_list_capacity: GPU_CLUSTERING_INITIAL_Z_SLICE_LIST_CAPACITY,
90 initial_index_list_capacity: GPU_CLUSTERING_INITIAL_INDEX_LIST_CAPACITY,
91 })
92 } else {
93 info!("GPU clustering isn't supported on this device; falling back to CPU clustering.");
94 None
95 };
96
97 GlobalClusterSettings {
98 supports_storage_buffers,
99 clustered_decals_are_usable,
100 gpu_clustering,
101 max_uniform_buffer_clusterable_objects: MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS,
102 view_cluster_bindings_max_indices: ViewClusterBindings::MAX_INDICES,
103 }
104}
105
106#[derive(Copy, Clone, ShaderType, Default, Pod, Zeroable, Debug)]
111#[repr(C)]
112pub struct GpuClusteredLight {
113 pub(crate) light_custom_data: Vec4,
117 pub(crate) color_inverse_square_range: Vec4,
119 pub(crate) position_radius: Vec4,
121 pub(crate) flags: u32,
122 pub(crate) shadow_depth_bias: f32,
123 pub(crate) shadow_normal_bias: f32,
124 pub(crate) spot_light_tan_angle: f32,
125 pub(crate) soft_shadow_size: f32,
126 pub(crate) shadow_map_near_z: f32,
127 pub(crate) decal_index: u32,
132 pub(crate) range: f32,
134}
135
136#[derive(Resource)]
139pub struct GlobalClusterableObjectMeta {
140 pub gpu_clustered_lights: GpuClusteredLights,
145
146 pub entity_to_index: EntityHashMap<usize>,
152}
153
154pub struct GpuClusteredLights {
162 data: RawBufferVec<GpuClusteredLight>,
163 is_storage_buffer: bool,
164}
165
166#[derive(Component)]
167pub struct ExtractedClusterConfig {
168 pub(crate) near: f32,
170 pub(crate) far: f32,
171 pub(crate) dimensions: UVec3,
173}
174
175impl<'a> From<&'a Clusters> for ExtractedClusterConfig {
176 fn from(clusters: &'a Clusters) -> Self {
177 Self {
178 near: clusters.near,
179 far: clusters.far,
180 dimensions: clusters.dimensions,
181 }
182 }
183}
184
185enum ExtractedClusterableObjectElement {
188 ClusterHeader(ClusterableObjectCounts),
190 Light(Entity),
194 ReflectionProbe(MainEntity),
199 IrradianceVolume(MainEntity),
204 Decal(Entity),
208}
209
210#[derive(Component)]
211pub struct ExtractedClusterableObjects {
212 data: Vec<ExtractedClusterableObjectElement>,
213}
214
215#[derive(ShaderType)]
216struct GpuClusterOffsetsAndCountsUniform {
217 data: Box<[UVec4; ViewClusterBindings::MAX_UNIFORM_ITEMS]>,
218}
219
220#[derive(ShaderType, Default)]
221struct GpuClusterableObjectIndexListsStorage {
222 #[shader(size(runtime))]
223 data: Vec<u32>,
224}
225
226#[derive(ShaderType, Default)]
227struct GpuClusterOffsetsAndCountsStorage {
228 #[shader(size(runtime))]
232 data: Vec<GpuClusterOffsetAndCounts>,
233}
234
235type GpuClusterOffsetAndCounts = [UVec4; 2];
237
238enum ViewClusterBuffers {
239 Uniform {
240 clusterable_object_index_lists: UniformBuffer<GpuClusterableObjectIndexListsUniform>,
242 cluster_offsets_and_counts: UniformBuffer<GpuClusterOffsetsAndCountsUniform>,
244 },
245 Storage {
246 clusterable_object_index_lists: StorageBuffer<GpuClusterableObjectIndexListsStorage>,
247 cluster_offsets_and_counts: StorageBuffer<GpuClusterOffsetsAndCountsStorage>,
248 },
249}
250
251#[derive(Component)]
252pub struct ViewClusterBindings {
253 n_indices: usize,
254 n_offsets: usize,
255 buffers: ViewClusterBuffers,
256}
257
258pub fn init_global_clusterable_object_meta(
259 mut commands: Commands,
260 render_device: Res<RenderDevice>,
261) {
262 commands.insert_resource(GlobalClusterableObjectMeta::new(
263 render_device.get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT),
264 ));
265}
266
267impl GlobalClusterableObjectMeta {
268 pub fn new(buffer_binding_type: BufferBindingType) -> Self {
269 Self {
270 gpu_clustered_lights: GpuClusteredLights::new(buffer_binding_type),
271 entity_to_index: EntityHashMap::default(),
272 }
273 }
274}
275
276impl GpuClusteredLights {
277 fn new(buffer_binding_type: BufferBindingType) -> Self {
278 match buffer_binding_type {
279 BufferBindingType::Storage { .. } => Self::storage(),
280 BufferBindingType::Uniform => Self::uniform(),
281 }
282 }
283
284 fn uniform() -> Self {
285 GpuClusteredLights {
286 data: RawBufferVec::new(BufferUsages::UNIFORM),
287 is_storage_buffer: false,
288 }
289 }
290
291 fn storage() -> Self {
292 GpuClusteredLights {
293 data: RawBufferVec::new(BufferUsages::STORAGE),
294 is_storage_buffer: true,
295 }
296 }
297
298 pub(crate) fn clear(&mut self) {
299 self.data.clear();
300 }
301
302 pub(crate) fn len(&self) -> usize {
303 self.data.len()
304 }
305
306 pub(crate) fn is_storage_buffer(&self) -> bool {
307 self.is_storage_buffer
308 }
309
310 pub(crate) fn add(&mut self, light: GpuClusteredLight) {
311 if self.is_storage_buffer || self.data.len() < MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS {
312 self.data.push(light);
313 }
314 }
315
316 pub(crate) fn write_buffer(
317 &mut self,
318 render_device: &RenderDevice,
319 render_queue: &RenderQueue,
320 ) {
321 if self.is_storage_buffer {
322 if self.data.is_empty() {
323 self.data.push(GpuClusteredLight::default());
324 }
325 } else {
326 while self.data.len() < MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS {
327 self.data.push(GpuClusteredLight::default());
328 }
329 }
330
331 self.data.write_buffer(render_device, render_queue);
332 }
333
334 pub fn binding(&self) -> Option<BindingResource<'_>> {
335 self.data.binding()
336 }
337
338 pub fn min_size(buffer_binding_type: BufferBindingType) -> NonZero<u64> {
339 match buffer_binding_type {
340 BufferBindingType::Storage { .. } => GpuClusteredLight::min_size(),
341 BufferBindingType::Uniform => NonZero::try_from(
342 u64::from(GpuClusteredLight::min_size())
343 * MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS as u64,
344 )
345 .unwrap(),
346 }
347 }
348
349 pub fn max_clustered_lights(&self) -> Option<usize> {
350 if self.is_storage_buffer {
351 None
352 } else {
353 Some(MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS)
354 }
355 }
356}
357
358type ClusterExtractionMapperQueryFlags = (
360 Has<PointLight>,
361 Has<SpotLight>,
362 Has<RectLight>,
363 Has<EnvironmentMapLight>,
364 Has<IrradianceVolume>,
365 Has<ClusteredDecal>,
366);
367type ClusterExtractionMapperQueryFilter = Or<(
369 With<PointLight>,
370 With<SpotLight>,
371 With<RectLight>,
372 With<EnvironmentMapLight>,
373 With<IrradianceVolume>,
374 With<ClusteredDecal>,
375)>;
376
377pub fn gpu_clustering_is_enabled_during_extraction(
381 global_cluster_settings: Extract<Res<GlobalClusterSettings>>,
382) -> bool {
383 global_cluster_settings.gpu_clustering.is_some()
384}
385
386pub fn gpu_clustering_is_enabled(global_cluster_settings: Res<GlobalClusterSettings>) -> bool {
390 global_cluster_settings.gpu_clustering.is_some()
391}
392
393pub fn extract_clusters_for_cpu_clustering(
395 mut commands: Commands,
396 views: Extract<Query<(RenderEntity, &Clusters, &Camera)>>,
397 mapper: Extract<
398 Query<
399 (Option<&RenderEntity>, ClusterExtractionMapperQueryFlags),
400 ClusterExtractionMapperQueryFilter,
401 >,
402 >,
403 global_cluster_settings: Extract<Res<GlobalClusterSettings>>,
404) {
405 for (entity, clusters, camera) in &views {
406 let mut entity_commands = commands
407 .get_entity(entity)
408 .expect("Clusters entity wasn't synced.");
409 if !camera.is_active {
410 entity_commands.remove::<(ExtractedClusterableObjects, ExtractedClusterConfig)>();
411 continue;
412 }
413
414 let clusterable_objects = match clusters.clusterable_objects {
415 ClusterableObjects::Cpu(ref cpu_clusterable_objects) => cpu_clusterable_objects,
416 ClusterableObjects::Gpu => {
417 error!("Clusterable objects must have been in CPU mode if doing CPU clustering");
418 continue;
419 }
420 };
421
422 let mut data = vec![];
423 for cluster_objects in clusterable_objects {
424 data.push(ExtractedClusterableObjectElement::ClusterHeader(
425 cluster_objects.counts,
426 ));
427 for clusterable_entity in cluster_objects.iter() {
428 let Ok((
429 maybe_render_entity,
430 (
431 is_point_light,
432 is_spot_light,
433 is_rect_light,
434 is_reflection_probe,
435 is_irradiance_volume,
436 is_clustered_decal,
437 ),
438 )) = mapper.get(*clusterable_entity)
439 else {
440 error!(
441 "Couldn't find clustered object {:?} in the main world",
442 clusterable_entity
443 );
444 continue;
445 };
446
447 if let Some(render_entity) = maybe_render_entity {
448 if is_clustered_decal {
449 data.push(ExtractedClusterableObjectElement::Decal(**render_entity));
450 } else if is_point_light || is_spot_light || is_rect_light {
451 data.push(ExtractedClusterableObjectElement::Light(**render_entity));
452 }
453 }
454 if is_reflection_probe {
455 data.push(ExtractedClusterableObjectElement::ReflectionProbe(
456 MainEntity::from(*clusterable_entity),
457 ));
458 }
459 if is_irradiance_volume {
460 data.push(ExtractedClusterableObjectElement::IrradianceVolume(
461 MainEntity::from(*clusterable_entity),
462 ));
463 }
464 }
465 }
466
467 entity_commands.insert((
468 ExtractedClusterableObjects { data },
469 ExtractedClusterConfig::from(clusters),
470 ));
471 }
472
473 commands.insert_resource(global_cluster_settings.clone());
474}
475
476pub fn prepare_clusters_for_cpu_clustering(
479 mut commands: Commands,
480 render_device: Res<RenderDevice>,
481 render_queue: Res<RenderQueue>,
482 mesh_pipeline: Res<MeshPipeline>,
483 global_clusterable_object_meta: Res<GlobalClusterableObjectMeta>,
484 views: Query<(
485 Entity,
486 &ExtractedClusterableObjects,
487 Option<&RenderViewLightProbes<EnvironmentMapLight>>,
488 Option<&RenderViewLightProbes<IrradianceVolume>>,
489 )>,
490) {
491 let render_device = render_device.into_inner();
492 let supports_storage_buffers = matches!(
493 mesh_pipeline.clustered_forward_buffer_binding_type,
494 BufferBindingType::Storage { .. }
495 );
496 for (entity, extracted_clusters, maybe_environment_maps, maybe_irradiance_volumes) in &views {
497 let mut view_clusters_bindings =
498 ViewClusterBindings::new(mesh_pipeline.clustered_forward_buffer_binding_type);
499 view_clusters_bindings.clear();
500
501 for record in &extracted_clusters.data {
502 match record {
503 ExtractedClusterableObjectElement::ClusterHeader(counts) => {
504 let offset = view_clusters_bindings.n_indices();
505 view_clusters_bindings.push_offset_and_counts(offset, counts);
506 }
507
508 ExtractedClusterableObjectElement::Light(entity)
509 | ExtractedClusterableObjectElement::Decal(entity) => {
510 if let Some(clusterable_object_index) =
511 global_clusterable_object_meta.entity_to_index.get(entity)
512 {
513 if view_clusters_bindings.n_indices() >= ViewClusterBindings::MAX_INDICES
514 && !supports_storage_buffers
515 {
516 warn!(
517 "Clusterable object index lists are full! The clusterable \
518 objects in the view are present in too many clusters."
519 );
520 break;
521 }
522 view_clusters_bindings.push_index(*clusterable_object_index);
523 } else {
524 error!(
528 "Clustered light or decal {:?} had no assigned index!",
529 entity
530 );
531 debug_assert!(false);
533 view_clusters_bindings.push_dummy_index();
534 }
535 }
536
537 ExtractedClusterableObjectElement::ReflectionProbe(main_entity) => {
538 match maybe_environment_maps.and_then(|environment_maps| {
539 environment_maps
540 .main_entity_to_render_light_probe_index
541 .get(main_entity)
542 }) {
543 Some(render_light_probe_index) => {
544 view_clusters_bindings.push_index(*render_light_probe_index as usize);
545 }
546 None => {
547 trace!(
549 "Clustered reflection probe {:?} had no assigned index",
550 main_entity,
551 );
552 view_clusters_bindings.push_dummy_index();
553 }
554 }
555 }
556
557 ExtractedClusterableObjectElement::IrradianceVolume(main_entity) => {
558 match maybe_irradiance_volumes.and_then(|irradiance_volumes| {
559 irradiance_volumes
560 .main_entity_to_render_light_probe_index
561 .get(main_entity)
562 }) {
563 Some(render_light_probe_index) => {
564 view_clusters_bindings.push_index(*render_light_probe_index as usize);
565 }
566 None => {
567 trace!(
568 "Clustered irradiance volume {:?} had no assigned index",
569 main_entity
570 );
571 view_clusters_bindings.push_dummy_index();
572 }
573 }
574 }
575 }
576 }
577
578 view_clusters_bindings.write_buffers(render_device, &render_queue);
579
580 commands.entity(entity).insert(view_clusters_bindings);
581 }
582}
583
584impl ViewClusterBindings {
585 pub const MAX_OFFSETS: usize = 16384 / 4;
586 const MAX_UNIFORM_ITEMS: usize = Self::MAX_OFFSETS / 4;
587 pub const MAX_INDICES: usize = 16384;
588
589 pub fn new(buffer_binding_type: BufferBindingType) -> Self {
590 Self {
591 n_indices: 0,
592 n_offsets: 0,
593 buffers: ViewClusterBuffers::new(buffer_binding_type),
594 }
595 }
596
597 pub fn clear(&mut self) {
598 match &mut self.buffers {
599 ViewClusterBuffers::Uniform {
600 clusterable_object_index_lists,
601 cluster_offsets_and_counts,
602 } => {
603 *clusterable_object_index_lists.get_mut().data =
604 [UVec4::ZERO; Self::MAX_UNIFORM_ITEMS];
605 *cluster_offsets_and_counts.get_mut().data = [UVec4::ZERO; Self::MAX_UNIFORM_ITEMS];
606 }
607 ViewClusterBuffers::Storage {
608 clusterable_object_index_lists,
609 cluster_offsets_and_counts,
610 ..
611 } => {
612 clusterable_object_index_lists.get_mut().data.clear();
613 cluster_offsets_and_counts.get_mut().data.clear();
614 }
615 }
616 }
617
618 fn push_offset_and_counts(&mut self, offset: usize, counts: &ClusterableObjectCounts) {
619 match &mut self.buffers {
620 ViewClusterBuffers::Uniform {
621 cluster_offsets_and_counts,
622 ..
623 } => {
624 let array_index = self.n_offsets >> 2; if array_index >= Self::MAX_UNIFORM_ITEMS {
626 warn!("cluster offset and count out of bounds!");
627 return;
628 }
629 let component = self.n_offsets & ((1 << 2) - 1);
630 let packed =
631 pack_offset_and_counts(offset, counts.point_lights, counts.spot_lights);
632
633 cluster_offsets_and_counts.get_mut().data[array_index][component] = packed;
634 }
635 ViewClusterBuffers::Storage {
636 cluster_offsets_and_counts,
637 ..
638 } => {
639 cluster_offsets_and_counts.get_mut().data.push([
640 uvec4(
641 offset as u32,
642 counts.point_lights,
643 counts.spot_lights,
644 counts.rect_lights,
645 ),
646 uvec4(
647 counts.reflection_probes,
648 counts.irradiance_volumes,
649 counts.decals,
650 0,
651 ),
652 ]);
653 }
654 }
655
656 self.n_offsets += 1;
657 }
658
659 pub fn n_indices(&self) -> usize {
660 self.n_indices
661 }
662
663 fn push_raw_index(&mut self, index: u32) {
666 match &mut self.buffers {
667 ViewClusterBuffers::Uniform {
668 clusterable_object_index_lists,
669 ..
670 } => {
671 let array_index = self.n_indices >> 4; let component = (self.n_indices >> 2) & ((1 << 2) - 1);
673 let sub_index = self.n_indices & ((1 << 2) - 1);
674
675 clusterable_object_index_lists.get_mut().data[array_index][component] |=
676 index << (8 * sub_index);
677 }
678 ViewClusterBuffers::Storage {
679 clusterable_object_index_lists,
680 ..
681 } => {
682 clusterable_object_index_lists.get_mut().data.push(index);
683 }
684 }
685
686 self.n_indices += 1;
687 }
688
689 pub fn push_index(&mut self, index: usize) {
691 self.push_raw_index(index as u32);
692 }
693
694 pub fn push_dummy_index(&mut self) {
699 self.push_raw_index(!0);
700 }
701
702 pub fn reserve_clusters(&mut self, clusters: usize) {
705 match &mut self.buffers {
706 ViewClusterBuffers::Uniform { .. } => {
707 error!("`reserve_clusters` should only be called in GPU clustering, which requires a storage buffer");
708 }
709 ViewClusterBuffers::Storage {
710 cluster_offsets_and_counts,
711 ..
712 } => {
713 cluster_offsets_and_counts
714 .get_mut()
715 .data
716 .extend(iter::repeat_n(
717 GpuClusterOffsetAndCounts::default(),
718 clusters,
719 ));
720 self.n_offsets += clusters;
721 }
722 }
723 }
724
725 pub fn reserve_indices(&mut self, elements: usize) {
727 match &mut self.buffers {
728 ViewClusterBuffers::Uniform { .. } => {
729 error!("`reserve_indices` should only be called in GPU clustering, which requires a storage buffer");
730 }
731 ViewClusterBuffers::Storage {
732 clusterable_object_index_lists,
733 ..
734 } => {
735 clusterable_object_index_lists
736 .get_mut()
737 .data
738 .extend(iter::repeat_n(0, elements));
739 self.n_indices += elements;
740 }
741 }
742 }
743
744 pub fn write_buffers(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
745 match &mut self.buffers {
746 ViewClusterBuffers::Uniform {
747 clusterable_object_index_lists,
748 cluster_offsets_and_counts,
749 } => {
750 clusterable_object_index_lists.write_buffer(render_device, render_queue);
751 cluster_offsets_and_counts.write_buffer(render_device, render_queue);
752 }
753 ViewClusterBuffers::Storage {
754 clusterable_object_index_lists,
755 cluster_offsets_and_counts,
756 } => {
757 clusterable_object_index_lists.write_buffer(render_device, render_queue);
758 cluster_offsets_and_counts.write_buffer(render_device, render_queue);
759 }
760 }
761 }
762
763 pub fn clusterable_object_index_lists_binding(&self) -> Option<BindingResource<'_>> {
764 match &self.buffers {
765 ViewClusterBuffers::Uniform {
766 clusterable_object_index_lists,
767 ..
768 } => clusterable_object_index_lists.binding(),
769 ViewClusterBuffers::Storage {
770 clusterable_object_index_lists,
771 ..
772 } => clusterable_object_index_lists.binding(),
773 }
774 }
775
776 pub fn offsets_and_counts_binding(&self) -> Option<BindingResource<'_>> {
777 match &self.buffers {
778 ViewClusterBuffers::Uniform {
779 cluster_offsets_and_counts,
780 ..
781 } => cluster_offsets_and_counts.binding(),
782 ViewClusterBuffers::Storage {
783 cluster_offsets_and_counts,
784 ..
785 } => cluster_offsets_and_counts.binding(),
786 }
787 }
788
789 pub fn min_size_clusterable_object_index_lists(
790 buffer_binding_type: BufferBindingType,
791 ) -> NonZero<u64> {
792 match buffer_binding_type {
793 BufferBindingType::Storage { .. } => GpuClusterableObjectIndexListsStorage::min_size(),
794 BufferBindingType::Uniform => GpuClusterableObjectIndexListsUniform::min_size(),
795 }
796 }
797
798 pub fn min_size_cluster_offsets_and_counts(
799 buffer_binding_type: BufferBindingType,
800 ) -> NonZero<u64> {
801 match buffer_binding_type {
802 BufferBindingType::Storage { .. } => GpuClusterOffsetsAndCountsStorage::min_size(),
803 BufferBindingType::Uniform => GpuClusterOffsetsAndCountsUniform::min_size(),
804 }
805 }
806}
807
808impl ViewClusterBuffers {
809 fn new(buffer_binding_type: BufferBindingType) -> Self {
810 match buffer_binding_type {
811 BufferBindingType::Storage { .. } => Self::storage(),
812 BufferBindingType::Uniform => Self::uniform(),
813 }
814 }
815
816 fn uniform() -> Self {
817 ViewClusterBuffers::Uniform {
818 clusterable_object_index_lists: UniformBuffer::default(),
819 cluster_offsets_and_counts: UniformBuffer::default(),
820 }
821 }
822
823 fn storage() -> Self {
824 ViewClusterBuffers::Storage {
825 clusterable_object_index_lists: StorageBuffer::default(),
826 cluster_offsets_and_counts: StorageBuffer::default(),
827 }
828 }
829}
830
831fn pack_offset_and_counts(offset: usize, point_count: u32, spot_count: u32) -> u32 {
856 ((offset as u32 & CLUSTER_OFFSET_MASK) << (CLUSTER_COUNT_SIZE * 2))
857 | ((point_count & CLUSTER_COUNT_MASK) << CLUSTER_COUNT_SIZE)
858 | (spot_count & CLUSTER_COUNT_MASK)
859}
860
861#[derive(ShaderType)]
862struct GpuClusterableObjectIndexListsUniform {
863 data: Box<[UVec4; ViewClusterBindings::MAX_UNIFORM_ITEMS]>,
864}
865
866const _: () = assert!(GpuClusterableObjectIndexListsUniform::SHADER_SIZE.get() <= 16384);
869
870impl Default for GpuClusterableObjectIndexListsUniform {
871 fn default() -> Self {
872 Self {
873 data: Box::new([UVec4::ZERO; ViewClusterBindings::MAX_UNIFORM_ITEMS]),
874 }
875 }
876}
877
878impl Default for GpuClusterOffsetsAndCountsUniform {
879 fn default() -> Self {
880 Self {
881 data: Box::new([UVec4::ZERO; ViewClusterBindings::MAX_UNIFORM_ITEMS]),
882 }
883 }
884}