1use core::num::{NonZero, NonZeroU64};
10
11use bevy_app::{App, Plugin};
12use bevy_asset::{embedded_asset, load_embedded_asset, Handle};
13use bevy_core_pipeline::{
14 deferred::node::late_deferred_prepass,
15 mip_generation::experimental::depth::{early_downsample_depth, ViewDepthPyramid},
16 prepass::{
17 node::{early_prepass, late_prepass},
18 DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass, PreviousViewData,
19 PreviousViewUniformOffset, PreviousViewUniforms,
20 },
21 schedule::{Core3d, Core3dSystems},
22};
23use bevy_derive::{Deref, DerefMut};
24use bevy_ecs::{
25 component::Component,
26 entity::Entity,
27 prelude::resource_exists,
28 query::{Has, Or, With, Without},
29 resource::Resource,
30 schedule::{common_conditions::any_match_filter, IntoScheduleConfigs as _},
31 system::{Commands, Query, Res, ResMut},
32 world::{FromWorld, World},
33};
34use bevy_log::warn_once;
35use bevy_math::Vec4;
36use bevy_platform::collections::HashMap;
37use bevy_render::{
38 batching::gpu_preprocessing::{
39 clear_scene_unpacking_buffers, BatchedInstanceBuffers, BinUnpackingMetadataIndex,
40 BuildIndirectParametersMetadata, GpuBinMetadata, GpuBinUnpackingMetadata,
41 GpuOcclusionCullingWorkItemBuffers, GpuPreprocessingMode, GpuPreprocessingSupport,
42 GpuUniformAllocationMetadata, IndirectBatchSet, IndirectParametersBuffers,
43 IndirectParametersBuildJob, IndirectParametersBuildJobs, IndirectParametersIndexed,
44 IndirectParametersMetadata, IndirectParametersNonIndexed,
45 LatePreprocessWorkItemIndirectParameters, PreprocessWorkItem, PreprocessWorkItemBuffers,
46 SceneUnpackingBuffers, SceneUnpackingBuffersKey, SceneUnpackingJob,
47 UniformAllocationMetadataIndex, UntypedPhaseBatchedInstanceBuffers,
48 UntypedPhaseIndirectParametersBuffers,
49 },
50 diagnostic::RecordDiagnostics as _,
51 occlusion_culling::OcclusionCulling,
52 render_phase::{GpuRenderBinnedMeshInstance, UNIFORM_ALLOCATION_WORKGROUP_SIZE},
53 render_resource::{
54 binding_types::{storage_buffer, storage_buffer_read_only, texture_2d, uniform_buffer},
55 BindGroup, BindGroupEntries, BindGroupLayoutDescriptor, BindGroupLayoutEntries,
56 BindingResource, Buffer, BufferBinding, BufferVec, CachedComputePipelineId,
57 ComputePassDescriptor, ComputePipelineDescriptor, DynamicBindGroupLayoutEntries,
58 PartialBufferVec, PipelineCache, RawBufferVec, ShaderStages, ShaderType,
59 SparseBufferUpdateBindGroups, SparseBufferUpdateJobs, SparseBufferUpdatePipelines,
60 SpecializedComputePipeline, SpecializedComputePipelines, TextureSampleType,
61 UninitBufferVec,
62 },
63 renderer::{RenderContext, RenderDevice, RenderQueue, ViewQuery},
64 settings::WgpuFeatures,
65 view::{
66 ExtractedView, NoIndirectDrawing, RenderVisibilityRanges, RetainedViewEntity, ViewUniform,
67 ViewUniformOffset, ViewUniforms,
68 },
69 GpuResourceAppExt, Render, RenderApp, RenderSystems,
70};
71use bevy_shader::Shader;
72use bevy_utils::{default, TypeIdHashMap};
73use bitflags::bitflags;
74use smallvec::{smallvec, SmallVec};
75use tracing::warn;
76
77use crate::{
78 LightEntity, MeshCullingData, MeshCullingDataBuffer, MeshInputUniform, MeshUniform,
79 PreviousMeshInputUniform,
80};
81
82use super::{ShadowView, ViewLightEntities};
83
84const WORKGROUP_SIZE: usize = 64;
86
87pub struct GpuMeshPreprocessPlugin {
92 pub use_gpu_instance_buffer_builder: bool,
97}
98
99#[derive(Resource)]
102pub struct PreprocessPipelines {
103 pub direct_preprocess: PreprocessPipeline,
106 pub gpu_frustum_culling_preprocess: PreprocessPipeline,
111 pub early_gpu_occlusion_culling_preprocess: PreprocessPipeline,
116 pub late_gpu_occlusion_culling_preprocess: PreprocessPipeline,
121 pub gpu_frustum_culling_build_indexed_indirect_params: BuildIndirectParametersPipeline,
124 pub gpu_frustum_culling_build_non_indexed_indirect_params: BuildIndirectParametersPipeline,
128 pub early_phase: PreprocessPhasePipelines,
131 pub late_phase: PreprocessPhasePipelines,
135 pub main_phase: PreprocessPhasePipelines,
137 pub bin_unpacking: BinUnpackingPipeline,
139 pub uniform_allocation: UniformAllocationPipelines,
141}
142
143#[derive(Clone)]
147pub struct PreprocessPhasePipelines {
148 pub reset_indirect_batch_sets: ResetIndirectBatchSetsPipeline,
151 pub gpu_occlusion_culling_build_indexed_indirect_params: BuildIndirectParametersPipeline,
156 pub gpu_occlusion_culling_build_non_indexed_indirect_params: BuildIndirectParametersPipeline,
161}
162
163pub struct PreprocessPipeline {
165 pub bind_group_layout: BindGroupLayoutDescriptor,
167 pub shader: Handle<Shader>,
169 pub pipeline_id: Option<CachedComputePipelineId>,
173}
174
175#[derive(Clone)]
180pub struct ResetIndirectBatchSetsPipeline {
181 pub bind_group_layout: BindGroupLayoutDescriptor,
183 pub shader: Handle<Shader>,
185 pub pipeline_id: Option<CachedComputePipelineId>,
189}
190
191#[derive(Clone)]
193pub struct BuildIndirectParametersPipeline {
194 pub bind_group_layout: BindGroupLayoutDescriptor,
196 pub shader: Handle<Shader>,
198 pub pipeline_id: Option<CachedComputePipelineId>,
202}
203
204#[derive(Clone)]
206pub struct BinUnpackingPipeline {
207 pub bind_group_layout: BindGroupLayoutDescriptor,
209 pub shader: Handle<Shader>,
211 pub pipeline_id: Option<CachedComputePipelineId>,
215}
216
217#[derive(Clone)]
225pub struct UniformAllocationPipelines {
226 pub local_scan: UniformAllocationLocalScanPipeline,
228 pub global_scan: UniformAllocationGlobalScanPipeline,
230 pub fan: UniformAllocationFanPipeline,
232}
233
234#[derive(Clone)]
236pub struct UniformAllocationLocalScanPipeline {
237 pub bind_group_layout: BindGroupLayoutDescriptor,
240 pub shader: Handle<Shader>,
242 pub pipeline_id_local_scan: Option<CachedComputePipelineId>,
244}
245
246#[derive(Clone)]
250pub struct UniformAllocationGlobalScanPipeline {
251 pub bind_group_layout: BindGroupLayoutDescriptor,
254 pub shader: Handle<Shader>,
256 pub pipeline_id_global_scan: Option<CachedComputePipelineId>,
258}
259
260#[derive(Clone)]
264pub struct UniformAllocationFanPipeline {
265 pub bind_group_layout: BindGroupLayoutDescriptor,
268 pub shader: Handle<Shader>,
270 pub pipeline_id_fan: Option<CachedComputePipelineId>,
272}
273
274bitflags! {
275 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
277 pub struct PreprocessPipelineKey: u8 {
278 const FRUSTUM_CULLING = 1;
282 const OCCLUSION_CULLING = 2;
286 const EARLY_PHASE = 4;
290 }
291
292 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
294 pub struct BuildIndirectParametersPipelineKey: u8 {
295 const INDEXED = 1;
300 const MULTI_DRAW_INDIRECT_COUNT_SUPPORTED = 2;
304 const OCCLUSION_CULLING = 4;
308 const EARLY_PHASE = 8;
312 const LATE_PHASE = 16;
316 const MAIN_PHASE = 32;
322 }
323}
324
325#[derive(Component, Clone, Deref, DerefMut)]
332pub struct PreprocessBindGroups(pub TypeIdHashMap<PhasePreprocessBindGroups>);
333
334#[derive(Clone)]
337pub enum PhasePreprocessBindGroups {
338 Direct(BindGroup),
344
345 IndirectFrustumCulling {
351 indexed: Option<BindGroup>,
353 non_indexed: Option<BindGroup>,
355 },
356
357 IndirectOcclusionCulling {
365 early_indexed: Option<BindGroup>,
368 early_non_indexed: Option<BindGroup>,
371 late_indexed: Option<BindGroup>,
374 late_non_indexed: Option<BindGroup>,
377 },
378}
379
380#[derive(Resource, Default, Deref, DerefMut)]
386pub struct BuildIndirectParametersBindGroups(
387 pub TypeIdHashMap<PhaseBuildIndirectParametersBindGroups>,
388);
389
390impl BuildIndirectParametersBindGroups {
391 pub fn new() -> BuildIndirectParametersBindGroups {
393 Self::default()
394 }
395}
396
397pub struct PhaseBuildIndirectParametersBindGroups {
400 reset_indexed_indirect_batch_sets: Option<BindGroup>,
403 reset_non_indexed_indirect_batch_sets: Option<BindGroup>,
406 build_indexed_indirect: Option<BindGroup>,
409 build_non_indexed_indirect: Option<BindGroup>,
412}
413
414#[derive(Clone, Resource, Default, Deref, DerefMut)]
420pub struct BinUnpackingBindGroups(
421 pub HashMap<SceneUnpackingBuffersKey, ViewPhaseBinUnpackingBindGroups>,
422);
423
424#[derive(Clone)]
427pub struct ViewPhaseBinUnpackingBindGroups {
428 indexed: Vec<ViewPhaseBinUnpackingBindGroup>,
430 non_indexed: Vec<ViewPhaseBinUnpackingBindGroup>,
432}
433
434#[derive(Clone)]
437pub struct ViewPhaseBinUnpackingBindGroup {
438 pub metadata_index: BinUnpackingMetadataIndex,
441 pub bind_group: BindGroup,
443 pub mesh_instance_count: u32,
446}
447
448#[derive(Clone, Resource, Default, Deref, DerefMut)]
454pub struct UniformAllocationBindGroups(
455 pub HashMap<SceneUnpackingBuffersKey, ViewPhaseUniformAllocationBindGroups>,
456);
457
458#[derive(Clone)]
461pub struct ViewPhaseUniformAllocationBindGroups {
462 indexed: Vec<ViewPhaseUniformAllocationBindGroup>,
464 non_indexed: Vec<ViewPhaseUniformAllocationBindGroup>,
466}
467
468#[derive(Clone)]
471pub struct ViewPhaseUniformAllocationBindGroup {
472 pub metadata_index: UniformAllocationMetadataIndex,
475 pub bind_group: BindGroup,
477 pub bin_count: u32,
479}
480
481#[derive(Component, Default)]
484pub struct SkipGpuPreprocess;
485
486type WithAnyPrepass = Or<(
487 With<DepthPrepass>,
488 With<NormalPrepass>,
489 With<MotionVectorPrepass>,
490 With<DeferredPrepass>,
491)>;
492
493impl Plugin for GpuMeshPreprocessPlugin {
494 fn build(&self, app: &mut App) {
495 embedded_asset!(app, "mesh_preprocess.wesl");
496 embedded_asset!(app, "reset_indirect_batch_sets.wesl");
497 embedded_asset!(app, "build_indirect_params.wesl");
498 embedded_asset!(app, "unpack_bins.wesl");
499 embedded_asset!(app, "allocate_uniforms.wesl");
500 }
501
502 fn finish(&self, app: &mut App) {
503 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
504 return;
505 };
506
507 let gpu_preprocessing_support = render_app.world().resource::<GpuPreprocessingSupport>();
510 if !self.use_gpu_instance_buffer_builder || !gpu_preprocessing_support.is_available() {
511 return;
512 }
513
514 render_app
515 .init_gpu_resource::<BinUnpackingBindGroups>()
516 .init_gpu_resource::<UniformAllocationBindGroups>()
517 .init_gpu_resource::<PreprocessPipelines>()
518 .init_gpu_resource::<SpecializedComputePipelines<PreprocessPipeline>>()
519 .init_gpu_resource::<SpecializedComputePipelines<ResetIndirectBatchSetsPipeline>>()
520 .init_gpu_resource::<SpecializedComputePipelines<BuildIndirectParametersPipeline>>()
521 .init_gpu_resource::<SpecializedComputePipelines<BinUnpackingPipeline>>()
522 .init_gpu_resource::<SpecializedComputePipelines<UniformAllocationLocalScanPipeline>>()
523 .init_gpu_resource::<SpecializedComputePipelines<UniformAllocationGlobalScanPipeline>>()
524 .init_gpu_resource::<SpecializedComputePipelines<UniformAllocationFanPipeline>>()
525 .add_systems(
526 Render,
527 (
528 clear_scene_unpacking_buffers.in_set(RenderSystems::PrepareResources),
529 prepare_preprocess_pipelines.in_set(RenderSystems::Prepare),
530 prepare_preprocess_bind_groups
531 .run_if(resource_exists::<BatchedInstanceBuffers<
532 MeshUniform,
533 MeshInputUniform
534 >>)
535 .in_set(RenderSystems::PrepareBindGroups)
536 .after(prepare_preprocess_pipelines),
537 write_mesh_culling_data_buffer.in_set(RenderSystems::PrepareResourcesFlush),
538 ),
539 )
540 .add_systems(
541 Core3d,
542 (
543 (
544 allocate_uniforms,
545 unpack_bins,
546 early_gpu_preprocess,
547 early_prepass_build_indirect_parameters.run_if(any_match_filter::<(
548 With<PreprocessBindGroups>,
549 Without<SkipGpuPreprocess>,
550 Without<NoIndirectDrawing>,
551 Or<(WithAnyPrepass, With<ShadowView>)>,
552 )>),
553 )
554 .chain()
555 .before(early_prepass),
556 (
557 late_gpu_preprocess,
558 late_prepass_build_indirect_parameters.run_if(any_match_filter::<(
559 With<PreprocessBindGroups>,
560 Without<SkipGpuPreprocess>,
561 Without<NoIndirectDrawing>,
562 Or<(WithAnyPrepass, With<ShadowView>)>,
563 With<OcclusionCulling>,
564 )>),
565 )
566 .chain()
567 .after(early_downsample_depth)
568 .before(late_prepass),
569 main_build_indirect_parameters
570 .run_if(any_match_filter::<(
571 With<PreprocessBindGroups>,
572 Without<SkipGpuPreprocess>,
573 Without<NoIndirectDrawing>,
574 )>)
575 .after(late_prepass_build_indirect_parameters)
576 .after(late_deferred_prepass)
577 .before(Core3dSystems::MainPass),
578 ),
579 );
580 }
581}
582
583pub fn allocate_uniforms(
590 current_view: ViewQuery<Option<&ViewLightEntities>, Without<SkipGpuPreprocess>>,
591 view_query: Query<&ExtractedView, Without<SkipGpuPreprocess>>,
592 light_query: Query<&LightEntity>,
593 batched_instance_buffers: Res<BatchedInstanceBuffers<MeshUniform, MeshInputUniform>>,
594 pipeline_cache: Res<PipelineCache>,
595 preprocess_pipelines: Res<PreprocessPipelines>,
596 uniform_allocation_bind_groups: Res<UniformAllocationBindGroups>,
597 mut render_context: RenderContext,
598) {
599 let diagnostics = render_context.diagnostic_recorder();
600 let diagnostics = diagnostics.as_deref();
601
602 let (
605 Some(uniform_allocation_local_scan_pipeline_id),
606 Some(uniform_allocation_global_scan_pipeline_id),
607 Some(uniform_allocation_fan_pipeline_id),
608 ) = (
609 preprocess_pipelines
610 .uniform_allocation
611 .local_scan
612 .pipeline_id_local_scan,
613 preprocess_pipelines
614 .uniform_allocation
615 .global_scan
616 .pipeline_id_global_scan,
617 preprocess_pipelines.uniform_allocation.fan.pipeline_id_fan,
618 )
619 else {
620 return;
621 };
622
623 let (
624 Some(uniform_allocation_local_scan_pipeline),
625 Some(uniform_allocation_global_scan_pipeline),
626 Some(uniform_allocation_fan_pipeline),
627 ) = (
628 pipeline_cache.get_compute_pipeline(uniform_allocation_local_scan_pipeline_id),
629 pipeline_cache.get_compute_pipeline(uniform_allocation_global_scan_pipeline_id),
630 pipeline_cache.get_compute_pipeline(uniform_allocation_fan_pipeline_id),
631 )
632 else {
633 return;
634 };
635
636 let command_encoder = render_context.command_encoder();
637 let mut compute_pass = command_encoder.begin_compute_pass(&ComputePassDescriptor {
638 label: Some("uniform allocation"),
639 timestamp_writes: None,
640 });
641
642 let pass_span = diagnostics.pass_span(&mut compute_pass, "uniform_allocation");
643
644 let view_entity = current_view.entity();
646 let shadow_cascade_views = current_view.into_inner();
647 let all_views =
648 gather_shadow_cascades_for_view(view_entity, shadow_cascade_views, &light_query);
649
650 for view_entity in all_views {
652 let Ok(view) = view_query.get(view_entity) else {
653 continue;
654 };
655
656 for phase_type_id in batched_instance_buffers.phase_instance_buffers.keys() {
658 let uniform_allocation_buffers_key = SceneUnpackingBuffersKey {
659 phase: *phase_type_id,
660 view: view.retained_view_entity,
661 };
662
663 let Some(phase_uniform_allocation_bind_groups) =
665 uniform_allocation_bind_groups.get(&uniform_allocation_buffers_key)
666 else {
667 continue;
668 };
669
670 for uniform_allocation_bind_group in phase_uniform_allocation_bind_groups
674 .indexed
675 .iter()
676 .chain(phase_uniform_allocation_bind_groups.non_indexed.iter())
677 {
678 compute_pass.set_pipeline(uniform_allocation_local_scan_pipeline);
680 compute_pass.set_bind_group(0, &uniform_allocation_bind_group.bind_group, &[]);
681 let local_scan_workgroup_count = uniform_allocation_bind_group
682 .bin_count
683 .div_ceil(UNIFORM_ALLOCATION_WORKGROUP_SIZE);
684 if local_scan_workgroup_count > 0 {
685 compute_pass.dispatch_workgroups(local_scan_workgroup_count, 1, 1);
686 }
687
688 if local_scan_workgroup_count > 1 {
691 compute_pass.set_pipeline(uniform_allocation_global_scan_pipeline);
693 compute_pass.dispatch_workgroups(1, 1, 1);
694
695 compute_pass.set_pipeline(uniform_allocation_fan_pipeline);
697 let fan_workgroup_count = local_scan_workgroup_count - 1;
698 compute_pass.dispatch_workgroups(fan_workgroup_count, 1, 1);
699 }
700 }
701 }
702 }
703
704 pass_span.end(&mut compute_pass);
705}
706
707pub fn unpack_bins(
715 current_view: ViewQuery<Option<&ViewLightEntities>, Without<SkipGpuPreprocess>>,
716 view_query: Query<&ExtractedView, Without<SkipGpuPreprocess>>,
717 light_query: Query<&LightEntity>,
718 batched_instance_buffers: Res<BatchedInstanceBuffers<MeshUniform, MeshInputUniform>>,
719 pipeline_cache: Res<PipelineCache>,
720 preprocess_pipelines: Res<PreprocessPipelines>,
721 bin_unpacking_bind_groups: Res<BinUnpackingBindGroups>,
722 mut render_context: RenderContext,
723) {
724 let diagnostics = render_context.diagnostic_recorder();
725 let diagnostics = diagnostics.as_deref();
726
727 let command_encoder = render_context.command_encoder();
728 let mut compute_pass = command_encoder.begin_compute_pass(&ComputePassDescriptor {
729 label: Some("bin unpacking"),
730 timestamp_writes: None,
731 });
732
733 let pass_span = diagnostics.pass_span(&mut compute_pass, "bin_unpacking");
734
735 let view_entity = current_view.entity();
737 let shadow_cascade_views = current_view.into_inner();
738 let all_views =
739 gather_shadow_cascades_for_view(view_entity, shadow_cascade_views, &light_query);
740
741 if let Some(bin_unpacking_pipeline_id) = preprocess_pipelines.bin_unpacking.pipeline_id
743 && let Some(bin_unpacking_pipeline) =
744 pipeline_cache.get_compute_pipeline(bin_unpacking_pipeline_id)
745 {
746 compute_pass.set_pipeline(bin_unpacking_pipeline);
747
748 for view_entity in all_views {
750 let Ok(view) = view_query.get(view_entity) else {
751 continue;
752 };
753
754 for phase_type_id in batched_instance_buffers.phase_instance_buffers.keys() {
756 let scene_unpacking_buffers_key = SceneUnpackingBuffersKey {
757 phase: *phase_type_id,
758 view: view.retained_view_entity,
759 };
760
761 let Some(phase_bin_unpacking_bind_groups) =
763 bin_unpacking_bind_groups.get(&scene_unpacking_buffers_key)
764 else {
765 continue;
766 };
767
768 for bin_unpacking_bind_group in phase_bin_unpacking_bind_groups
772 .indexed
773 .iter()
774 .chain(phase_bin_unpacking_bind_groups.non_indexed.iter())
775 {
776 compute_pass.set_bind_group(0, &bin_unpacking_bind_group.bind_group, &[]);
777 let workgroup_count = (bin_unpacking_bind_group.mesh_instance_count as usize)
778 .div_ceil(WORKGROUP_SIZE);
779 if workgroup_count > 0 {
780 compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
781 }
782 }
783 }
784 }
785 }
786
787 pass_span.end(&mut compute_pass);
788}
789
790pub fn early_gpu_preprocess(
791 current_view: ViewQuery<Option<&ViewLightEntities>, Without<SkipGpuPreprocess>>,
792 view_query: Query<
793 (
794 &ExtractedView,
795 Option<&PreprocessBindGroups>,
796 Option<&ViewUniformOffset>,
797 Has<NoIndirectDrawing>,
798 Has<OcclusionCulling>,
799 ),
800 Without<SkipGpuPreprocess>,
801 >,
802 light_query: Query<&LightEntity>,
803 batched_instance_buffers: Res<BatchedInstanceBuffers<MeshUniform, MeshInputUniform>>,
804 pipeline_cache: Res<PipelineCache>,
805 preprocess_pipelines: Res<PreprocessPipelines>,
806 mut ctx: RenderContext,
807) {
808 let diagnostics = ctx.diagnostic_recorder();
809 let diagnostics = diagnostics.as_deref();
810
811 let command_encoder = ctx.command_encoder();
812
813 let mut compute_pass = command_encoder.begin_compute_pass(&ComputePassDescriptor {
814 label: Some("early_mesh_preprocessing"),
815 timestamp_writes: None,
816 });
817
818 let pass_span = diagnostics.pass_span(&mut compute_pass, "early_mesh_preprocessing");
819
820 let view_entity = current_view.entity();
821 let shadow_cascade_views = current_view.into_inner();
822 let all_views =
823 gather_shadow_cascades_for_view(view_entity, shadow_cascade_views, &light_query);
824
825 for view_entity in all_views {
827 let Ok((view, bind_groups, view_uniform_offset, no_indirect_drawing, occlusion_culling)) =
828 view_query.get(view_entity)
829 else {
830 continue;
831 };
832
833 let Some(bind_groups) = bind_groups else {
834 continue;
835 };
836 let Some(view_uniform_offset) = view_uniform_offset else {
837 continue;
838 };
839
840 let maybe_pipeline_id = if no_indirect_drawing {
843 preprocess_pipelines.direct_preprocess.pipeline_id
844 } else if occlusion_culling {
845 preprocess_pipelines
846 .early_gpu_occlusion_culling_preprocess
847 .pipeline_id
848 } else {
849 preprocess_pipelines
850 .gpu_frustum_culling_preprocess
851 .pipeline_id
852 };
853
854 let Some(preprocess_pipeline_id) = maybe_pipeline_id else {
856 warn!("The build mesh uniforms pipeline wasn't ready");
857 continue;
858 };
859
860 let Some(preprocess_pipeline) = pipeline_cache.get_compute_pipeline(preprocess_pipeline_id)
861 else {
862 continue;
864 };
865
866 compute_pass.set_pipeline(preprocess_pipeline);
867
868 for (phase_type_id, batched_phase_instance_buffers) in
870 &batched_instance_buffers.phase_instance_buffers
871 {
872 let Some(work_item_buffers) = batched_phase_instance_buffers
874 .work_item_buffers
875 .get(&view.retained_view_entity)
876 else {
877 continue;
878 };
879
880 let Some(phase_bind_groups) = bind_groups.get(phase_type_id) else {
882 continue;
883 };
884
885 let dynamic_offsets = [view_uniform_offset.offset];
889
890 match *phase_bind_groups {
892 PhasePreprocessBindGroups::Direct(ref bind_group) => {
893 let PreprocessWorkItemBuffers::Direct(work_item_buffer) = work_item_buffers
896 else {
897 continue;
898 };
899 compute_pass.set_bind_group(0, bind_group, &dynamic_offsets);
900 let workgroup_count = work_item_buffer.len().div_ceil(WORKGROUP_SIZE);
901 if workgroup_count > 0 {
902 compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
903 }
904 }
905
906 PhasePreprocessBindGroups::IndirectFrustumCulling {
907 indexed: ref maybe_indexed_bind_group,
908 non_indexed: ref maybe_non_indexed_bind_group,
909 }
910 | PhasePreprocessBindGroups::IndirectOcclusionCulling {
911 early_indexed: ref maybe_indexed_bind_group,
912 early_non_indexed: ref maybe_non_indexed_bind_group,
913 ..
914 } => {
915 let PreprocessWorkItemBuffers::Indirect {
918 indexed: indexed_buffer,
919 non_indexed: non_indexed_buffer,
920 ..
921 } = work_item_buffers
922 else {
923 continue;
924 };
925
926 if let Some(indexed_bind_group) = maybe_indexed_bind_group {
928 if let PreprocessWorkItemBuffers::Indirect {
929 gpu_occlusion_culling:
930 Some(GpuOcclusionCullingWorkItemBuffers {
931 late_indirect_parameters_indexed_offset,
932 ..
933 }),
934 ..
935 } = *work_item_buffers
936 {
937 compute_pass.set_immediates(
938 0,
939 bytemuck::bytes_of(&late_indirect_parameters_indexed_offset),
940 );
941 }
942
943 compute_pass.set_bind_group(0, indexed_bind_group, &dynamic_offsets);
944 let workgroup_count = indexed_buffer.len().div_ceil(WORKGROUP_SIZE);
945 if workgroup_count > 0 {
946 compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
947 }
948 }
949
950 if let Some(non_indexed_bind_group) = maybe_non_indexed_bind_group {
952 if let PreprocessWorkItemBuffers::Indirect {
953 gpu_occlusion_culling:
954 Some(GpuOcclusionCullingWorkItemBuffers {
955 late_indirect_parameters_non_indexed_offset,
956 ..
957 }),
958 ..
959 } = *work_item_buffers
960 {
961 compute_pass.set_immediates(
962 0,
963 bytemuck::bytes_of(&late_indirect_parameters_non_indexed_offset),
964 );
965 }
966
967 compute_pass.set_bind_group(0, non_indexed_bind_group, &dynamic_offsets);
968 let workgroup_count = non_indexed_buffer.len().div_ceil(WORKGROUP_SIZE);
969 if workgroup_count > 0 {
970 compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
971 }
972 }
973 }
974 }
975 }
976 }
977
978 pass_span.end(&mut compute_pass);
979}
980
981fn gather_shadow_cascades_for_view(
984 view_entity: Entity,
985 shadow_cascade_views: Option<&ViewLightEntities>,
986 light_query: &Query<&LightEntity>,
987) -> SmallVec<[Entity; 8]> {
988 let mut all_views: SmallVec<[_; 8]> = SmallVec::new();
989 all_views.push(view_entity);
990 if let Some(shadow_cascade_views) = shadow_cascade_views {
991 all_views.extend(
992 shadow_cascade_views
993 .lights
994 .iter()
995 .filter(|light_entity| {
996 light_query.get(**light_entity).is_ok_and(|light_entity| {
997 matches!(*light_entity, LightEntity::Directional { .. })
998 })
999 })
1000 .copied(),
1001 );
1002 }
1003 all_views
1004}
1005
1006pub fn late_gpu_preprocess(
1007 current_view: ViewQuery<
1008 (&ExtractedView, &PreprocessBindGroups, &ViewUniformOffset),
1009 (
1010 Without<SkipGpuPreprocess>,
1011 Without<NoIndirectDrawing>,
1012 With<OcclusionCulling>,
1013 With<DepthPrepass>,
1014 ),
1015 >,
1016 batched_instance_buffers: Res<BatchedInstanceBuffers<MeshUniform, MeshInputUniform>>,
1017 pipeline_cache: Res<PipelineCache>,
1018 preprocess_pipelines: Res<PreprocessPipelines>,
1019 mut ctx: RenderContext,
1020) {
1021 let (view, bind_groups, view_uniform_offset) = current_view.into_inner();
1022
1023 let maybe_pipeline_id = preprocess_pipelines
1025 .late_gpu_occlusion_culling_preprocess
1026 .pipeline_id;
1027
1028 let Some(preprocess_pipeline_id) = maybe_pipeline_id else {
1029 warn_once!("The build mesh uniforms pipeline wasn't ready");
1030 return;
1031 };
1032
1033 let Some(preprocess_pipeline) = pipeline_cache.get_compute_pipeline(preprocess_pipeline_id)
1034 else {
1035 return;
1037 };
1038
1039 let diagnostics = ctx.diagnostic_recorder();
1040 let diagnostics = diagnostics.as_deref();
1041
1042 let command_encoder = ctx.command_encoder();
1043
1044 let mut compute_pass = command_encoder.begin_compute_pass(&ComputePassDescriptor {
1045 label: Some("late_mesh_preprocessing"),
1046 timestamp_writes: None,
1047 });
1048
1049 let pass_span = diagnostics.pass_span(&mut compute_pass, "late_mesh_preprocessing");
1050
1051 compute_pass.set_pipeline(preprocess_pipeline);
1052
1053 for (phase_type_id, batched_phase_instance_buffers) in
1056 &batched_instance_buffers.phase_instance_buffers
1057 {
1058 let UntypedPhaseBatchedInstanceBuffers {
1059 ref work_item_buffers,
1060 ref late_indexed_indirect_parameters_buffer,
1061 ref late_non_indexed_indirect_parameters_buffer,
1062 ..
1063 } = *batched_phase_instance_buffers;
1064
1065 let Some(phase_work_item_buffers) = work_item_buffers.get(&view.retained_view_entity)
1067 else {
1068 continue;
1069 };
1070
1071 let (
1072 PreprocessWorkItemBuffers::Indirect {
1073 gpu_occlusion_culling:
1074 Some(GpuOcclusionCullingWorkItemBuffers {
1075 late_indirect_parameters_indexed_offset,
1076 late_indirect_parameters_non_indexed_offset,
1077 ..
1078 }),
1079 ..
1080 },
1081 Some(PhasePreprocessBindGroups::IndirectOcclusionCulling {
1082 late_indexed: maybe_late_indexed_bind_group,
1083 late_non_indexed: maybe_late_non_indexed_bind_group,
1084 ..
1085 }),
1086 Some(late_indexed_indirect_parameters_buffer),
1087 Some(late_non_indexed_indirect_parameters_buffer),
1088 ) = (
1089 phase_work_item_buffers,
1090 bind_groups.get(phase_type_id),
1091 late_indexed_indirect_parameters_buffer.buffer(),
1092 late_non_indexed_indirect_parameters_buffer.buffer(),
1093 )
1094 else {
1095 continue;
1096 };
1097
1098 let mut dynamic_offsets: SmallVec<[u32; 1]> = smallvec![];
1099 dynamic_offsets.push(view_uniform_offset.offset);
1100
1101 if let Some(late_indexed_bind_group) = maybe_late_indexed_bind_group {
1108 compute_pass.set_immediates(
1109 0,
1110 bytemuck::bytes_of(late_indirect_parameters_indexed_offset),
1111 );
1112
1113 compute_pass.set_bind_group(0, late_indexed_bind_group, &dynamic_offsets);
1114 compute_pass.dispatch_workgroups_indirect(
1115 late_indexed_indirect_parameters_buffer,
1116 (*late_indirect_parameters_indexed_offset as u64)
1117 * (size_of::<LatePreprocessWorkItemIndirectParameters>() as u64),
1118 );
1119 }
1120
1121 if let Some(late_non_indexed_bind_group) = maybe_late_non_indexed_bind_group {
1123 compute_pass.set_immediates(
1124 0,
1125 bytemuck::bytes_of(late_indirect_parameters_non_indexed_offset),
1126 );
1127
1128 compute_pass.set_bind_group(0, late_non_indexed_bind_group, &dynamic_offsets);
1129 compute_pass.dispatch_workgroups_indirect(
1130 late_non_indexed_indirect_parameters_buffer,
1131 (*late_indirect_parameters_non_indexed_offset as u64)
1132 * (size_of::<LatePreprocessWorkItemIndirectParameters>() as u64),
1133 );
1134 }
1135 }
1136
1137 pass_span.end(&mut compute_pass);
1138}
1139
1140pub fn early_prepass_build_indirect_parameters(
1146 current_view: ViewQuery<&ExtractedView>,
1147 preprocess_pipelines: Res<PreprocessPipelines>,
1148 build_indirect_params_bind_groups: Option<Res<BuildIndirectParametersBindGroups>>,
1149 pipeline_cache: Res<PipelineCache>,
1150 indirect_parameters_buffers: Option<Res<IndirectParametersBuffers>>,
1151 build_indirect_parameters_uniform_indices: Res<BuildIndirectParametersMetadata>,
1152 mut ctx: RenderContext,
1153) {
1154 run_build_indirect_parameters(
1155 &mut ctx,
1156 current_view.into_inner().retained_view_entity,
1157 build_indirect_params_bind_groups.as_deref(),
1158 &pipeline_cache,
1159 indirect_parameters_buffers.as_deref(),
1160 &build_indirect_parameters_uniform_indices,
1161 &preprocess_pipelines.early_phase,
1162 "early_prepass_indirect_parameters_building",
1163 );
1164}
1165
1166pub fn late_prepass_build_indirect_parameters(
1173 current_view: ViewQuery<&ExtractedView>,
1174 preprocess_pipelines: Res<PreprocessPipelines>,
1175 build_indirect_params_bind_groups: Option<Res<BuildIndirectParametersBindGroups>>,
1176 pipeline_cache: Res<PipelineCache>,
1177 indirect_parameters_buffers: Option<Res<IndirectParametersBuffers>>,
1178 build_indirect_parameters_uniform_indices: Res<BuildIndirectParametersMetadata>,
1179 mut ctx: RenderContext,
1180) {
1181 run_build_indirect_parameters(
1182 &mut ctx,
1183 current_view.into_inner().retained_view_entity,
1184 build_indirect_params_bind_groups.as_deref(),
1185 &pipeline_cache,
1186 indirect_parameters_buffers.as_deref(),
1187 &build_indirect_parameters_uniform_indices,
1188 &preprocess_pipelines.late_phase,
1189 "late_prepass_indirect_parameters_building",
1190 );
1191}
1192
1193pub fn main_build_indirect_parameters(
1197 current_view: ViewQuery<&ExtractedView, Without<ShadowView>>,
1198 preprocess_pipelines: Res<PreprocessPipelines>,
1199 build_indirect_params_bind_groups: Option<Res<BuildIndirectParametersBindGroups>>,
1200 pipeline_cache: Res<PipelineCache>,
1201 indirect_parameters_buffers: Option<Res<IndirectParametersBuffers>>,
1202 build_indirect_parameters_uniform_indices: Res<BuildIndirectParametersMetadata>,
1203 mut ctx: RenderContext,
1204) {
1205 run_build_indirect_parameters(
1206 &mut ctx,
1207 current_view.into_inner().retained_view_entity,
1208 build_indirect_params_bind_groups.as_deref(),
1209 &pipeline_cache,
1210 indirect_parameters_buffers.as_deref(),
1211 &build_indirect_parameters_uniform_indices,
1212 &preprocess_pipelines.main_phase,
1213 "main_indirect_parameters_building",
1214 );
1215}
1216
1217pub(crate) fn run_build_indirect_parameters(
1220 ctx: &mut RenderContext,
1221 retained_view_entity: RetainedViewEntity,
1222 build_indirect_params_bind_groups: Option<&BuildIndirectParametersBindGroups>,
1223 pipeline_cache: &PipelineCache,
1224 indirect_parameters_buffers: Option<&IndirectParametersBuffers>,
1225 build_indirect_parameters_uniform_indices: &BuildIndirectParametersMetadata,
1226 preprocess_phase_pipelines: &PreprocessPhasePipelines,
1227 label: &'static str,
1228) {
1229 let Some(build_indirect_params_bind_groups) = build_indirect_params_bind_groups else {
1230 return;
1231 };
1232 let Some(indirect_parameters_buffers) = indirect_parameters_buffers else {
1233 return;
1234 };
1235 let Some(view_build_indirect_parameters_uniform_indices) =
1236 build_indirect_parameters_uniform_indices.get(&retained_view_entity)
1237 else {
1238 return;
1239 };
1240
1241 let command_encoder = ctx.command_encoder();
1242
1243 let mut compute_pass = command_encoder.begin_compute_pass(&ComputePassDescriptor {
1244 label: Some(label),
1245 timestamp_writes: None,
1246 });
1247
1248 let (
1250 Some(reset_indirect_batch_sets_pipeline_id),
1251 Some(build_indexed_indirect_params_pipeline_id),
1252 Some(build_non_indexed_indirect_params_pipeline_id),
1253 ) = (
1254 preprocess_phase_pipelines
1255 .reset_indirect_batch_sets
1256 .pipeline_id,
1257 preprocess_phase_pipelines
1258 .gpu_occlusion_culling_build_indexed_indirect_params
1259 .pipeline_id,
1260 preprocess_phase_pipelines
1261 .gpu_occlusion_culling_build_non_indexed_indirect_params
1262 .pipeline_id,
1263 )
1264 else {
1265 warn!("The build indirect parameters pipelines weren't ready");
1266 return;
1267 };
1268
1269 let (
1270 Some(reset_indirect_batch_sets_pipeline),
1271 Some(build_indexed_indirect_params_pipeline),
1272 Some(build_non_indexed_indirect_params_pipeline),
1273 ) = (
1274 pipeline_cache.get_compute_pipeline(reset_indirect_batch_sets_pipeline_id),
1275 pipeline_cache.get_compute_pipeline(build_indexed_indirect_params_pipeline_id),
1276 pipeline_cache.get_compute_pipeline(build_non_indexed_indirect_params_pipeline_id),
1277 )
1278 else {
1279 return;
1281 };
1282
1283 for (phase_type_id, phase_build_indirect_params_bind_groups) in
1286 build_indirect_params_bind_groups.iter()
1287 {
1288 let Some(phase_indirect_parameters_buffers) =
1289 indirect_parameters_buffers.get(phase_type_id)
1290 else {
1291 continue;
1292 };
1293 let Some(build_indirect_parameters_uniform_index) =
1294 view_build_indirect_parameters_uniform_indices.get(phase_type_id)
1295 else {
1296 continue;
1297 };
1298
1299 if let (
1301 Some(reset_indexed_indirect_batch_sets_bind_group),
1302 Some(build_indirect_indexed_params_bind_group),
1303 ) = (
1304 &phase_build_indirect_params_bind_groups.reset_indexed_indirect_batch_sets,
1305 &phase_build_indirect_params_bind_groups.build_indexed_indirect,
1306 ) {
1307 compute_pass.set_pipeline(reset_indirect_batch_sets_pipeline);
1308 compute_pass.set_bind_group(0, reset_indexed_indirect_batch_sets_bind_group, &[]);
1309 let workgroup_count = phase_indirect_parameters_buffers
1310 .batch_set_count(true)
1311 .div_ceil(WORKGROUP_SIZE);
1312 if workgroup_count > 0 {
1313 compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
1314 }
1315
1316 compute_pass.set_pipeline(build_indexed_indirect_params_pipeline);
1317
1318 for indexed_build_indirect_parameters_metadata in
1319 &build_indirect_parameters_uniform_index.indexed
1320 {
1321 compute_pass.set_bind_group(
1322 0,
1323 build_indirect_indexed_params_bind_group,
1324 &[indexed_build_indirect_parameters_metadata.uniform_offset],
1325 );
1326 let workgroup_count = indexed_build_indirect_parameters_metadata
1327 .batch_count
1328 .div_ceil(WORKGROUP_SIZE as u32);
1329 if workgroup_count > 0 {
1330 compute_pass.dispatch_workgroups(workgroup_count, 1, 1);
1331 }
1332 }
1333 }
1334
1335 if let (
1337 Some(reset_non_indexed_indirect_batch_sets_bind_group),
1338 Some(build_indirect_non_indexed_params_bind_group),
1339 ) = (
1340 &phase_build_indirect_params_bind_groups.reset_non_indexed_indirect_batch_sets,
1341 &phase_build_indirect_params_bind_groups.build_non_indexed_indirect,
1342 ) {
1343 compute_pass.set_pipeline(reset_indirect_batch_sets_pipeline);
1344 compute_pass.set_bind_group(0, reset_non_indexed_indirect_batch_sets_bind_group, &[]);
1345 let workgroup_count = phase_indirect_parameters_buffers
1346 .batch_set_count(false)
1347 .div_ceil(WORKGROUP_SIZE);
1348 if workgroup_count > 0 {
1349 compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
1350 }
1351
1352 compute_pass.set_pipeline(build_non_indexed_indirect_params_pipeline);
1353
1354 for non_indexed_build_indirect_parameters_metadata in
1355 &build_indirect_parameters_uniform_index.non_indexed
1356 {
1357 compute_pass.set_bind_group(
1358 0,
1359 build_indirect_non_indexed_params_bind_group,
1360 &[non_indexed_build_indirect_parameters_metadata.uniform_offset],
1361 );
1362 let workgroup_count = non_indexed_build_indirect_parameters_metadata
1363 .batch_count
1364 .div_ceil(WORKGROUP_SIZE as u32);
1365 if workgroup_count > 0 {
1366 compute_pass.dispatch_workgroups(workgroup_count, 1, 1);
1367 }
1368 }
1369 }
1370 }
1371}
1372
1373impl PreprocessPipelines {
1374 pub(crate) fn pipelines_are_loaded(
1377 &self,
1378 pipeline_cache: &PipelineCache,
1379 preprocessing_support: &GpuPreprocessingSupport,
1380 ) -> bool {
1381 match preprocessing_support.max_supported_mode {
1382 GpuPreprocessingMode::None => false,
1383 GpuPreprocessingMode::PreprocessingOnly => {
1384 self.direct_preprocess.is_loaded(pipeline_cache)
1385 && self
1386 .gpu_frustum_culling_preprocess
1387 .is_loaded(pipeline_cache)
1388 }
1389 GpuPreprocessingMode::Culling => {
1390 self.direct_preprocess.is_loaded(pipeline_cache)
1391 && self
1392 .gpu_frustum_culling_preprocess
1393 .is_loaded(pipeline_cache)
1394 && self
1395 .early_gpu_occlusion_culling_preprocess
1396 .is_loaded(pipeline_cache)
1397 && self
1398 .late_gpu_occlusion_culling_preprocess
1399 .is_loaded(pipeline_cache)
1400 && self
1401 .gpu_frustum_culling_build_indexed_indirect_params
1402 .is_loaded(pipeline_cache)
1403 && self
1404 .gpu_frustum_culling_build_non_indexed_indirect_params
1405 .is_loaded(pipeline_cache)
1406 && self.early_phase.is_loaded(pipeline_cache)
1407 && self.late_phase.is_loaded(pipeline_cache)
1408 && self.main_phase.is_loaded(pipeline_cache)
1409 }
1410 }
1411 }
1412}
1413
1414impl PreprocessPhasePipelines {
1415 fn is_loaded(&self, pipeline_cache: &PipelineCache) -> bool {
1416 self.reset_indirect_batch_sets.is_loaded(pipeline_cache)
1417 && self
1418 .gpu_occlusion_culling_build_indexed_indirect_params
1419 .is_loaded(pipeline_cache)
1420 && self
1421 .gpu_occlusion_culling_build_non_indexed_indirect_params
1422 .is_loaded(pipeline_cache)
1423 }
1424}
1425
1426impl PreprocessPipeline {
1427 fn is_loaded(&self, pipeline_cache: &PipelineCache) -> bool {
1428 self.pipeline_id
1429 .is_some_and(|pipeline_id| pipeline_cache.get_compute_pipeline(pipeline_id).is_some())
1430 }
1431}
1432
1433impl ResetIndirectBatchSetsPipeline {
1434 fn is_loaded(&self, pipeline_cache: &PipelineCache) -> bool {
1435 self.pipeline_id
1436 .is_some_and(|pipeline_id| pipeline_cache.get_compute_pipeline(pipeline_id).is_some())
1437 }
1438}
1439
1440impl BuildIndirectParametersPipeline {
1441 fn is_loaded(&self, pipeline_cache: &PipelineCache) -> bool {
1444 self.pipeline_id
1445 .is_some_and(|pipeline_id| pipeline_cache.get_compute_pipeline(pipeline_id).is_some())
1446 }
1447}
1448
1449impl SpecializedComputePipeline for PreprocessPipeline {
1450 type Key = PreprocessPipelineKey;
1451
1452 fn specialize(&self, key: Self::Key) -> ComputePipelineDescriptor {
1453 let mut shader_defs = vec!["WRITE_INDIRECT_PARAMETERS_METADATA".into()];
1454 if key.contains(PreprocessPipelineKey::FRUSTUM_CULLING) {
1455 shader_defs.push("INDIRECT".into());
1456 shader_defs.push("FRUSTUM_CULLING".into());
1457 }
1458 if key.contains(PreprocessPipelineKey::OCCLUSION_CULLING) {
1459 shader_defs.push("OCCLUSION_CULLING".into());
1460 if key.contains(PreprocessPipelineKey::EARLY_PHASE) {
1461 shader_defs.push("EARLY_PHASE".into());
1462 } else {
1463 shader_defs.push("LATE_PHASE".into());
1464 }
1465 }
1466
1467 ComputePipelineDescriptor {
1468 label: Some(
1469 format!(
1470 "mesh preprocessing ({})",
1471 if key.contains(
1472 PreprocessPipelineKey::OCCLUSION_CULLING
1473 | PreprocessPipelineKey::EARLY_PHASE
1474 ) {
1475 "early GPU occlusion culling"
1476 } else if key.contains(PreprocessPipelineKey::OCCLUSION_CULLING) {
1477 "late GPU occlusion culling"
1478 } else if key.contains(PreprocessPipelineKey::FRUSTUM_CULLING) {
1479 "GPU frustum culling"
1480 } else {
1481 "direct"
1482 }
1483 )
1484 .into(),
1485 ),
1486 layout: vec![self.bind_group_layout.clone()],
1487 immediate_size: if key.contains(PreprocessPipelineKey::OCCLUSION_CULLING) {
1488 4
1489 } else {
1490 0
1491 },
1492 shader: self.shader.clone(),
1493 shader_defs,
1494 ..default()
1495 }
1496 }
1497}
1498
1499impl FromWorld for PreprocessPipelines {
1500 fn from_world(world: &mut World) -> Self {
1501 let direct_bind_group_layout_entries = preprocess_direct_bind_group_layout_entries();
1504 let gpu_frustum_culling_bind_group_layout_entries = gpu_culling_bind_group_layout_entries();
1505 let gpu_early_occlusion_culling_bind_group_layout_entries =
1506 gpu_occlusion_culling_bind_group_layout_entries().extend_with_indices((
1507 (
1508 12,
1509 storage_buffer::<PreprocessWorkItem>(false),
1510 ),
1511 (
1512 13,
1513 storage_buffer::<LatePreprocessWorkItemIndirectParameters>(
1514 false,
1515 ),
1516 ),
1517 ));
1518 let gpu_late_occlusion_culling_bind_group_layout_entries =
1519 gpu_occlusion_culling_bind_group_layout_entries().extend_with_indices(((
1520 13,
1521 storage_buffer_read_only::<LatePreprocessWorkItemIndirectParameters>(
1522 false,
1523 ),
1524 ),));
1525
1526 let reset_indirect_batch_sets_bind_group_layout_entries =
1527 DynamicBindGroupLayoutEntries::sequential(
1528 ShaderStages::COMPUTE,
1529 (storage_buffer::<IndirectBatchSet>(false),),
1530 );
1531
1532 let build_indexed_indirect_params_bind_group_layout_entries =
1535 build_indirect_params_bind_group_layout_entries()
1536 .extend_sequential((storage_buffer::<IndirectParametersIndexed>(false),));
1537 let build_non_indexed_indirect_params_bind_group_layout_entries =
1538 build_indirect_params_bind_group_layout_entries()
1539 .extend_sequential((storage_buffer::<IndirectParametersNonIndexed>(false),));
1540
1541 let bin_unpacking_bind_group_layout_entries = bin_unpacking_bind_group_layout_entries();
1542 let uniform_allocation_bind_group_layout_entries =
1543 uniform_allocation_bind_group_layout_entries();
1544
1545 let direct_bind_group_layout = BindGroupLayoutDescriptor::new(
1547 "build mesh uniforms direct bind group layout",
1548 &direct_bind_group_layout_entries,
1549 );
1550 let gpu_frustum_culling_bind_group_layout = BindGroupLayoutDescriptor::new(
1551 "build mesh uniforms GPU frustum culling bind group layout",
1552 &gpu_frustum_culling_bind_group_layout_entries,
1553 );
1554 let gpu_early_occlusion_culling_bind_group_layout = BindGroupLayoutDescriptor::new(
1555 "build mesh uniforms GPU early occlusion culling bind group layout",
1556 &gpu_early_occlusion_culling_bind_group_layout_entries,
1557 );
1558 let gpu_late_occlusion_culling_bind_group_layout = BindGroupLayoutDescriptor::new(
1559 "build mesh uniforms GPU late occlusion culling bind group layout",
1560 &gpu_late_occlusion_culling_bind_group_layout_entries,
1561 );
1562 let reset_indirect_batch_sets_bind_group_layout = BindGroupLayoutDescriptor::new(
1563 "reset indirect batch sets bind group layout",
1564 &reset_indirect_batch_sets_bind_group_layout_entries,
1565 );
1566 let build_indexed_indirect_params_bind_group_layout = BindGroupLayoutDescriptor::new(
1567 "build indexed indirect parameters bind group layout",
1568 &build_indexed_indirect_params_bind_group_layout_entries,
1569 );
1570 let build_non_indexed_indirect_params_bind_group_layout = BindGroupLayoutDescriptor::new(
1571 "build non-indexed indirect parameters bind group layout",
1572 &build_non_indexed_indirect_params_bind_group_layout_entries,
1573 );
1574 let bin_unpacking_bind_group_layout = BindGroupLayoutDescriptor::new(
1575 "bin unpacking bind group layout",
1576 &bin_unpacking_bind_group_layout_entries,
1577 );
1578 let uniform_allocation_bind_group_layout = BindGroupLayoutDescriptor::new(
1579 "uniform allocation bind group layout",
1580 &uniform_allocation_bind_group_layout_entries,
1581 );
1582
1583 let preprocess_shader = load_embedded_asset!(world, "mesh_preprocess.wesl");
1584 let reset_indirect_batch_sets_shader =
1585 load_embedded_asset!(world, "reset_indirect_batch_sets.wesl");
1586 let build_indirect_params_shader =
1587 load_embedded_asset!(world, "build_indirect_params.wesl");
1588 let bin_unpacking_shader = load_embedded_asset!(world, "unpack_bins.wesl");
1589 let uniform_allocation_shader = load_embedded_asset!(world, "allocate_uniforms.wesl");
1590
1591 let preprocess_phase_pipelines = PreprocessPhasePipelines {
1592 reset_indirect_batch_sets: ResetIndirectBatchSetsPipeline {
1593 bind_group_layout: reset_indirect_batch_sets_bind_group_layout.clone(),
1594 shader: reset_indirect_batch_sets_shader,
1595 pipeline_id: None,
1596 },
1597 gpu_occlusion_culling_build_indexed_indirect_params: BuildIndirectParametersPipeline {
1598 bind_group_layout: build_indexed_indirect_params_bind_group_layout.clone(),
1599 shader: build_indirect_params_shader.clone(),
1600 pipeline_id: None,
1601 },
1602 gpu_occlusion_culling_build_non_indexed_indirect_params:
1603 BuildIndirectParametersPipeline {
1604 bind_group_layout: build_non_indexed_indirect_params_bind_group_layout.clone(),
1605 shader: build_indirect_params_shader.clone(),
1606 pipeline_id: None,
1607 },
1608 };
1609
1610 PreprocessPipelines {
1611 direct_preprocess: PreprocessPipeline {
1612 bind_group_layout: direct_bind_group_layout,
1613 shader: preprocess_shader.clone(),
1614 pipeline_id: None,
1615 },
1616 gpu_frustum_culling_preprocess: PreprocessPipeline {
1617 bind_group_layout: gpu_frustum_culling_bind_group_layout,
1618 shader: preprocess_shader.clone(),
1619 pipeline_id: None,
1620 },
1621 early_gpu_occlusion_culling_preprocess: PreprocessPipeline {
1622 bind_group_layout: gpu_early_occlusion_culling_bind_group_layout,
1623 shader: preprocess_shader.clone(),
1624 pipeline_id: None,
1625 },
1626 late_gpu_occlusion_culling_preprocess: PreprocessPipeline {
1627 bind_group_layout: gpu_late_occlusion_culling_bind_group_layout,
1628 shader: preprocess_shader,
1629 pipeline_id: None,
1630 },
1631 gpu_frustum_culling_build_indexed_indirect_params: BuildIndirectParametersPipeline {
1632 bind_group_layout: build_indexed_indirect_params_bind_group_layout.clone(),
1633 shader: build_indirect_params_shader.clone(),
1634 pipeline_id: None,
1635 },
1636 gpu_frustum_culling_build_non_indexed_indirect_params:
1637 BuildIndirectParametersPipeline {
1638 bind_group_layout: build_non_indexed_indirect_params_bind_group_layout.clone(),
1639 shader: build_indirect_params_shader,
1640 pipeline_id: None,
1641 },
1642 early_phase: preprocess_phase_pipelines.clone(),
1643 late_phase: preprocess_phase_pipelines.clone(),
1644 main_phase: preprocess_phase_pipelines.clone(),
1645 bin_unpacking: BinUnpackingPipeline {
1646 bind_group_layout: bin_unpacking_bind_group_layout,
1647 shader: bin_unpacking_shader,
1648 pipeline_id: None,
1649 },
1650 uniform_allocation: UniformAllocationPipelines {
1651 local_scan: UniformAllocationLocalScanPipeline {
1652 bind_group_layout: uniform_allocation_bind_group_layout.clone(),
1653 shader: uniform_allocation_shader.clone(),
1654 pipeline_id_local_scan: None,
1655 },
1656 global_scan: UniformAllocationGlobalScanPipeline {
1657 bind_group_layout: uniform_allocation_bind_group_layout.clone(),
1658 shader: uniform_allocation_shader.clone(),
1659 pipeline_id_global_scan: None,
1660 },
1661 fan: UniformAllocationFanPipeline {
1662 bind_group_layout: uniform_allocation_bind_group_layout.clone(),
1663 shader: uniform_allocation_shader.clone(),
1664 pipeline_id_fan: None,
1665 },
1666 },
1667 }
1668 }
1669}
1670
1671fn preprocess_direct_bind_group_layout_entries() -> DynamicBindGroupLayoutEntries {
1672 DynamicBindGroupLayoutEntries::new_with_indices(
1673 ShaderStages::COMPUTE,
1674 (
1675 (
1677 0,
1678 uniform_buffer::<ViewUniform>(true),
1679 ),
1680 (3, storage_buffer_read_only::<MeshInputUniform>(false)),
1682 (
1684 4,
1685 storage_buffer_read_only::<PreviousMeshInputUniform>(false),
1686 ),
1687 (5, storage_buffer_read_only::<PreprocessWorkItem>(false)),
1689 (6, storage_buffer::<MeshUniform>(false)),
1691 ),
1692 )
1693}
1694
1695fn build_indirect_params_bind_group_layout_entries() -> DynamicBindGroupLayoutEntries {
1698 DynamicBindGroupLayoutEntries::new_with_indices(
1699 ShaderStages::COMPUTE,
1700 (
1701 (0, storage_buffer_read_only::<MeshInputUniform>(false)),
1704 (
1707 1,
1708 storage_buffer_read_only::<IndirectParametersMetadata>(false),
1709 ),
1710 (3, storage_buffer::<IndirectBatchSet>(false)),
1713 (4, uniform_buffer::<IndirectParametersBuildJob>(true)),
1716 ),
1717 )
1718}
1719
1720fn gpu_culling_bind_group_layout_entries() -> DynamicBindGroupLayoutEntries {
1723 preprocess_direct_bind_group_layout_entries().extend_with_indices((
1726 (
1729 7,
1730 storage_buffer::<IndirectParametersMetadata>(false),
1731 ),
1732 (
1734 9,
1735 storage_buffer_read_only::<MeshCullingData>(false),
1736 ),
1737 (
1739 10,
1740 storage_buffer_read_only::<Vec4>(false),
1741 ),
1742 ))
1743}
1744
1745fn gpu_occlusion_culling_bind_group_layout_entries() -> DynamicBindGroupLayoutEntries {
1746 gpu_culling_bind_group_layout_entries().extend_with_indices((
1747 (
1748 2,
1749 uniform_buffer::<PreviousViewData>(false),
1750 ),
1751 (
1752 11,
1753 texture_2d(TextureSampleType::Float { filterable: true }),
1754 ),
1755 ))
1756}
1757
1758fn bin_unpacking_bind_group_layout_entries() -> BindGroupLayoutEntries<5> {
1761 BindGroupLayoutEntries::sequential(
1762 ShaderStages::COMPUTE,
1763 (
1764 uniform_buffer::<GpuBinUnpackingMetadata>(false),
1767 storage_buffer_read_only::<GpuRenderBinnedMeshInstance>(false),
1770 storage_buffer::<PreprocessWorkItem>(false),
1773 storage_buffer_read_only::<GpuBinMetadata>(false),
1776 storage_buffer_read_only::<u32>(false),
1779 ),
1780 )
1781}
1782
1783fn uniform_allocation_bind_group_layout_entries() -> BindGroupLayoutEntries<4> {
1786 BindGroupLayoutEntries::sequential(
1787 ShaderStages::COMPUTE,
1788 (
1789 uniform_buffer::<GpuUniformAllocationMetadata>(false),
1792 storage_buffer_read_only::<GpuBinMetadata>(false),
1794 storage_buffer::<IndirectParametersMetadata>(false),
1797 storage_buffer::<u32>(false),
1800 ),
1801 )
1802}
1803
1804pub fn prepare_preprocess_pipelines(
1812 pipeline_cache: Res<PipelineCache>,
1813 render_device: Res<RenderDevice>,
1814 mut specialized_preprocess_pipelines: ResMut<SpecializedComputePipelines<PreprocessPipeline>>,
1815 mut specialized_reset_indirect_batch_sets_pipelines: ResMut<
1816 SpecializedComputePipelines<ResetIndirectBatchSetsPipeline>,
1817 >,
1818 mut specialized_build_indirect_parameters_pipelines: ResMut<
1819 SpecializedComputePipelines<BuildIndirectParametersPipeline>,
1820 >,
1821 mut specialized_bin_unpacking_pipelines: ResMut<
1822 SpecializedComputePipelines<BinUnpackingPipeline>,
1823 >,
1824 mut specialized_uniform_allocation_local_scan_pipelines: ResMut<
1825 SpecializedComputePipelines<UniformAllocationLocalScanPipeline>,
1826 >,
1827 mut specialized_uniform_allocation_global_scan_pipelines: ResMut<
1828 SpecializedComputePipelines<UniformAllocationGlobalScanPipeline>,
1829 >,
1830 mut specialized_uniform_allocation_fan_pipelines: ResMut<
1831 SpecializedComputePipelines<UniformAllocationFanPipeline>,
1832 >,
1833 preprocess_pipelines: ResMut<PreprocessPipelines>,
1834 gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
1835) {
1836 let preprocess_pipelines = preprocess_pipelines.into_inner();
1837
1838 preprocess_pipelines.direct_preprocess.prepare(
1839 &pipeline_cache,
1840 &mut specialized_preprocess_pipelines,
1841 PreprocessPipelineKey::empty(),
1842 );
1843 preprocess_pipelines.gpu_frustum_culling_preprocess.prepare(
1844 &pipeline_cache,
1845 &mut specialized_preprocess_pipelines,
1846 PreprocessPipelineKey::FRUSTUM_CULLING,
1847 );
1848
1849 if gpu_preprocessing_support.is_culling_supported() {
1850 preprocess_pipelines
1851 .early_gpu_occlusion_culling_preprocess
1852 .prepare(
1853 &pipeline_cache,
1854 &mut specialized_preprocess_pipelines,
1855 PreprocessPipelineKey::FRUSTUM_CULLING
1856 | PreprocessPipelineKey::OCCLUSION_CULLING
1857 | PreprocessPipelineKey::EARLY_PHASE,
1858 );
1859 preprocess_pipelines
1860 .late_gpu_occlusion_culling_preprocess
1861 .prepare(
1862 &pipeline_cache,
1863 &mut specialized_preprocess_pipelines,
1864 PreprocessPipelineKey::FRUSTUM_CULLING | PreprocessPipelineKey::OCCLUSION_CULLING,
1865 );
1866 }
1867
1868 let mut build_indirect_parameters_pipeline_key = BuildIndirectParametersPipelineKey::empty();
1869
1870 if render_device
1873 .wgpu_device()
1874 .features()
1875 .contains(WgpuFeatures::MULTI_DRAW_INDIRECT_COUNT)
1876 {
1877 build_indirect_parameters_pipeline_key
1878 .insert(BuildIndirectParametersPipelineKey::MULTI_DRAW_INDIRECT_COUNT_SUPPORTED);
1879 }
1880
1881 preprocess_pipelines
1882 .gpu_frustum_culling_build_indexed_indirect_params
1883 .prepare(
1884 &pipeline_cache,
1885 &mut specialized_build_indirect_parameters_pipelines,
1886 build_indirect_parameters_pipeline_key | BuildIndirectParametersPipelineKey::INDEXED,
1887 );
1888 preprocess_pipelines
1889 .gpu_frustum_culling_build_non_indexed_indirect_params
1890 .prepare(
1891 &pipeline_cache,
1892 &mut specialized_build_indirect_parameters_pipelines,
1893 build_indirect_parameters_pipeline_key,
1894 );
1895
1896 if !gpu_preprocessing_support.is_culling_supported() {
1897 return;
1898 }
1899
1900 for (preprocess_phase_pipelines, build_indirect_parameters_phase_pipeline_key) in [
1901 (
1902 &mut preprocess_pipelines.early_phase,
1903 BuildIndirectParametersPipelineKey::EARLY_PHASE,
1904 ),
1905 (
1906 &mut preprocess_pipelines.late_phase,
1907 BuildIndirectParametersPipelineKey::LATE_PHASE,
1908 ),
1909 (
1910 &mut preprocess_pipelines.main_phase,
1911 BuildIndirectParametersPipelineKey::MAIN_PHASE,
1912 ),
1913 ] {
1914 preprocess_phase_pipelines
1915 .reset_indirect_batch_sets
1916 .prepare(
1917 &pipeline_cache,
1918 &mut specialized_reset_indirect_batch_sets_pipelines,
1919 );
1920 preprocess_phase_pipelines
1921 .gpu_occlusion_culling_build_indexed_indirect_params
1922 .prepare(
1923 &pipeline_cache,
1924 &mut specialized_build_indirect_parameters_pipelines,
1925 build_indirect_parameters_pipeline_key
1926 | build_indirect_parameters_phase_pipeline_key
1927 | BuildIndirectParametersPipelineKey::INDEXED
1928 | BuildIndirectParametersPipelineKey::OCCLUSION_CULLING,
1929 );
1930 preprocess_phase_pipelines
1931 .gpu_occlusion_culling_build_non_indexed_indirect_params
1932 .prepare(
1933 &pipeline_cache,
1934 &mut specialized_build_indirect_parameters_pipelines,
1935 build_indirect_parameters_pipeline_key
1936 | build_indirect_parameters_phase_pipeline_key
1937 | BuildIndirectParametersPipelineKey::OCCLUSION_CULLING,
1938 );
1939 }
1940
1941 preprocess_pipelines
1943 .bin_unpacking
1944 .prepare(&pipeline_cache, &mut specialized_bin_unpacking_pipelines);
1945
1946 preprocess_pipelines.uniform_allocation.prepare(
1948 &pipeline_cache,
1949 &mut specialized_uniform_allocation_local_scan_pipelines,
1950 &mut specialized_uniform_allocation_global_scan_pipelines,
1951 &mut specialized_uniform_allocation_fan_pipelines,
1952 );
1953}
1954
1955impl PreprocessPipeline {
1956 fn prepare(
1957 &mut self,
1958 pipeline_cache: &PipelineCache,
1959 pipelines: &mut SpecializedComputePipelines<PreprocessPipeline>,
1960 key: PreprocessPipelineKey,
1961 ) {
1962 if self.pipeline_id.is_some() {
1963 return;
1964 }
1965
1966 let preprocess_pipeline_id = pipelines.specialize(pipeline_cache, self, key);
1967 self.pipeline_id = Some(preprocess_pipeline_id);
1968 }
1969}
1970
1971impl SpecializedComputePipeline for ResetIndirectBatchSetsPipeline {
1972 type Key = ();
1973
1974 fn specialize(&self, _: Self::Key) -> ComputePipelineDescriptor {
1975 ComputePipelineDescriptor {
1976 label: Some("reset indirect batch sets".into()),
1977 layout: vec![self.bind_group_layout.clone()],
1978 shader: self.shader.clone(),
1979 ..default()
1980 }
1981 }
1982}
1983
1984impl SpecializedComputePipeline for BuildIndirectParametersPipeline {
1985 type Key = BuildIndirectParametersPipelineKey;
1986
1987 fn specialize(&self, key: Self::Key) -> ComputePipelineDescriptor {
1988 let mut shader_defs = vec![];
1989 if key.contains(BuildIndirectParametersPipelineKey::INDEXED) {
1990 shader_defs.push("INDEXED".into());
1991 }
1992 if key.contains(BuildIndirectParametersPipelineKey::MULTI_DRAW_INDIRECT_COUNT_SUPPORTED) {
1993 shader_defs.push("MULTI_DRAW_INDIRECT_COUNT_SUPPORTED".into());
1994 }
1995 if key.contains(BuildIndirectParametersPipelineKey::OCCLUSION_CULLING) {
1996 shader_defs.push("OCCLUSION_CULLING".into());
1997 }
1998 if key.contains(BuildIndirectParametersPipelineKey::EARLY_PHASE) {
1999 shader_defs.push("EARLY_PHASE".into());
2000 }
2001 if key.contains(BuildIndirectParametersPipelineKey::LATE_PHASE) {
2002 shader_defs.push("LATE_PHASE".into());
2003 }
2004 if key.contains(BuildIndirectParametersPipelineKey::MAIN_PHASE) {
2005 shader_defs.push("MAIN_PHASE".into());
2006 }
2007
2008 let label = format!(
2009 "{} build {}indexed indirect parameters",
2010 if !key.contains(BuildIndirectParametersPipelineKey::OCCLUSION_CULLING) {
2011 "frustum culling"
2012 } else if key.contains(BuildIndirectParametersPipelineKey::EARLY_PHASE) {
2013 "early occlusion culling"
2014 } else if key.contains(BuildIndirectParametersPipelineKey::LATE_PHASE) {
2015 "late occlusion culling"
2016 } else {
2017 "main occlusion culling"
2018 },
2019 if key.contains(BuildIndirectParametersPipelineKey::INDEXED) {
2020 ""
2021 } else {
2022 "non-"
2023 }
2024 );
2025
2026 ComputePipelineDescriptor {
2027 label: Some(label.into()),
2028 layout: vec![self.bind_group_layout.clone()],
2029 shader: self.shader.clone(),
2030 shader_defs,
2031 ..default()
2032 }
2033 }
2034}
2035
2036impl SpecializedComputePipeline for BinUnpackingPipeline {
2037 type Key = ();
2038
2039 fn specialize(&self, _: Self::Key) -> ComputePipelineDescriptor {
2040 ComputePipelineDescriptor {
2041 label: Some("bin unpacking".into()),
2042 layout: vec![self.bind_group_layout.clone()],
2043 shader: self.shader.clone(),
2044 shader_defs: vec![],
2045 ..default()
2046 }
2047 }
2048}
2049
2050impl SpecializedComputePipeline for UniformAllocationLocalScanPipeline {
2051 type Key = ();
2052
2053 fn specialize(&self, _: Self::Key) -> ComputePipelineDescriptor {
2054 ComputePipelineDescriptor {
2055 label: Some("uniform allocation, local scan".into()),
2056 layout: vec![self.bind_group_layout.clone()],
2057 shader: self.shader.clone(),
2058 shader_defs: vec![],
2059 entry_point: Some("allocate_local_scan".into()),
2060 ..Default::default()
2061 }
2062 }
2063}
2064
2065impl SpecializedComputePipeline for UniformAllocationGlobalScanPipeline {
2066 type Key = ();
2067
2068 fn specialize(&self, _: Self::Key) -> ComputePipelineDescriptor {
2069 ComputePipelineDescriptor {
2070 label: Some("uniform allocation, global scan".into()),
2071 layout: vec![self.bind_group_layout.clone()],
2072 shader: self.shader.clone(),
2073 shader_defs: vec![],
2074 entry_point: Some("allocate_global_scan".into()),
2075 ..Default::default()
2076 }
2077 }
2078}
2079
2080impl SpecializedComputePipeline for UniformAllocationFanPipeline {
2081 type Key = ();
2082
2083 fn specialize(&self, _: Self::Key) -> ComputePipelineDescriptor {
2084 ComputePipelineDescriptor {
2085 label: Some("uniform allocation, fan".into()),
2086 layout: vec![self.bind_group_layout.clone()],
2087 shader: self.shader.clone(),
2088 shader_defs: vec![],
2089 entry_point: Some("allocate_fan".into()),
2090 ..Default::default()
2091 }
2092 }
2093}
2094
2095impl ResetIndirectBatchSetsPipeline {
2096 fn prepare(
2097 &mut self,
2098 pipeline_cache: &PipelineCache,
2099 pipelines: &mut SpecializedComputePipelines<ResetIndirectBatchSetsPipeline>,
2100 ) {
2101 if self.pipeline_id.is_some() {
2102 return;
2103 }
2104
2105 let reset_indirect_batch_sets_pipeline_id = pipelines.specialize(pipeline_cache, self, ());
2106 self.pipeline_id = Some(reset_indirect_batch_sets_pipeline_id);
2107 }
2108}
2109
2110impl BuildIndirectParametersPipeline {
2111 fn prepare(
2112 &mut self,
2113 pipeline_cache: &PipelineCache,
2114 pipelines: &mut SpecializedComputePipelines<BuildIndirectParametersPipeline>,
2115 key: BuildIndirectParametersPipelineKey,
2116 ) {
2117 if self.pipeline_id.is_some() {
2118 return;
2119 }
2120
2121 let build_indirect_parameters_pipeline_id = pipelines.specialize(pipeline_cache, self, key);
2122 self.pipeline_id = Some(build_indirect_parameters_pipeline_id);
2123 }
2124}
2125
2126impl BinUnpackingPipeline {
2127 fn prepare(
2129 &mut self,
2130 pipeline_cache: &PipelineCache,
2131 pipelines: &mut SpecializedComputePipelines<BinUnpackingPipeline>,
2132 ) {
2133 if self.pipeline_id.is_some() {
2134 return;
2135 }
2136
2137 let bin_unpacking_pipeline_id = pipelines.specialize(pipeline_cache, self, ());
2138 self.pipeline_id = Some(bin_unpacking_pipeline_id);
2139 }
2140}
2141
2142impl UniformAllocationPipelines {
2143 fn prepare(
2145 &mut self,
2146 pipeline_cache: &PipelineCache,
2147 uniform_allocation_local_scan_pipelines: &mut SpecializedComputePipelines<
2148 UniformAllocationLocalScanPipeline,
2149 >,
2150 uniform_allocation_global_scan_pipelines: &mut SpecializedComputePipelines<
2151 UniformAllocationGlobalScanPipeline,
2152 >,
2153 uniform_allocation_fan_pipelines: &mut SpecializedComputePipelines<
2154 UniformAllocationFanPipeline,
2155 >,
2156 ) {
2157 if self.local_scan.pipeline_id_local_scan.is_none() {
2158 self.local_scan.pipeline_id_local_scan =
2159 Some(uniform_allocation_local_scan_pipelines.specialize(
2160 pipeline_cache,
2161 &self.local_scan,
2162 (),
2163 ));
2164 }
2165
2166 if self.global_scan.pipeline_id_global_scan.is_none() {
2167 self.global_scan.pipeline_id_global_scan =
2168 Some(uniform_allocation_global_scan_pipelines.specialize(
2169 pipeline_cache,
2170 &self.global_scan,
2171 (),
2172 ));
2173 }
2174
2175 if self.fan.pipeline_id_fan.is_none() {
2176 self.fan.pipeline_id_fan =
2177 Some(uniform_allocation_fan_pipelines.specialize(pipeline_cache, &self.fan, ()));
2178 }
2179 }
2180}
2181
2182#[expect(
2185 clippy::too_many_arguments,
2186 reason = "it's a system that needs a lot of arguments"
2187)]
2188pub fn prepare_preprocess_bind_groups(
2189 mut commands: Commands,
2190 views: Query<(Entity, &ExtractedView)>,
2191 view_depth_pyramids: Query<(&ViewDepthPyramid, &PreviousViewUniformOffset)>,
2192 render_device: Res<RenderDevice>,
2193 pipeline_cache: Res<PipelineCache>,
2194 batched_instance_buffers: Res<BatchedInstanceBuffers<MeshUniform, MeshInputUniform>>,
2195 indirect_parameters_buffers: Res<IndirectParametersBuffers>,
2196 indirect_parameters_build_jobs: Res<IndirectParametersBuildJobs>,
2197 scene_unpacking_buffers: Res<SceneUnpackingBuffers>,
2198 mesh_culling_data_buffer: Res<MeshCullingDataBuffer>,
2199 visibility_ranges: Res<RenderVisibilityRanges>,
2200 view_uniforms: Res<ViewUniforms>,
2201 previous_view_uniforms: Res<PreviousViewUniforms>,
2202 pipelines: Res<PreprocessPipelines>,
2203 mut bin_unpacking_bind_groups: ResMut<BinUnpackingBindGroups>,
2204 mut uniform_allocation_bind_groups: ResMut<UniformAllocationBindGroups>,
2205) {
2206 let BatchedInstanceBuffers {
2208 current_input_buffer: current_input_buffer_vec,
2209 previous_input_buffer: previous_input_buffer_vec,
2210 phase_instance_buffers,
2211 } = batched_instance_buffers.into_inner();
2212
2213 let (Some(current_input_buffer), Some(previous_input_buffer)) = (
2214 current_input_buffer_vec.buffer().buffer(),
2215 previous_input_buffer_vec.buffer(),
2216 ) else {
2217 return;
2218 };
2219
2220 let mut any_indirect = false;
2223
2224 for (view_entity, view) in &views {
2226 let mut bind_groups = TypeIdHashMap::default();
2227
2228 for (phase_type_id, phase_instance_buffers) in phase_instance_buffers {
2230 let UntypedPhaseBatchedInstanceBuffers {
2231 data_buffer: ref data_buffer_vec,
2232 ref work_item_buffers,
2233 ref late_indexed_indirect_parameters_buffer,
2234 ref late_non_indexed_indirect_parameters_buffer,
2235 } = *phase_instance_buffers;
2236
2237 let Some(data_buffer) = data_buffer_vec.buffer() else {
2238 continue;
2239 };
2240
2241 let Some(phase_indirect_parameters_buffers) =
2243 indirect_parameters_buffers.get(phase_type_id)
2244 else {
2245 continue;
2246 };
2247
2248 let Some(work_item_buffers) = work_item_buffers.get(&view.retained_view_entity) else {
2249 continue;
2250 };
2251
2252 let preprocess_bind_group_builder = PreprocessBindGroupBuilder {
2254 view: view_entity,
2255 late_indexed_indirect_parameters_buffer,
2256 late_non_indexed_indirect_parameters_buffer,
2257 render_device: &render_device,
2258 pipeline_cache: &pipeline_cache,
2259 phase_indirect_parameters_buffers,
2260 mesh_culling_data_buffer: &mesh_culling_data_buffer,
2261 visibility_range_data_buffer: visibility_ranges.buffer(),
2262 view_uniforms: &view_uniforms,
2263 previous_view_uniforms: &previous_view_uniforms,
2264 pipelines: &pipelines,
2265 current_input_buffer,
2266 previous_input_buffer,
2267 data_buffer,
2268 };
2269
2270 let (was_indirect, bind_group) = match *work_item_buffers {
2273 PreprocessWorkItemBuffers::Direct(ref work_item_buffer) => (
2274 false,
2275 preprocess_bind_group_builder
2276 .create_direct_preprocess_bind_groups(work_item_buffer),
2277 ),
2278
2279 PreprocessWorkItemBuffers::Indirect {
2280 indexed: ref indexed_work_item_buffer,
2281 non_indexed: ref non_indexed_work_item_buffer,
2282 gpu_occlusion_culling: Some(ref gpu_occlusion_culling_work_item_buffers),
2283 } => (
2284 true,
2285 preprocess_bind_group_builder
2286 .create_indirect_occlusion_culling_preprocess_bind_groups(
2287 &view_depth_pyramids,
2288 indexed_work_item_buffer,
2289 non_indexed_work_item_buffer,
2290 gpu_occlusion_culling_work_item_buffers,
2291 ),
2292 ),
2293
2294 PreprocessWorkItemBuffers::Indirect {
2295 indexed: ref indexed_work_item_buffer,
2296 non_indexed: ref non_indexed_work_item_buffer,
2297 gpu_occlusion_culling: None,
2298 } => (
2299 true,
2300 preprocess_bind_group_builder
2301 .create_indirect_frustum_culling_preprocess_bind_groups(
2302 indexed_work_item_buffer,
2303 non_indexed_work_item_buffer,
2304 ),
2305 ),
2306 };
2307
2308 if let Some(bind_group) = bind_group {
2310 any_indirect = any_indirect || was_indirect;
2311 bind_groups.insert(*phase_type_id, bind_group);
2312 }
2313 }
2314
2315 commands
2317 .entity(view_entity)
2318 .insert(PreprocessBindGroups(bind_groups));
2319 }
2320
2321 if any_indirect {
2324 create_build_indirect_parameters_bind_groups(
2325 &mut commands,
2326 &render_device,
2327 &pipeline_cache,
2328 &pipelines,
2329 current_input_buffer,
2330 &indirect_parameters_buffers,
2331 &indirect_parameters_build_jobs,
2332 );
2333 }
2334
2335 for (_, view) in &views {
2338 create_bin_unpacking_bind_groups(
2339 &mut bin_unpacking_bind_groups,
2340 &render_device,
2341 &pipeline_cache,
2342 &pipelines,
2343 &indirect_parameters_buffers,
2344 phase_instance_buffers,
2345 &scene_unpacking_buffers,
2346 &view.retained_view_entity,
2347 );
2348 create_uniform_allocation_bind_groups(
2349 &mut uniform_allocation_bind_groups,
2350 &render_device,
2351 &pipeline_cache,
2352 &pipelines,
2353 &indirect_parameters_buffers,
2354 &scene_unpacking_buffers,
2355 &view.retained_view_entity,
2356 );
2357 }
2358}
2359
2360struct PreprocessBindGroupBuilder<'a> {
2363 view: Entity,
2365 late_indexed_indirect_parameters_buffer:
2368 &'a RawBufferVec<LatePreprocessWorkItemIndirectParameters>,
2369 late_non_indexed_indirect_parameters_buffer:
2372 &'a RawBufferVec<LatePreprocessWorkItemIndirectParameters>,
2373 render_device: &'a RenderDevice,
2375 pipeline_cache: &'a PipelineCache,
2377 phase_indirect_parameters_buffers: &'a UntypedPhaseIndirectParametersBuffers,
2379 mesh_culling_data_buffer: &'a MeshCullingDataBuffer,
2381 visibility_range_data_buffer: &'a BufferVec<Vec4>,
2384 view_uniforms: &'a ViewUniforms,
2386 previous_view_uniforms: &'a PreviousViewUniforms,
2388 pipelines: &'a PreprocessPipelines,
2390 current_input_buffer: &'a Buffer,
2393 previous_input_buffer: &'a Buffer,
2396 data_buffer: &'a Buffer,
2402}
2403
2404impl<'a> PreprocessBindGroupBuilder<'a> {
2405 fn create_direct_preprocess_bind_groups(
2408 &self,
2409 work_item_buffer: &RawBufferVec<PreprocessWorkItem>,
2410 ) -> Option<PhasePreprocessBindGroups> {
2411 let work_item_buffer_size = NonZero::<u64>::try_from(
2415 work_item_buffer.len() as u64 * u64::from(PreprocessWorkItem::min_size()),
2416 )
2417 .ok();
2418
2419 Some(PhasePreprocessBindGroups::Direct(
2420 self.render_device.create_bind_group(
2421 "preprocess_direct_bind_group",
2422 &self
2423 .pipeline_cache
2424 .get_bind_group_layout(&self.pipelines.direct_preprocess.bind_group_layout),
2425 &BindGroupEntries::with_indices((
2426 (0, self.view_uniforms.uniforms.binding()?),
2427 (3, self.current_input_buffer.as_entire_binding()),
2428 (4, self.previous_input_buffer.as_entire_binding()),
2429 (
2430 5,
2431 BindingResource::Buffer(BufferBinding {
2432 buffer: work_item_buffer.buffer()?,
2433 offset: 0,
2434 size: work_item_buffer_size,
2435 }),
2436 ),
2437 (6, self.data_buffer.as_entire_binding()),
2438 )),
2439 ),
2440 ))
2441 }
2442
2443 fn create_indirect_occlusion_culling_preprocess_bind_groups(
2446 &self,
2447 view_depth_pyramids: &Query<(&ViewDepthPyramid, &PreviousViewUniformOffset)>,
2448 indexed_work_item_buffer: &PartialBufferVec<PreprocessWorkItem>,
2449 non_indexed_work_item_buffer: &PartialBufferVec<PreprocessWorkItem>,
2450 gpu_occlusion_culling_work_item_buffers: &GpuOcclusionCullingWorkItemBuffers,
2451 ) -> Option<PhasePreprocessBindGroups> {
2452 let GpuOcclusionCullingWorkItemBuffers {
2453 late_indexed: ref late_indexed_work_item_buffer,
2454 late_non_indexed: ref late_non_indexed_work_item_buffer,
2455 ..
2456 } = *gpu_occlusion_culling_work_item_buffers;
2457
2458 let (view_depth_pyramid, previous_view_uniform_offset) =
2459 view_depth_pyramids.get(self.view).ok()?;
2460
2461 Some(PhasePreprocessBindGroups::IndirectOcclusionCulling {
2462 early_indexed: self.create_indirect_occlusion_culling_early_indexed_bind_group(
2463 view_depth_pyramid,
2464 previous_view_uniform_offset,
2465 indexed_work_item_buffer,
2466 late_indexed_work_item_buffer,
2467 ),
2468
2469 early_non_indexed: self.create_indirect_occlusion_culling_early_non_indexed_bind_group(
2470 view_depth_pyramid,
2471 previous_view_uniform_offset,
2472 non_indexed_work_item_buffer,
2473 late_non_indexed_work_item_buffer,
2474 ),
2475
2476 late_indexed: self.create_indirect_occlusion_culling_late_indexed_bind_group(
2477 view_depth_pyramid,
2478 previous_view_uniform_offset,
2479 late_indexed_work_item_buffer,
2480 ),
2481
2482 late_non_indexed: self.create_indirect_occlusion_culling_late_non_indexed_bind_group(
2483 view_depth_pyramid,
2484 previous_view_uniform_offset,
2485 late_non_indexed_work_item_buffer,
2486 ),
2487 })
2488 }
2489
2490 fn create_indirect_occlusion_culling_early_indexed_bind_group(
2493 &self,
2494 view_depth_pyramid: &ViewDepthPyramid,
2495 previous_view_uniform_offset: &PreviousViewUniformOffset,
2496 indexed_work_item_buffer: &PartialBufferVec<PreprocessWorkItem>,
2497 late_indexed_work_item_buffer: &UninitBufferVec<PreprocessWorkItem>,
2498 ) -> Option<BindGroup> {
2499 let mesh_culling_data_buffer = self.mesh_culling_data_buffer.buffer()?;
2500 let visibility_range_binding = self.visibility_range_data_buffer.binding()?;
2501 let view_uniforms_binding = self.view_uniforms.uniforms.binding()?;
2502 let previous_view_buffer = self.previous_view_uniforms.uniforms.buffer()?;
2503
2504 match (
2505 self.phase_indirect_parameters_buffers
2506 .indexed
2507 .metadata_buffer(),
2508 indexed_work_item_buffer.buffer(),
2509 late_indexed_work_item_buffer.buffer(),
2510 self.late_indexed_indirect_parameters_buffer.buffer(),
2511 ) {
2512 (
2513 Some(indexed_metadata_buffer),
2514 Some(indexed_work_item_gpu_buffer),
2515 Some(late_indexed_work_item_gpu_buffer),
2516 Some(late_indexed_indirect_parameters_buffer),
2517 ) => {
2518 let indexed_work_item_buffer_size = NonZero::<u64>::try_from(
2522 indexed_work_item_buffer.len() as u64
2523 * u64::from(PreprocessWorkItem::min_size()),
2524 )
2525 .ok();
2526
2527 Some(
2528 self.render_device.create_bind_group(
2529 "preprocess_early_indexed_gpu_occlusion_culling_bind_group",
2530 &self.pipeline_cache.get_bind_group_layout(
2531 &self
2532 .pipelines
2533 .early_gpu_occlusion_culling_preprocess
2534 .bind_group_layout,
2535 ),
2536 &BindGroupEntries::with_indices((
2537 (3, self.current_input_buffer.as_entire_binding()),
2540 (4, self.previous_input_buffer.as_entire_binding()),
2543 (
2546 5,
2547 BindingResource::Buffer(BufferBinding {
2548 buffer: indexed_work_item_gpu_buffer,
2549 offset: 0,
2550 size: indexed_work_item_buffer_size,
2551 }),
2552 ),
2553 (6, self.data_buffer.as_entire_binding()),
2556 (7, indexed_metadata_buffer.as_entire_binding()),
2560 (9, mesh_culling_data_buffer.as_entire_binding()),
2563 (10, visibility_range_binding.clone()),
2566 (0, view_uniforms_binding.clone()),
2568 (11, &view_depth_pyramid.all_mips),
2571 (
2574 2,
2575 BufferBinding {
2576 buffer: previous_view_buffer,
2577 offset: previous_view_uniform_offset.offset as u64,
2578 size: NonZeroU64::new(size_of::<PreviousViewData>() as u64),
2579 },
2580 ),
2581 (
2585 12,
2586 BufferBinding {
2587 buffer: late_indexed_work_item_gpu_buffer,
2588 offset: 0,
2589 size: indexed_work_item_buffer_size,
2590 },
2591 ),
2592 (
2596 13,
2597 BufferBinding {
2598 buffer: late_indexed_indirect_parameters_buffer,
2599 offset: 0,
2600 size: NonZeroU64::new(
2601 late_indexed_indirect_parameters_buffer.size(),
2602 ),
2603 },
2604 ),
2605 )),
2606 ),
2607 )
2608 }
2609 _ => None,
2610 }
2611 }
2612
2613 fn create_indirect_occlusion_culling_early_non_indexed_bind_group(
2616 &self,
2617 view_depth_pyramid: &ViewDepthPyramid,
2618 previous_view_uniform_offset: &PreviousViewUniformOffset,
2619 non_indexed_work_item_buffer: &PartialBufferVec<PreprocessWorkItem>,
2620 late_non_indexed_work_item_buffer: &UninitBufferVec<PreprocessWorkItem>,
2621 ) -> Option<BindGroup> {
2622 let mesh_culling_data_buffer = self.mesh_culling_data_buffer.buffer()?;
2623 let visibility_range_binding = self.visibility_range_data_buffer.binding()?;
2624 let view_uniforms_binding = self.view_uniforms.uniforms.binding()?;
2625 let previous_view_buffer = self.previous_view_uniforms.uniforms.buffer()?;
2626
2627 match (
2628 self.phase_indirect_parameters_buffers
2629 .non_indexed
2630 .metadata_buffer(),
2631 non_indexed_work_item_buffer.buffer(),
2632 late_non_indexed_work_item_buffer.buffer(),
2633 self.late_non_indexed_indirect_parameters_buffer.buffer(),
2634 ) {
2635 (
2636 Some(non_indexed_metadata_buffer),
2637 Some(non_indexed_work_item_gpu_buffer),
2638 Some(late_non_indexed_work_item_buffer),
2639 Some(late_non_indexed_indirect_parameters_buffer),
2640 ) => {
2641 let non_indexed_work_item_buffer_size = NonZero::<u64>::try_from(
2645 non_indexed_work_item_buffer.len() as u64
2646 * u64::from(PreprocessWorkItem::min_size()),
2647 )
2648 .ok();
2649
2650 Some(
2651 self.render_device.create_bind_group(
2652 "preprocess_early_non_indexed_gpu_occlusion_culling_bind_group",
2653 &self.pipeline_cache.get_bind_group_layout(
2654 &self
2655 .pipelines
2656 .early_gpu_occlusion_culling_preprocess
2657 .bind_group_layout,
2658 ),
2659 &BindGroupEntries::with_indices((
2660 (3, self.current_input_buffer.as_entire_binding()),
2663 (4, self.previous_input_buffer.as_entire_binding()),
2666 (
2669 5,
2670 BindingResource::Buffer(BufferBinding {
2671 buffer: non_indexed_work_item_gpu_buffer,
2672 offset: 0,
2673 size: non_indexed_work_item_buffer_size,
2674 }),
2675 ),
2676 (6, self.data_buffer.as_entire_binding()),
2677 (7, non_indexed_metadata_buffer.as_entire_binding()),
2681 (9, mesh_culling_data_buffer.as_entire_binding()),
2684 (10, visibility_range_binding.clone()),
2687 (0, view_uniforms_binding.clone()),
2689 (11, &view_depth_pyramid.all_mips),
2692 (
2695 2,
2696 BufferBinding {
2697 buffer: previous_view_buffer,
2698 offset: previous_view_uniform_offset.offset as u64,
2699 size: NonZeroU64::new(size_of::<PreviousViewData>() as u64),
2700 },
2701 ),
2702 (
2706 12,
2707 BufferBinding {
2708 buffer: late_non_indexed_work_item_buffer,
2709 offset: 0,
2710 size: non_indexed_work_item_buffer_size,
2711 },
2712 ),
2713 (
2717 13,
2718 BufferBinding {
2719 buffer: late_non_indexed_indirect_parameters_buffer,
2720 offset: 0,
2721 size: NonZeroU64::new(
2722 late_non_indexed_indirect_parameters_buffer.size(),
2723 ),
2724 },
2725 ),
2726 )),
2727 ),
2728 )
2729 }
2730 _ => None,
2731 }
2732 }
2733
2734 fn create_indirect_occlusion_culling_late_indexed_bind_group(
2737 &self,
2738 view_depth_pyramid: &ViewDepthPyramid,
2739 previous_view_uniform_offset: &PreviousViewUniformOffset,
2740 late_indexed_work_item_buffer: &UninitBufferVec<PreprocessWorkItem>,
2741 ) -> Option<BindGroup> {
2742 let mesh_culling_data_buffer = self.mesh_culling_data_buffer.buffer()?;
2743 let visibility_range_binding = self.visibility_range_data_buffer.binding()?;
2744 let view_uniforms_binding = self.view_uniforms.uniforms.binding()?;
2745 let previous_view_buffer = self.previous_view_uniforms.uniforms.buffer()?;
2746
2747 match (
2748 self.phase_indirect_parameters_buffers
2749 .indexed
2750 .metadata_buffer(),
2751 late_indexed_work_item_buffer.buffer(),
2752 self.late_indexed_indirect_parameters_buffer.buffer(),
2753 ) {
2754 (
2755 Some(indexed_metadata_buffer),
2756 Some(late_indexed_work_item_gpu_buffer),
2757 Some(late_indexed_indirect_parameters_buffer),
2758 ) => {
2759 let late_indexed_work_item_buffer_size = NonZero::<u64>::try_from(
2763 late_indexed_work_item_buffer.len() as u64
2764 * u64::from(PreprocessWorkItem::min_size()),
2765 )
2766 .ok();
2767
2768 Some(
2769 self.render_device.create_bind_group(
2770 "preprocess_late_indexed_gpu_occlusion_culling_bind_group",
2771 &self.pipeline_cache.get_bind_group_layout(
2772 &self
2773 .pipelines
2774 .late_gpu_occlusion_culling_preprocess
2775 .bind_group_layout,
2776 ),
2777 &BindGroupEntries::with_indices((
2778 (3, self.current_input_buffer.as_entire_binding()),
2781 (4, self.previous_input_buffer.as_entire_binding()),
2784 (
2787 5,
2788 BindingResource::Buffer(BufferBinding {
2789 buffer: late_indexed_work_item_gpu_buffer,
2790 offset: 0,
2791 size: late_indexed_work_item_buffer_size,
2792 }),
2793 ),
2794 (6, self.data_buffer.as_entire_binding()),
2797 (7, indexed_metadata_buffer.as_entire_binding()),
2801 (9, mesh_culling_data_buffer.as_entire_binding()),
2804 (10, visibility_range_binding.clone()),
2807 (0, view_uniforms_binding.clone()),
2809 (11, &view_depth_pyramid.all_mips),
2812 (
2815 2,
2816 BufferBinding {
2817 buffer: previous_view_buffer,
2818 offset: previous_view_uniform_offset.offset as u64,
2819 size: NonZeroU64::new(size_of::<PreviousViewData>() as u64),
2820 },
2821 ),
2822 (
2826 13,
2827 BufferBinding {
2828 buffer: late_indexed_indirect_parameters_buffer,
2829 offset: 0,
2830 size: NonZeroU64::new(
2831 late_indexed_indirect_parameters_buffer.size(),
2832 ),
2833 },
2834 ),
2835 )),
2836 ),
2837 )
2838 }
2839 _ => None,
2840 }
2841 }
2842
2843 fn create_indirect_occlusion_culling_late_non_indexed_bind_group(
2846 &self,
2847 view_depth_pyramid: &ViewDepthPyramid,
2848 previous_view_uniform_offset: &PreviousViewUniformOffset,
2849 late_non_indexed_work_item_buffer: &UninitBufferVec<PreprocessWorkItem>,
2850 ) -> Option<BindGroup> {
2851 let mesh_culling_data_buffer = self.mesh_culling_data_buffer.buffer()?;
2852 let visibility_range_binding = self.visibility_range_data_buffer.binding()?;
2853 let view_uniforms_binding = self.view_uniforms.uniforms.binding()?;
2854 let previous_view_buffer = self.previous_view_uniforms.uniforms.buffer()?;
2855
2856 match (
2857 self.phase_indirect_parameters_buffers
2858 .non_indexed
2859 .metadata_buffer(),
2860 late_non_indexed_work_item_buffer.buffer(),
2861 self.late_non_indexed_indirect_parameters_buffer.buffer(),
2862 ) {
2863 (
2864 Some(non_indexed_metadata_buffer),
2865 Some(non_indexed_work_item_gpu_buffer),
2866 Some(late_non_indexed_indirect_parameters_buffer),
2867 ) => {
2868 let non_indexed_work_item_buffer_size = NonZero::<u64>::try_from(
2872 late_non_indexed_work_item_buffer.len() as u64
2873 * u64::from(PreprocessWorkItem::min_size()),
2874 )
2875 .ok();
2876
2877 Some(
2878 self.render_device.create_bind_group(
2879 "preprocess_late_non_indexed_gpu_occlusion_culling_bind_group",
2880 &self.pipeline_cache.get_bind_group_layout(
2881 &self
2882 .pipelines
2883 .late_gpu_occlusion_culling_preprocess
2884 .bind_group_layout,
2885 ),
2886 &BindGroupEntries::with_indices((
2887 (3, self.current_input_buffer.as_entire_binding()),
2890 (4, self.previous_input_buffer.as_entire_binding()),
2893 (
2896 5,
2897 BindingResource::Buffer(BufferBinding {
2898 buffer: non_indexed_work_item_gpu_buffer,
2899 offset: 0,
2900 size: non_indexed_work_item_buffer_size,
2901 }),
2902 ),
2903 (6, self.data_buffer.as_entire_binding()),
2906 (7, non_indexed_metadata_buffer.as_entire_binding()),
2910 (9, mesh_culling_data_buffer.as_entire_binding()),
2913 (10, visibility_range_binding.clone()),
2916 (0, view_uniforms_binding.clone()),
2918 (11, &view_depth_pyramid.all_mips),
2921 (
2924 2,
2925 BufferBinding {
2926 buffer: previous_view_buffer,
2927 offset: previous_view_uniform_offset.offset as u64,
2928 size: NonZeroU64::new(size_of::<PreviousViewData>() as u64),
2929 },
2930 ),
2931 (
2935 13,
2936 BufferBinding {
2937 buffer: late_non_indexed_indirect_parameters_buffer,
2938 offset: 0,
2939 size: NonZeroU64::new(
2940 late_non_indexed_indirect_parameters_buffer.size(),
2941 ),
2942 },
2943 ),
2944 )),
2945 ),
2946 )
2947 }
2948 _ => None,
2949 }
2950 }
2951
2952 fn create_indirect_frustum_culling_preprocess_bind_groups(
2955 &self,
2956 indexed_work_item_buffer: &PartialBufferVec<PreprocessWorkItem>,
2957 non_indexed_work_item_buffer: &PartialBufferVec<PreprocessWorkItem>,
2958 ) -> Option<PhasePreprocessBindGroups> {
2959 Some(PhasePreprocessBindGroups::IndirectFrustumCulling {
2960 indexed: self
2961 .create_indirect_frustum_culling_indexed_bind_group(indexed_work_item_buffer),
2962 non_indexed: self.create_indirect_frustum_culling_non_indexed_bind_group(
2963 non_indexed_work_item_buffer,
2964 ),
2965 })
2966 }
2967
2968 fn create_indirect_frustum_culling_indexed_bind_group(
2971 &self,
2972 indexed_work_item_buffer: &PartialBufferVec<PreprocessWorkItem>,
2973 ) -> Option<BindGroup> {
2974 let mesh_culling_data_buffer = self.mesh_culling_data_buffer.buffer()?;
2975 let visibility_range_binding = self.visibility_range_data_buffer.binding()?;
2976 let view_uniforms_binding = self.view_uniforms.uniforms.binding()?;
2977
2978 match (
2979 self.phase_indirect_parameters_buffers
2980 .indexed
2981 .metadata_buffer(),
2982 indexed_work_item_buffer.buffer(),
2983 ) {
2984 (Some(indexed_metadata_buffer), Some(indexed_work_item_gpu_buffer)) => {
2985 let indexed_work_item_buffer_size = NonZero::<u64>::try_from(
2989 indexed_work_item_buffer.len() as u64
2990 * u64::from(PreprocessWorkItem::min_size()),
2991 )
2992 .ok();
2993
2994 Some(
2995 self.render_device.create_bind_group(
2996 "preprocess_gpu_indexed_frustum_culling_bind_group",
2997 &self.pipeline_cache.get_bind_group_layout(
2998 &self
2999 .pipelines
3000 .gpu_frustum_culling_preprocess
3001 .bind_group_layout,
3002 ),
3003 &BindGroupEntries::with_indices((
3004 (3, self.current_input_buffer.as_entire_binding()),
3005 (4, self.previous_input_buffer.as_entire_binding()),
3006 (
3007 5,
3008 BindingResource::Buffer(BufferBinding {
3009 buffer: indexed_work_item_gpu_buffer,
3010 offset: 0,
3011 size: indexed_work_item_buffer_size,
3012 }),
3013 ),
3014 (6, self.data_buffer.as_entire_binding()),
3015 (7, indexed_metadata_buffer.as_entire_binding()),
3016 (9, mesh_culling_data_buffer.as_entire_binding()),
3017 (10, visibility_range_binding.clone()),
3018 (0, view_uniforms_binding.clone()),
3019 )),
3020 ),
3021 )
3022 }
3023 _ => None,
3024 }
3025 }
3026
3027 fn create_indirect_frustum_culling_non_indexed_bind_group(
3030 &self,
3031 non_indexed_work_item_buffer: &PartialBufferVec<PreprocessWorkItem>,
3032 ) -> Option<BindGroup> {
3033 let mesh_culling_data_buffer = self.mesh_culling_data_buffer.buffer()?;
3034 let visibility_range_binding = self.visibility_range_data_buffer.binding()?;
3035 let view_uniforms_binding = self.view_uniforms.uniforms.binding()?;
3036
3037 match (
3038 self.phase_indirect_parameters_buffers
3039 .non_indexed
3040 .metadata_buffer(),
3041 non_indexed_work_item_buffer.buffer(),
3042 ) {
3043 (Some(non_indexed_metadata_buffer), Some(non_indexed_work_item_gpu_buffer)) => {
3044 let non_indexed_work_item_buffer_size = NonZero::<u64>::try_from(
3048 non_indexed_work_item_buffer.len() as u64
3049 * u64::from(PreprocessWorkItem::min_size()),
3050 )
3051 .ok();
3052
3053 Some(
3054 self.render_device.create_bind_group(
3055 "preprocess_gpu_non_indexed_frustum_culling_bind_group",
3056 &self.pipeline_cache.get_bind_group_layout(
3057 &self
3058 .pipelines
3059 .gpu_frustum_culling_preprocess
3060 .bind_group_layout,
3061 ),
3062 &BindGroupEntries::with_indices((
3063 (3, self.current_input_buffer.as_entire_binding()),
3066 (4, self.previous_input_buffer.as_entire_binding()),
3069 (
3072 5,
3073 BindingResource::Buffer(BufferBinding {
3074 buffer: non_indexed_work_item_gpu_buffer,
3075 offset: 0,
3076 size: non_indexed_work_item_buffer_size,
3077 }),
3078 ),
3079 (6, self.data_buffer.as_entire_binding()),
3082 (7, non_indexed_metadata_buffer.as_entire_binding()),
3086 (9, mesh_culling_data_buffer.as_entire_binding()),
3089 (10, visibility_range_binding.clone()),
3092 (0, view_uniforms_binding.clone()),
3094 )),
3095 ),
3096 )
3097 }
3098 _ => None,
3099 }
3100 }
3101}
3102
3103fn create_build_indirect_parameters_bind_groups(
3107 commands: &mut Commands,
3108 render_device: &RenderDevice,
3109 pipeline_cache: &PipelineCache,
3110 pipelines: &PreprocessPipelines,
3111 current_input_buffer: &Buffer,
3112 indirect_parameters_buffers: &IndirectParametersBuffers,
3113 indirect_parameters_build_jobs: &IndirectParametersBuildJobs,
3114) {
3115 let mut build_indirect_parameters_bind_groups = BuildIndirectParametersBindGroups::new();
3116
3117 for (phase_type_id, phase_indirect_parameters_buffer) in indirect_parameters_buffers.iter() {
3118 build_indirect_parameters_bind_groups.insert(
3119 *phase_type_id,
3120 PhaseBuildIndirectParametersBindGroups {
3121 reset_indexed_indirect_batch_sets: phase_indirect_parameters_buffer
3122 .indexed
3123 .batch_sets_buffer()
3124 .map(|indexed_batch_sets_buffer| {
3125 render_device.create_bind_group(
3126 "reset_indexed_indirect_batch_sets_bind_group",
3127 &pipeline_cache.get_bind_group_layout(
3130 &pipelines
3131 .early_phase
3132 .reset_indirect_batch_sets
3133 .bind_group_layout,
3134 ),
3135 &BindGroupEntries::sequential((
3136 indexed_batch_sets_buffer.as_entire_binding(),
3137 )),
3138 )
3139 }),
3140
3141 reset_non_indexed_indirect_batch_sets: phase_indirect_parameters_buffer
3142 .non_indexed
3143 .batch_sets_buffer()
3144 .map(|non_indexed_batch_sets_buffer| {
3145 render_device.create_bind_group(
3146 "reset_non_indexed_indirect_batch_sets_bind_group",
3147 &pipeline_cache.get_bind_group_layout(
3150 &pipelines
3151 .early_phase
3152 .reset_indirect_batch_sets
3153 .bind_group_layout,
3154 ),
3155 &BindGroupEntries::sequential((
3156 non_indexed_batch_sets_buffer.as_entire_binding(),
3157 )),
3158 )
3159 }),
3160
3161 build_indexed_indirect: match (
3162 phase_indirect_parameters_buffer.indexed.metadata_buffer(),
3163 phase_indirect_parameters_buffer.indexed.data_buffer(),
3164 phase_indirect_parameters_buffer.indexed.batch_sets_buffer(),
3165 indirect_parameters_build_jobs.buffer(),
3166 ) {
3167 (
3168 Some(indexed_indirect_parameters_metadata_buffer),
3169 Some(indexed_indirect_parameters_data_buffer),
3170 Some(indexed_batch_sets_buffer),
3171 Some(indirect_parameters_build_job_buffer),
3172 ) => Some(
3173 render_device.create_bind_group(
3174 "build_indexed_indirect_parameters_bind_group",
3175 &pipeline_cache.get_bind_group_layout(
3178 &pipelines
3179 .gpu_frustum_culling_build_indexed_indirect_params
3180 .bind_group_layout,
3181 ),
3182 &BindGroupEntries::with_indices((
3183 (0, current_input_buffer.as_entire_binding()),
3186 (
3190 1,
3191 indexed_indirect_parameters_metadata_buffer.as_entire_binding(),
3192 ),
3193 (3, indexed_batch_sets_buffer.as_entire_binding()),
3197 (
3201 4,
3202 BindingResource::Buffer(BufferBinding {
3203 buffer: indirect_parameters_build_job_buffer,
3204 offset: 0,
3205 size: NonZeroU64::new(
3206 size_of::<IndirectParametersBuildJob>() as u64,
3207 ),
3208 }),
3209 ),
3210 (
3214 5,
3215 indexed_indirect_parameters_data_buffer.as_entire_binding(),
3216 ),
3217 )),
3218 ),
3219 ),
3220 _ => None,
3221 },
3222
3223 build_non_indexed_indirect: match (
3224 phase_indirect_parameters_buffer
3225 .non_indexed
3226 .metadata_buffer(),
3227 phase_indirect_parameters_buffer.non_indexed.data_buffer(),
3228 phase_indirect_parameters_buffer
3229 .non_indexed
3230 .batch_sets_buffer(),
3231 indirect_parameters_build_jobs.buffer(),
3232 ) {
3233 (
3234 Some(non_indexed_indirect_parameters_metadata_buffer),
3235 Some(non_indexed_indirect_parameters_data_buffer),
3236 Some(non_indexed_batch_sets_buffer),
3237 Some(indirect_parameters_build_job_buffer),
3238 ) => Some(
3239 render_device.create_bind_group(
3240 "build_non_indexed_indirect_parameters_bind_group",
3241 &pipeline_cache.get_bind_group_layout(
3244 &pipelines
3245 .gpu_frustum_culling_build_non_indexed_indirect_params
3246 .bind_group_layout,
3247 ),
3248 &BindGroupEntries::with_indices((
3249 (0, current_input_buffer.as_entire_binding()),
3252 (
3259 1,
3260 BufferBinding {
3261 buffer: non_indexed_indirect_parameters_metadata_buffer,
3262 offset: 0,
3263 size: NonZeroU64::new(
3264 phase_indirect_parameters_buffer
3265 .non_indexed
3266 .batch_count()
3267 as u64
3268 * size_of::<IndirectParametersMetadata>() as u64,
3269 ),
3270 },
3271 ),
3272 (3, non_indexed_batch_sets_buffer.as_entire_binding()),
3276 (
3280 4,
3281 BindingResource::Buffer(BufferBinding {
3282 buffer: indirect_parameters_build_job_buffer,
3283 offset: 0,
3284 size: NonZeroU64::new(
3285 size_of::<IndirectParametersBuildJob>() as u64,
3286 ),
3287 }),
3288 ),
3289 (
3293 5,
3294 non_indexed_indirect_parameters_data_buffer.as_entire_binding(),
3295 ),
3296 )),
3297 ),
3298 ),
3299 _ => None,
3300 },
3301 },
3302 );
3303 }
3304
3305 commands.insert_resource(build_indirect_parameters_bind_groups);
3306}
3307
3308fn create_bin_unpacking_bind_groups(
3311 bin_unpacking_bind_groups: &mut BinUnpackingBindGroups,
3312 render_device: &RenderDevice,
3313 pipeline_cache: &PipelineCache,
3314 preprocess_pipelines: &PreprocessPipelines,
3315 indirect_parameters_buffers: &IndirectParametersBuffers,
3316 phase_instance_buffers: &TypeIdHashMap<UntypedPhaseBatchedInstanceBuffers<MeshUniform>>,
3317 scene_unpacking_buffers: &SceneUnpackingBuffers,
3318 view_entity: &RetainedViewEntity,
3319) {
3320 let Some(bin_unpacking_metadata_buffer) =
3321 scene_unpacking_buffers.bin_unpacking_metadata.buffer()
3322 else {
3323 return;
3324 };
3325
3326 for phase_type_id in indirect_parameters_buffers.keys() {
3328 let Some(phase_batched_instance_buffers) = phase_instance_buffers.get(phase_type_id) else {
3330 continue;
3331 };
3332 let Some(work_item_buffers) = phase_batched_instance_buffers
3333 .work_item_buffers
3334 .get(view_entity)
3335 else {
3336 continue;
3337 };
3338 let Some(view_phase_bin_unpacking_buffers) = scene_unpacking_buffers
3339 .view_phase_buffers
3340 .get(&SceneUnpackingBuffersKey {
3341 phase: *phase_type_id,
3342 view: *view_entity,
3343 })
3344 else {
3345 continue;
3346 };
3347
3348 let maybe_indexed_work_item_buffer = match *work_item_buffers {
3350 PreprocessWorkItemBuffers::Direct(ref raw_buffer_vec) => raw_buffer_vec.buffer(),
3351 PreprocessWorkItemBuffers::Indirect { ref indexed, .. } => indexed.buffer(),
3352 };
3353 let maybe_non_indexed_work_item_buffer = match *work_item_buffers {
3354 PreprocessWorkItemBuffers::Direct(ref raw_buffer_vec) => raw_buffer_vec.buffer(),
3355 PreprocessWorkItemBuffers::Indirect {
3356 ref non_indexed, ..
3357 } => non_indexed.buffer(),
3358 };
3359
3360 bin_unpacking_bind_groups.insert(
3362 SceneUnpackingBuffersKey {
3363 phase: *phase_type_id,
3364 view: *view_entity,
3365 },
3366 ViewPhaseBinUnpackingBindGroups {
3367 indexed: match maybe_indexed_work_item_buffer {
3368 Some(indexed_work_item_buffer) => view_phase_bin_unpacking_buffers
3369 .indexed_unpacking_jobs
3370 .iter()
3371 .map(|job| {
3372 create_bin_unpacking_bind_group(
3373 render_device,
3374 preprocess_pipelines,
3375 pipeline_cache,
3376 job,
3377 bin_unpacking_metadata_buffer,
3378 indexed_work_item_buffer,
3379 true,
3380 )
3381 })
3382 .collect(),
3383 None => vec![],
3384 },
3385 non_indexed: match maybe_non_indexed_work_item_buffer {
3386 Some(non_indexed_work_item_buffer) => view_phase_bin_unpacking_buffers
3387 .non_indexed_unpacking_jobs
3388 .iter()
3389 .map(|job| {
3390 create_bin_unpacking_bind_group(
3391 render_device,
3392 preprocess_pipelines,
3393 pipeline_cache,
3394 job,
3395 bin_unpacking_metadata_buffer,
3396 non_indexed_work_item_buffer,
3397 false,
3398 )
3399 })
3400 .collect(),
3401 None => vec![],
3402 },
3403 },
3404 );
3405 }
3406}
3407
3408fn create_bin_unpacking_bind_group(
3411 render_device: &RenderDevice,
3412 preprocess_pipelines: &PreprocessPipelines,
3413 pipeline_cache: &PipelineCache,
3414 job: &SceneUnpackingJob,
3415 bin_unpacking_metadata_buffer: &Buffer,
3416 work_item_buffer: &Buffer,
3417 indexed: bool,
3418) -> ViewPhaseBinUnpackingBindGroup {
3419 let bind_group = render_device.create_bind_group(
3420 if indexed {
3421 "bin unpacking indexed bind group"
3422 } else {
3423 "bin unpacking non-indexed bind group"
3424 },
3425 &pipeline_cache
3426 .get_bind_group_layout(&preprocess_pipelines.bin_unpacking.bind_group_layout),
3427 &BindGroupEntries::sequential((
3428 BindingResource::Buffer(BufferBinding {
3432 buffer: bin_unpacking_metadata_buffer,
3433 offset: job.bin_unpacking_metadata_index.uniform_offset() as u64,
3434 size: NonZeroU64::new(size_of::<GpuBinUnpackingMetadata>() as u64),
3435 }),
3436 job.render_binned_mesh_instance_buffer.as_entire_binding(),
3440 work_item_buffer.as_entire_binding(),
3444 job.bin_metadata_buffer.as_entire_binding(),
3447 job.bin_index_to_bin_metadata_index_buffer
3450 .as_entire_binding(),
3451 )),
3452 );
3453 ViewPhaseBinUnpackingBindGroup {
3454 metadata_index: job.bin_unpacking_metadata_index,
3455 bind_group,
3456 mesh_instance_count: job.mesh_instance_count,
3457 }
3458}
3459
3460fn create_uniform_allocation_bind_groups(
3463 uniform_allocation_bind_groups: &mut UniformAllocationBindGroups,
3464 render_device: &RenderDevice,
3465 pipeline_cache: &PipelineCache,
3466 preprocess_pipelines: &PreprocessPipelines,
3467 indirect_parameters_buffers: &IndirectParametersBuffers,
3468 scene_unpacking_buffers: &SceneUnpackingBuffers,
3469 view_entity: &RetainedViewEntity,
3470) {
3471 let Some(uniform_allocation_metadata_buffer) =
3472 scene_unpacking_buffers.uniform_allocation_metadata.buffer()
3473 else {
3474 return;
3475 };
3476
3477 for (phase_type_id, phase_indirect_parameters_buffers) in indirect_parameters_buffers.iter() {
3478 let Some(view_phase_bin_unpacking_buffers) = scene_unpacking_buffers
3479 .view_phase_buffers
3480 .get(&SceneUnpackingBuffersKey {
3481 phase: *phase_type_id,
3482 view: *view_entity,
3483 })
3484 else {
3485 continue;
3486 };
3487
3488 uniform_allocation_bind_groups.insert(
3490 SceneUnpackingBuffersKey {
3491 phase: *phase_type_id,
3492 view: *view_entity,
3493 },
3494 ViewPhaseUniformAllocationBindGroups {
3495 indexed: match phase_indirect_parameters_buffers.indexed.metadata_buffer() {
3496 None => vec![],
3497 Some(indexed_indirect_parameters_metadata_buffer) => {
3498 view_phase_bin_unpacking_buffers
3499 .indexed_unpacking_jobs
3500 .iter()
3501 .map(|job| {
3502 create_uniform_allocation_bind_group(
3503 render_device,
3504 preprocess_pipelines,
3505 pipeline_cache,
3506 job,
3507 uniform_allocation_metadata_buffer,
3508 indexed_indirect_parameters_metadata_buffer,
3509 true,
3510 )
3511 })
3512 .collect()
3513 }
3514 },
3515 non_indexed: match phase_indirect_parameters_buffers
3516 .non_indexed
3517 .metadata_buffer()
3518 {
3519 None => vec![],
3520 Some(non_indexed_indirect_parameters_metadata_buffer) => {
3521 view_phase_bin_unpacking_buffers
3522 .non_indexed_unpacking_jobs
3523 .iter()
3524 .map(|job| {
3525 create_uniform_allocation_bind_group(
3526 render_device,
3527 preprocess_pipelines,
3528 pipeline_cache,
3529 job,
3530 uniform_allocation_metadata_buffer,
3531 non_indexed_indirect_parameters_metadata_buffer,
3532 false,
3533 )
3534 })
3535 .collect()
3536 }
3537 },
3538 },
3539 );
3540 }
3541}
3542
3543fn create_uniform_allocation_bind_group(
3546 render_device: &RenderDevice,
3547 preprocess_pipelines: &PreprocessPipelines,
3548 pipeline_cache: &PipelineCache,
3549 job: &SceneUnpackingJob,
3550 uniform_allocation_metadata_buffer: &Buffer,
3551 indirect_parameters_metadata_buffer: &Buffer,
3552 indexed: bool,
3553) -> ViewPhaseUniformAllocationBindGroup {
3554 let bind_group = render_device.create_bind_group(
3555 if indexed {
3556 "uniform allocation indexed bind group"
3557 } else {
3558 "uniform allocation non-indexed bind group"
3559 },
3560 &pipeline_cache.get_bind_group_layout(
3561 &preprocess_pipelines
3563 .uniform_allocation
3564 .local_scan
3565 .bind_group_layout,
3566 ),
3567 &BindGroupEntries::sequential((
3568 BindingResource::Buffer(BufferBinding {
3571 buffer: uniform_allocation_metadata_buffer,
3572 offset: job.uniform_allocation_metadata_index.uniform_offset() as u64,
3573 size: NonZeroU64::new(size_of::<GpuUniformAllocationMetadata>() as u64),
3574 }),
3575 job.bin_metadata_buffer.as_entire_binding(),
3578 indirect_parameters_metadata_buffer.as_entire_binding(),
3581 job.fan_buffer.as_entire_binding(),
3584 )),
3585 );
3586 ViewPhaseUniformAllocationBindGroup {
3587 metadata_index: job.uniform_allocation_metadata_index,
3588 bind_group,
3589 bin_count: job.bin_count,
3590 }
3591}
3592
3593pub fn write_mesh_culling_data_buffer(
3595 render_device: Res<RenderDevice>,
3596 render_queue: Res<RenderQueue>,
3597 mut mesh_culling_data_buffer: ResMut<MeshCullingDataBuffer>,
3598 pipeline_cache: Res<PipelineCache>,
3599 mut sparse_buffer_update_jobs: ResMut<SparseBufferUpdateJobs>,
3600 mut sparse_buffer_update_bind_groups: ResMut<SparseBufferUpdateBindGroups>,
3601 sparse_buffer_update_pipelines: Res<SparseBufferUpdatePipelines>,
3602) {
3603 mesh_culling_data_buffer.write_buffers(&render_device, &render_queue);
3604 mesh_culling_data_buffer.prepare_to_populate_buffers(
3605 &render_device,
3606 &pipeline_cache,
3607 &mut sparse_buffer_update_jobs,
3608 &mut sparse_buffer_update_bind_groups,
3609 &sparse_buffer_update_pipelines,
3610 );
3611}