1use 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
116const ALLOCATION_WORKGROUP_SIZE: u32 = 256;
118const Z_SLICING_WORKGROUP_SIZE: u32 = 64;
120
121pub 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 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#[derive(Component, Deref, DerefMut)]
194pub struct ViewClusteringDummyTexture(CachedTexture);
195
196#[derive(Component)]
198pub struct ViewClusteringBindGroups {
199 clustering_bind_group_z_slicing_pass: BindGroup,
201 clustering_bind_group_count_pass: BindGroup,
203 clustering_bind_group_allocate_pass: BindGroup,
205 clustering_bind_group_populate_pass: BindGroup,
207}
208
209#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, ShaderType, Pod, Zeroable)]
214#[repr(C)]
215pub struct ClusterableObjectZSlice {
216 pub object_index: u32,
218 pub object_type: u32,
223 pub z_slice: u32,
225}
226
227#[derive(Clone, Copy, Default, ShaderType, Pod, Zeroable)]
229#[repr(C)]
230pub struct ClusterMetadata {
231 indirect_draw_params: ClusterRasterIndirectDrawParams,
233
234 clustered_light_count: u32,
236 reflection_probe_count: u32,
238 irradiance_volume_count: u32,
240 decal_count: u32,
242
243 z_slice_list_capacity: u32,
245
246 index_list_capacity: u32,
251
252 farthest_z: u32,
260}
261
262#[derive(Clone, Copy, Default, ShaderType, Pod, Zeroable)]
268#[repr(C)]
269pub struct ClusterRasterIndirectDrawParams {
270 index_count: u32,
271
272 instance_count: u32,
276
277 first_index: u32,
278 base_vertex: u32,
279 first_instance: u32,
280}
281
282#[derive(Component)]
285pub struct ViewGpuClusteringBuffers {
286 pub z_slices_buffer: UninitBufferVec<ClusterableObjectZSlice>,
291 scratchpad_offsets_and_counts_buffer: UninitBufferVec<GpuClusterOffsetAndCounts>,
297 cluster_metadata_buffer: StorageBuffer<ClusterMetadata>,
301}
302
303impl ViewGpuClusteringBuffers {
304 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#[derive(Resource, Default)]
324pub(crate) struct RenderViewClusteringReadbackData {
325 views: MainEntityHashMap<Arc<Mutex<ViewClusteringReadbackData>>>,
330}
331
332struct ViewClusteringReadbackData {
334 z_slice_list_capacity: usize,
339 max_index_list_capacity: usize,
344 metadata_staging_pending_buffers: Vec<Buffer>,
346 metadata_staging_free_buffers: Vec<Buffer>,
350 last_frame_statistics: Option<ViewClusteringLastFrameStatistics>,
352}
353
354struct ViewClusteringLastFrameStatistics {
356 index_list_size: u32,
361 farthest_z: f32,
364}
365
366impl ViewClusteringReadbackData {
367 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 fn update_from_metadata(&mut self, gpu_clustering_metadata: &ClusterMetadata) {
399 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 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 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
442fn sortable_u32_to_f32_bits(bits: u32) -> u32 {
450 let mask = (!((bits as i32) >> 31)) as u32 | 0x80000000;
451 bits ^ mask
452}
453
454#[derive(Resource)]
456pub struct ClusteringRasterPipeline {
457 pub bind_group_layout_count_pass: BindGroupLayoutDescriptor,
459 pub bind_group_layout_populate_pass: BindGroupLayoutDescriptor,
461 pub shader: Handle<Shader>,
463}
464
465#[derive(Resource)]
467pub struct ClusteringZSlicingPipeline {
468 pub bind_group_layout: BindGroupLayoutDescriptor,
470 pub shader: Handle<Shader>,
472}
473
474#[derive(Resource)]
476pub struct ClusteringAllocationPipeline {
477 pub bind_group_layout: BindGroupLayoutDescriptor,
479 pub shader: Handle<Shader>,
481}
482
483#[derive(Clone, Copy, PartialEq, Eq, Hash)]
486pub struct ClusteringRasterPipelineKey {
487 populate_pass: bool,
490}
491
492#[derive(Clone, Copy, PartialEq, Eq, Hash)]
495pub struct ClusteringAllocationPipelineKey {
496 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 binding_types::storage_buffer_read_only::<ClusterableObjectZSlice>(false)
509 .build(0, ShaderStages::VERTEX_FRAGMENT),
510 binding_types::storage_buffer::<GpuClusterableObjectIndexListsStorage>(false)
513 .build(1, ShaderStages::FRAGMENT),
514 binding_types::storage_buffer_read_only::<GpuClusteredLight>(false)
517 .build(2, ShaderStages::VERTEX_FRAGMENT),
518 binding_types::uniform_buffer::<LightProbesUniform>(true)
520 .build(3, ShaderStages::VERTEX_FRAGMENT),
521 binding_types::storage_buffer_read_only::<RenderClusteredDecal>(false)
524 .build(4, ShaderStages::VERTEX_FRAGMENT),
525 binding_types::uniform_buffer::<GpuLights>(true)
527 .build(5, ShaderStages::VERTEX_FRAGMENT),
528 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 bind_group_layout_entries_count_pass.push(
539 binding_types::storage_buffer::<GpuClusterOffsetsAndCountsStorage>(false)
540 .build(7, ShaderStages::FRAGMENT),
541 );
542
543 bind_group_layout_entries_populate_pass.push(
546 binding_types::storage_buffer_read_only::<GpuClusterOffsetsAndCountsStorage>(false)
547 .build(7, ShaderStages::FRAGMENT),
548 );
549 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 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 binding_types::storage_buffer::<ClusterMetadata>(false),
645 binding_types::storage_buffer::<ClusterableObjectZSlice>(false),
648 binding_types::storage_buffer_read_only::<GpuClusteredLight>(false),
651 binding_types::uniform_buffer::<LightProbesUniform>(true),
654 binding_types::storage_buffer_read_only::<RenderClusteredDecal>(false),
657 binding_types::uniform_buffer::<GpuLights>(true),
659 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 binding_types::storage_buffer::<GpuClusterOffsetsAndCountsStorage>(false),
702 binding_types::uniform_buffer::<GpuLights>(true),
704 binding_types::storage_buffer::<ClusterMetadata>(false),
707 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
747static 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
756static GPU_CLUSTERING_INDICES: [u32; 6] = [0, 1, 2, 1, 3, 2];
759
760#[derive(Resource)]
763struct GpuClusteringMeshBuffers {
764 vertex_buffer: Buffer,
766 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#[derive(Component)]
790pub struct ViewGpuClusteringPipelineIds {
791 clustering_z_slicing_pipeline_id: CachedComputePipelineId,
793 clustering_count_pipeline_id: CachedRenderPipelineId,
795 clustering_allocation_local_pipeline_id: CachedComputePipelineId,
797 clustering_allocation_global_pipeline_id: CachedComputePipelineId,
799 clustering_populate_pipeline_id: CachedRenderPipelineId,
801}
802
803fn 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 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 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 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 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 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 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_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 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 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 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 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 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 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 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 error!("No clustering Z slicing metadata buffer found");
1129 }
1130 Some(metadata_buffer) => {
1131 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 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 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 view_clustering_readback_data.update_from_metadata(&gpu_clustering_metadata);
1174 }
1175
1176 captured_staging_buffer.unmap();
1179
1180 view_clustering_readback_data
1182 .metadata_staging_free_buffers
1183 .push(captured_staging_buffer);
1184 });
1185 }
1186}
1187
1188fn 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 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 BindGroupEntry {
1258 binding: 0,
1259 resource: cluster_metadata_buffer.as_entire_binding(),
1260 },
1261 BindGroupEntry {
1264 binding: 1,
1265 resource: z_slices_buffer.as_entire_binding(),
1266 },
1267 BindGroupEntry {
1270 binding: 2,
1271 resource: gpu_clustered_lights_binding.clone(),
1272 },
1273 BindGroupEntry {
1275 binding: 3,
1276 resource: light_probes_binding.clone(),
1277 },
1278 BindGroupEntry {
1281 binding: 4,
1282 resource: decals_buffer.as_entire_binding(),
1283 },
1284 BindGroupEntry {
1286 binding: 5,
1287 resource: lights_binding.clone(),
1288 },
1289 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 BindGroupEntry {
1300 binding: 0,
1301 resource: z_slices_buffer.as_entire_binding(),
1302 },
1303 BindGroupEntry {
1306 binding: 1,
1307 resource: clusterable_object_index_lists.as_entire_binding(),
1308 },
1309 BindGroupEntry {
1312 binding: 2,
1313 resource: gpu_clustered_lights_binding.clone(),
1314 },
1315 BindGroupEntry {
1317 binding: 3,
1318 resource: light_probes_binding.clone(),
1319 },
1320 BindGroupEntry {
1323 binding: 4,
1324 resource: decals_buffer.as_entire_binding(),
1325 },
1326 BindGroupEntry {
1328 binding: 5,
1329 resource: lights_binding.clone(),
1330 },
1331 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 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 BindGroupEntry {
1354 binding: 7,
1355 resource: cluster_offsets_and_counts.as_entire_binding(),
1356 },
1357 );
1358 clustering_bind_group_entries_populate_pass.push(
1359 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 BindGroupEntry {
1371 binding: 0,
1372 resource: cluster_offsets_and_counts.as_entire_binding(),
1373 },
1374 BindGroupEntry {
1376 binding: 1,
1377 resource: lights_binding.clone(),
1378 },
1379 BindGroupEntry {
1382 binding: 2,
1383 resource: cluster_metadata_buffer.as_entire_binding(),
1384 },
1385 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
1428fn 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 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
1466fn 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
1526fn 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 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
1556pub 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 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
1602pub(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 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 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 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 let decal_count = render_clustered_decals.clusterable_decal_count() as u32;
1679
1680 *view_gpu_clustering_buffers
1682 .cluster_metadata_buffer
1683 .get_mut() = ClusterMetadata {
1684 indirect_draw_params: ClusterRasterIndirectDrawParams {
1685 index_count: 6,
1686 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 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 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 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 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}