1use core::num::NonZero;
4
5use bevy_core_pipeline::core_3d::Camera3d;
6use bevy_ecs::{
7 component::Component,
8 entity::{Entity, EntityHashMap},
9 query::{With, Without},
10 reflect::ReflectComponent,
11 resource::Resource,
12 system::{Commands, Query, Res},
13 world::{FromWorld, World},
14};
15use bevy_math::{uvec4, AspectRatio, UVec2, UVec3, UVec4, Vec3Swizzles as _, Vec4};
16use bevy_platform::collections::HashSet;
17use bevy_reflect::{std_traits::ReflectDefault, Reflect};
18use bevy_render::{
19 camera::Camera,
20 render_resource::{
21 BindingResource, BufferBindingType, ShaderSize as _, ShaderType, StorageBuffer,
22 UniformBuffer,
23 },
24 renderer::{RenderDevice, RenderQueue},
25 sync_world::RenderEntity,
26 Extract,
27};
28use tracing::warn;
29
30pub(crate) use crate::cluster::assign::assign_objects_to_clusters;
31use crate::MeshPipeline;
32
33pub(crate) mod assign;
34
35#[cfg(test)]
36mod test;
37
38pub const MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS: usize = 204;
41const _: () =
44 assert!(size_of::<GpuClusterableObject>() * MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS <= 16384);
45
46pub const CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT: u32 = 3;
49
50const CLUSTER_COUNT_SIZE: u32 = 9;
53
54const CLUSTER_OFFSET_MASK: u32 = (1 << (32 - (CLUSTER_COUNT_SIZE * 2))) - 1;
55const CLUSTER_COUNT_MASK: u32 = (1 << CLUSTER_COUNT_SIZE) - 1;
56
57#[derive(Debug, Copy, Clone, Reflect)]
69#[reflect(Clone)]
70pub enum ClusterFarZMode {
71 MaxClusterableObjectRange,
76 Constant(f32),
78}
79
80#[derive(Debug, Copy, Clone, Reflect)]
82#[reflect(Default, Clone)]
83pub struct ClusterZConfig {
84 pub first_slice_depth: f32,
86 pub far_z_mode: ClusterFarZMode,
88}
89
90#[derive(Debug, Copy, Clone, Component, Reflect)]
92#[reflect(Component, Debug, Default, Clone)]
93pub enum ClusterConfig {
94 None,
96 Single,
99 XYZ {
101 dimensions: UVec3,
102 z_config: ClusterZConfig,
103 dynamic_resizing: bool,
106 },
107 FixedZ {
113 total: u32,
114 z_slices: u32,
115 z_config: ClusterZConfig,
116 dynamic_resizing: bool,
119 },
120}
121
122#[derive(Component, Debug, Default)]
123pub struct Clusters {
124 pub(crate) tile_size: UVec2,
126 pub(crate) dimensions: UVec3,
128 pub(crate) near: f32,
131 pub(crate) far: f32,
132 pub(crate) clusterable_objects: Vec<VisibleClusterableObjects>,
133}
134
135#[derive(Clone, Component, Debug, Default)]
136pub struct VisibleClusterableObjects {
137 pub(crate) entities: Vec<Entity>,
138 counts: ClusterableObjectCounts,
139}
140
141#[derive(Resource, Default)]
142pub struct GlobalVisibleClusterableObjects {
143 pub(crate) entities: HashSet<Entity>,
144}
145
146#[derive(Resource)]
147pub struct GlobalClusterableObjectMeta {
148 pub gpu_clusterable_objects: GpuClusterableObjects,
149 pub entity_to_index: EntityHashMap<usize>,
150}
151
152#[derive(Copy, Clone, ShaderType, Default, Debug)]
153pub struct GpuClusterableObject {
154 pub(crate) light_custom_data: Vec4,
157 pub(crate) color_inverse_square_range: Vec4,
158 pub(crate) position_radius: Vec4,
159 pub(crate) flags: u32,
160 pub(crate) shadow_depth_bias: f32,
161 pub(crate) shadow_normal_bias: f32,
162 pub(crate) spot_light_tan_angle: f32,
163 pub(crate) soft_shadow_size: f32,
164 pub(crate) shadow_map_near_z: f32,
165 pub(crate) pad_a: f32,
166 pub(crate) pad_b: f32,
167}
168
169pub enum GpuClusterableObjects {
170 Uniform(UniformBuffer<GpuClusterableObjectsUniform>),
171 Storage(StorageBuffer<GpuClusterableObjectsStorage>),
172}
173
174#[derive(ShaderType)]
175pub struct GpuClusterableObjectsUniform {
176 data: Box<[GpuClusterableObject; MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS]>,
177}
178
179#[derive(ShaderType, Default)]
180pub struct GpuClusterableObjectsStorage {
181 #[size(runtime)]
182 data: Vec<GpuClusterableObject>,
183}
184
185#[derive(Component)]
186pub struct ExtractedClusterConfig {
187 pub(crate) near: f32,
189 pub(crate) far: f32,
190 pub(crate) dimensions: UVec3,
192}
193
194#[derive(Clone, Copy, Default, Debug)]
199struct ClusterableObjectCounts {
200 point_lights: u32,
202 spot_lights: u32,
204 reflection_probes: u32,
206 irradiance_volumes: u32,
208 decals: u32,
210}
211
212enum ExtractedClusterableObjectElement {
213 ClusterHeader(ClusterableObjectCounts),
214 ClusterableObjectEntity(Entity),
215}
216
217#[derive(Component)]
218pub struct ExtractedClusterableObjects {
219 data: Vec<ExtractedClusterableObjectElement>,
220}
221
222#[derive(ShaderType)]
223struct GpuClusterOffsetsAndCountsUniform {
224 data: Box<[UVec4; ViewClusterBindings::MAX_UNIFORM_ITEMS]>,
225}
226
227#[derive(ShaderType, Default)]
228struct GpuClusterableObjectIndexListsStorage {
229 #[size(runtime)]
230 data: Vec<u32>,
231}
232
233#[derive(ShaderType, Default)]
234struct GpuClusterOffsetsAndCountsStorage {
235 #[size(runtime)]
239 data: Vec<[UVec4; 2]>,
240}
241
242enum ViewClusterBuffers {
243 Uniform {
244 clusterable_object_index_lists: UniformBuffer<GpuClusterableObjectIndexListsUniform>,
246 cluster_offsets_and_counts: UniformBuffer<GpuClusterOffsetsAndCountsUniform>,
248 },
249 Storage {
250 clusterable_object_index_lists: StorageBuffer<GpuClusterableObjectIndexListsStorage>,
251 cluster_offsets_and_counts: StorageBuffer<GpuClusterOffsetsAndCountsStorage>,
252 },
253}
254
255#[derive(Component)]
256pub struct ViewClusterBindings {
257 n_indices: usize,
258 n_offsets: usize,
259 buffers: ViewClusterBuffers,
260}
261
262impl Default for ClusterZConfig {
263 fn default() -> Self {
264 Self {
265 first_slice_depth: 5.0,
266 far_z_mode: ClusterFarZMode::MaxClusterableObjectRange,
267 }
268 }
269}
270
271impl Default for ClusterConfig {
272 fn default() -> Self {
273 Self::FixedZ {
276 total: 4096,
277 z_slices: 24,
278 z_config: ClusterZConfig::default(),
279 dynamic_resizing: true,
280 }
281 }
282}
283
284impl ClusterConfig {
285 fn dimensions_for_screen_size(&self, screen_size: UVec2) -> UVec3 {
286 match &self {
287 ClusterConfig::None => UVec3::ZERO,
288 ClusterConfig::Single => UVec3::ONE,
289 ClusterConfig::XYZ { dimensions, .. } => *dimensions,
290 ClusterConfig::FixedZ {
291 total, z_slices, ..
292 } => {
293 let aspect_ratio: f32 = AspectRatio::try_from_pixels(screen_size.x, screen_size.y)
294 .expect("Failed to calculate aspect ratio for Cluster: screen dimensions must be positive, non-zero values")
295 .ratio();
296 let mut z_slices = *z_slices;
297 if *total < z_slices {
298 warn!("ClusterConfig has more z-slices than total clusters!");
299 z_slices = *total;
300 }
301 let per_layer = *total as f32 / z_slices as f32;
302
303 let y = f32::sqrt(per_layer / aspect_ratio);
304
305 let mut x = (y * aspect_ratio) as u32;
306 let mut y = y as u32;
307
308 if x == 0 {
310 x = 1;
311 y = per_layer as u32;
312 }
313 if y == 0 {
314 x = per_layer as u32;
315 y = 1;
316 }
317
318 UVec3::new(x, y, z_slices)
319 }
320 }
321 }
322
323 fn first_slice_depth(&self) -> f32 {
324 match self {
325 ClusterConfig::None | ClusterConfig::Single => 0.0,
326 ClusterConfig::XYZ { z_config, .. } | ClusterConfig::FixedZ { z_config, .. } => {
327 z_config.first_slice_depth
328 }
329 }
330 }
331
332 fn far_z_mode(&self) -> ClusterFarZMode {
333 match self {
334 ClusterConfig::None => ClusterFarZMode::Constant(0.0),
335 ClusterConfig::Single => ClusterFarZMode::MaxClusterableObjectRange,
336 ClusterConfig::XYZ { z_config, .. } | ClusterConfig::FixedZ { z_config, .. } => {
337 z_config.far_z_mode
338 }
339 }
340 }
341
342 fn dynamic_resizing(&self) -> bool {
343 match self {
344 ClusterConfig::None | ClusterConfig::Single => false,
345 ClusterConfig::XYZ {
346 dynamic_resizing, ..
347 }
348 | ClusterConfig::FixedZ {
349 dynamic_resizing, ..
350 } => *dynamic_resizing,
351 }
352 }
353}
354
355impl Clusters {
356 fn update(&mut self, screen_size: UVec2, requested_dimensions: UVec3) {
357 debug_assert!(
358 requested_dimensions.x > 0 && requested_dimensions.y > 0 && requested_dimensions.z > 0
359 );
360
361 let tile_size = (screen_size.as_vec2() / requested_dimensions.xy().as_vec2())
362 .ceil()
363 .as_uvec2()
364 .max(UVec2::ONE);
365 self.tile_size = tile_size;
366 self.dimensions = (screen_size.as_vec2() / tile_size.as_vec2())
367 .ceil()
368 .as_uvec2()
369 .extend(requested_dimensions.z)
370 .max(UVec3::ONE);
371
372 debug_assert!(self.dimensions.x * self.dimensions.y * self.dimensions.z <= 4096);
374 }
375 fn clear(&mut self) {
376 self.tile_size = UVec2::ONE;
377 self.dimensions = UVec3::ZERO;
378 self.near = 0.0;
379 self.far = 0.0;
380 self.clusterable_objects.clear();
381 }
382}
383
384pub fn add_clusters(
385 mut commands: Commands,
386 cameras: Query<(Entity, Option<&ClusterConfig>, &Camera), (Without<Clusters>, With<Camera3d>)>,
387) {
388 for (entity, config, camera) in &cameras {
389 if !camera.is_active {
390 continue;
391 }
392
393 let config = config.copied().unwrap_or_default();
394 commands
397 .entity(entity)
398 .insert((Clusters::default(), config));
399 }
400}
401
402impl VisibleClusterableObjects {
403 #[inline]
404 pub fn iter(&self) -> impl DoubleEndedIterator<Item = &Entity> {
405 self.entities.iter()
406 }
407
408 #[inline]
409 pub fn len(&self) -> usize {
410 self.entities.len()
411 }
412
413 #[inline]
414 pub fn is_empty(&self) -> bool {
415 self.entities.is_empty()
416 }
417}
418
419impl GlobalVisibleClusterableObjects {
420 #[inline]
421 pub fn iter(&self) -> impl Iterator<Item = &Entity> {
422 self.entities.iter()
423 }
424
425 #[inline]
426 pub fn contains(&self, entity: Entity) -> bool {
427 self.entities.contains(&entity)
428 }
429}
430
431impl FromWorld for GlobalClusterableObjectMeta {
432 fn from_world(world: &mut World) -> Self {
433 Self::new(
434 world
435 .resource::<RenderDevice>()
436 .get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT),
437 )
438 }
439}
440
441impl GlobalClusterableObjectMeta {
442 pub fn new(buffer_binding_type: BufferBindingType) -> Self {
443 Self {
444 gpu_clusterable_objects: GpuClusterableObjects::new(buffer_binding_type),
445 entity_to_index: EntityHashMap::default(),
446 }
447 }
448}
449
450impl GpuClusterableObjects {
451 fn new(buffer_binding_type: BufferBindingType) -> Self {
452 match buffer_binding_type {
453 BufferBindingType::Storage { .. } => Self::storage(),
454 BufferBindingType::Uniform => Self::uniform(),
455 }
456 }
457
458 fn uniform() -> Self {
459 Self::Uniform(UniformBuffer::default())
460 }
461
462 fn storage() -> Self {
463 Self::Storage(StorageBuffer::default())
464 }
465
466 pub(crate) fn set(&mut self, mut clusterable_objects: Vec<GpuClusterableObject>) {
467 match self {
468 GpuClusterableObjects::Uniform(buffer) => {
469 let len = clusterable_objects
470 .len()
471 .min(MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS);
472 let src = &clusterable_objects[..len];
473 let dst = &mut buffer.get_mut().data[..len];
474 dst.copy_from_slice(src);
475 }
476 GpuClusterableObjects::Storage(buffer) => {
477 buffer.get_mut().data.clear();
478 buffer.get_mut().data.append(&mut clusterable_objects);
479 }
480 }
481 }
482
483 pub(crate) fn write_buffer(
484 &mut self,
485 render_device: &RenderDevice,
486 render_queue: &RenderQueue,
487 ) {
488 match self {
489 GpuClusterableObjects::Uniform(buffer) => {
490 buffer.write_buffer(render_device, render_queue);
491 }
492 GpuClusterableObjects::Storage(buffer) => {
493 buffer.write_buffer(render_device, render_queue);
494 }
495 }
496 }
497
498 pub fn binding(&self) -> Option<BindingResource> {
499 match self {
500 GpuClusterableObjects::Uniform(buffer) => buffer.binding(),
501 GpuClusterableObjects::Storage(buffer) => buffer.binding(),
502 }
503 }
504
505 pub fn min_size(buffer_binding_type: BufferBindingType) -> NonZero<u64> {
506 match buffer_binding_type {
507 BufferBindingType::Storage { .. } => GpuClusterableObjectsStorage::min_size(),
508 BufferBindingType::Uniform => GpuClusterableObjectsUniform::min_size(),
509 }
510 }
511}
512
513impl Default for GpuClusterableObjectsUniform {
514 fn default() -> Self {
515 Self {
516 data: Box::new(
517 [GpuClusterableObject::default(); MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS],
518 ),
519 }
520 }
521}
522
523pub fn extract_clusters(
525 mut commands: Commands,
526 views: Extract<Query<(RenderEntity, &Clusters, &Camera)>>,
527 mapper: Extract<Query<RenderEntity>>,
528) {
529 for (entity, clusters, camera) in &views {
530 let mut entity_commands = commands
531 .get_entity(entity)
532 .expect("Clusters entity wasn't synced.");
533 if !camera.is_active {
534 entity_commands.remove::<(ExtractedClusterableObjects, ExtractedClusterConfig)>();
535 continue;
536 }
537
538 let num_entities: usize = clusters
539 .clusterable_objects
540 .iter()
541 .map(|l| l.entities.len())
542 .sum();
543 let mut data = Vec::with_capacity(clusters.clusterable_objects.len() + num_entities);
544 for cluster_objects in &clusters.clusterable_objects {
545 data.push(ExtractedClusterableObjectElement::ClusterHeader(
546 cluster_objects.counts,
547 ));
548 for clusterable_entity in &cluster_objects.entities {
549 if let Ok(entity) = mapper.get(*clusterable_entity) {
550 data.push(ExtractedClusterableObjectElement::ClusterableObjectEntity(
551 entity,
552 ));
553 }
554 }
555 }
556
557 entity_commands.insert((
558 ExtractedClusterableObjects { data },
559 ExtractedClusterConfig {
560 near: clusters.near,
561 far: clusters.far,
562 dimensions: clusters.dimensions,
563 },
564 ));
565 }
566}
567
568pub fn prepare_clusters(
569 mut commands: Commands,
570 render_device: Res<RenderDevice>,
571 render_queue: Res<RenderQueue>,
572 mesh_pipeline: Res<MeshPipeline>,
573 global_clusterable_object_meta: Res<GlobalClusterableObjectMeta>,
574 views: Query<(Entity, &ExtractedClusterableObjects)>,
575) {
576 let render_device = render_device.into_inner();
577 let supports_storage_buffers = matches!(
578 mesh_pipeline.clustered_forward_buffer_binding_type,
579 BufferBindingType::Storage { .. }
580 );
581 for (entity, extracted_clusters) in &views {
582 let mut view_clusters_bindings =
583 ViewClusterBindings::new(mesh_pipeline.clustered_forward_buffer_binding_type);
584 view_clusters_bindings.clear();
585
586 for record in &extracted_clusters.data {
587 match record {
588 ExtractedClusterableObjectElement::ClusterHeader(counts) => {
589 let offset = view_clusters_bindings.n_indices();
590 view_clusters_bindings.push_offset_and_counts(offset, counts);
591 }
592 ExtractedClusterableObjectElement::ClusterableObjectEntity(entity) => {
593 if let Some(clusterable_object_index) =
594 global_clusterable_object_meta.entity_to_index.get(entity)
595 {
596 if view_clusters_bindings.n_indices() >= ViewClusterBindings::MAX_INDICES
597 && !supports_storage_buffers
598 {
599 warn!(
600 "Clusterable object index lists are full! The clusterable \
601 objects in the view are present in too many clusters."
602 );
603 break;
604 }
605 view_clusters_bindings.push_index(*clusterable_object_index);
606 }
607 }
608 }
609 }
610
611 view_clusters_bindings.write_buffers(render_device, &render_queue);
612
613 commands.entity(entity).insert(view_clusters_bindings);
614 }
615}
616
617impl ViewClusterBindings {
618 pub const MAX_OFFSETS: usize = 16384 / 4;
619 const MAX_UNIFORM_ITEMS: usize = Self::MAX_OFFSETS / 4;
620 pub const MAX_INDICES: usize = 16384;
621
622 pub fn new(buffer_binding_type: BufferBindingType) -> Self {
623 Self {
624 n_indices: 0,
625 n_offsets: 0,
626 buffers: ViewClusterBuffers::new(buffer_binding_type),
627 }
628 }
629
630 pub fn clear(&mut self) {
631 match &mut self.buffers {
632 ViewClusterBuffers::Uniform {
633 clusterable_object_index_lists,
634 cluster_offsets_and_counts,
635 } => {
636 *clusterable_object_index_lists.get_mut().data =
637 [UVec4::ZERO; Self::MAX_UNIFORM_ITEMS];
638 *cluster_offsets_and_counts.get_mut().data = [UVec4::ZERO; Self::MAX_UNIFORM_ITEMS];
639 }
640 ViewClusterBuffers::Storage {
641 clusterable_object_index_lists,
642 cluster_offsets_and_counts,
643 ..
644 } => {
645 clusterable_object_index_lists.get_mut().data.clear();
646 cluster_offsets_and_counts.get_mut().data.clear();
647 }
648 }
649 }
650
651 fn push_offset_and_counts(&mut self, offset: usize, counts: &ClusterableObjectCounts) {
652 match &mut self.buffers {
653 ViewClusterBuffers::Uniform {
654 cluster_offsets_and_counts,
655 ..
656 } => {
657 let array_index = self.n_offsets >> 2; if array_index >= Self::MAX_UNIFORM_ITEMS {
659 warn!("cluster offset and count out of bounds!");
660 return;
661 }
662 let component = self.n_offsets & ((1 << 2) - 1);
663 let packed =
664 pack_offset_and_counts(offset, counts.point_lights, counts.spot_lights);
665
666 cluster_offsets_and_counts.get_mut().data[array_index][component] = packed;
667 }
668 ViewClusterBuffers::Storage {
669 cluster_offsets_and_counts,
670 ..
671 } => {
672 cluster_offsets_and_counts.get_mut().data.push([
673 uvec4(
674 offset as u32,
675 counts.point_lights,
676 counts.spot_lights,
677 counts.reflection_probes,
678 ),
679 uvec4(counts.irradiance_volumes, counts.decals, 0, 0),
680 ]);
681 }
682 }
683
684 self.n_offsets += 1;
685 }
686
687 pub fn n_indices(&self) -> usize {
688 self.n_indices
689 }
690
691 pub fn push_index(&mut self, index: usize) {
692 match &mut self.buffers {
693 ViewClusterBuffers::Uniform {
694 clusterable_object_index_lists,
695 ..
696 } => {
697 let array_index = self.n_indices >> 4; let component = (self.n_indices >> 2) & ((1 << 2) - 1);
699 let sub_index = self.n_indices & ((1 << 2) - 1);
700 let index = index as u32;
701
702 clusterable_object_index_lists.get_mut().data[array_index][component] |=
703 index << (8 * sub_index);
704 }
705 ViewClusterBuffers::Storage {
706 clusterable_object_index_lists,
707 ..
708 } => {
709 clusterable_object_index_lists
710 .get_mut()
711 .data
712 .push(index as u32);
713 }
714 }
715
716 self.n_indices += 1;
717 }
718
719 pub fn write_buffers(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
720 match &mut self.buffers {
721 ViewClusterBuffers::Uniform {
722 clusterable_object_index_lists,
723 cluster_offsets_and_counts,
724 } => {
725 clusterable_object_index_lists.write_buffer(render_device, render_queue);
726 cluster_offsets_and_counts.write_buffer(render_device, render_queue);
727 }
728 ViewClusterBuffers::Storage {
729 clusterable_object_index_lists,
730 cluster_offsets_and_counts,
731 } => {
732 clusterable_object_index_lists.write_buffer(render_device, render_queue);
733 cluster_offsets_and_counts.write_buffer(render_device, render_queue);
734 }
735 }
736 }
737
738 pub fn clusterable_object_index_lists_binding(&self) -> Option<BindingResource> {
739 match &self.buffers {
740 ViewClusterBuffers::Uniform {
741 clusterable_object_index_lists,
742 ..
743 } => clusterable_object_index_lists.binding(),
744 ViewClusterBuffers::Storage {
745 clusterable_object_index_lists,
746 ..
747 } => clusterable_object_index_lists.binding(),
748 }
749 }
750
751 pub fn offsets_and_counts_binding(&self) -> Option<BindingResource> {
752 match &self.buffers {
753 ViewClusterBuffers::Uniform {
754 cluster_offsets_and_counts,
755 ..
756 } => cluster_offsets_and_counts.binding(),
757 ViewClusterBuffers::Storage {
758 cluster_offsets_and_counts,
759 ..
760 } => cluster_offsets_and_counts.binding(),
761 }
762 }
763
764 pub fn min_size_clusterable_object_index_lists(
765 buffer_binding_type: BufferBindingType,
766 ) -> NonZero<u64> {
767 match buffer_binding_type {
768 BufferBindingType::Storage { .. } => GpuClusterableObjectIndexListsStorage::min_size(),
769 BufferBindingType::Uniform => GpuClusterableObjectIndexListsUniform::min_size(),
770 }
771 }
772
773 pub fn min_size_cluster_offsets_and_counts(
774 buffer_binding_type: BufferBindingType,
775 ) -> NonZero<u64> {
776 match buffer_binding_type {
777 BufferBindingType::Storage { .. } => GpuClusterOffsetsAndCountsStorage::min_size(),
778 BufferBindingType::Uniform => GpuClusterOffsetsAndCountsUniform::min_size(),
779 }
780 }
781}
782
783impl ViewClusterBuffers {
784 fn new(buffer_binding_type: BufferBindingType) -> Self {
785 match buffer_binding_type {
786 BufferBindingType::Storage { .. } => Self::storage(),
787 BufferBindingType::Uniform => Self::uniform(),
788 }
789 }
790
791 fn uniform() -> Self {
792 ViewClusterBuffers::Uniform {
793 clusterable_object_index_lists: UniformBuffer::default(),
794 cluster_offsets_and_counts: UniformBuffer::default(),
795 }
796 }
797
798 fn storage() -> Self {
799 ViewClusterBuffers::Storage {
800 clusterable_object_index_lists: StorageBuffer::default(),
801 cluster_offsets_and_counts: StorageBuffer::default(),
802 }
803 }
804}
805
806fn pack_offset_and_counts(offset: usize, point_count: u32, spot_count: u32) -> u32 {
831 ((offset as u32 & CLUSTER_OFFSET_MASK) << (CLUSTER_COUNT_SIZE * 2))
832 | ((point_count & CLUSTER_COUNT_MASK) << CLUSTER_COUNT_SIZE)
833 | (spot_count & CLUSTER_COUNT_MASK)
834}
835
836#[derive(ShaderType)]
837struct GpuClusterableObjectIndexListsUniform {
838 data: Box<[UVec4; ViewClusterBindings::MAX_UNIFORM_ITEMS]>,
839}
840
841const _: () = assert!(GpuClusterableObjectIndexListsUniform::SHADER_SIZE.get() <= 16384);
844
845impl Default for GpuClusterableObjectIndexListsUniform {
846 fn default() -> Self {
847 Self {
848 data: Box::new([UVec4::ZERO; ViewClusterBindings::MAX_UNIFORM_ITEMS]),
849 }
850 }
851}
852
853impl Default for GpuClusterOffsetsAndCountsUniform {
854 fn default() -> Self {
855 Self {
856 data: Box::new([UVec4::ZERO; ViewClusterBindings::MAX_UNIFORM_ITEMS]),
857 }
858 }
859}