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, 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,
116 pub(crate) color_inverse_square_range: Vec4,
117 pub(crate) position_radius: Vec4,
118 pub(crate) flags: u32,
119 pub(crate) shadow_depth_bias: f32,
120 pub(crate) shadow_normal_bias: f32,
121 pub(crate) spot_light_tan_angle: f32,
122 pub(crate) soft_shadow_size: f32,
123 pub(crate) shadow_map_near_z: f32,
124 pub(crate) decal_index: u32,
129 pub(crate) range: f32,
131}
132
133#[derive(Resource)]
136pub struct GlobalClusterableObjectMeta {
137 pub gpu_clustered_lights: GpuClusteredLights,
142
143 pub entity_to_index: EntityHashMap<usize>,
149}
150
151pub struct GpuClusteredLights {
159 data: RawBufferVec<GpuClusteredLight>,
160 is_storage_buffer: bool,
161}
162
163#[derive(Component)]
164pub struct ExtractedClusterConfig {
165 pub(crate) near: f32,
167 pub(crate) far: f32,
168 pub(crate) dimensions: UVec3,
170}
171
172impl<'a> From<&'a Clusters> for ExtractedClusterConfig {
173 fn from(clusters: &'a Clusters) -> Self {
174 Self {
175 near: clusters.near,
176 far: clusters.far,
177 dimensions: clusters.dimensions,
178 }
179 }
180}
181
182enum ExtractedClusterableObjectElement {
185 ClusterHeader(ClusterableObjectCounts),
187 Light(Entity),
191 ReflectionProbe(MainEntity),
196 IrradianceVolume(MainEntity),
201 Decal(Entity),
205}
206
207#[derive(Component)]
208pub struct ExtractedClusterableObjects {
209 data: Vec<ExtractedClusterableObjectElement>,
210}
211
212#[derive(ShaderType)]
213struct GpuClusterOffsetsAndCountsUniform {
214 data: Box<[UVec4; ViewClusterBindings::MAX_UNIFORM_ITEMS]>,
215}
216
217#[derive(ShaderType, Default)]
218struct GpuClusterableObjectIndexListsStorage {
219 #[shader(size(runtime))]
220 data: Vec<u32>,
221}
222
223#[derive(ShaderType, Default)]
224struct GpuClusterOffsetsAndCountsStorage {
225 #[shader(size(runtime))]
229 data: Vec<GpuClusterOffsetAndCounts>,
230}
231
232type GpuClusterOffsetAndCounts = [UVec4; 2];
234
235enum ViewClusterBuffers {
236 Uniform {
237 clusterable_object_index_lists: UniformBuffer<GpuClusterableObjectIndexListsUniform>,
239 cluster_offsets_and_counts: UniformBuffer<GpuClusterOffsetsAndCountsUniform>,
241 },
242 Storage {
243 clusterable_object_index_lists: StorageBuffer<GpuClusterableObjectIndexListsStorage>,
244 cluster_offsets_and_counts: StorageBuffer<GpuClusterOffsetsAndCountsStorage>,
245 },
246}
247
248#[derive(Component)]
249pub struct ViewClusterBindings {
250 n_indices: usize,
251 n_offsets: usize,
252 buffers: ViewClusterBuffers,
253}
254
255pub fn init_global_clusterable_object_meta(
256 mut commands: Commands,
257 render_device: Res<RenderDevice>,
258) {
259 commands.insert_resource(GlobalClusterableObjectMeta::new(
260 render_device.get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT),
261 ));
262}
263
264impl GlobalClusterableObjectMeta {
265 pub fn new(buffer_binding_type: BufferBindingType) -> Self {
266 Self {
267 gpu_clustered_lights: GpuClusteredLights::new(buffer_binding_type),
268 entity_to_index: EntityHashMap::default(),
269 }
270 }
271}
272
273impl GpuClusteredLights {
274 fn new(buffer_binding_type: BufferBindingType) -> Self {
275 match buffer_binding_type {
276 BufferBindingType::Storage { .. } => Self::storage(),
277 BufferBindingType::Uniform => Self::uniform(),
278 }
279 }
280
281 fn uniform() -> Self {
282 GpuClusteredLights {
283 data: RawBufferVec::new(BufferUsages::UNIFORM),
284 is_storage_buffer: false,
285 }
286 }
287
288 fn storage() -> Self {
289 GpuClusteredLights {
290 data: RawBufferVec::new(BufferUsages::STORAGE),
291 is_storage_buffer: true,
292 }
293 }
294
295 pub(crate) fn clear(&mut self) {
296 self.data.clear();
297 }
298
299 pub(crate) fn len(&self) -> usize {
300 self.data.len()
301 }
302
303 pub(crate) fn add(&mut self, light: GpuClusteredLight) {
304 if self.is_storage_buffer || self.data.len() < MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS {
305 self.data.push(light);
306 }
307 }
308
309 pub(crate) fn write_buffer(
310 &mut self,
311 render_device: &RenderDevice,
312 render_queue: &RenderQueue,
313 ) {
314 if self.is_storage_buffer {
315 if self.data.is_empty() {
316 self.data.push(GpuClusteredLight::default());
317 }
318 } else {
319 while self.data.len() < MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS {
320 self.data.push(GpuClusteredLight::default());
321 }
322 }
323
324 self.data.write_buffer(render_device, render_queue);
325 }
326
327 pub fn binding(&self) -> Option<BindingResource<'_>> {
328 self.data.binding()
329 }
330
331 pub fn min_size(buffer_binding_type: BufferBindingType) -> NonZero<u64> {
332 match buffer_binding_type {
333 BufferBindingType::Storage { .. } => GpuClusteredLight::min_size(),
334 BufferBindingType::Uniform => NonZero::try_from(
335 u64::from(GpuClusteredLight::min_size())
336 * MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS as u64,
337 )
338 .unwrap(),
339 }
340 }
341
342 pub fn max_clustered_lights(&self) -> Option<usize> {
343 if self.is_storage_buffer {
344 None
345 } else {
346 Some(MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS)
347 }
348 }
349}
350
351type ClusterExtractionMapperQueryFlags = (
353 Has<PointLight>,
354 Has<SpotLight>,
355 Has<EnvironmentMapLight>,
356 Has<IrradianceVolume>,
357 Has<ClusteredDecal>,
358);
359type ClusterExtractionMapperQueryFilter = Or<(
361 With<PointLight>,
362 With<SpotLight>,
363 With<EnvironmentMapLight>,
364 With<IrradianceVolume>,
365 With<ClusteredDecal>,
366)>;
367
368pub fn gpu_clustering_is_enabled_during_extraction(
372 global_cluster_settings: Extract<Res<GlobalClusterSettings>>,
373) -> bool {
374 global_cluster_settings.gpu_clustering.is_some()
375}
376
377pub fn gpu_clustering_is_enabled(global_cluster_settings: Res<GlobalClusterSettings>) -> bool {
381 global_cluster_settings.gpu_clustering.is_some()
382}
383
384pub fn extract_clusters_for_cpu_clustering(
386 mut commands: Commands,
387 views: Extract<Query<(RenderEntity, &Clusters, &Camera)>>,
388 mapper: Extract<
389 Query<
390 (Option<&RenderEntity>, ClusterExtractionMapperQueryFlags),
391 ClusterExtractionMapperQueryFilter,
392 >,
393 >,
394 global_cluster_settings: Extract<Res<GlobalClusterSettings>>,
395) {
396 for (entity, clusters, camera) in &views {
397 let mut entity_commands = commands
398 .get_entity(entity)
399 .expect("Clusters entity wasn't synced.");
400 if !camera.is_active {
401 entity_commands.remove::<(ExtractedClusterableObjects, ExtractedClusterConfig)>();
402 continue;
403 }
404
405 let clusterable_objects = match clusters.clusterable_objects {
406 ClusterableObjects::Cpu(ref cpu_clusterable_objects) => cpu_clusterable_objects,
407 ClusterableObjects::Gpu => {
408 error!("Clusterable objects must have been in CPU mode if doing CPU clustering");
409 continue;
410 }
411 };
412
413 let mut data = vec![];
414 for cluster_objects in clusterable_objects {
415 data.push(ExtractedClusterableObjectElement::ClusterHeader(
416 cluster_objects.counts,
417 ));
418 for clusterable_entity in cluster_objects.iter() {
419 let Ok((
420 maybe_render_entity,
421 (
422 is_point_light,
423 is_spot_light,
424 is_reflection_probe,
425 is_irradiance_volume,
426 is_clustered_decal,
427 ),
428 )) = mapper.get(*clusterable_entity)
429 else {
430 error!(
431 "Couldn't find clustered object {:?} in the main world",
432 clusterable_entity
433 );
434 continue;
435 };
436
437 if let Some(render_entity) = maybe_render_entity {
438 if is_clustered_decal {
439 data.push(ExtractedClusterableObjectElement::Decal(**render_entity));
440 } else if is_point_light || is_spot_light {
441 data.push(ExtractedClusterableObjectElement::Light(**render_entity));
442 }
443 }
444 if is_reflection_probe {
445 data.push(ExtractedClusterableObjectElement::ReflectionProbe(
446 MainEntity::from(*clusterable_entity),
447 ));
448 }
449 if is_irradiance_volume {
450 data.push(ExtractedClusterableObjectElement::IrradianceVolume(
451 MainEntity::from(*clusterable_entity),
452 ));
453 }
454 }
455 }
456
457 entity_commands.insert((
458 ExtractedClusterableObjects { data },
459 ExtractedClusterConfig::from(clusters),
460 ));
461 }
462
463 commands.insert_resource(global_cluster_settings.clone());
464}
465
466pub fn prepare_clusters_for_cpu_clustering(
469 mut commands: Commands,
470 render_device: Res<RenderDevice>,
471 render_queue: Res<RenderQueue>,
472 mesh_pipeline: Res<MeshPipeline>,
473 global_clusterable_object_meta: Res<GlobalClusterableObjectMeta>,
474 views: Query<(
475 Entity,
476 &ExtractedClusterableObjects,
477 Option<&RenderViewLightProbes<EnvironmentMapLight>>,
478 Option<&RenderViewLightProbes<IrradianceVolume>>,
479 )>,
480) {
481 let render_device = render_device.into_inner();
482 let supports_storage_buffers = matches!(
483 mesh_pipeline.clustered_forward_buffer_binding_type,
484 BufferBindingType::Storage { .. }
485 );
486 for (entity, extracted_clusters, maybe_environment_maps, maybe_irradiance_volumes) in &views {
487 let mut view_clusters_bindings =
488 ViewClusterBindings::new(mesh_pipeline.clustered_forward_buffer_binding_type);
489 view_clusters_bindings.clear();
490
491 for record in &extracted_clusters.data {
492 match record {
493 ExtractedClusterableObjectElement::ClusterHeader(counts) => {
494 let offset = view_clusters_bindings.n_indices();
495 view_clusters_bindings.push_offset_and_counts(offset, counts);
496 }
497
498 ExtractedClusterableObjectElement::Light(entity)
499 | ExtractedClusterableObjectElement::Decal(entity) => {
500 if let Some(clusterable_object_index) =
501 global_clusterable_object_meta.entity_to_index.get(entity)
502 {
503 if view_clusters_bindings.n_indices() >= ViewClusterBindings::MAX_INDICES
504 && !supports_storage_buffers
505 {
506 warn!(
507 "Clusterable object index lists are full! The clusterable \
508 objects in the view are present in too many clusters."
509 );
510 break;
511 }
512 view_clusters_bindings.push_index(*clusterable_object_index);
513 } else {
514 error!(
518 "Clustered light or decal {:?} had no assigned index!",
519 entity
520 );
521 debug_assert!(false);
523 view_clusters_bindings.push_dummy_index();
524 }
525 }
526
527 ExtractedClusterableObjectElement::ReflectionProbe(main_entity) => {
528 match maybe_environment_maps.and_then(|environment_maps| {
529 environment_maps
530 .main_entity_to_render_light_probe_index
531 .get(main_entity)
532 }) {
533 Some(render_light_probe_index) => {
534 view_clusters_bindings.push_index(*render_light_probe_index as usize);
535 }
536 None => {
537 trace!(
539 "Clustered reflection probe {:?} had no assigned index",
540 main_entity,
541 );
542 view_clusters_bindings.push_dummy_index();
543 }
544 }
545 }
546
547 ExtractedClusterableObjectElement::IrradianceVolume(main_entity) => {
548 match maybe_irradiance_volumes.and_then(|irradiance_volumes| {
549 irradiance_volumes
550 .main_entity_to_render_light_probe_index
551 .get(main_entity)
552 }) {
553 Some(render_light_probe_index) => {
554 view_clusters_bindings.push_index(*render_light_probe_index as usize);
555 }
556 None => {
557 trace!(
558 "Clustered irradiance volume {:?} had no assigned index",
559 main_entity
560 );
561 view_clusters_bindings.push_dummy_index();
562 }
563 }
564 }
565 }
566 }
567
568 view_clusters_bindings.write_buffers(render_device, &render_queue);
569
570 commands.entity(entity).insert(view_clusters_bindings);
571 }
572}
573
574impl ViewClusterBindings {
575 pub const MAX_OFFSETS: usize = 16384 / 4;
576 const MAX_UNIFORM_ITEMS: usize = Self::MAX_OFFSETS / 4;
577 pub const MAX_INDICES: usize = 16384;
578
579 pub fn new(buffer_binding_type: BufferBindingType) -> Self {
580 Self {
581 n_indices: 0,
582 n_offsets: 0,
583 buffers: ViewClusterBuffers::new(buffer_binding_type),
584 }
585 }
586
587 pub fn clear(&mut self) {
588 match &mut self.buffers {
589 ViewClusterBuffers::Uniform {
590 clusterable_object_index_lists,
591 cluster_offsets_and_counts,
592 } => {
593 *clusterable_object_index_lists.get_mut().data =
594 [UVec4::ZERO; Self::MAX_UNIFORM_ITEMS];
595 *cluster_offsets_and_counts.get_mut().data = [UVec4::ZERO; Self::MAX_UNIFORM_ITEMS];
596 }
597 ViewClusterBuffers::Storage {
598 clusterable_object_index_lists,
599 cluster_offsets_and_counts,
600 ..
601 } => {
602 clusterable_object_index_lists.get_mut().data.clear();
603 cluster_offsets_and_counts.get_mut().data.clear();
604 }
605 }
606 }
607
608 fn push_offset_and_counts(&mut self, offset: usize, counts: &ClusterableObjectCounts) {
609 match &mut self.buffers {
610 ViewClusterBuffers::Uniform {
611 cluster_offsets_and_counts,
612 ..
613 } => {
614 let array_index = self.n_offsets >> 2; if array_index >= Self::MAX_UNIFORM_ITEMS {
616 warn!("cluster offset and count out of bounds!");
617 return;
618 }
619 let component = self.n_offsets & ((1 << 2) - 1);
620 let packed =
621 pack_offset_and_counts(offset, counts.point_lights, counts.spot_lights);
622
623 cluster_offsets_and_counts.get_mut().data[array_index][component] = packed;
624 }
625 ViewClusterBuffers::Storage {
626 cluster_offsets_and_counts,
627 ..
628 } => {
629 cluster_offsets_and_counts.get_mut().data.push([
630 uvec4(
631 offset as u32,
632 counts.point_lights,
633 counts.spot_lights,
634 counts.reflection_probes,
635 ),
636 uvec4(counts.irradiance_volumes, counts.decals, 0, 0),
637 ]);
638 }
639 }
640
641 self.n_offsets += 1;
642 }
643
644 pub fn n_indices(&self) -> usize {
645 self.n_indices
646 }
647
648 fn push_raw_index(&mut self, index: u32) {
651 match &mut self.buffers {
652 ViewClusterBuffers::Uniform {
653 clusterable_object_index_lists,
654 ..
655 } => {
656 let array_index = self.n_indices >> 4; let component = (self.n_indices >> 2) & ((1 << 2) - 1);
658 let sub_index = self.n_indices & ((1 << 2) - 1);
659
660 clusterable_object_index_lists.get_mut().data[array_index][component] |=
661 index << (8 * sub_index);
662 }
663 ViewClusterBuffers::Storage {
664 clusterable_object_index_lists,
665 ..
666 } => {
667 clusterable_object_index_lists.get_mut().data.push(index);
668 }
669 }
670
671 self.n_indices += 1;
672 }
673
674 pub fn push_index(&mut self, index: usize) {
676 self.push_raw_index(index as u32);
677 }
678
679 pub fn push_dummy_index(&mut self) {
684 self.push_raw_index(!0);
685 }
686
687 pub fn reserve_clusters(&mut self, clusters: usize) {
690 match &mut self.buffers {
691 ViewClusterBuffers::Uniform { .. } => {
692 error!("`reserve_clusters` should only be called in GPU clustering, which requires a storage buffer");
693 }
694 ViewClusterBuffers::Storage {
695 cluster_offsets_and_counts,
696 ..
697 } => {
698 cluster_offsets_and_counts
699 .get_mut()
700 .data
701 .extend(iter::repeat_n(
702 GpuClusterOffsetAndCounts::default(),
703 clusters,
704 ));
705 self.n_offsets += clusters;
706 }
707 }
708 }
709
710 pub fn reserve_indices(&mut self, elements: usize) {
712 match &mut self.buffers {
713 ViewClusterBuffers::Uniform { .. } => {
714 error!("`reserve_indices` should only be called in GPU clustering, which requires a storage buffer");
715 }
716 ViewClusterBuffers::Storage {
717 clusterable_object_index_lists,
718 ..
719 } => {
720 clusterable_object_index_lists
721 .get_mut()
722 .data
723 .extend(iter::repeat_n(0, elements));
724 self.n_indices += elements;
725 }
726 }
727 }
728
729 pub fn write_buffers(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
730 match &mut self.buffers {
731 ViewClusterBuffers::Uniform {
732 clusterable_object_index_lists,
733 cluster_offsets_and_counts,
734 } => {
735 clusterable_object_index_lists.write_buffer(render_device, render_queue);
736 cluster_offsets_and_counts.write_buffer(render_device, render_queue);
737 }
738 ViewClusterBuffers::Storage {
739 clusterable_object_index_lists,
740 cluster_offsets_and_counts,
741 } => {
742 clusterable_object_index_lists.write_buffer(render_device, render_queue);
743 cluster_offsets_and_counts.write_buffer(render_device, render_queue);
744 }
745 }
746 }
747
748 pub fn clusterable_object_index_lists_binding(&self) -> Option<BindingResource<'_>> {
749 match &self.buffers {
750 ViewClusterBuffers::Uniform {
751 clusterable_object_index_lists,
752 ..
753 } => clusterable_object_index_lists.binding(),
754 ViewClusterBuffers::Storage {
755 clusterable_object_index_lists,
756 ..
757 } => clusterable_object_index_lists.binding(),
758 }
759 }
760
761 pub fn offsets_and_counts_binding(&self) -> Option<BindingResource<'_>> {
762 match &self.buffers {
763 ViewClusterBuffers::Uniform {
764 cluster_offsets_and_counts,
765 ..
766 } => cluster_offsets_and_counts.binding(),
767 ViewClusterBuffers::Storage {
768 cluster_offsets_and_counts,
769 ..
770 } => cluster_offsets_and_counts.binding(),
771 }
772 }
773
774 pub fn min_size_clusterable_object_index_lists(
775 buffer_binding_type: BufferBindingType,
776 ) -> NonZero<u64> {
777 match buffer_binding_type {
778 BufferBindingType::Storage { .. } => GpuClusterableObjectIndexListsStorage::min_size(),
779 BufferBindingType::Uniform => GpuClusterableObjectIndexListsUniform::min_size(),
780 }
781 }
782
783 pub fn min_size_cluster_offsets_and_counts(
784 buffer_binding_type: BufferBindingType,
785 ) -> NonZero<u64> {
786 match buffer_binding_type {
787 BufferBindingType::Storage { .. } => GpuClusterOffsetsAndCountsStorage::min_size(),
788 BufferBindingType::Uniform => GpuClusterOffsetsAndCountsUniform::min_size(),
789 }
790 }
791}
792
793impl ViewClusterBuffers {
794 fn new(buffer_binding_type: BufferBindingType) -> Self {
795 match buffer_binding_type {
796 BufferBindingType::Storage { .. } => Self::storage(),
797 BufferBindingType::Uniform => Self::uniform(),
798 }
799 }
800
801 fn uniform() -> Self {
802 ViewClusterBuffers::Uniform {
803 clusterable_object_index_lists: UniformBuffer::default(),
804 cluster_offsets_and_counts: UniformBuffer::default(),
805 }
806 }
807
808 fn storage() -> Self {
809 ViewClusterBuffers::Storage {
810 clusterable_object_index_lists: StorageBuffer::default(),
811 cluster_offsets_and_counts: StorageBuffer::default(),
812 }
813 }
814}
815
816fn pack_offset_and_counts(offset: usize, point_count: u32, spot_count: u32) -> u32 {
841 ((offset as u32 & CLUSTER_OFFSET_MASK) << (CLUSTER_COUNT_SIZE * 2))
842 | ((point_count & CLUSTER_COUNT_MASK) << CLUSTER_COUNT_SIZE)
843 | (spot_count & CLUSTER_COUNT_MASK)
844}
845
846#[derive(ShaderType)]
847struct GpuClusterableObjectIndexListsUniform {
848 data: Box<[UVec4; ViewClusterBindings::MAX_UNIFORM_ITEMS]>,
849}
850
851const _: () = assert!(GpuClusterableObjectIndexListsUniform::SHADER_SIZE.get() <= 16384);
854
855impl Default for GpuClusterableObjectIndexListsUniform {
856 fn default() -> Self {
857 Self {
858 data: Box::new([UVec4::ZERO; ViewClusterBindings::MAX_UNIFORM_ITEMS]),
859 }
860 }
861}
862
863impl Default for GpuClusterOffsetsAndCountsUniform {
864 fn default() -> Self {
865 Self {
866 data: Box::new([UVec4::ZERO; ViewClusterBindings::MAX_UNIFORM_ITEMS]),
867 }
868 }
869}