Skip to main content

bevy_pbr/cluster/
mod.rs

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
29// NOTE: this must be kept in sync with the same constants in
30// `mesh_view_types.wesl`.
31pub const MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS: usize = 204;
32// Make sure that the clusterable object buffer doesn't overflow the maximum
33// size of a UBO on WebGL 2.
34const _: () =
35    assert!(size_of::<GpuClusteredLight>() * MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS <= 16384);
36
37// NOTE: Clustered-forward rendering requires 3 storage buffer bindings so check that
38// at least that many are supported using this constant and SupportedBindingType::from_device()
39pub const CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT: u32 = 3;
40
41// this must match CLUSTER_COUNT_SIZE in pbr.wesl
42// and must be large enough to contain MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS
43const 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
48/// The initial capacity of the Z slice list.
49///
50/// The application can override this by setting
51/// [`GlobalClusterGpuSettings::initial_z_slice_list_capacity`].
52pub const GPU_CLUSTERING_INITIAL_Z_SLICE_LIST_CAPACITY: usize = 1024;
53
54/// The initial capacity of the clustered object index list.
55///
56/// The application can override this by setting
57/// [`GlobalClusterGpuSettings::initial_index_list_capacity`].
58pub const GPU_CLUSTERING_INITIAL_INDEX_LIST_CAPACITY: usize = 65536;
59
60/// Creates the default [`GlobalClusterSettings`] resource.
61pub(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    // We need to support compute shaders to use GPU clustering. To deal with
72    // the `WGPU_SETTINGS_PRIO="webgl2"` environment setting, we check the
73    // `RenderDevice` limits in addition to the `RenderAdapter`.
74    //
75    // Some android devices report the capabilities and limits wrong, so we can't rely on them.
76    // See <https://github.com/bevyengine/bevy/issues/23208> for Android issues
77    //
78    // GPU clustering doesn't work properly on iOS simulator. See https://github.com/bevyengine/bevy/issues/23428
79    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/// The GPU-side structure that stores information about a clustered light
107/// (point, spot or rectangle).
108///
109/// This is *not* used for other clustered objects, such as light probes.
110#[derive(Copy, Clone, ShaderType, Default, Pod, Zeroable, Debug)]
111#[repr(C)]
112pub struct GpuClusteredLight {
113    // For point lights: the lower-right 2x2 values of the projection matrix [2][2] [2][3] [3][2] [3][3]
114    // For spot lights: 2 components of the direction (x,z), spot_scale and spot_offset
115    // For rect lights: rotation quaternion.
116    pub(crate) light_custom_data: Vec4,
117    // For rect lights: pack height in color_inverse_square_range.w.
118    pub(crate) color_inverse_square_range: Vec4,
119    // For rect lights: pack width in position_radius.w.
120    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    /// The decal applied to this light.
128    ///
129    /// Note that this is separate from clustered decals. Clustered decals have
130    /// their own structures and don't use [`GpuClusteredLight`].
131    pub(crate) decal_index: u32,
132    /// The radius of the range that the light affects, used for clustering.
133    pub(crate) range: f32,
134}
135
136/// Contains information about clusterable objects in the scene that's global:
137/// i.e. not specific to any view.
138#[derive(Resource)]
139pub struct GlobalClusterableObjectMeta {
140    /// GPU buffers that hold data about the clustered lights.
141    ///
142    /// This is only for lights. Data about other clusterable objects are stored
143    /// in other buffers.
144    pub gpu_clustered_lights: GpuClusteredLights,
145
146    /// Maps a *render-world* entity to the index in the appropriate list.
147    ///
148    /// Only clusterable objects that have render-world entities are in this
149    /// list! In particular, light probes (reflection probes and irradiance
150    /// volumes) are not.
151    pub entity_to_index: EntityHashMap<usize>,
152}
153
154/// GPU buffers that hold data about the clustered lights.
155///
156/// This is only for lights. Data about other clusterable objects are stored in
157/// other buffers.
158///
159/// This has two variants in order to handle platforms in which storage buffers
160/// aren't available.
161pub struct GpuClusteredLights {
162    data: RawBufferVec<GpuClusteredLight>,
163    is_storage_buffer: bool,
164}
165
166#[derive(Component)]
167pub struct ExtractedClusterConfig {
168    /// Special near value for cluster calculations
169    pub(crate) near: f32,
170    pub(crate) far: f32,
171    /// Number of clusters in `X` / `Y` / `Z` in the view frustum
172    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
185/// A single command in the stream that [`extract_clusters_for_cpu_clustering`]
186/// produces.
187enum ExtractedClusterableObjectElement {
188    /// Marks the beginning of a new cluster.
189    ClusterHeader(ClusterableObjectCounts),
190    /// Represents a light.
191    ///
192    /// The given entity is the render-world entity.
193    Light(Entity),
194    /// Represents a reflection probe.
195    ///
196    /// The given entity is the main-world entity of the light probe, as light
197    /// probes don't have render world entities.
198    ReflectionProbe(MainEntity),
199    /// Represents an irradiance volume.
200    ///
201    /// The given entity is the main-world entity of the light probe, as light
202    /// probes don't have render world entities.
203    IrradianceVolume(MainEntity),
204    /// Represents a clustered decal.
205    ///
206    /// The given entity is the render-world entity.
207    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    /// The starting offset, followed by the number of point lights, spot
229    /// lights, rect lights, reflection probes, and irradiance volumes in each cluster,
230    /// in that order. The remaining fields are filled with zeroes.
231    #[shader(size(runtime))]
232    data: Vec<GpuClusterOffsetAndCounts>,
233}
234
235/// The type we use for the offset and counts for each cluster.
236type GpuClusterOffsetAndCounts = [UVec4; 2];
237
238enum ViewClusterBuffers {
239    Uniform {
240        // NOTE: UVec4 is because all arrays in Std140 layout have 16-byte alignment
241        clusterable_object_index_lists: UniformBuffer<GpuClusterableObjectIndexListsUniform>,
242        // NOTE: UVec4 is because all arrays in Std140 layout have 16-byte alignment
243        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
358/// A shortcut for testing the type of a clusterable object.
359type ClusterExtractionMapperQueryFlags = (
360    Has<PointLight>,
361    Has<SpotLight>,
362    Has<RectLight>,
363    Has<EnvironmentMapLight>,
364    Has<IrradianceVolume>,
365    Has<ClusteredDecal>,
366);
367/// A shortcut for testing whether an entity is any type of clusterable object.
368type ClusterExtractionMapperQueryFilter = Or<(
369    With<PointLight>,
370    With<SpotLight>,
371    With<RectLight>,
372    With<EnvironmentMapLight>,
373    With<IrradianceVolume>,
374    With<ClusteredDecal>,
375)>;
376
377/// A run condition that tests whether GPU clustering is enabled.
378///
379/// This is the version for use in extraction systems.
380pub 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
386/// A run condition that tests whether GPU clustering is enabled.
387///
388/// This is the version for use in non-extraction systems.
389pub fn gpu_clustering_is_enabled(global_cluster_settings: Res<GlobalClusterSettings>) -> bool {
390    global_cluster_settings.gpu_clustering.is_some()
391}
392
393/// Extracts the clusters that the CPU produced into the render world.
394pub 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
476/// Creates and populates the GPU buffers that store clusters when CPU
477/// clustering is being used.
478pub 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                        // This should never happen. The appropriate systems
525                        // should have populated
526                        // `global_clusterable_object_meta` by now.
527                        error!(
528                            "Clustered light or decal {:?} had no assigned index!",
529                            entity
530                        );
531                        // Things that should never happen won't happen in debug mode.
532                        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                            // This can happen while the reflection probe is loading.
548                            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; // >> 2 is equivalent to / 4
625                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    // An internal helper method that pushes a raw clustered object index to the
664    // GPU buffer.
665    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; // >> 4 is equivalent to / 16
672                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    /// Pushes the index of a clustered object to the GPU buffer.
690    pub fn push_index(&mut self, index: usize) {
691        self.push_raw_index(index as u32);
692    }
693
694    /// Pushes a placeholder -1 index to the GPU buffer.
695    ///
696    /// This is used when processing reflection probes and irradiance volumes
697    /// that haven't loaded yet.
698    pub fn push_dummy_index(&mut self) {
699        self.push_raw_index(!0);
700    }
701
702    /// Reserves space in the cluster offsets-and-counts list for `clusters`
703    /// clusters.
704    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    /// Reserves space in the index lists for `elements` indices.
726    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
831// Compresses the offset and counts of point and spot lights so that they fit in
832// a UBO.
833//
834// This function is only used if storage buffers are unavailable on this
835// platform: typically, on WebGL 2.
836//
837// NOTE: With uniform buffer max binding size as 16384 bytes
838// that means we can fit 204 clusterable objects in one uniform
839// buffer, which means the count can be at most 204 so it
840// needs 9 bits.
841// The array of indices can also use u8 and that means the
842// offset in to the array of indices needs to be able to address
843// 16384 values. log2(16384) = 14 bits.
844// We use 32 bits to store the offset and counts so
845// we pack the offset into the upper 14 bits of a u32,
846// the point light count into bits 9-17, and the spot light count into bits 0-8.
847//  [ 31     ..     18 | 17      ..      9 | 8       ..     0 ]
848//  [      offset      | point light count | spot light count ]
849//
850// NOTE: This assumes CPU and GPU endianness are the same which is true
851// for all common and tested x86/ARM CPUs and AMD/NVIDIA/Intel/Apple/etc GPUs
852//
853// NOTE: On platforms that use this function, we don't cluster light probes, so
854// the number of light probes is irrelevant.
855fn 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
866// NOTE: Assert at compile time that GpuClusterableObjectIndexListsUniform
867// fits within the maximum uniform buffer binding size
868const _: () = 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}