Skip to main content

bevy_pbr/cluster/
gpu.rs

1//! Clustering of lights and other clusterable objects on GPU.
2//!
3//! GPU light clustering uses the hardware rasterizer for compute purposes as a
4//! way to automatically distribute workloads within 2D axis-aligned bounding
5//! boxes without actually rendering any pixels. The algorithm is as follows,
6//! with each step corresponding to a raster or compute command
7//!
8//! 1. *Z slicing*: We have a 3D cluster froxel grid of size W×H×D and seek to
9//!    rasterize D axis-aligned quads, each of size W×H, representing the range of
10//!    each clusterable object. In this compute phase, we generate D indirect
11//!    instances for each clusterable object for the subsequent indirect draws.
12//!
13//! 2. *Count rasterization*: We use instanced indirect drawing to rasterize
14//!    each quad generated in step 1 to a viewport of size W×H, with color
15//!    writes disabled. Each rasterized fragment represents a cluster-object
16//!    pair. In the fragment shader, we check to see if the object
17//!    intersects the cluster, and, if it does, we atomically bump a counter
18//!    corresponding to the number of objects of the given type intersecting
19//!    the cluster in question. We don't record the ID of the object in this
20//!    phase; we simply count the number of objects.
21//!
22//! 3. *Local allocation*: Now that we know the number of objects of each
23//!    type in each cluster, we can proceed to allocate space in the
24//!    clustered object buffer for each clustered object list. To do this,
25//!    we need to perform a [*prefix sum*] operation so that each list is
26//!    tightly packed with the others. For example, if adjacent clusters
27//!    have 2, 5, and 3 objects, they'll be allocated at offsets 0, 2, and 7
28//!    respectively. This *local* step uses a [Hillis-Steele scan] in shared
29//!    memory to compute the prefix sum of each chunk of 256 clusters. We
30//!    can't go beyond 256 clusters in this local step because 256 is the
31//!    maximum workgroup size in `wgpu`.
32//!
33//! 4. *Global allocation*: To deal with the fact that we can't calculate
34//!    prefix sums beyond 256 clusters in step 3, we employ this second step
35//!    that does a sequential loop over every 256-cluster chunk, propagating
36//!    the prefix sum. At the end of this step, every list of clustered
37//!    objects is allocated.
38//!
39//! 5. *Populate rasterization*: Finally, we issue an instanced indirect
40//!    draw command using the same parameters as step (2). We test each
41//!    cluster-object pair for intersection, and, if the test passes, we
42//!    record the ID of each clustered object into the correct space in the
43//!    list, using an scratch pad buffer of atomics to store the position of
44//!    the next object in each list.
45//!
46//! [*prefix sum*]: https://en.wikipedia.org/wiki/Prefix_sum
47//!
48//! [Hillis-Steele scan]: https://en.wikipedia.org/wiki/Prefix_sum#Algorithm_1:_Shorter_span,_more_parallel
49
50use alloc::sync::Arc;
51use std::sync::Mutex;
52
53use bevy_app::{App, Plugin};
54use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer, Handle};
55use bevy_camera::Camera;
56use bevy_color::Color;
57use bevy_core_pipeline::{prepass::node::early_prepass, Core3d, Core3dSystems};
58use bevy_derive::{Deref, DerefMut};
59use bevy_ecs::{
60    component::Component,
61    entity::Entity,
62    query::With,
63    resource::Resource,
64    schedule::IntoScheduleConfigs as _,
65    system::{Commands, Query, Res, ResMut},
66    world::{FromWorld, World},
67};
68use bevy_light::{
69    cluster::{Clusters, GlobalClusterGpuSettings, GlobalClusterSettings},
70    EnvironmentMapLight, IrradianceVolume,
71};
72use bevy_material::descriptor::{
73    BindGroupLayoutDescriptor, CachedComputePipelineId, CachedRenderPipelineId,
74    ComputePipelineDescriptor, FragmentState, RenderPipelineDescriptor, VertexState,
75};
76use bevy_math::{vec2, Vec2};
77use bevy_mesh::{VertexBufferLayout, VertexFormat};
78use bevy_render::{
79    diagnostic::RecordDiagnostics as _,
80    extract_resource::{ExtractResource, ExtractResourcePlugin},
81    render_resource::{
82        binding_types,
83        encase::internal::{CreateFrom as _, Reader},
84        BindGroup, BindGroupEntry, BindGroupLayoutEntries, Buffer, BufferBindingType,
85        BufferDescriptor, BufferInitDescriptor, BufferUsages, ColorTargetState, ColorWrites,
86        CommandEncoder, ComputePassDescriptor, ComputePipeline, Extent3d, IndexFormat, LoadOp,
87        MapMode, Operations, PipelineCache, RenderPassColorAttachment, RenderPassDescriptor,
88        RenderPipeline, ShaderStages, ShaderType, SpecializedComputePipeline,
89        SpecializedComputePipelines, SpecializedRenderPipeline, SpecializedRenderPipelines,
90        StorageBuffer, StoreOp, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
91        UninitBufferVec, VertexAttribute, VertexStepMode,
92    },
93    renderer::{RenderContext, RenderDevice, RenderQueue, ViewQuery},
94    sync_world::{MainEntity, MainEntityHashMap, MainEntityHashSet, RenderEntity},
95    texture::{CachedTexture, TextureCache},
96    view::{ExtractedView, ViewUniform, ViewUniformOffset, ViewUniforms},
97    GpuResourceAppExt, MainWorld, Render, RenderApp, RenderSystems,
98};
99use bevy_shader::{Shader, ShaderDefVal};
100use bevy_utils::default;
101use bytemuck::{Pod, Zeroable};
102use tracing::{error, trace, warn};
103
104use crate::{
105    cluster::{
106        GpuClusterOffsetAndCounts, GpuClusterOffsetsAndCountsStorage,
107        GpuClusterableObjectIndexListsStorage, ViewClusterBuffers,
108    },
109    decal::clustered::{DecalsBuffer, RenderClusteredDecal, RenderClusteredDecals},
110    gpu_clustering_is_enabled, ExtractedClusterConfig, GlobalClusterableObjectMeta,
111    GpuClusteredLight, GpuLights, LightMeta, LightProbesBuffer, LightProbesUniform,
112    RenderViewLightProbes, ViewClusterBindings, ViewLightProbesUniformOffset,
113    ViewLightsUniformOffset,
114};
115
116/// The workgroup size of the `cluster_allocate.wesl` shader.
117const ALLOCATION_WORKGROUP_SIZE: u32 = 256;
118/// The workgroup size of the `cluster_z_slice.wesl` shader.
119const Z_SLICING_WORKGROUP_SIZE: u32 = 64;
120
121/// A plugin that enables GPU clustering of lights and other objects.
122pub struct GpuClusteringPlugin;
123
124impl Plugin for GpuClusteringPlugin {
125    fn build(&self, app: &mut App) {
126        embedded_asset!(app, "cluster_z_slice.wesl");
127        embedded_asset!(app, "cluster_raster.wesl");
128        embedded_asset!(app, "cluster_allocate.wesl");
129
130        app.add_plugins(ExtractResourcePlugin::<
131            GlobalClusterSettings,
132            GpuClusteringPlugin,
133        >::default());
134    }
135
136    fn finish(&self, app: &mut App) {
137        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
138            return;
139        };
140
141        // Bail out if we have no storage buffers. This is the case when we have
142        // `WGPU_SETTINGS_PRIO="webgl2"`.
143        let render_device = render_app.world().resource::<RenderDevice>();
144        if render_device.limits().max_storage_buffers_per_shader_stage == 0 {
145            return;
146        }
147
148        render_app
149            .init_gpu_resource::<SpecializedRenderPipelines<ClusteringRasterPipeline>>()
150            .init_gpu_resource::<SpecializedComputePipelines<ClusteringZSlicingPipeline>>()
151            .init_gpu_resource::<SpecializedComputePipelines<ClusteringAllocationPipeline>>()
152            .init_gpu_resource::<RenderViewClusteringReadbackData>()
153            .init_gpu_resource::<GpuClusteringMeshBuffers>()
154            .init_gpu_resource::<ClusteringRasterPipeline>()
155            .init_gpu_resource::<ClusteringZSlicingPipeline>()
156            .init_gpu_resource::<ClusteringAllocationPipeline>()
157            .add_systems(
158                Render,
159                (prepare_clustering_pipelines, prepare_cluster_dummy_textures)
160                    .in_set(RenderSystems::Prepare)
161                    .run_if(gpu_clustering_is_enabled),
162            )
163            .add_systems(
164                Render,
165                (
166                    prepare_clusters_for_gpu_clustering,
167                    upload_view_gpu_clustering_buffers,
168                )
169                    .chain()
170                    .in_set(RenderSystems::PrepareResources)
171                    .run_if(gpu_clustering_is_enabled),
172            )
173            .add_systems(
174                Render,
175                prepare_clustering_bind_groups
176                    .in_set(RenderSystems::PrepareBindGroups)
177                    .run_if(gpu_clustering_is_enabled),
178            )
179            .add_systems(
180                Core3d,
181                cluster_on_gpu
182                    .before(early_prepass)
183                    .in_set(Core3dSystems::Prepass)
184                    .run_if(gpu_clustering_is_enabled),
185            );
186    }
187}
188
189/// The texture that we bind when performing the raster passes.
190///
191/// We don't actually write to this texture; it exists only so that we can set a
192/// viewport.
193#[derive(Component, Deref, DerefMut)]
194pub struct ViewClusteringDummyTexture(CachedTexture);
195
196/// The bind groups for each pass of GPU clustering.
197#[derive(Component)]
198pub struct ViewClusteringBindGroups {
199    /// The bind group for the Z-slicing compute pass.
200    clustering_bind_group_z_slicing_pass: BindGroup,
201    /// The bind group for the count rasterization pass.
202    clustering_bind_group_count_pass: BindGroup,
203    /// The bind group for both local and global allocation passes.
204    clustering_bind_group_allocate_pass: BindGroup,
205    /// The bind group for the populate rasterization pass.
206    clustering_bind_group_populate_pass: BindGroup,
207}
208
209/// The GPU representation of a single Z-slice of a clusterable object.
210///
211/// A Z-slice is an axis-aligned bounding box representing the potential
212/// bounding box of a clusterable object in a single Z slice of the froxel grid.
213#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, ShaderType, Pod, Zeroable)]
214#[repr(C)]
215pub struct ClusterableObjectZSlice {
216    /// The index of the object to be clustered.
217    pub object_index: u32,
218    /// The type of the object to be clustered.
219    ///
220    /// This is one of the `CLUSTERABLE_OBJECT_TYPE_` constants in
221    /// `cluster.wesl`.
222    pub object_type: u32,
223    /// The Z coordinate of the froxels that this slice covers.
224    pub z_slice: u32,
225}
226
227/// Metadata stored on GPU that's global to all clusters for a view.
228#[derive(Clone, Copy, Default, ShaderType, Pod, Zeroable)]
229#[repr(C)]
230pub struct ClusterMetadata {
231    /// The indirect draw parameters for the raster passes.
232    indirect_draw_params: ClusterRasterIndirectDrawParams,
233
234    /// The total number of clustered lights, set by the CPU.
235    clustered_light_count: u32,
236    /// The total number of reflection probes, set by the CPU.
237    reflection_probe_count: u32,
238    /// The total number of irradiance volumes, set by the CPU.
239    irradiance_volume_count: u32,
240    /// The total number of clustered decals, set by the CPU.
241    decal_count: u32,
242
243    /// The current maximum size of the Z-slice list.
244    z_slice_list_capacity: u32,
245
246    /// The current size of the clustered object index list.
247    ///
248    /// This is set to 0 by the CPU, and the GPU updates it with the computed
249    /// value.
250    index_list_capacity: u32,
251
252    /// The farthest depth that any clustered object AABB has extended to this
253    /// frame.
254    ///
255    /// This is set to 0 by the CPU, and the GPU updates it with the computed
256    /// value.
257    ///
258    /// This is a float encoded by `f32_bits_to_sortable_u32`. Decode with `sortable_u32_to_f32_bits`.
259    farthest_z: u32,
260}
261
262/// Indirect draw parameters for the raster dispatch phase, built partially by
263/// the CPU and partially by the GPU.
264///
265/// These must conform to the format that `wgpu` demands, so this structure
266/// layout must not be modified.
267#[derive(Clone, Copy, Default, ShaderType, Pod, Zeroable)]
268#[repr(C)]
269pub struct ClusterRasterIndirectDrawParams {
270    index_count: u32,
271
272    /// Represents the total number of Z slices.
273    ///
274    /// This field is the one that the GPU modifies.
275    instance_count: u32,
276
277    first_index: u32,
278    base_vertex: u32,
279    first_instance: u32,
280}
281
282/// A component, stored on [`ExtractedView`], that stores buffers needed to
283/// perform GPU clustering for that view.
284#[derive(Component)]
285pub struct ViewGpuClusteringBuffers {
286    /// The buffer that holds the Z slices for each clusterable object.
287    ///
288    /// The `cluster_z_slice.wesl` shader fills this buffer out, and the raster
289    /// passes read it.
290    pub z_slices_buffer: UninitBufferVec<ClusterableObjectZSlice>,
291    /// The buffer that holds the scratchpad offsets and counts for each
292    /// clusterable object.
293    ///
294    /// The populate pass uses this to coordinate where to write indices for
295    /// each clusterable object. The allocation pass zeroes it out.
296    scratchpad_offsets_and_counts_buffer: UninitBufferVec<GpuClusterOffsetAndCounts>,
297    /// The buffer that stores the [`ClusterMetadata`].
298    ///
299    /// Since this buffer is small, [`StorageBuffer`] is fine to use.
300    cluster_metadata_buffer: StorageBuffer<ClusterMetadata>,
301}
302
303impl ViewGpuClusteringBuffers {
304    /// Creates a new, empty set of [`ViewGpuClusteringBuffers`] for a single
305    /// view.
306    pub(crate) fn new() -> ViewGpuClusteringBuffers {
307        let mut cluster_metadata_buffer = StorageBuffer::from(ClusterMetadata::default());
308        cluster_metadata_buffer.add_usages(BufferUsages::COPY_SRC | BufferUsages::INDIRECT);
309        cluster_metadata_buffer.set_label(Some("clustering Z slicing metadata buffer"));
310
311        ViewGpuClusteringBuffers {
312            cluster_metadata_buffer,
313            z_slices_buffer: UninitBufferVec::new(BufferUsages::STORAGE | BufferUsages::COPY_DST),
314            scratchpad_offsets_and_counts_buffer: UninitBufferVec::new(
315                BufferUsages::STORAGE | BufferUsages::COPY_DST,
316            ),
317        }
318    }
319}
320
321/// Stores data associated with reading back clustering statistics from GPU to
322/// CPU for all views.
323#[derive(Resource, Default)]
324pub(crate) struct RenderViewClusteringReadbackData {
325    /// The data for each view.
326    ///
327    /// This is locked behind a mutex so that the buffer readback callbacks,
328    /// which execute concurrently, can access it alongside the render world.
329    views: MainEntityHashMap<Arc<Mutex<ViewClusteringReadbackData>>>,
330}
331
332/// Data associated with reading back clustering statistics for a single view.
333struct ViewClusteringReadbackData {
334    /// The current capacity of the Z slice list.
335    ///
336    /// This starts out at the default size as specified by the allocation and
337    /// can grow based on the results of GPU readback.
338    z_slice_list_capacity: usize,
339    /// The current capacity of the clustered object index list.
340    ///
341    /// This starts out at the default size as specified by the allocation and
342    /// can grow based on the results of GPU readback.
343    max_index_list_capacity: usize,
344    /// Buffers corresponding to GPU readback operations in progress.
345    metadata_staging_pending_buffers: Vec<Buffer>,
346    /// Buffers corresponding to GPU readback operations that are finished.
347    ///
348    /// These buffers are ready for reuse.
349    metadata_staging_free_buffers: Vec<Buffer>,
350    /// Statistics about GPU clustering that the GPU calculated last frame.
351    last_frame_statistics: Option<ViewClusteringLastFrameStatistics>,
352}
353
354/// Statistics about GPU clustering that the GPU calculated last frame.
355struct ViewClusteringLastFrameStatistics {
356    /// The actual used size of the index list.
357    ///
358    /// If this is greater than the capacity of the index list, the CPU will
359    /// resize the index list buffer.
360    index_list_size: u32,
361    /// The maximum depth of all axis-aligned bounding boxes corresponding to
362    /// clusterable objects in view.
363    farthest_z: f32,
364}
365
366impl ViewClusteringReadbackData {
367    /// Creates a new [`ViewClusteringReadbackData`] for a view.
368    ///
369    /// The [`Self::z_slice_list_capacity`] and
370    /// [`Self::max_index_list_capacity`] are calculated based on the initial
371    /// capacities that the application set in the [`GlobalClusterGpuSettings`].
372    fn new(settings: &GlobalClusterGpuSettings) -> ViewClusteringReadbackData {
373        ViewClusteringReadbackData {
374            z_slice_list_capacity: settings.initial_z_slice_list_capacity,
375            max_index_list_capacity: settings.initial_index_list_capacity,
376            metadata_staging_pending_buffers: vec![],
377            metadata_staging_free_buffers: vec![],
378            last_frame_statistics: None,
379        }
380    }
381
382    fn get_or_create_staging_buffer(&mut self, render_device: &RenderDevice) -> Buffer {
383        let staging_buffer = self.metadata_staging_free_buffers.pop().unwrap_or_else(|| {
384            render_device.create_buffer(&BufferDescriptor {
385                label: Some("clustering metadata staging buffer"),
386                size: ClusterMetadata::min_size().into(),
387                usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ,
388                mapped_at_creation: false,
389            })
390        });
391        self.metadata_staging_pending_buffers
392            .push(staging_buffer.clone());
393        staging_buffer
394    }
395
396    /// Updates this [`ViewClusteringReadbackData`] with new information from
397    /// the given metadata read back from the GPU.
398    fn update_from_metadata(&mut self, gpu_clustering_metadata: &ClusterMetadata) {
399        // Schedule a resize of the Z slice list if the GPU overflowed.
400        if self.z_slice_list_capacity
401            < gpu_clustering_metadata.indirect_draw_params.instance_count as usize
402        {
403            let new_capacity = gpu_clustering_metadata
404                .indirect_draw_params
405                .instance_count
406                .next_power_of_two();
407            warn!(
408                "Resizing the view clustering Z slice list from a capacity of {0} elements to \
409                a capacity of {1} elements. The scene lighting may have been corrupted for a \
410                few frames. To avoid this, set the `gpu_clustering.z_slice_list_capacity` field \
411                on the `GlobalClusterSettings` resource to at least {1}.",
412                self.z_slice_list_capacity, new_capacity
413            );
414            self.z_slice_list_capacity = new_capacity as usize;
415        }
416
417        // Schedule a resize of the index slice list if the GPU overflowed.
418        if self.max_index_list_capacity < gpu_clustering_metadata.index_list_capacity as usize {
419            let new_capacity = gpu_clustering_metadata
420                .index_list_capacity
421                .next_power_of_two();
422            warn!(
423                "Resizing the view clustering index list from a capacity of {0} elements to a \
424                capacity of {1} elements. The scene lighting may have been corrupted for a \
425                few frames. To avoid this, set the `gpu_clustering.index_list_capacity` field on \
426                the `GlobalClusterSettings` resource to at least {1}.",
427                self.max_index_list_capacity, new_capacity
428            );
429            self.max_index_list_capacity = new_capacity as usize;
430        }
431
432        // Record the statistics we just received.
433        self.last_frame_statistics = Some(ViewClusteringLastFrameStatistics {
434            index_list_size: gpu_clustering_metadata.index_list_capacity,
435            farthest_z: f32::from_bits(sortable_u32_to_f32_bits(
436                gpu_clustering_metadata.farthest_z,
437            )),
438        });
439    }
440}
441
442/// Decodes a `u32` produced by `f32_bits_to_sortable_u32` (in
443/// `cluster_z_slice.wesl`) back into `f32` bits.
444///
445/// The encode flips the sign bit for positive floats and all bits for
446/// negative floats, so the decode must inspect the *encoded* sign bit
447/// (which is inverted relative to the original) and apply the
448/// complementary mask.
449fn sortable_u32_to_f32_bits(bits: u32) -> u32 {
450    let mask = (!((bits as i32) >> 31)) as u32 | 0x80000000;
451    bits ^ mask
452}
453
454/// Global data relating to the `cluster_raster.wesl` shader.
455#[derive(Resource)]
456pub struct ClusteringRasterPipeline {
457    /// The bind group layout for group 0 for the count (first) pass.
458    pub bind_group_layout_count_pass: BindGroupLayoutDescriptor,
459    /// The bind group layout for group 0 for the populate (second) pass.
460    pub bind_group_layout_populate_pass: BindGroupLayoutDescriptor,
461    /// A handle to the shader itself.
462    pub shader: Handle<Shader>,
463}
464
465/// Global data relating to the `cluster_z_slice.wesl` shader.
466#[derive(Resource)]
467pub struct ClusteringZSlicingPipeline {
468    /// The bind group layout for group 0.
469    pub bind_group_layout: BindGroupLayoutDescriptor,
470    /// A handle to the shader itself.
471    pub shader: Handle<Shader>,
472}
473
474/// Global data relating to the `cluster_allocate.wesl` shader.
475#[derive(Resource)]
476pub struct ClusteringAllocationPipeline {
477    /// The bind group layout of group 0 for both shader invocations.
478    pub bind_group_layout: BindGroupLayoutDescriptor,
479    /// A handle to the `cluster_allocate.wesl` shader itself.
480    pub shader: Handle<Shader>,
481}
482
483/// The pipeline key that identifies specializations of the
484/// `cluster_raster.wesl` shader.
485#[derive(Clone, Copy, PartialEq, Eq, Hash)]
486pub struct ClusteringRasterPipelineKey {
487    /// True if this is the populate (second) pass; false if it's the count
488    /// (first) one.
489    populate_pass: bool,
490}
491
492/// The pipeline key that identifies specializations of the
493/// `cluster_allocate.wesl` shader.
494#[derive(Clone, Copy, PartialEq, Eq, Hash)]
495pub struct ClusteringAllocationPipelineKey {
496    /// True if this is the global (second) pass; false if it's the local
497    /// (first) one.
498    global_pass: bool,
499}
500
501impl FromWorld for ClusteringRasterPipeline {
502    fn from_world(world: &mut World) -> Self {
503        let asset_server = world.resource::<AssetServer>();
504
505        let mut bind_group_layout_entries_count_pass = vec![
506            // @group(0) @binding(0) var<storage> z_slices:
507            // array<ClusterableObjectZSlice>;
508            binding_types::storage_buffer_read_only::<ClusterableObjectZSlice>(false)
509                .build(0, ShaderStages::VERTEX_FRAGMENT),
510            // @group(0) @binding(1) var<storage, read_write> index_lists:
511            // ClusterableObjectIndexLists;
512            binding_types::storage_buffer::<GpuClusterableObjectIndexListsStorage>(false)
513                .build(1, ShaderStages::FRAGMENT),
514            // @group(0) @binding(2) var<storage> clustered_lights:
515            // ClusteredLights;
516            binding_types::storage_buffer_read_only::<GpuClusteredLight>(false)
517                .build(2, ShaderStages::VERTEX_FRAGMENT),
518            // @group(0) @binding(3) var<uniform> light_probes: LightProbes;
519            binding_types::uniform_buffer::<LightProbesUniform>(true)
520                .build(3, ShaderStages::VERTEX_FRAGMENT),
521            // @group(0) @binding(4) var<storage> clustered_decals:
522            // ClusteredDecals;
523            binding_types::storage_buffer_read_only::<RenderClusteredDecal>(false)
524                .build(4, ShaderStages::VERTEX_FRAGMENT),
525            // @group(0) @binding(5) var<uniform> lights: Lights;
526            binding_types::uniform_buffer::<GpuLights>(true)
527                .build(5, ShaderStages::VERTEX_FRAGMENT),
528            // @group(0) @binding(6) var<uniform> view: View;
529            binding_types::uniform_buffer::<ViewUniform>(true)
530                .build(6, ShaderStages::VERTEX_FRAGMENT),
531        ];
532
533        let mut bind_group_layout_entries_populate_pass =
534            bind_group_layout_entries_count_pass.clone();
535
536        // @group(0) @binding(7) var<storage, read_write> offsets_and_counts:
537        // ClusterOffsetsAndCountsAtomic;
538        bind_group_layout_entries_count_pass.push(
539            binding_types::storage_buffer::<GpuClusterOffsetsAndCountsStorage>(false)
540                .build(7, ShaderStages::FRAGMENT),
541        );
542
543        // @group(0) @binding(7) var<storage> offsets_and_counts:
544        // ClusterOffsetsAndCounts;
545        bind_group_layout_entries_populate_pass.push(
546            binding_types::storage_buffer_read_only::<GpuClusterOffsetsAndCountsStorage>(false)
547                .build(7, ShaderStages::FRAGMENT),
548        );
549        // @group(0) @binding(8) var<storage, read_write>
550        // scratchpad_offsets_and_counts: ClusterOffsetsAndCountsAtomic;
551        bind_group_layout_entries_populate_pass.push(
552            binding_types::storage_buffer::<GpuClusterOffsetsAndCountsStorage>(false)
553                .build(8, ShaderStages::FRAGMENT),
554        );
555
556        let bind_group_layout_count_pass = BindGroupLayoutDescriptor::new(
557            "clustering count pass bind group layout",
558            &bind_group_layout_entries_count_pass,
559        );
560        let bind_group_layout_populate_pass = BindGroupLayoutDescriptor::new(
561            "clustering populate pass bind group layout",
562            &bind_group_layout_entries_populate_pass,
563        );
564
565        let shader = load_embedded_asset!(asset_server, "cluster_raster.wesl");
566
567        ClusteringRasterPipeline {
568            bind_group_layout_count_pass,
569            bind_group_layout_populate_pass,
570            shader,
571        }
572    }
573}
574
575impl SpecializedRenderPipeline for ClusteringRasterPipeline {
576    type Key = ClusteringRasterPipelineKey;
577
578    fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
579        let mut fragment_shader_defs = vec![];
580        if key.populate_pass {
581            fragment_shader_defs.push(ShaderDefVal::from("POPULATE_PASS"));
582        } else {
583            fragment_shader_defs.push(ShaderDefVal::from("COUNT_PASS"));
584        }
585
586        let mut vertex_shader_defs = fragment_shader_defs.clone();
587        vertex_shader_defs.push(ShaderDefVal::from("VERTEX_SHADER"));
588
589        RenderPipelineDescriptor {
590            label: if key.populate_pass {
591                Some("clustering populate pipeline".into())
592            } else {
593                Some("clustering count pipeline".into())
594            },
595            layout: vec![if key.populate_pass {
596                self.bind_group_layout_populate_pass.clone()
597            } else {
598                self.bind_group_layout_count_pass.clone()
599            }],
600            immediate_size: 0,
601            vertex: VertexState {
602                shader: self.shader.clone(),
603                shader_defs: vertex_shader_defs,
604                entry_point: Some("vertex_main".into()),
605                buffers: vec![VertexBufferLayout {
606                    array_stride: size_of::<Vec2>() as u64,
607                    step_mode: VertexStepMode::Vertex,
608                    attributes: vec![VertexAttribute {
609                        format: VertexFormat::Float32x2,
610                        offset: 0,
611                        shader_location: 0,
612                    }],
613                }],
614                constants: vec![],
615            },
616            fragment: Some(FragmentState {
617                shader: self.shader.clone(),
618                shader_defs: fragment_shader_defs,
619                entry_point: Some("fragment_main".into()),
620                targets: vec![Some(ColorTargetState {
621                    format: TextureFormat::R8Unorm,
622                    blend: None,
623                    // Disable writing.
624                    write_mask: ColorWrites::empty(),
625                })],
626                constants: vec![],
627            }),
628            ..default()
629        }
630    }
631}
632
633impl FromWorld for ClusteringZSlicingPipeline {
634    fn from_world(world: &mut World) -> Self {
635        let asset_server = world.resource::<AssetServer>();
636
637        let bind_group_layout = BindGroupLayoutDescriptor::new(
638            "clustering Z slicing pass bind group layout",
639            &BindGroupLayoutEntries::sequential(
640                ShaderStages::COMPUTE,
641                (
642                    // @group(0) @binding(0) var<storage, read_write>
643                    // cluster_metadata: ClusterMetadata;
644                    binding_types::storage_buffer::<ClusterMetadata>(false),
645                    // @group(0) @binding(1) var<storage, read_write> z_slices:
646                    // array<ClusterableObjectZSlice>;
647                    binding_types::storage_buffer::<ClusterableObjectZSlice>(false),
648                    // @group(0) @binding(2) var<storage> clustered_lights:
649                    // ClusteredLights;
650                    binding_types::storage_buffer_read_only::<GpuClusteredLight>(false),
651                    // @group(0) @binding(3) var<uniform> light_probes:
652                    // LightProbes;
653                    binding_types::uniform_buffer::<LightProbesUniform>(true),
654                    // @group(0) @binding(4) var<storage> clustered_decals:
655                    // ClusteredDecals;
656                    binding_types::storage_buffer_read_only::<RenderClusteredDecal>(false),
657                    // @group(0) @binding(5) var<uniform> lights: Lights;
658                    binding_types::uniform_buffer::<GpuLights>(true),
659                    // @group(0) @binding(6) var<uniform> view: View;
660                    binding_types::uniform_buffer::<ViewUniform>(true),
661                ),
662            ),
663        );
664
665        let shader = load_embedded_asset!(asset_server, "cluster_z_slice.wesl");
666
667        ClusteringZSlicingPipeline {
668            bind_group_layout,
669            shader,
670        }
671    }
672}
673
674impl SpecializedComputePipeline for ClusteringZSlicingPipeline {
675    type Key = ();
676
677    fn specialize(&self, _: Self::Key) -> ComputePipelineDescriptor {
678        ComputePipelineDescriptor {
679            label: Some("clustering Z slicing pipeline".into()),
680            layout: vec![self.bind_group_layout.clone()],
681            shader: self.shader.clone(),
682            shader_defs: vec![],
683            entry_point: Some("z_slice_main".into()),
684            zero_initialize_workgroup_memory: true,
685            ..default()
686        }
687    }
688}
689
690impl FromWorld for ClusteringAllocationPipeline {
691    fn from_world(world: &mut World) -> Self {
692        let asset_server = world.resource::<AssetServer>();
693
694        let bind_group_layout = BindGroupLayoutDescriptor::new(
695            "clustering allocation pass bind group layout",
696            &BindGroupLayoutEntries::sequential(
697                ShaderStages::COMPUTE,
698                (
699                    // @group(0) @binding(0) var<storage, read_write>
700                    // offsets_and_counts: ClusterOffsetsAndCounts;
701                    binding_types::storage_buffer::<GpuClusterOffsetsAndCountsStorage>(false),
702                    // @group(0) @binding(1) var<uniform> lights: Lights;
703                    binding_types::uniform_buffer::<GpuLights>(true),
704                    // @group(0) @binding(2) var<storage, read_write>
705                    // clustering_metadata: ClusterMetadata;
706                    binding_types::storage_buffer::<ClusterMetadata>(false),
707                    // @group(0) @binding(3) var<storage, read_write>
708                    // scratchpad_offsets_and_counts: ClusterOffsetsAndCounts;
709                    binding_types::storage_buffer::<GpuClusterOffsetsAndCountsStorage>(false),
710                ),
711            ),
712        );
713
714        let shader = load_embedded_asset!(asset_server, "cluster_allocate.wesl");
715
716        ClusteringAllocationPipeline {
717            bind_group_layout,
718            shader,
719        }
720    }
721}
722
723impl SpecializedComputePipeline for ClusteringAllocationPipeline {
724    type Key = ClusteringAllocationPipelineKey;
725
726    fn specialize(&self, key: Self::Key) -> ComputePipelineDescriptor {
727        ComputePipelineDescriptor {
728            label: if key.global_pass {
729                Some("clustering allocation global pass pipeline".into())
730            } else {
731                Some("clustering allocation local pass pipeline".into())
732            },
733            layout: vec![self.bind_group_layout.clone()],
734            shader: self.shader.clone(),
735            shader_defs: vec![],
736            entry_point: if key.global_pass {
737                Some("allocate_global_main".into())
738            } else {
739                Some("allocate_local_main".into())
740            },
741            zero_initialize_workgroup_memory: true,
742            ..default()
743        }
744    }
745}
746
747/// The vertices of the quad that we rasterize to represent a clusterable object
748/// Z slice.
749static GPU_CLUSTERING_VERTICES: [Vec2; 4] = [
750    vec2(0.0, 0.0),
751    vec2(1.0, 0.0),
752    vec2(0.0, 1.0),
753    vec2(1.0, 1.0),
754];
755
756/// The indices of the quad that we rasterize to represent a clusterable object
757/// Z slice.
758static GPU_CLUSTERING_INDICES: [u32; 6] = [0, 1, 2, 1, 3, 2];
759
760/// The buffers that store the vertices and indices for the quad that we
761/// rasterize to represent each clusterable object Z slice.
762#[derive(Resource)]
763struct GpuClusteringMeshBuffers {
764    /// The vertex buffer containing the 4 vertices of a quad.
765    vertex_buffer: Buffer,
766    /// The index buffer containing the 6 indices of a quad.
767    index_buffer: Buffer,
768}
769
770impl FromWorld for GpuClusteringMeshBuffers {
771    fn from_world(world: &mut World) -> Self {
772        let render_device = world.resource::<RenderDevice>();
773        GpuClusteringMeshBuffers {
774            vertex_buffer: render_device.create_buffer_with_data(&BufferInitDescriptor {
775                label: Some("GPU clustering vertex buffer"),
776                contents: bytemuck::bytes_of(&GPU_CLUSTERING_VERTICES),
777                usage: BufferUsages::COPY_DST | BufferUsages::VERTEX,
778            }),
779            index_buffer: render_device.create_buffer_with_data(&BufferInitDescriptor {
780                label: Some("GPU clustering index buffer"),
781                contents: bytemuck::bytes_of(&GPU_CLUSTERING_INDICES),
782                usage: BufferUsages::COPY_DST | BufferUsages::INDEX,
783            }),
784        }
785    }
786}
787
788/// The IDs of each pipeline used for GPU clustering for a single view.
789#[derive(Component)]
790pub struct ViewGpuClusteringPipelineIds {
791    /// The compute pipeline for the Z slicing compute pass (pass 1).
792    clustering_z_slicing_pipeline_id: CachedComputePipelineId,
793    /// The compute pipeline for the count raster pass (pass 2).
794    clustering_count_pipeline_id: CachedRenderPipelineId,
795    /// The compute pipeline for the local allocation compute pass (pass 3).
796    clustering_allocation_local_pipeline_id: CachedComputePipelineId,
797    /// The compute pipeline for the global allocation compute pass (pass 4).
798    clustering_allocation_global_pipeline_id: CachedComputePipelineId,
799    /// The compute pipeline for the populate raster pass (pass 5).
800    clustering_populate_pipeline_id: CachedRenderPipelineId,
801}
802
803/// The render command building system that performs GPU clustering on each
804/// view.
805fn cluster_on_gpu(
806    view_query: ViewQuery<(
807        &MainEntity,
808        Option<&ViewGpuClusteringBuffers>,
809        Option<&ViewGpuClusteringPipelineIds>,
810        Option<&ViewClusteringDummyTexture>,
811        Option<&ViewClusteringBindGroups>,
812        Option<&ViewLightProbesUniformOffset>,
813        Option<&ViewLightsUniformOffset>,
814        Option<&ViewUniformOffset>,
815        Option<&ExtractedClusterConfig>,
816    )>,
817    pipeline_cache: Res<PipelineCache>,
818    clustering_mesh_buffers: Res<GpuClusteringMeshBuffers>,
819    render_view_clustering_readback_data: Res<RenderViewClusteringReadbackData>,
820    mut render_context: RenderContext,
821) {
822    let (
823        view_main_entity,
824        Some(view_gpu_clustering_buffers),
825        Some(view_gpu_clustering_pipeline_ids),
826        Some(view_clustering_dummy_texture),
827        Some(view_clustering_bind_groups),
828        Some(view_light_probes_uniform_offset),
829        Some(view_lights_uniform_offset),
830        Some(view_uniform_offset),
831        Some(extracted_cluster_config),
832    ) = view_query.into_inner()
833    else {
834        trace!("Failed to match view query; not clustering");
835        return;
836    };
837
838    let Some(view_clustering_readback_data) = render_view_clustering_readback_data
839        .views
840        .get(view_main_entity)
841    else {
842        return;
843    };
844
845    let (
846        Some(clustering_z_slicing_compute_pipeline),
847        Some(clustering_count_render_pipeline),
848        Some(clustering_allocate_local_compute_pipeline),
849        Some(clustering_allocate_global_compute_pipeline),
850        Some(clustering_populate_render_pipeline),
851    ) = (
852        pipeline_cache.get_compute_pipeline(
853            view_gpu_clustering_pipeline_ids.clustering_z_slicing_pipeline_id,
854        ),
855        pipeline_cache
856            .get_render_pipeline(view_gpu_clustering_pipeline_ids.clustering_count_pipeline_id),
857        pipeline_cache.get_compute_pipeline(
858            view_gpu_clustering_pipeline_ids.clustering_allocation_local_pipeline_id,
859        ),
860        pipeline_cache.get_compute_pipeline(
861            view_gpu_clustering_pipeline_ids.clustering_allocation_global_pipeline_id,
862        ),
863        pipeline_cache
864            .get_render_pipeline(view_gpu_clustering_pipeline_ids.clustering_populate_pipeline_id),
865    )
866    else {
867        trace!("One or more clustering pipelines not found; not clustering");
868        return;
869    };
870
871    let diagnostics = render_context.diagnostic_recorder();
872    let diagnostics = diagnostics.as_deref();
873    let time_span = diagnostics.time_span(render_context.command_encoder(), "clustering");
874
875    // Fetch a staging buffer for us to perform readback with.
876    let Ok(staging_buffer) = view_clustering_readback_data
877        .lock()
878        .map(|mut data| data.get_or_create_staging_buffer(render_context.render_device()))
879    else {
880        error!("Failed to fetch staging buffer; not clustering.");
881        return;
882    };
883
884    let command_encoder = render_context.command_encoder();
885    command_encoder.push_debug_group("clustering");
886
887    // Pass 1: Z slicing.
888    run_clustering_z_slicing_pass(
889        command_encoder,
890        clustering_z_slicing_compute_pipeline,
891        &view_clustering_bind_groups.clustering_bind_group_z_slicing_pass,
892        &view_gpu_clustering_buffers.cluster_metadata_buffer,
893        view_light_probes_uniform_offset,
894        view_lights_uniform_offset,
895        view_uniform_offset,
896    );
897
898    // Pass 2: Count raster.
899    run_clustering_rasterization_pass(
900        command_encoder,
901        clustering_count_render_pipeline,
902        &view_clustering_bind_groups.clustering_bind_group_count_pass,
903        view_gpu_clustering_buffers,
904        view_light_probes_uniform_offset,
905        view_lights_uniform_offset,
906        view_uniform_offset,
907        view_clustering_dummy_texture,
908        extracted_cluster_config,
909        &clustering_mesh_buffers,
910        false,
911    );
912
913    // Pass 3: local allocation.
914    run_clustering_allocation_pass(
915        command_encoder,
916        clustering_allocate_local_compute_pipeline,
917        view_clustering_bind_groups,
918        view_lights_uniform_offset,
919        extracted_cluster_config,
920        false,
921    );
922
923    // Pass 4: global allocation.
924    run_clustering_allocation_pass(
925        command_encoder,
926        clustering_allocate_global_compute_pipeline,
927        view_clustering_bind_groups,
928        view_lights_uniform_offset,
929        extracted_cluster_config,
930        true,
931    );
932
933    // Pass 5: populate raster.
934    run_clustering_rasterization_pass(
935        command_encoder,
936        clustering_populate_render_pipeline,
937        &view_clustering_bind_groups.clustering_bind_group_populate_pass,
938        view_gpu_clustering_buffers,
939        view_light_probes_uniform_offset,
940        view_lights_uniform_offset,
941        view_uniform_offset,
942        view_clustering_dummy_texture,
943        extracted_cluster_config,
944        &clustering_mesh_buffers,
945        true,
946    );
947
948    // Schedule a readback of the readback data.
949    schedule_readback_staging(
950        command_encoder,
951        view_gpu_clustering_buffers,
952        &staging_buffer,
953    );
954    schedule_readback_buffer_map(
955        command_encoder,
956        view_clustering_readback_data.clone(),
957        &staging_buffer,
958    );
959
960    command_encoder.pop_debug_group();
961    time_span.end(render_context.command_encoder());
962
963    /// Runs the Z slicing pass (step 1).
964    fn run_clustering_z_slicing_pass(
965        command_encoder: &mut CommandEncoder,
966        clustering_z_slicing_pipeline: &ComputePipeline,
967        clustering_z_slicing_bind_group: &BindGroup,
968        clustering_cluster_metadata_buffer: &StorageBuffer<ClusterMetadata>,
969        view_light_probes_uniform_offset: &ViewLightProbesUniformOffset,
970        view_lights_uniform_offset: &ViewLightsUniformOffset,
971        view_uniform_offset: &ViewUniformOffset,
972    ) {
973        let mut compute_pass = command_encoder.begin_compute_pass(&ComputePassDescriptor {
974            label: Some("clustering Z slicing pass"),
975            ..default()
976        });
977        compute_pass.set_pipeline(clustering_z_slicing_pipeline);
978        compute_pass.set_bind_group(
979            0,
980            Some(&**clustering_z_slicing_bind_group),
981            &[
982                **view_light_probes_uniform_offset,
983                view_lights_uniform_offset.offset,
984                view_uniform_offset.offset,
985            ],
986        );
987
988        let clustering_cluster_metadata = clustering_cluster_metadata_buffer.get();
989        let clusterable_object_count = clustering_cluster_metadata.clustered_light_count
990            + clustering_cluster_metadata.reflection_probe_count
991            + clustering_cluster_metadata.irradiance_volume_count
992            + clustering_cluster_metadata.decal_count;
993
994        let workgroup_count = clusterable_object_count.div_ceil(Z_SLICING_WORKGROUP_SIZE);
995        compute_pass.dispatch_workgroups(workgroup_count, 1, 1);
996    }
997
998    /// Runs either the count or populate rasterization pass (steps 2 and 5
999    /// respectively) for a single view.
1000    ///
1001    /// The `populate_pass` parameter specifies whether this is a count pass
1002    /// (false) or a populate pass (true).
1003    fn run_clustering_rasterization_pass(
1004        command_encoder: &mut CommandEncoder,
1005        clustering_render_pipeline: &RenderPipeline,
1006        clustering_bind_group: &BindGroup,
1007        view_gpu_clustering_buffers: &ViewGpuClusteringBuffers,
1008        view_light_probes_uniform_offset: &ViewLightProbesUniformOffset,
1009        view_lights_uniform_offset: &ViewLightsUniformOffset,
1010        view_uniform_offset: &ViewUniformOffset,
1011        view_clustering_dummy_texture: &ViewClusteringDummyTexture,
1012        extracted_cluster_config: &ExtractedClusterConfig,
1013        clustering_mesh_buffers: &GpuClusteringMeshBuffers,
1014        populate_pass: bool,
1015    ) {
1016        let Some(cluster_metadata_buffer) =
1017            view_gpu_clustering_buffers.cluster_metadata_buffer.buffer()
1018        else {
1019            error!("Z slicing metadata buffer was never uploaded");
1020            return;
1021        };
1022
1023        let mut render_pass = command_encoder.begin_render_pass(&RenderPassDescriptor {
1024            label: if populate_pass {
1025                Some("clustering populate pass")
1026            } else {
1027                Some("clustering count pass")
1028            },
1029            color_attachments: &[Some(RenderPassColorAttachment {
1030                view: &view_clustering_dummy_texture.default_view,
1031                depth_slice: None,
1032                resolve_target: None,
1033                ops: Operations {
1034                    // Do nothing to the color buffer. We only care about using
1035                    // the rasterizer for fragment scheduling; we're not going
1036                    // to actually paint any pixels.
1037                    load: LoadOp::Clear(Color::BLACK.to_linear().into()),
1038                    store: StoreOp::Discard,
1039                },
1040            })],
1041            depth_stencil_attachment: None,
1042            ..default()
1043        });
1044        render_pass.set_pipeline(clustering_render_pipeline);
1045        render_pass.set_bind_group(
1046            0,
1047            Some(&**clustering_bind_group),
1048            &[
1049                **view_light_probes_uniform_offset,
1050                view_lights_uniform_offset.offset,
1051                view_uniform_offset.offset,
1052            ],
1053        );
1054
1055        // Since we rounded up the dummy texture size to prevent thrashing, we
1056        // need to use an explicit viewport here so that we only render to the
1057        // correct portion.
1058        render_pass.set_viewport(
1059            0.0,
1060            0.0,
1061            extracted_cluster_config.dimensions.x as f32,
1062            extracted_cluster_config.dimensions.y as f32,
1063            0.0,
1064            1.0,
1065        );
1066
1067        render_pass.set_vertex_buffer(0, *clustering_mesh_buffers.vertex_buffer.slice(..));
1068        render_pass.set_index_buffer(
1069            *clustering_mesh_buffers.index_buffer.slice(..),
1070            IndexFormat::Uint32,
1071        );
1072        render_pass.draw_indexed_indirect(cluster_metadata_buffer, 0);
1073    }
1074
1075    /// Runs either the local or global allocation pass (steps 3 and 4
1076    /// respectively) for GPU clustering for a single view.
1077    ///
1078    /// The `global_pass` parameter specifies whether this is the local pass
1079    /// (false) or the global pass (true).
1080    fn run_clustering_allocation_pass(
1081        command_encoder: &mut CommandEncoder,
1082        clustering_allocation_pipeline: &ComputePipeline,
1083        view_clustering_bind_groups: &ViewClusteringBindGroups,
1084        view_lights_uniform_offset: &ViewLightsUniformOffset,
1085        extracted_cluster_config: &ExtractedClusterConfig,
1086        global_pass: bool,
1087    ) {
1088        let mut compute_pass = command_encoder.begin_compute_pass(&ComputePassDescriptor {
1089            label: if global_pass {
1090                Some("clustering allocation global pass")
1091            } else {
1092                Some("clustering allocation local pass")
1093            },
1094            ..default()
1095        });
1096        compute_pass.set_pipeline(clustering_allocation_pipeline);
1097        compute_pass.set_bind_group(
1098            0,
1099            Some(&*view_clustering_bind_groups.clustering_bind_group_allocate_pass),
1100            &[view_lights_uniform_offset.offset],
1101        );
1102
1103        // The global pass has only one workgroup because it runs sequentially
1104        // over chunks, while the local pass has a number of workgroups equal to
1105        // the number of chunks because it runs in parallel over them.
1106        let workgroup_count = if global_pass {
1107            1
1108        } else {
1109            extracted_cluster_config
1110                .dimensions
1111                .element_product()
1112                .div_ceil(ALLOCATION_WORKGROUP_SIZE)
1113        };
1114        compute_pass.dispatch_workgroups(workgroup_count, 1, 1);
1115    }
1116
1117    /// Schedules the staging part of readback of the data from GPU.
1118    fn schedule_readback_staging(
1119        command_encoder: &mut CommandEncoder,
1120        view_gpu_clustering_buffers: &ViewGpuClusteringBuffers,
1121        staging_buffer: &Buffer,
1122    ) {
1123        match view_gpu_clustering_buffers.cluster_metadata_buffer.buffer() {
1124            None => {
1125                // This should never happen. It shouldn't have been possible to
1126                // create the necessary bind groups without this buffer's being
1127                // present.
1128                error!("No clustering Z slicing metadata buffer found");
1129            }
1130            Some(metadata_buffer) => {
1131                // Copy the metadata buffer to the staging buffer so we can read
1132                // it back.
1133                command_encoder.copy_buffer_to_buffer(
1134                    metadata_buffer,
1135                    0,
1136                    staging_buffer,
1137                    0,
1138                    Some(u64::from(ClusterMetadata::min_size())),
1139                );
1140            }
1141        }
1142    }
1143
1144    /// Schedules the buffer map operation part of the readback of the data from
1145    /// GPU.
1146    fn schedule_readback_buffer_map(
1147        command_encoder: &mut CommandEncoder,
1148        view_clustering_readback_data: Arc<Mutex<ViewClusteringReadbackData>>,
1149        staging_buffer: &Buffer,
1150    ) {
1151        let captured_staging_buffer = staging_buffer.clone();
1152        command_encoder.map_buffer_on_submit(staging_buffer, MapMode::Read, .., move |result| {
1153            if result.is_err() {
1154                return;
1155            };
1156
1157            let mut view_clustering_readback_data = view_clustering_readback_data.lock().unwrap();
1158
1159            {
1160                // Use `encase` to populate a `ClusterMetadata`.
1161                let buffer_view = captured_staging_buffer
1162                    .slice(..)
1163                    .get_mapped_range()
1164                    .unwrap();
1165                let Ok(mut buffer_reader) =
1166                    Reader::new::<ClusterMetadata>(buffer_view[..].to_vec(), 0)
1167                else {
1168                    return;
1169                };
1170                let gpu_clustering_metadata = ClusterMetadata::create_from(&mut buffer_reader);
1171
1172                // Update readback data.
1173                view_clustering_readback_data.update_from_metadata(&gpu_clustering_metadata);
1174            }
1175
1176            // `wgpu` will error if we didn't drop the buffer view at this
1177            // point, which is why we use a separate block above.
1178            captured_staging_buffer.unmap();
1179
1180            // Recycle the staging buffer.
1181            view_clustering_readback_data
1182                .metadata_staging_free_buffers
1183                .push(captured_staging_buffer);
1184        });
1185    }
1186}
1187
1188/// Prepares bind groups for each of the shaders involved in GPU clustering.
1189fn prepare_clustering_bind_groups(
1190    mut commands: Commands,
1191    views_query: Query<
1192        (Entity, &ViewGpuClusteringBuffers, &ViewClusterBindings),
1193        With<ExtractedView>,
1194    >,
1195    render_device: Res<RenderDevice>,
1196    clustering_z_slicing_pipeline: Res<ClusteringZSlicingPipeline>,
1197    clustering_raster_pipeline: Res<ClusteringRasterPipeline>,
1198    clustering_allocation_pipeline: Res<ClusteringAllocationPipeline>,
1199    global_clusterable_object_meta: Res<GlobalClusterableObjectMeta>,
1200    pipeline_cache: Res<PipelineCache>,
1201    light_probes_buffer: Res<LightProbesBuffer>,
1202    decals_buffer: Res<DecalsBuffer>,
1203    light_meta: Res<LightMeta>,
1204    view_uniforms: Res<ViewUniforms>,
1205) {
1206    let (
1207        Some(gpu_clustered_lights_binding),
1208        Some(light_probes_binding),
1209        Some(decals_buffer),
1210        Some(lights_binding),
1211        Some(view_binding),
1212    ) = (
1213        global_clusterable_object_meta
1214            .gpu_clustered_lights
1215            .binding(),
1216        light_probes_buffer.binding(),
1217        decals_buffer.buffer(),
1218        light_meta.view_gpu_lights.binding(),
1219        view_uniforms.uniforms.binding(),
1220    )
1221    else {
1222        return;
1223    };
1224
1225    // Create separate bind groups for each view.
1226    for (view_entity, view_gpu_clustering_buffers, view_cluster_bindings) in &views_query {
1227        let ViewClusterBuffers::Storage {
1228            clusterable_object_index_lists: ref maybe_clusterable_object_index_lists,
1229            cluster_offsets_and_counts: ref maybe_cluster_offsets_and_counts,
1230        } = view_cluster_bindings.buffers
1231        else {
1232            continue;
1233        };
1234
1235        let (
1236            Some(z_slices_buffer),
1237            Some(cluster_metadata_buffer),
1238            Some(scratchpad_offsets_and_counts_buffer),
1239            Some(clusterable_object_index_lists),
1240            Some(cluster_offsets_and_counts),
1241        ) = (
1242            view_gpu_clustering_buffers.z_slices_buffer.buffer(),
1243            view_gpu_clustering_buffers.cluster_metadata_buffer.buffer(),
1244            view_gpu_clustering_buffers
1245                .scratchpad_offsets_and_counts_buffer
1246                .buffer(),
1247            maybe_clusterable_object_index_lists.buffer(),
1248            maybe_cluster_offsets_and_counts.buffer(),
1249        )
1250        else {
1251            continue;
1252        };
1253
1254        let clustering_bind_group_entries_z_slicing_pass = [
1255            // @group(0) @binding(0) var<storage, read_write>
1256            // cluster_metadata: ClusterMetadata;
1257            BindGroupEntry {
1258                binding: 0,
1259                resource: cluster_metadata_buffer.as_entire_binding(),
1260            },
1261            // @group(0) @binding(1) var<storage, read_write> z_slices:
1262            // array<ClusterableObjectZSlice>;
1263            BindGroupEntry {
1264                binding: 1,
1265                resource: z_slices_buffer.as_entire_binding(),
1266            },
1267            // @group(0) @binding(2) var<storage> clustered_lights:
1268            // ClusteredLights;
1269            BindGroupEntry {
1270                binding: 2,
1271                resource: gpu_clustered_lights_binding.clone(),
1272            },
1273            // @group(0) @binding(3) var<uniform> light_probes: LightProbes;
1274            BindGroupEntry {
1275                binding: 3,
1276                resource: light_probes_binding.clone(),
1277            },
1278            // @group(0) @binding(4) var<storage> clustered_decals:
1279            // ClusteredDecals;
1280            BindGroupEntry {
1281                binding: 4,
1282                resource: decals_buffer.as_entire_binding(),
1283            },
1284            // @group(0) @binding(5) var<uniform> lights: Lights;
1285            BindGroupEntry {
1286                binding: 5,
1287                resource: lights_binding.clone(),
1288            },
1289            // @group(0) @binding(6) var<uniform> view: View;
1290            BindGroupEntry {
1291                binding: 6,
1292                resource: view_binding.clone(),
1293            },
1294        ];
1295
1296        let mut clustering_bind_group_entries_count_pass: Vec<BindGroupEntry> = vec![
1297            // @group(0) @binding(0) var<storage> z_slices:
1298            // array<ClusterableObjectZSlice>;
1299            BindGroupEntry {
1300                binding: 0,
1301                resource: z_slices_buffer.as_entire_binding(),
1302            },
1303            // @group(0) @binding(1) var<storage, read_write> index_lists:
1304            // ClusterableObjectIndexLists;
1305            BindGroupEntry {
1306                binding: 1,
1307                resource: clusterable_object_index_lists.as_entire_binding(),
1308            },
1309            // @group(0) @binding(2) var<storage> clustered_lights:
1310            // ClusteredLights;
1311            BindGroupEntry {
1312                binding: 2,
1313                resource: gpu_clustered_lights_binding.clone(),
1314            },
1315            // @group(0) @binding(3) var<uniform> light_probes: LightProbes;
1316            BindGroupEntry {
1317                binding: 3,
1318                resource: light_probes_binding.clone(),
1319            },
1320            // @group(0) @binding(4) var<storage> clustered_decals:
1321            // ClusteredDecals;
1322            BindGroupEntry {
1323                binding: 4,
1324                resource: decals_buffer.as_entire_binding(),
1325            },
1326            // @group(0) @binding(5) var<uniform> lights: Lights;
1327            BindGroupEntry {
1328                binding: 5,
1329                resource: lights_binding.clone(),
1330            },
1331            // @group(0) @binding(6) var<uniform> view: View;
1332            BindGroupEntry {
1333                binding: 6,
1334                resource: view_binding.clone(),
1335            },
1336        ];
1337
1338        let mut clustering_bind_group_entries_populate_pass =
1339            clustering_bind_group_entries_count_pass.clone();
1340
1341        clustering_bind_group_entries_count_pass.push(
1342            // @group(0) @binding(7) var<storage, read_write>
1343            // offsets_and_counts: ClusterOffsetsAndCounts;
1344            BindGroupEntry {
1345                binding: 7,
1346                resource: cluster_offsets_and_counts.as_entire_binding(),
1347            },
1348        );
1349
1350        clustering_bind_group_entries_populate_pass.push(
1351            // @group(0) @binding(7) var<storage>
1352            // offsets_and_counts: ClusterOffsetsAndCounts;
1353            BindGroupEntry {
1354                binding: 7,
1355                resource: cluster_offsets_and_counts.as_entire_binding(),
1356            },
1357        );
1358        clustering_bind_group_entries_populate_pass.push(
1359            // @group(0) @binding(8) var<storage, read_write>
1360            // scratchpad_offsets_and_counts: ClusterOffsetsAndCountsAtomic;
1361            BindGroupEntry {
1362                binding: 8,
1363                resource: scratchpad_offsets_and_counts_buffer.as_entire_binding(),
1364            },
1365        );
1366
1367        let clustering_bind_group_entries_allocation_pass: [BindGroupEntry; _] = [
1368            // @group(0) @binding(0) var<storage, read_write>
1369            // offsets_and_counts: ClusterOffsetsAndCounts;
1370            BindGroupEntry {
1371                binding: 0,
1372                resource: cluster_offsets_and_counts.as_entire_binding(),
1373            },
1374            // @group(0) @binding(1) var<uniform> lights: Lights;
1375            BindGroupEntry {
1376                binding: 1,
1377                resource: lights_binding.clone(),
1378            },
1379            // @group(0) @binding(2) var<storage, read_write>
1380            // clustering_metadata: ClusterMetadata;
1381            BindGroupEntry {
1382                binding: 2,
1383                resource: cluster_metadata_buffer.as_entire_binding(),
1384            },
1385            // @group(0) @binding(3) var<storage, read_write>
1386            // scratchpad_offsets_and_counts: ClusterOffsetsAndCounts;
1387            BindGroupEntry {
1388                binding: 3,
1389                resource: scratchpad_offsets_and_counts_buffer.as_entire_binding(),
1390            },
1391        ];
1392
1393        let clustering_bind_group_z_slicing_pass = render_device.create_bind_group(
1394            "clustering Z slicing pass bind group",
1395            &pipeline_cache.get_bind_group_layout(&clustering_z_slicing_pipeline.bind_group_layout),
1396            &clustering_bind_group_entries_z_slicing_pass,
1397        );
1398        let clustering_bind_group_count_pass = render_device.create_bind_group(
1399            "clustering count pass bind group",
1400            &pipeline_cache
1401                .get_bind_group_layout(&clustering_raster_pipeline.bind_group_layout_count_pass),
1402            &clustering_bind_group_entries_count_pass,
1403        );
1404        let clustering_bind_group_allocate_pass = render_device.create_bind_group(
1405            "clustering allocate pass bind group",
1406            &pipeline_cache
1407                .get_bind_group_layout(&clustering_allocation_pipeline.bind_group_layout),
1408            &clustering_bind_group_entries_allocation_pass,
1409        );
1410        let clustering_bind_group_populate_pass = render_device.create_bind_group(
1411            "clustering populate pass bind group",
1412            &pipeline_cache
1413                .get_bind_group_layout(&clustering_raster_pipeline.bind_group_layout_populate_pass),
1414            &clustering_bind_group_entries_populate_pass,
1415        );
1416
1417        commands
1418            .entity(view_entity)
1419            .insert(ViewClusteringBindGroups {
1420                clustering_bind_group_z_slicing_pass,
1421                clustering_bind_group_count_pass,
1422                clustering_bind_group_allocate_pass,
1423                clustering_bind_group_populate_pass,
1424            });
1425    }
1426}
1427
1428/// Creates the dummy textures that we use to establish a viewport for the
1429/// rasterization phases of GPU clustering.
1430///
1431/// We don't actually write to these textures, but they need to exist so that a
1432/// viewport of the appropriate size can be set.
1433fn prepare_cluster_dummy_textures(
1434    mut commands: Commands,
1435    views_query: Query<(Entity, &ExtractedClusterConfig), With<ExtractedView>>,
1436    render_device: Res<RenderDevice>,
1437    mut texture_cache: ResMut<TextureCache>,
1438) {
1439    for (view_entity, view_cluster_config) in &views_query {
1440        let dummy_texture = texture_cache.get(
1441            &render_device,
1442            TextureDescriptor {
1443                label: Some("clustering dummy texture"),
1444                // We round these up to the nearest multiple of 32 to guard
1445                // against the risk of thrashing between different sizes,
1446                // especially if the auto-resize feature is on.
1447                size: Extent3d {
1448                    width: view_cluster_config.dimensions.x.next_multiple_of(32),
1449                    height: view_cluster_config.dimensions.y.next_multiple_of(32),
1450                    depth_or_array_layers: 1,
1451                },
1452                mip_level_count: 1,
1453                sample_count: 1,
1454                dimension: TextureDimension::D2,
1455                format: TextureFormat::R8Unorm,
1456                usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::COPY_DST,
1457                view_formats: &[],
1458            },
1459        );
1460        commands
1461            .entity(view_entity)
1462            .insert(ViewClusteringDummyTexture(dummy_texture));
1463    }
1464}
1465
1466/// Prepares the compute and raster pipelines for the various shader invocations
1467/// in GPU clustering for each view.
1468fn prepare_clustering_pipelines(
1469    mut commands: Commands,
1470    views_query: Query<Entity, With<ExtractedView>>,
1471    pipeline_cache: Res<PipelineCache>,
1472    mut clustering_z_slicing_pipelines: ResMut<
1473        SpecializedComputePipelines<ClusteringZSlicingPipeline>,
1474    >,
1475    mut clustering_raster_pipelines: ResMut<SpecializedRenderPipelines<ClusteringRasterPipeline>>,
1476    mut clustering_allocation_pipelines: ResMut<
1477        SpecializedComputePipelines<ClusteringAllocationPipeline>,
1478    >,
1479    clustering_z_slicing_pipeline: Res<ClusteringZSlicingPipeline>,
1480    clustering_raster_pipeline: Res<ClusteringRasterPipeline>,
1481    clustering_allocation_pipeline: Res<ClusteringAllocationPipeline>,
1482) {
1483    for view_entity in &views_query {
1484        let clustering_z_slicing_pipeline_id = clustering_z_slicing_pipelines.specialize(
1485            &pipeline_cache,
1486            &clustering_z_slicing_pipeline,
1487            (),
1488        );
1489        let clustering_count_pipeline_id = clustering_raster_pipelines.specialize(
1490            &pipeline_cache,
1491            &clustering_raster_pipeline,
1492            ClusteringRasterPipelineKey {
1493                populate_pass: false,
1494            },
1495        );
1496        let clustering_local_allocation_pipeline_id = clustering_allocation_pipelines.specialize(
1497            &pipeline_cache,
1498            &clustering_allocation_pipeline,
1499            ClusteringAllocationPipelineKey { global_pass: false },
1500        );
1501        let clustering_global_allocation_pipeline_id = clustering_allocation_pipelines.specialize(
1502            &pipeline_cache,
1503            &clustering_allocation_pipeline,
1504            ClusteringAllocationPipelineKey { global_pass: true },
1505        );
1506        let clustering_populate_pipeline_id = clustering_raster_pipelines.specialize(
1507            &pipeline_cache,
1508            &clustering_raster_pipeline,
1509            ClusteringRasterPipelineKey {
1510                populate_pass: true,
1511            },
1512        );
1513
1514        commands
1515            .entity(view_entity)
1516            .insert(ViewGpuClusteringPipelineIds {
1517                clustering_z_slicing_pipeline_id,
1518                clustering_count_pipeline_id,
1519                clustering_allocation_local_pipeline_id: clustering_local_allocation_pipeline_id,
1520                clustering_allocation_global_pipeline_id: clustering_global_allocation_pipeline_id,
1521                clustering_populate_pipeline_id,
1522            });
1523    }
1524}
1525
1526/// Uploads the buffers needed to perform GPU clustering to the GPU.
1527fn upload_view_gpu_clustering_buffers(
1528    mut views_query: Query<&mut ViewGpuClusteringBuffers>,
1529    render_device: Res<RenderDevice>,
1530    render_queue: Res<RenderQueue>,
1531) {
1532    for mut view_gpu_clustering_buffers in &mut views_query {
1533        view_gpu_clustering_buffers
1534            .z_slices_buffer
1535            .write_buffer(&render_device);
1536
1537        view_gpu_clustering_buffers
1538            .cluster_metadata_buffer
1539            .write_buffer(&render_device, &render_queue);
1540
1541        // Make sure the scratchpad buffer is nonempty, and upload it.
1542        if view_gpu_clustering_buffers
1543            .scratchpad_offsets_and_counts_buffer
1544            .is_empty()
1545        {
1546            view_gpu_clustering_buffers
1547                .scratchpad_offsets_and_counts_buffer
1548                .add();
1549        }
1550        view_gpu_clustering_buffers
1551            .scratchpad_offsets_and_counts_buffer
1552            .write_buffer(&render_device);
1553    }
1554}
1555
1556/// Extracts information needed for GPU clustering from each view in the render
1557/// world, and synchronizes statistics back from the render world to the main
1558/// world if needed.
1559pub fn extract_clusters_for_gpu_clustering(
1560    mut commands: Commands,
1561    mut main_world: ResMut<MainWorld>,
1562    render_view_clustering_index_list_sizes: Res<RenderViewClusteringReadbackData>,
1563) {
1564    let mut views = main_world.query::<(Entity, RenderEntity, &mut Clusters, &Camera)>();
1565
1566    for (main_view_entity, render_view_entity, mut clusters, camera) in
1567        views.iter_mut(&mut main_world)
1568    {
1569        let mut entity_commands = commands
1570            .get_entity(render_view_entity)
1571            .expect("Clusters entity wasn't synced.");
1572        if !camera.is_active {
1573            entity_commands.remove::<ExtractedClusterConfig>();
1574            continue;
1575        }
1576
1577        entity_commands.insert(ExtractedClusterConfig::from(&*clusters));
1578
1579        // Read back statistics from the render world to the main world if we
1580        // have some.
1581        // The clustering systems in the main world will pick them up and adjust
1582        // cluster settings if necessary.
1583        if let Some(view_clustering_buffer_size_data) = render_view_clustering_index_list_sizes
1584            .views
1585            .get(&MainEntity::from(main_view_entity))
1586        {
1587            let view_clustering_buffer_size_data = view_clustering_buffer_size_data.lock().unwrap();
1588            if let Some(last_frame_statistics) =
1589                &view_clustering_buffer_size_data.last_frame_statistics
1590            {
1591                clusters.last_frame_farthest_z = Some(last_frame_statistics.farthest_z);
1592                clusters.last_frame_total_cluster_index_count =
1593                    Some(last_frame_statistics.index_list_size as usize);
1594            }
1595        }
1596    }
1597
1598    let global_cluster_settings = main_world.resource::<GlobalClusterSettings>();
1599    commands.insert_resource(global_cluster_settings.clone());
1600}
1601
1602/// Creates associated buffers necessary to perform GPU clustering for all
1603/// views.
1604pub(crate) fn prepare_clusters_for_gpu_clustering(
1605    mut commands: Commands,
1606    views_query: Query<(
1607        Entity,
1608        &MainEntity,
1609        &ExtractedClusterConfig,
1610        Option<&RenderViewLightProbes<EnvironmentMapLight>>,
1611        Option<&RenderViewLightProbes<IrradianceVolume>>,
1612    )>,
1613    render_clustered_decals: Res<RenderClusteredDecals>,
1614    render_device: Res<RenderDevice>,
1615    render_queue: Res<RenderQueue>,
1616    global_clusterable_object_meta: Res<GlobalClusterableObjectMeta>,
1617    global_cluster_settings: Res<GlobalClusterSettings>,
1618    mut render_view_clustering_index_list_sizes: ResMut<RenderViewClusteringReadbackData>,
1619) {
1620    let render_device = render_device.into_inner();
1621
1622    let Some(ref global_cluster_settings_gpu) = global_cluster_settings.gpu_clustering else {
1623        error!("`prepare_clusters_for_gpu_clustering() called when not GPU clustering");
1624        return;
1625    };
1626
1627    let gpu_clustered_lights_storage = &global_clusterable_object_meta.gpu_clustered_lights;
1628
1629    let mut all_view_main_entities = MainEntityHashSet::default();
1630
1631    for (
1632        view_entity,
1633        view_main_entity,
1634        extracted_cluster_config,
1635        maybe_environment_maps,
1636        maybe_irradiance_volumes,
1637    ) in &views_query
1638    {
1639        // Allocate the cluster array.
1640        let mut view_clusters_bindings =
1641            ViewClusterBindings::new(BufferBindingType::Storage { read_only: false });
1642        view_clusters_bindings.clear();
1643        let cluster_count = extracted_cluster_config.dimensions.x as usize
1644            * extracted_cluster_config.dimensions.y as usize
1645            * extracted_cluster_config.dimensions.z as usize;
1646        view_clusters_bindings.reserve_clusters(cluster_count);
1647
1648        all_view_main_entities.insert(*view_main_entity);
1649
1650        // Create the readback data.
1651        let Ok(view_clustering_buffer_size_data) = render_view_clustering_index_list_sizes
1652            .views
1653            .entry(*view_main_entity)
1654            .or_insert_with(|| {
1655                Arc::new(Mutex::new(ViewClusteringReadbackData::new(
1656                    global_cluster_settings_gpu,
1657                )))
1658            })
1659            .lock()
1660        else {
1661            warn!("Failed to acquire lock for view clustering buffer size data; skipping buffer creation for view: {}", view_entity.to_bits());
1662            continue;
1663        };
1664
1665        let mut view_gpu_clustering_buffers = ViewGpuClusteringBuffers::new();
1666
1667        // Count the number of each type of clusterable object that we have.
1668        let clustered_light_count = gpu_clustered_lights_storage.data.len() as u32;
1669        let reflection_probe_count = match maybe_environment_maps {
1670            Some(view_reflection_probes) => view_reflection_probes.len() as u32,
1671            None => 0,
1672        };
1673        let irradiance_volume_count = match maybe_irradiance_volumes {
1674            Some(view_irradiance_volumes) => view_irradiance_volumes.len() as u32,
1675            None => 0,
1676        };
1677        // Light textures share the decal buffer but aren't clusterable objects.
1678        let decal_count = render_clustered_decals.clusterable_decal_count() as u32;
1679
1680        // Initialize the metadata.
1681        *view_gpu_clustering_buffers
1682            .cluster_metadata_buffer
1683            .get_mut() = ClusterMetadata {
1684            indirect_draw_params: ClusterRasterIndirectDrawParams {
1685                index_count: 6,
1686                // This will be filled in by the GPU.
1687                instance_count: 0,
1688                first_index: 0,
1689                base_vertex: 0,
1690                first_instance: 0,
1691            },
1692            clustered_light_count,
1693            reflection_probe_count,
1694            irradiance_volume_count,
1695            decal_count,
1696            index_list_capacity: view_clustering_buffer_size_data.max_index_list_capacity as u32,
1697            z_slice_list_capacity: view_clustering_buffer_size_data.z_slice_list_capacity as u32,
1698            farthest_z: 0,
1699        };
1700
1701        // Allocate Z slices.
1702        if view_gpu_clustering_buffers.z_slices_buffer.len()
1703            < view_clustering_buffer_size_data.z_slice_list_capacity
1704        {
1705            view_gpu_clustering_buffers.z_slices_buffer.add_multiple(
1706                view_clustering_buffer_size_data.z_slice_list_capacity
1707                    - view_gpu_clustering_buffers.z_slices_buffer.len(),
1708            );
1709        }
1710
1711        // Make room for the appropriate number of indices.
1712        view_clusters_bindings
1713            .reserve_indices(view_clustering_buffer_size_data.max_index_list_capacity);
1714        view_clusters_bindings.write_buffers(render_device, &render_queue);
1715
1716        // Allocate scratchpad offsets and counts.
1717        view_gpu_clustering_buffers
1718            .scratchpad_offsets_and_counts_buffer
1719            .add_multiple(cluster_count);
1720
1721        commands
1722            .entity(view_entity)
1723            .insert((view_clusters_bindings, view_gpu_clustering_buffers));
1724    }
1725
1726    // Clear out clustering allocations corresponding to views that don't exist
1727    // any longer.
1728    render_view_clustering_index_list_sizes
1729        .views
1730        .retain(|view_main_entity, _| all_view_main_entities.contains(view_main_entity));
1731}
1732
1733impl ExtractResource<RenderApp, GpuClusteringPlugin> for GlobalClusterSettings {
1734    type Source = GlobalClusterSettings;
1735
1736    fn extract_resource(source: &Self::Source) -> Self {
1737        source.clone()
1738    }
1739}