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_bin_unpacking_buffers, BatchedInstanceBuffers, BinUnpackingBuffers,
40 BinUnpackingBuffersKey, BinUnpackingJob, BinUnpackingMetadataIndex,
41 GpuBinUnpackingMetadata, GpuOcclusionCullingWorkItemBuffers, GpuPreprocessingMode,
42 GpuPreprocessingSupport, IndirectBatchSet, IndirectParametersBuffers,
43 IndirectParametersCpuMetadata, IndirectParametersGpuMetadata, IndirectParametersIndexed,
44 IndirectParametersNonIndexed, LatePreprocessWorkItemIndirectParameters, PreprocessWorkItem,
45 PreprocessWorkItemBuffers, UntypedPhaseBatchedInstanceBuffers,
46 UntypedPhaseIndirectParametersBuffers,
47 },
48 diagnostic::RecordDiagnostics as _,
49 occlusion_culling::OcclusionCulling,
50 render_phase::GpuRenderBinnedMeshInstance,
51 render_resource::{
52 binding_types::{storage_buffer, storage_buffer_read_only, texture_2d, uniform_buffer},
53 BindGroup, BindGroupEntries, BindGroupLayoutDescriptor, BindGroupLayoutEntries,
54 BindingResource, Buffer, BufferBinding, BufferVec, CachedComputePipelineId,
55 ComputePassDescriptor, ComputePipelineDescriptor, DynamicBindGroupLayoutEntries,
56 PartialBufferVec, PipelineCache, RawBufferVec, ShaderStages, ShaderType,
57 SparseBufferUpdateBindGroups, SparseBufferUpdateJobs, SparseBufferUpdatePipelines,
58 SpecializedComputePipeline, SpecializedComputePipelines, TextureSampleType,
59 UninitBufferVec,
60 },
61 renderer::{RenderContext, RenderDevice, RenderQueue, ViewQuery},
62 settings::WgpuFeatures,
63 view::{
64 ExtractedView, NoIndirectDrawing, RenderVisibilityRanges, RetainedViewEntity, ViewUniform,
65 ViewUniformOffset, ViewUniforms,
66 },
67 GpuResourceAppExt, Render, RenderApp, RenderSystems,
68};
69use bevy_shader::Shader;
70use bevy_utils::{default, TypeIdMap};
71use bitflags::bitflags;
72use smallvec::{smallvec, SmallVec};
73use tracing::warn;
74
75use crate::{LightEntity, MeshCullingData, MeshCullingDataBuffer, MeshInputUniform, MeshUniform};
76
77use super::{ShadowView, ViewLightEntities};
78
79const WORKGROUP_SIZE: usize = 64;
81
82pub struct GpuMeshPreprocessPlugin {
87 pub use_gpu_instance_buffer_builder: bool,
92}
93
94#[derive(Resource)]
97pub struct PreprocessPipelines {
98 pub direct_preprocess: PreprocessPipeline,
101 pub gpu_frustum_culling_preprocess: PreprocessPipeline,
106 pub early_gpu_occlusion_culling_preprocess: PreprocessPipeline,
111 pub late_gpu_occlusion_culling_preprocess: PreprocessPipeline,
116 pub gpu_frustum_culling_build_indexed_indirect_params: BuildIndirectParametersPipeline,
119 pub gpu_frustum_culling_build_non_indexed_indirect_params: BuildIndirectParametersPipeline,
123 pub early_phase: PreprocessPhasePipelines,
126 pub late_phase: PreprocessPhasePipelines,
130 pub main_phase: PreprocessPhasePipelines,
132 pub bin_unpacking: BinUnpackingPipeline,
134}
135
136#[derive(Clone)]
140pub struct PreprocessPhasePipelines {
141 pub reset_indirect_batch_sets: ResetIndirectBatchSetsPipeline,
144 pub gpu_occlusion_culling_build_indexed_indirect_params: BuildIndirectParametersPipeline,
149 pub gpu_occlusion_culling_build_non_indexed_indirect_params: BuildIndirectParametersPipeline,
154}
155
156pub struct PreprocessPipeline {
158 pub bind_group_layout: BindGroupLayoutDescriptor,
160 pub shader: Handle<Shader>,
162 pub pipeline_id: Option<CachedComputePipelineId>,
166}
167
168#[derive(Clone)]
173pub struct ResetIndirectBatchSetsPipeline {
174 pub bind_group_layout: BindGroupLayoutDescriptor,
176 pub shader: Handle<Shader>,
178 pub pipeline_id: Option<CachedComputePipelineId>,
182}
183
184#[derive(Clone)]
186pub struct BuildIndirectParametersPipeline {
187 pub bind_group_layout: BindGroupLayoutDescriptor,
189 pub shader: Handle<Shader>,
191 pub pipeline_id: Option<CachedComputePipelineId>,
195}
196
197#[derive(Clone)]
199pub struct BinUnpackingPipeline {
200 pub bind_group_layout: BindGroupLayoutDescriptor,
202 pub shader: Handle<Shader>,
204 pub pipeline_id: Option<CachedComputePipelineId>,
208}
209
210bitflags! {
211 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
213 pub struct PreprocessPipelineKey: u8 {
214 const FRUSTUM_CULLING = 1;
218 const OCCLUSION_CULLING = 2;
222 const EARLY_PHASE = 4;
226 }
227
228 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
230 pub struct BuildIndirectParametersPipelineKey: u8 {
231 const INDEXED = 1;
236 const MULTI_DRAW_INDIRECT_COUNT_SUPPORTED = 2;
240 const OCCLUSION_CULLING = 4;
244 const EARLY_PHASE = 8;
248 const LATE_PHASE = 16;
252 const MAIN_PHASE = 32;
258 }
259}
260
261#[derive(Component, Clone, Deref, DerefMut)]
268pub struct PreprocessBindGroups(pub TypeIdMap<PhasePreprocessBindGroups>);
269
270#[derive(Clone)]
273pub enum PhasePreprocessBindGroups {
274 Direct(BindGroup),
280
281 IndirectFrustumCulling {
287 indexed: Option<BindGroup>,
289 non_indexed: Option<BindGroup>,
291 },
292
293 IndirectOcclusionCulling {
301 early_indexed: Option<BindGroup>,
304 early_non_indexed: Option<BindGroup>,
307 late_indexed: Option<BindGroup>,
310 late_non_indexed: Option<BindGroup>,
313 },
314}
315
316#[derive(Resource, Default, Deref, DerefMut)]
322pub struct BuildIndirectParametersBindGroups(pub TypeIdMap<PhaseBuildIndirectParametersBindGroups>);
323
324impl BuildIndirectParametersBindGroups {
325 pub fn new() -> BuildIndirectParametersBindGroups {
327 Self::default()
328 }
329}
330
331pub struct PhaseBuildIndirectParametersBindGroups {
334 reset_indexed_indirect_batch_sets: Option<BindGroup>,
337 reset_non_indexed_indirect_batch_sets: Option<BindGroup>,
340 build_indexed_indirect: Option<BindGroup>,
343 build_non_indexed_indirect: Option<BindGroup>,
346}
347
348#[derive(Clone, Resource, Default, Deref, DerefMut)]
354pub struct BinUnpackingBindGroups(
355 pub HashMap<BinUnpackingBuffersKey, ViewPhaseBinUnpackingBindGroups>,
356);
357
358#[derive(Clone)]
361pub struct ViewPhaseBinUnpackingBindGroups {
362 indexed: Vec<ViewPhaseBinUnpackingBindGroup>,
364 non_indexed: Vec<ViewPhaseBinUnpackingBindGroup>,
366}
367
368#[derive(Clone)]
371pub struct ViewPhaseBinUnpackingBindGroup {
372 pub metadata_index: BinUnpackingMetadataIndex,
375 pub bind_group: BindGroup,
377 pub mesh_instance_count: u32,
380}
381
382#[derive(Component, Default)]
385pub struct SkipGpuPreprocess;
386
387type WithAnyPrepass = Or<(
388 With<DepthPrepass>,
389 With<NormalPrepass>,
390 With<MotionVectorPrepass>,
391 With<DeferredPrepass>,
392)>;
393
394impl Plugin for GpuMeshPreprocessPlugin {
395 fn build(&self, app: &mut App) {
396 embedded_asset!(app, "mesh_preprocess.wgsl");
397 embedded_asset!(app, "reset_indirect_batch_sets.wgsl");
398 embedded_asset!(app, "build_indirect_params.wgsl");
399 embedded_asset!(app, "unpack_bins.wgsl");
400 }
401
402 fn finish(&self, app: &mut App) {
403 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
404 return;
405 };
406
407 let gpu_preprocessing_support = render_app.world().resource::<GpuPreprocessingSupport>();
410 if !self.use_gpu_instance_buffer_builder || !gpu_preprocessing_support.is_available() {
411 return;
412 }
413
414 render_app
415 .init_gpu_resource::<BinUnpackingBindGroups>()
416 .init_gpu_resource::<PreprocessPipelines>()
417 .init_gpu_resource::<SpecializedComputePipelines<PreprocessPipeline>>()
418 .init_gpu_resource::<SpecializedComputePipelines<ResetIndirectBatchSetsPipeline>>()
419 .init_gpu_resource::<SpecializedComputePipelines<BuildIndirectParametersPipeline>>()
420 .init_gpu_resource::<SpecializedComputePipelines<BinUnpackingPipeline>>()
421 .add_systems(
422 Render,
423 (
424 clear_bin_unpacking_buffers.in_set(RenderSystems::PrepareResources),
425 prepare_preprocess_pipelines.in_set(RenderSystems::Prepare),
426 prepare_preprocess_bind_groups
427 .run_if(resource_exists::<BatchedInstanceBuffers<
428 MeshUniform,
429 MeshInputUniform
430 >>)
431 .in_set(RenderSystems::PrepareBindGroups)
432 .after(prepare_preprocess_pipelines),
433 write_mesh_culling_data_buffer.in_set(RenderSystems::PrepareResourcesFlush),
434 ),
435 )
436 .add_systems(
437 Core3d,
438 (
439 (
440 clear_indirect_parameters_metadata,
441 unpack_bins,
442 early_gpu_preprocess,
443 early_prepass_build_indirect_parameters.run_if(any_match_filter::<(
444 With<PreprocessBindGroups>,
445 Without<SkipGpuPreprocess>,
446 Without<NoIndirectDrawing>,
447 Or<(WithAnyPrepass, With<ShadowView>)>,
448 )>),
449 )
450 .chain()
451 .before(early_prepass),
452 (
453 late_gpu_preprocess,
454 late_prepass_build_indirect_parameters.run_if(any_match_filter::<(
455 With<PreprocessBindGroups>,
456 Without<SkipGpuPreprocess>,
457 Without<NoIndirectDrawing>,
458 Or<(WithAnyPrepass, With<ShadowView>)>,
459 With<OcclusionCulling>,
460 )>),
461 )
462 .chain()
463 .after(early_downsample_depth)
464 .before(late_prepass),
465 main_build_indirect_parameters
466 .run_if(any_match_filter::<(
467 With<PreprocessBindGroups>,
468 Without<SkipGpuPreprocess>,
469 Without<NoIndirectDrawing>,
470 )>)
471 .after(late_prepass_build_indirect_parameters)
472 .after(late_deferred_prepass)
473 .before(Core3dSystems::MainPass),
474 ),
475 );
476 }
477}
478
479pub fn clear_indirect_parameters_metadata(
480 indirect_parameters_buffers: Option<Res<IndirectParametersBuffers>>,
481 mut ctx: RenderContext,
482) {
483 let Some(indirect_parameters_buffers) = indirect_parameters_buffers else {
484 return;
485 };
486
487 for phase_indirect_parameters_buffers in indirect_parameters_buffers.values() {
489 if let Some(indexed_gpu_metadata_buffer) = phase_indirect_parameters_buffers
490 .indexed
491 .gpu_metadata_buffer()
492 {
493 ctx.command_encoder().clear_buffer(
494 indexed_gpu_metadata_buffer,
495 0,
496 Some(
497 phase_indirect_parameters_buffers.indexed.batch_count() as u64
498 * size_of::<IndirectParametersGpuMetadata>() as u64,
499 ),
500 );
501 }
502
503 if let Some(non_indexed_gpu_metadata_buffer) = phase_indirect_parameters_buffers
504 .non_indexed
505 .gpu_metadata_buffer()
506 {
507 ctx.command_encoder().clear_buffer(
508 non_indexed_gpu_metadata_buffer,
509 0,
510 Some(
511 phase_indirect_parameters_buffers.non_indexed.batch_count() as u64
512 * size_of::<IndirectParametersGpuMetadata>() as u64,
513 ),
514 );
515 }
516 }
517}
518
519pub fn unpack_bins(
527 current_view: ViewQuery<Option<&ViewLightEntities>, Without<SkipGpuPreprocess>>,
528 view_query: Query<&ExtractedView, Without<SkipGpuPreprocess>>,
529 light_query: Query<&LightEntity>,
530 batched_instance_buffers: Res<BatchedInstanceBuffers<MeshUniform, MeshInputUniform>>,
531 pipeline_cache: Res<PipelineCache>,
532 preprocess_pipelines: Res<PreprocessPipelines>,
533 bin_unpacking_bind_groups: Res<BinUnpackingBindGroups>,
534 mut render_context: RenderContext,
535) {
536 let diagnostics = render_context.diagnostic_recorder();
537 let diagnostics = diagnostics.as_deref();
538
539 let command_encoder = render_context.command_encoder();
540 let mut compute_pass = command_encoder.begin_compute_pass(&ComputePassDescriptor {
541 label: Some("bin unpacking"),
542 timestamp_writes: None,
543 });
544
545 let pass_span = diagnostics.pass_span(&mut compute_pass, "bin_unpacking");
546
547 let view_entity = current_view.entity();
549 let shadow_cascade_views = current_view.into_inner();
550 let all_views =
551 gather_shadow_cascades_for_view(view_entity, shadow_cascade_views, &light_query);
552
553 if let Some(bin_unpacking_pipeline_id) = preprocess_pipelines.bin_unpacking.pipeline_id
555 && let Some(bin_unpacking_pipeline) =
556 pipeline_cache.get_compute_pipeline(bin_unpacking_pipeline_id)
557 {
558 compute_pass.set_pipeline(bin_unpacking_pipeline);
559
560 for view_entity in all_views {
562 let Ok(view) = view_query.get(view_entity) else {
563 continue;
564 };
565
566 for phase_type_id in batched_instance_buffers.phase_instance_buffers.keys() {
568 let bin_unpacking_buffers_key = BinUnpackingBuffersKey {
569 phase: *phase_type_id,
570 view: view.retained_view_entity,
571 };
572
573 let Some(phase_bin_unpacking_bind_groups) =
575 bin_unpacking_bind_groups.get(&bin_unpacking_buffers_key)
576 else {
577 continue;
578 };
579
580 for bin_unpacking_bind_group in phase_bin_unpacking_bind_groups
584 .indexed
585 .iter()
586 .chain(phase_bin_unpacking_bind_groups.non_indexed.iter())
587 {
588 compute_pass.set_bind_group(0, &bin_unpacking_bind_group.bind_group, &[]);
589 let workgroup_count = (bin_unpacking_bind_group.mesh_instance_count as usize)
590 .div_ceil(WORKGROUP_SIZE);
591 if workgroup_count > 0 {
592 compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
593 }
594 }
595 }
596 }
597 }
598
599 pass_span.end(&mut compute_pass);
600}
601
602pub fn early_gpu_preprocess(
603 current_view: ViewQuery<Option<&ViewLightEntities>, Without<SkipGpuPreprocess>>,
604 view_query: Query<
605 (
606 &ExtractedView,
607 Option<&PreprocessBindGroups>,
608 Option<&ViewUniformOffset>,
609 Has<NoIndirectDrawing>,
610 Has<OcclusionCulling>,
611 ),
612 Without<SkipGpuPreprocess>,
613 >,
614 light_query: Query<&LightEntity>,
615 batched_instance_buffers: Res<BatchedInstanceBuffers<MeshUniform, MeshInputUniform>>,
616 pipeline_cache: Res<PipelineCache>,
617 preprocess_pipelines: Res<PreprocessPipelines>,
618 mut ctx: RenderContext,
619) {
620 let diagnostics = ctx.diagnostic_recorder();
621 let diagnostics = diagnostics.as_deref();
622
623 let command_encoder = ctx.command_encoder();
624
625 let mut compute_pass = command_encoder.begin_compute_pass(&ComputePassDescriptor {
626 label: Some("early_mesh_preprocessing"),
627 timestamp_writes: None,
628 });
629
630 let pass_span = diagnostics.pass_span(&mut compute_pass, "early_mesh_preprocessing");
631
632 let view_entity = current_view.entity();
633 let shadow_cascade_views = current_view.into_inner();
634 let all_views =
635 gather_shadow_cascades_for_view(view_entity, shadow_cascade_views, &light_query);
636
637 for view_entity in all_views {
639 let Ok((view, bind_groups, view_uniform_offset, no_indirect_drawing, occlusion_culling)) =
640 view_query.get(view_entity)
641 else {
642 continue;
643 };
644
645 let Some(bind_groups) = bind_groups else {
646 continue;
647 };
648 let Some(view_uniform_offset) = view_uniform_offset else {
649 continue;
650 };
651
652 let maybe_pipeline_id = if no_indirect_drawing {
655 preprocess_pipelines.direct_preprocess.pipeline_id
656 } else if occlusion_culling {
657 preprocess_pipelines
658 .early_gpu_occlusion_culling_preprocess
659 .pipeline_id
660 } else {
661 preprocess_pipelines
662 .gpu_frustum_culling_preprocess
663 .pipeline_id
664 };
665
666 let Some(preprocess_pipeline_id) = maybe_pipeline_id else {
668 warn!("The build mesh uniforms pipeline wasn't ready");
669 continue;
670 };
671
672 let Some(preprocess_pipeline) = pipeline_cache.get_compute_pipeline(preprocess_pipeline_id)
673 else {
674 continue;
676 };
677
678 compute_pass.set_pipeline(preprocess_pipeline);
679
680 for (phase_type_id, batched_phase_instance_buffers) in
682 &batched_instance_buffers.phase_instance_buffers
683 {
684 let Some(work_item_buffers) = batched_phase_instance_buffers
686 .work_item_buffers
687 .get(&view.retained_view_entity)
688 else {
689 continue;
690 };
691
692 let Some(phase_bind_groups) = bind_groups.get(phase_type_id) else {
694 continue;
695 };
696
697 let dynamic_offsets = [view_uniform_offset.offset];
701
702 match *phase_bind_groups {
704 PhasePreprocessBindGroups::Direct(ref bind_group) => {
705 let PreprocessWorkItemBuffers::Direct(work_item_buffer) = work_item_buffers
708 else {
709 continue;
710 };
711 compute_pass.set_bind_group(0, bind_group, &dynamic_offsets);
712 let workgroup_count = work_item_buffer.len().div_ceil(WORKGROUP_SIZE);
713 if workgroup_count > 0 {
714 compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
715 }
716 }
717
718 PhasePreprocessBindGroups::IndirectFrustumCulling {
719 indexed: ref maybe_indexed_bind_group,
720 non_indexed: ref maybe_non_indexed_bind_group,
721 }
722 | PhasePreprocessBindGroups::IndirectOcclusionCulling {
723 early_indexed: ref maybe_indexed_bind_group,
724 early_non_indexed: ref maybe_non_indexed_bind_group,
725 ..
726 } => {
727 let PreprocessWorkItemBuffers::Indirect {
730 indexed: indexed_buffer,
731 non_indexed: non_indexed_buffer,
732 ..
733 } = work_item_buffers
734 else {
735 continue;
736 };
737
738 if let Some(indexed_bind_group) = maybe_indexed_bind_group {
740 if let PreprocessWorkItemBuffers::Indirect {
741 gpu_occlusion_culling:
742 Some(GpuOcclusionCullingWorkItemBuffers {
743 late_indirect_parameters_indexed_offset,
744 ..
745 }),
746 ..
747 } = *work_item_buffers
748 {
749 compute_pass.set_immediates(
750 0,
751 bytemuck::bytes_of(&late_indirect_parameters_indexed_offset),
752 );
753 }
754
755 compute_pass.set_bind_group(0, indexed_bind_group, &dynamic_offsets);
756 let workgroup_count = indexed_buffer.len().div_ceil(WORKGROUP_SIZE);
757 if workgroup_count > 0 {
758 compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
759 }
760 }
761
762 if let Some(non_indexed_bind_group) = maybe_non_indexed_bind_group {
764 if let PreprocessWorkItemBuffers::Indirect {
765 gpu_occlusion_culling:
766 Some(GpuOcclusionCullingWorkItemBuffers {
767 late_indirect_parameters_non_indexed_offset,
768 ..
769 }),
770 ..
771 } = *work_item_buffers
772 {
773 compute_pass.set_immediates(
774 0,
775 bytemuck::bytes_of(&late_indirect_parameters_non_indexed_offset),
776 );
777 }
778
779 compute_pass.set_bind_group(0, non_indexed_bind_group, &dynamic_offsets);
780 let workgroup_count = non_indexed_buffer.len().div_ceil(WORKGROUP_SIZE);
781 if workgroup_count > 0 {
782 compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
783 }
784 }
785 }
786 }
787 }
788 }
789
790 pass_span.end(&mut compute_pass);
791}
792
793fn gather_shadow_cascades_for_view(
796 view_entity: Entity,
797 shadow_cascade_views: Option<&ViewLightEntities>,
798 light_query: &Query<&LightEntity>,
799) -> SmallVec<[Entity; 8]> {
800 let mut all_views: SmallVec<[_; 8]> = SmallVec::new();
801 all_views.push(view_entity);
802 if let Some(shadow_cascade_views) = shadow_cascade_views {
803 all_views.extend(
804 shadow_cascade_views
805 .lights
806 .iter()
807 .filter(|light_entity| {
808 light_query.get(**light_entity).is_ok_and(|light_entity| {
809 matches!(*light_entity, LightEntity::Directional { .. })
810 })
811 })
812 .copied(),
813 );
814 }
815 all_views
816}
817
818pub fn late_gpu_preprocess(
819 current_view: ViewQuery<
820 (&ExtractedView, &PreprocessBindGroups, &ViewUniformOffset),
821 (
822 Without<SkipGpuPreprocess>,
823 Without<NoIndirectDrawing>,
824 With<OcclusionCulling>,
825 With<DepthPrepass>,
826 ),
827 >,
828 batched_instance_buffers: Res<BatchedInstanceBuffers<MeshUniform, MeshInputUniform>>,
829 pipeline_cache: Res<PipelineCache>,
830 preprocess_pipelines: Res<PreprocessPipelines>,
831 mut ctx: RenderContext,
832) {
833 let (view, bind_groups, view_uniform_offset) = current_view.into_inner();
834
835 let maybe_pipeline_id = preprocess_pipelines
837 .late_gpu_occlusion_culling_preprocess
838 .pipeline_id;
839
840 let Some(preprocess_pipeline_id) = maybe_pipeline_id else {
841 warn_once!("The build mesh uniforms pipeline wasn't ready");
842 return;
843 };
844
845 let Some(preprocess_pipeline) = pipeline_cache.get_compute_pipeline(preprocess_pipeline_id)
846 else {
847 return;
849 };
850
851 let diagnostics = ctx.diagnostic_recorder();
852 let diagnostics = diagnostics.as_deref();
853
854 let command_encoder = ctx.command_encoder();
855
856 let mut compute_pass = command_encoder.begin_compute_pass(&ComputePassDescriptor {
857 label: Some("late_mesh_preprocessing"),
858 timestamp_writes: None,
859 });
860
861 let pass_span = diagnostics.pass_span(&mut compute_pass, "late_mesh_preprocessing");
862
863 compute_pass.set_pipeline(preprocess_pipeline);
864
865 for (phase_type_id, batched_phase_instance_buffers) in
868 &batched_instance_buffers.phase_instance_buffers
869 {
870 let UntypedPhaseBatchedInstanceBuffers {
871 ref work_item_buffers,
872 ref late_indexed_indirect_parameters_buffer,
873 ref late_non_indexed_indirect_parameters_buffer,
874 ..
875 } = *batched_phase_instance_buffers;
876
877 let Some(phase_work_item_buffers) = work_item_buffers.get(&view.retained_view_entity)
879 else {
880 continue;
881 };
882
883 let (
884 PreprocessWorkItemBuffers::Indirect {
885 gpu_occlusion_culling:
886 Some(GpuOcclusionCullingWorkItemBuffers {
887 late_indirect_parameters_indexed_offset,
888 late_indirect_parameters_non_indexed_offset,
889 ..
890 }),
891 ..
892 },
893 Some(PhasePreprocessBindGroups::IndirectOcclusionCulling {
894 late_indexed: maybe_late_indexed_bind_group,
895 late_non_indexed: maybe_late_non_indexed_bind_group,
896 ..
897 }),
898 Some(late_indexed_indirect_parameters_buffer),
899 Some(late_non_indexed_indirect_parameters_buffer),
900 ) = (
901 phase_work_item_buffers,
902 bind_groups.get(phase_type_id),
903 late_indexed_indirect_parameters_buffer.buffer(),
904 late_non_indexed_indirect_parameters_buffer.buffer(),
905 )
906 else {
907 continue;
908 };
909
910 let mut dynamic_offsets: SmallVec<[u32; 1]> = smallvec![];
911 dynamic_offsets.push(view_uniform_offset.offset);
912
913 if let Some(late_indexed_bind_group) = maybe_late_indexed_bind_group {
920 compute_pass.set_immediates(
921 0,
922 bytemuck::bytes_of(late_indirect_parameters_indexed_offset),
923 );
924
925 compute_pass.set_bind_group(0, late_indexed_bind_group, &dynamic_offsets);
926 compute_pass.dispatch_workgroups_indirect(
927 late_indexed_indirect_parameters_buffer,
928 (*late_indirect_parameters_indexed_offset as u64)
929 * (size_of::<LatePreprocessWorkItemIndirectParameters>() as u64),
930 );
931 }
932
933 if let Some(late_non_indexed_bind_group) = maybe_late_non_indexed_bind_group {
935 compute_pass.set_immediates(
936 0,
937 bytemuck::bytes_of(late_indirect_parameters_non_indexed_offset),
938 );
939
940 compute_pass.set_bind_group(0, late_non_indexed_bind_group, &dynamic_offsets);
941 compute_pass.dispatch_workgroups_indirect(
942 late_non_indexed_indirect_parameters_buffer,
943 (*late_indirect_parameters_non_indexed_offset as u64)
944 * (size_of::<LatePreprocessWorkItemIndirectParameters>() as u64),
945 );
946 }
947 }
948
949 pass_span.end(&mut compute_pass);
950}
951
952pub fn early_prepass_build_indirect_parameters(
953 preprocess_pipelines: Res<PreprocessPipelines>,
954 build_indirect_params_bind_groups: Option<Res<BuildIndirectParametersBindGroups>>,
955 pipeline_cache: Res<PipelineCache>,
956 indirect_parameters_buffers: Option<Res<IndirectParametersBuffers>>,
957 mut ctx: RenderContext,
958) {
959 run_build_indirect_parameters(
960 &mut ctx,
961 build_indirect_params_bind_groups.as_deref(),
962 &pipeline_cache,
963 indirect_parameters_buffers.as_deref(),
964 &preprocess_pipelines.early_phase,
965 "early_prepass_indirect_parameters_building",
966 );
967}
968
969pub fn late_prepass_build_indirect_parameters(
970 preprocess_pipelines: Res<PreprocessPipelines>,
971 build_indirect_params_bind_groups: Option<Res<BuildIndirectParametersBindGroups>>,
972 pipeline_cache: Res<PipelineCache>,
973 indirect_parameters_buffers: Option<Res<IndirectParametersBuffers>>,
974 mut ctx: RenderContext,
975) {
976 run_build_indirect_parameters(
977 &mut ctx,
978 build_indirect_params_bind_groups.as_deref(),
979 &pipeline_cache,
980 indirect_parameters_buffers.as_deref(),
981 &preprocess_pipelines.late_phase,
982 "late_prepass_indirect_parameters_building",
983 );
984}
985
986pub fn main_build_indirect_parameters(
991 _current_view: ViewQuery<Entity, Without<ShadowView>>,
992 preprocess_pipelines: Res<PreprocessPipelines>,
993 build_indirect_params_bind_groups: Option<Res<BuildIndirectParametersBindGroups>>,
994 pipeline_cache: Res<PipelineCache>,
995 indirect_parameters_buffers: Option<Res<IndirectParametersBuffers>>,
996 mut ctx: RenderContext,
997) {
998 run_build_indirect_parameters(
999 &mut ctx,
1000 build_indirect_params_bind_groups.as_deref(),
1001 &pipeline_cache,
1002 indirect_parameters_buffers.as_deref(),
1003 &preprocess_pipelines.main_phase,
1004 "main_indirect_parameters_building",
1005 );
1006}
1007
1008fn run_build_indirect_parameters(
1009 ctx: &mut RenderContext,
1010 build_indirect_params_bind_groups: Option<&BuildIndirectParametersBindGroups>,
1011 pipeline_cache: &PipelineCache,
1012 indirect_parameters_buffers: Option<&IndirectParametersBuffers>,
1013 preprocess_phase_pipelines: &PreprocessPhasePipelines,
1014 label: &'static str,
1015) {
1016 let Some(build_indirect_params_bind_groups) = build_indirect_params_bind_groups else {
1017 return;
1018 };
1019
1020 let Some(indirect_parameters_buffers) = indirect_parameters_buffers else {
1021 return;
1022 };
1023
1024 let command_encoder = ctx.command_encoder();
1025
1026 let mut compute_pass = command_encoder.begin_compute_pass(&ComputePassDescriptor {
1027 label: Some(label),
1028 timestamp_writes: None,
1029 });
1030
1031 let (
1033 Some(reset_indirect_batch_sets_pipeline_id),
1034 Some(build_indexed_indirect_params_pipeline_id),
1035 Some(build_non_indexed_indirect_params_pipeline_id),
1036 ) = (
1037 preprocess_phase_pipelines
1038 .reset_indirect_batch_sets
1039 .pipeline_id,
1040 preprocess_phase_pipelines
1041 .gpu_occlusion_culling_build_indexed_indirect_params
1042 .pipeline_id,
1043 preprocess_phase_pipelines
1044 .gpu_occlusion_culling_build_non_indexed_indirect_params
1045 .pipeline_id,
1046 )
1047 else {
1048 warn!("The build indirect parameters pipelines weren't ready");
1049 return;
1050 };
1051
1052 let (
1053 Some(reset_indirect_batch_sets_pipeline),
1054 Some(build_indexed_indirect_params_pipeline),
1055 Some(build_non_indexed_indirect_params_pipeline),
1056 ) = (
1057 pipeline_cache.get_compute_pipeline(reset_indirect_batch_sets_pipeline_id),
1058 pipeline_cache.get_compute_pipeline(build_indexed_indirect_params_pipeline_id),
1059 pipeline_cache.get_compute_pipeline(build_non_indexed_indirect_params_pipeline_id),
1060 )
1061 else {
1062 return;
1064 };
1065
1066 for (phase_type_id, phase_build_indirect_params_bind_groups) in
1069 build_indirect_params_bind_groups.iter()
1070 {
1071 let Some(phase_indirect_parameters_buffers) =
1072 indirect_parameters_buffers.get(phase_type_id)
1073 else {
1074 continue;
1075 };
1076
1077 if let (
1079 Some(reset_indexed_indirect_batch_sets_bind_group),
1080 Some(build_indirect_indexed_params_bind_group),
1081 ) = (
1082 &phase_build_indirect_params_bind_groups.reset_indexed_indirect_batch_sets,
1083 &phase_build_indirect_params_bind_groups.build_indexed_indirect,
1084 ) {
1085 compute_pass.set_pipeline(reset_indirect_batch_sets_pipeline);
1086 compute_pass.set_bind_group(0, reset_indexed_indirect_batch_sets_bind_group, &[]);
1087 let workgroup_count = phase_indirect_parameters_buffers
1088 .batch_set_count(true)
1089 .div_ceil(WORKGROUP_SIZE);
1090 if workgroup_count > 0 {
1091 compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
1092 }
1093
1094 compute_pass.set_pipeline(build_indexed_indirect_params_pipeline);
1095 compute_pass.set_bind_group(0, build_indirect_indexed_params_bind_group, &[]);
1096 let workgroup_count = phase_indirect_parameters_buffers
1097 .indexed
1098 .batch_count()
1099 .div_ceil(WORKGROUP_SIZE);
1100 if workgroup_count > 0 {
1101 compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
1102 }
1103 }
1104
1105 if let (
1107 Some(reset_non_indexed_indirect_batch_sets_bind_group),
1108 Some(build_indirect_non_indexed_params_bind_group),
1109 ) = (
1110 &phase_build_indirect_params_bind_groups.reset_non_indexed_indirect_batch_sets,
1111 &phase_build_indirect_params_bind_groups.build_non_indexed_indirect,
1112 ) {
1113 compute_pass.set_pipeline(reset_indirect_batch_sets_pipeline);
1114 compute_pass.set_bind_group(0, reset_non_indexed_indirect_batch_sets_bind_group, &[]);
1115 let workgroup_count = phase_indirect_parameters_buffers
1116 .batch_set_count(false)
1117 .div_ceil(WORKGROUP_SIZE);
1118 if workgroup_count > 0 {
1119 compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
1120 }
1121
1122 compute_pass.set_pipeline(build_non_indexed_indirect_params_pipeline);
1123 compute_pass.set_bind_group(0, build_indirect_non_indexed_params_bind_group, &[]);
1124 let workgroup_count = phase_indirect_parameters_buffers
1125 .non_indexed
1126 .batch_count()
1127 .div_ceil(WORKGROUP_SIZE);
1128 if workgroup_count > 0 {
1129 compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
1130 }
1131 }
1132 }
1133}
1134
1135impl PreprocessPipelines {
1136 pub(crate) fn pipelines_are_loaded(
1139 &self,
1140 pipeline_cache: &PipelineCache,
1141 preprocessing_support: &GpuPreprocessingSupport,
1142 ) -> bool {
1143 match preprocessing_support.max_supported_mode {
1144 GpuPreprocessingMode::None => false,
1145 GpuPreprocessingMode::PreprocessingOnly => {
1146 self.direct_preprocess.is_loaded(pipeline_cache)
1147 && self
1148 .gpu_frustum_culling_preprocess
1149 .is_loaded(pipeline_cache)
1150 }
1151 GpuPreprocessingMode::Culling => {
1152 self.direct_preprocess.is_loaded(pipeline_cache)
1153 && self
1154 .gpu_frustum_culling_preprocess
1155 .is_loaded(pipeline_cache)
1156 && self
1157 .early_gpu_occlusion_culling_preprocess
1158 .is_loaded(pipeline_cache)
1159 && self
1160 .late_gpu_occlusion_culling_preprocess
1161 .is_loaded(pipeline_cache)
1162 && self
1163 .gpu_frustum_culling_build_indexed_indirect_params
1164 .is_loaded(pipeline_cache)
1165 && self
1166 .gpu_frustum_culling_build_non_indexed_indirect_params
1167 .is_loaded(pipeline_cache)
1168 && self.early_phase.is_loaded(pipeline_cache)
1169 && self.late_phase.is_loaded(pipeline_cache)
1170 && self.main_phase.is_loaded(pipeline_cache)
1171 }
1172 }
1173 }
1174}
1175
1176impl PreprocessPhasePipelines {
1177 fn is_loaded(&self, pipeline_cache: &PipelineCache) -> bool {
1178 self.reset_indirect_batch_sets.is_loaded(pipeline_cache)
1179 && self
1180 .gpu_occlusion_culling_build_indexed_indirect_params
1181 .is_loaded(pipeline_cache)
1182 && self
1183 .gpu_occlusion_culling_build_non_indexed_indirect_params
1184 .is_loaded(pipeline_cache)
1185 }
1186}
1187
1188impl PreprocessPipeline {
1189 fn is_loaded(&self, pipeline_cache: &PipelineCache) -> bool {
1190 self.pipeline_id
1191 .is_some_and(|pipeline_id| pipeline_cache.get_compute_pipeline(pipeline_id).is_some())
1192 }
1193}
1194
1195impl ResetIndirectBatchSetsPipeline {
1196 fn is_loaded(&self, pipeline_cache: &PipelineCache) -> bool {
1197 self.pipeline_id
1198 .is_some_and(|pipeline_id| pipeline_cache.get_compute_pipeline(pipeline_id).is_some())
1199 }
1200}
1201
1202impl BuildIndirectParametersPipeline {
1203 fn is_loaded(&self, pipeline_cache: &PipelineCache) -> bool {
1206 self.pipeline_id
1207 .is_some_and(|pipeline_id| pipeline_cache.get_compute_pipeline(pipeline_id).is_some())
1208 }
1209}
1210
1211impl SpecializedComputePipeline for PreprocessPipeline {
1212 type Key = PreprocessPipelineKey;
1213
1214 fn specialize(&self, key: Self::Key) -> ComputePipelineDescriptor {
1215 let mut shader_defs = vec!["WRITE_INDIRECT_PARAMETERS_METADATA".into()];
1216 if key.contains(PreprocessPipelineKey::FRUSTUM_CULLING) {
1217 shader_defs.push("INDIRECT".into());
1218 shader_defs.push("FRUSTUM_CULLING".into());
1219 }
1220 if key.contains(PreprocessPipelineKey::OCCLUSION_CULLING) {
1221 shader_defs.push("OCCLUSION_CULLING".into());
1222 if key.contains(PreprocessPipelineKey::EARLY_PHASE) {
1223 shader_defs.push("EARLY_PHASE".into());
1224 } else {
1225 shader_defs.push("LATE_PHASE".into());
1226 }
1227 }
1228
1229 ComputePipelineDescriptor {
1230 label: Some(
1231 format!(
1232 "mesh preprocessing ({})",
1233 if key.contains(
1234 PreprocessPipelineKey::OCCLUSION_CULLING
1235 | PreprocessPipelineKey::EARLY_PHASE
1236 ) {
1237 "early GPU occlusion culling"
1238 } else if key.contains(PreprocessPipelineKey::OCCLUSION_CULLING) {
1239 "late GPU occlusion culling"
1240 } else if key.contains(PreprocessPipelineKey::FRUSTUM_CULLING) {
1241 "GPU frustum culling"
1242 } else {
1243 "direct"
1244 }
1245 )
1246 .into(),
1247 ),
1248 layout: vec![self.bind_group_layout.clone()],
1249 immediate_size: if key.contains(PreprocessPipelineKey::OCCLUSION_CULLING) {
1250 4
1251 } else {
1252 0
1253 },
1254 shader: self.shader.clone(),
1255 shader_defs,
1256 ..default()
1257 }
1258 }
1259}
1260
1261impl FromWorld for PreprocessPipelines {
1262 fn from_world(world: &mut World) -> Self {
1263 let direct_bind_group_layout_entries = preprocess_direct_bind_group_layout_entries();
1266 let gpu_frustum_culling_bind_group_layout_entries = gpu_culling_bind_group_layout_entries();
1267 let gpu_early_occlusion_culling_bind_group_layout_entries =
1268 gpu_occlusion_culling_bind_group_layout_entries().extend_with_indices((
1269 (
1270 12,
1271 storage_buffer::<PreprocessWorkItem>(false),
1272 ),
1273 (
1274 13,
1275 storage_buffer::<LatePreprocessWorkItemIndirectParameters>(
1276 false,
1277 ),
1278 ),
1279 ));
1280 let gpu_late_occlusion_culling_bind_group_layout_entries =
1281 gpu_occlusion_culling_bind_group_layout_entries().extend_with_indices(((
1282 13,
1283 storage_buffer_read_only::<LatePreprocessWorkItemIndirectParameters>(
1284 false,
1285 ),
1286 ),));
1287
1288 let reset_indirect_batch_sets_bind_group_layout_entries =
1289 DynamicBindGroupLayoutEntries::sequential(
1290 ShaderStages::COMPUTE,
1291 (storage_buffer::<IndirectBatchSet>(false),),
1292 );
1293
1294 let build_indexed_indirect_params_bind_group_layout_entries =
1297 build_indirect_params_bind_group_layout_entries()
1298 .extend_sequential((storage_buffer::<IndirectParametersIndexed>(false),));
1299 let build_non_indexed_indirect_params_bind_group_layout_entries =
1300 build_indirect_params_bind_group_layout_entries()
1301 .extend_sequential((storage_buffer::<IndirectParametersNonIndexed>(false),));
1302
1303 let bin_unpacking_bind_group_layout_entries = bin_unpacking_bind_group_layout_entries();
1304
1305 let direct_bind_group_layout = BindGroupLayoutDescriptor::new(
1307 "build mesh uniforms direct bind group layout",
1308 &direct_bind_group_layout_entries,
1309 );
1310 let gpu_frustum_culling_bind_group_layout = BindGroupLayoutDescriptor::new(
1311 "build mesh uniforms GPU frustum culling bind group layout",
1312 &gpu_frustum_culling_bind_group_layout_entries,
1313 );
1314 let gpu_early_occlusion_culling_bind_group_layout = BindGroupLayoutDescriptor::new(
1315 "build mesh uniforms GPU early occlusion culling bind group layout",
1316 &gpu_early_occlusion_culling_bind_group_layout_entries,
1317 );
1318 let gpu_late_occlusion_culling_bind_group_layout = BindGroupLayoutDescriptor::new(
1319 "build mesh uniforms GPU late occlusion culling bind group layout",
1320 &gpu_late_occlusion_culling_bind_group_layout_entries,
1321 );
1322 let reset_indirect_batch_sets_bind_group_layout = BindGroupLayoutDescriptor::new(
1323 "reset indirect batch sets bind group layout",
1324 &reset_indirect_batch_sets_bind_group_layout_entries,
1325 );
1326 let build_indexed_indirect_params_bind_group_layout = BindGroupLayoutDescriptor::new(
1327 "build indexed indirect parameters bind group layout",
1328 &build_indexed_indirect_params_bind_group_layout_entries,
1329 );
1330 let build_non_indexed_indirect_params_bind_group_layout = BindGroupLayoutDescriptor::new(
1331 "build non-indexed indirect parameters bind group layout",
1332 &build_non_indexed_indirect_params_bind_group_layout_entries,
1333 );
1334 let bin_unpacking_bind_group_layout = BindGroupLayoutDescriptor::new(
1335 "bin unpacking bind group layout",
1336 &bin_unpacking_bind_group_layout_entries,
1337 );
1338
1339 let preprocess_shader = load_embedded_asset!(world, "mesh_preprocess.wgsl");
1340 let reset_indirect_batch_sets_shader =
1341 load_embedded_asset!(world, "reset_indirect_batch_sets.wgsl");
1342 let build_indirect_params_shader =
1343 load_embedded_asset!(world, "build_indirect_params.wgsl");
1344 let bin_unpacking_shader = load_embedded_asset!(world, "unpack_bins.wgsl");
1345
1346 let preprocess_phase_pipelines = PreprocessPhasePipelines {
1347 reset_indirect_batch_sets: ResetIndirectBatchSetsPipeline {
1348 bind_group_layout: reset_indirect_batch_sets_bind_group_layout.clone(),
1349 shader: reset_indirect_batch_sets_shader,
1350 pipeline_id: None,
1351 },
1352 gpu_occlusion_culling_build_indexed_indirect_params: BuildIndirectParametersPipeline {
1353 bind_group_layout: build_indexed_indirect_params_bind_group_layout.clone(),
1354 shader: build_indirect_params_shader.clone(),
1355 pipeline_id: None,
1356 },
1357 gpu_occlusion_culling_build_non_indexed_indirect_params:
1358 BuildIndirectParametersPipeline {
1359 bind_group_layout: build_non_indexed_indirect_params_bind_group_layout.clone(),
1360 shader: build_indirect_params_shader.clone(),
1361 pipeline_id: None,
1362 },
1363 };
1364
1365 PreprocessPipelines {
1366 direct_preprocess: PreprocessPipeline {
1367 bind_group_layout: direct_bind_group_layout,
1368 shader: preprocess_shader.clone(),
1369 pipeline_id: None,
1370 },
1371 gpu_frustum_culling_preprocess: PreprocessPipeline {
1372 bind_group_layout: gpu_frustum_culling_bind_group_layout,
1373 shader: preprocess_shader.clone(),
1374 pipeline_id: None,
1375 },
1376 early_gpu_occlusion_culling_preprocess: PreprocessPipeline {
1377 bind_group_layout: gpu_early_occlusion_culling_bind_group_layout,
1378 shader: preprocess_shader.clone(),
1379 pipeline_id: None,
1380 },
1381 late_gpu_occlusion_culling_preprocess: PreprocessPipeline {
1382 bind_group_layout: gpu_late_occlusion_culling_bind_group_layout,
1383 shader: preprocess_shader,
1384 pipeline_id: None,
1385 },
1386 gpu_frustum_culling_build_indexed_indirect_params: BuildIndirectParametersPipeline {
1387 bind_group_layout: build_indexed_indirect_params_bind_group_layout.clone(),
1388 shader: build_indirect_params_shader.clone(),
1389 pipeline_id: None,
1390 },
1391 gpu_frustum_culling_build_non_indexed_indirect_params:
1392 BuildIndirectParametersPipeline {
1393 bind_group_layout: build_non_indexed_indirect_params_bind_group_layout.clone(),
1394 shader: build_indirect_params_shader,
1395 pipeline_id: None,
1396 },
1397 early_phase: preprocess_phase_pipelines.clone(),
1398 late_phase: preprocess_phase_pipelines.clone(),
1399 main_phase: preprocess_phase_pipelines.clone(),
1400 bin_unpacking: BinUnpackingPipeline {
1401 bind_group_layout: bin_unpacking_bind_group_layout,
1402 shader: bin_unpacking_shader,
1403 pipeline_id: None,
1404 },
1405 }
1406 }
1407}
1408
1409fn preprocess_direct_bind_group_layout_entries() -> DynamicBindGroupLayoutEntries {
1410 DynamicBindGroupLayoutEntries::new_with_indices(
1411 ShaderStages::COMPUTE,
1412 (
1413 (
1415 0,
1416 uniform_buffer::<ViewUniform>(true),
1417 ),
1418 (3, storage_buffer_read_only::<MeshInputUniform>(false)),
1420 (4, storage_buffer_read_only::<MeshInputUniform>(false)),
1422 (5, storage_buffer_read_only::<PreprocessWorkItem>(false)),
1424 (6, storage_buffer::<MeshUniform>(false)),
1426 ),
1427 )
1428}
1429
1430fn build_indirect_params_bind_group_layout_entries() -> DynamicBindGroupLayoutEntries {
1433 DynamicBindGroupLayoutEntries::new_with_indices(
1434 ShaderStages::COMPUTE,
1435 (
1436 (0, storage_buffer_read_only::<MeshInputUniform>(false)),
1437 (
1438 1,
1439 storage_buffer_read_only::<IndirectParametersCpuMetadata>(false),
1440 ),
1441 (
1442 2,
1443 storage_buffer_read_only::<IndirectParametersGpuMetadata>(false),
1444 ),
1445 (3, storage_buffer::<IndirectBatchSet>(false)),
1446 ),
1447 )
1448}
1449
1450fn gpu_culling_bind_group_layout_entries() -> DynamicBindGroupLayoutEntries {
1453 preprocess_direct_bind_group_layout_entries().extend_with_indices((
1456 (
1458 7,
1459 storage_buffer_read_only::<IndirectParametersCpuMetadata>(
1460 false,
1461 ),
1462 ),
1463 (
1465 8,
1466 storage_buffer::<IndirectParametersGpuMetadata>(false),
1467 ),
1468 (
1470 9,
1471 storage_buffer_read_only::<MeshCullingData>(false),
1472 ),
1473 (
1475 10,
1476 storage_buffer_read_only::<Vec4>(false),
1477 ),
1478 ))
1479}
1480
1481fn gpu_occlusion_culling_bind_group_layout_entries() -> DynamicBindGroupLayoutEntries {
1482 gpu_culling_bind_group_layout_entries().extend_with_indices((
1483 (
1484 2,
1485 uniform_buffer::<PreviousViewData>(false),
1486 ),
1487 (
1488 11,
1489 texture_2d(TextureSampleType::Float { filterable: true }),
1490 ),
1491 ))
1492}
1493
1494fn bin_unpacking_bind_group_layout_entries() -> BindGroupLayoutEntries<4> {
1497 BindGroupLayoutEntries::sequential(
1498 ShaderStages::COMPUTE,
1499 (
1500 uniform_buffer::<GpuBinUnpackingMetadata>(false),
1503 storage_buffer_read_only::<GpuRenderBinnedMeshInstance>(false),
1506 storage_buffer::<PreprocessWorkItem>(false),
1509 storage_buffer_read_only::<u32>(false),
1512 ),
1513 )
1514}
1515
1516pub fn prepare_preprocess_pipelines(
1524 pipeline_cache: Res<PipelineCache>,
1525 render_device: Res<RenderDevice>,
1526 mut specialized_preprocess_pipelines: ResMut<SpecializedComputePipelines<PreprocessPipeline>>,
1527 mut specialized_reset_indirect_batch_sets_pipelines: ResMut<
1528 SpecializedComputePipelines<ResetIndirectBatchSetsPipeline>,
1529 >,
1530 mut specialized_build_indirect_parameters_pipelines: ResMut<
1531 SpecializedComputePipelines<BuildIndirectParametersPipeline>,
1532 >,
1533 mut specialized_bin_unpacking_pipelines: ResMut<
1534 SpecializedComputePipelines<BinUnpackingPipeline>,
1535 >,
1536 preprocess_pipelines: ResMut<PreprocessPipelines>,
1537 gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
1538) {
1539 let preprocess_pipelines = preprocess_pipelines.into_inner();
1540
1541 preprocess_pipelines.direct_preprocess.prepare(
1542 &pipeline_cache,
1543 &mut specialized_preprocess_pipelines,
1544 PreprocessPipelineKey::empty(),
1545 );
1546 preprocess_pipelines.gpu_frustum_culling_preprocess.prepare(
1547 &pipeline_cache,
1548 &mut specialized_preprocess_pipelines,
1549 PreprocessPipelineKey::FRUSTUM_CULLING,
1550 );
1551
1552 if gpu_preprocessing_support.is_culling_supported() {
1553 preprocess_pipelines
1554 .early_gpu_occlusion_culling_preprocess
1555 .prepare(
1556 &pipeline_cache,
1557 &mut specialized_preprocess_pipelines,
1558 PreprocessPipelineKey::FRUSTUM_CULLING
1559 | PreprocessPipelineKey::OCCLUSION_CULLING
1560 | PreprocessPipelineKey::EARLY_PHASE,
1561 );
1562 preprocess_pipelines
1563 .late_gpu_occlusion_culling_preprocess
1564 .prepare(
1565 &pipeline_cache,
1566 &mut specialized_preprocess_pipelines,
1567 PreprocessPipelineKey::FRUSTUM_CULLING | PreprocessPipelineKey::OCCLUSION_CULLING,
1568 );
1569 }
1570
1571 let mut build_indirect_parameters_pipeline_key = BuildIndirectParametersPipelineKey::empty();
1572
1573 if render_device
1576 .wgpu_device()
1577 .features()
1578 .contains(WgpuFeatures::MULTI_DRAW_INDIRECT_COUNT)
1579 {
1580 build_indirect_parameters_pipeline_key
1581 .insert(BuildIndirectParametersPipelineKey::MULTI_DRAW_INDIRECT_COUNT_SUPPORTED);
1582 }
1583
1584 preprocess_pipelines
1585 .gpu_frustum_culling_build_indexed_indirect_params
1586 .prepare(
1587 &pipeline_cache,
1588 &mut specialized_build_indirect_parameters_pipelines,
1589 build_indirect_parameters_pipeline_key | BuildIndirectParametersPipelineKey::INDEXED,
1590 );
1591 preprocess_pipelines
1592 .gpu_frustum_culling_build_non_indexed_indirect_params
1593 .prepare(
1594 &pipeline_cache,
1595 &mut specialized_build_indirect_parameters_pipelines,
1596 build_indirect_parameters_pipeline_key,
1597 );
1598
1599 if !gpu_preprocessing_support.is_culling_supported() {
1600 return;
1601 }
1602
1603 for (preprocess_phase_pipelines, build_indirect_parameters_phase_pipeline_key) in [
1604 (
1605 &mut preprocess_pipelines.early_phase,
1606 BuildIndirectParametersPipelineKey::EARLY_PHASE,
1607 ),
1608 (
1609 &mut preprocess_pipelines.late_phase,
1610 BuildIndirectParametersPipelineKey::LATE_PHASE,
1611 ),
1612 (
1613 &mut preprocess_pipelines.main_phase,
1614 BuildIndirectParametersPipelineKey::MAIN_PHASE,
1615 ),
1616 ] {
1617 preprocess_phase_pipelines
1618 .reset_indirect_batch_sets
1619 .prepare(
1620 &pipeline_cache,
1621 &mut specialized_reset_indirect_batch_sets_pipelines,
1622 );
1623 preprocess_phase_pipelines
1624 .gpu_occlusion_culling_build_indexed_indirect_params
1625 .prepare(
1626 &pipeline_cache,
1627 &mut specialized_build_indirect_parameters_pipelines,
1628 build_indirect_parameters_pipeline_key
1629 | build_indirect_parameters_phase_pipeline_key
1630 | BuildIndirectParametersPipelineKey::INDEXED
1631 | BuildIndirectParametersPipelineKey::OCCLUSION_CULLING,
1632 );
1633 preprocess_phase_pipelines
1634 .gpu_occlusion_culling_build_non_indexed_indirect_params
1635 .prepare(
1636 &pipeline_cache,
1637 &mut specialized_build_indirect_parameters_pipelines,
1638 build_indirect_parameters_pipeline_key
1639 | build_indirect_parameters_phase_pipeline_key
1640 | BuildIndirectParametersPipelineKey::OCCLUSION_CULLING,
1641 );
1642 }
1643
1644 preprocess_pipelines
1646 .bin_unpacking
1647 .prepare(&pipeline_cache, &mut specialized_bin_unpacking_pipelines);
1648}
1649
1650impl PreprocessPipeline {
1651 fn prepare(
1652 &mut self,
1653 pipeline_cache: &PipelineCache,
1654 pipelines: &mut SpecializedComputePipelines<PreprocessPipeline>,
1655 key: PreprocessPipelineKey,
1656 ) {
1657 if self.pipeline_id.is_some() {
1658 return;
1659 }
1660
1661 let preprocess_pipeline_id = pipelines.specialize(pipeline_cache, self, key);
1662 self.pipeline_id = Some(preprocess_pipeline_id);
1663 }
1664}
1665
1666impl SpecializedComputePipeline for ResetIndirectBatchSetsPipeline {
1667 type Key = ();
1668
1669 fn specialize(&self, _: Self::Key) -> ComputePipelineDescriptor {
1670 ComputePipelineDescriptor {
1671 label: Some("reset indirect batch sets".into()),
1672 layout: vec![self.bind_group_layout.clone()],
1673 shader: self.shader.clone(),
1674 ..default()
1675 }
1676 }
1677}
1678
1679impl SpecializedComputePipeline for BuildIndirectParametersPipeline {
1680 type Key = BuildIndirectParametersPipelineKey;
1681
1682 fn specialize(&self, key: Self::Key) -> ComputePipelineDescriptor {
1683 let mut shader_defs = vec![];
1684 if key.contains(BuildIndirectParametersPipelineKey::INDEXED) {
1685 shader_defs.push("INDEXED".into());
1686 }
1687 if key.contains(BuildIndirectParametersPipelineKey::MULTI_DRAW_INDIRECT_COUNT_SUPPORTED) {
1688 shader_defs.push("MULTI_DRAW_INDIRECT_COUNT_SUPPORTED".into());
1689 }
1690 if key.contains(BuildIndirectParametersPipelineKey::OCCLUSION_CULLING) {
1691 shader_defs.push("OCCLUSION_CULLING".into());
1692 }
1693 if key.contains(BuildIndirectParametersPipelineKey::EARLY_PHASE) {
1694 shader_defs.push("EARLY_PHASE".into());
1695 }
1696 if key.contains(BuildIndirectParametersPipelineKey::LATE_PHASE) {
1697 shader_defs.push("LATE_PHASE".into());
1698 }
1699 if key.contains(BuildIndirectParametersPipelineKey::MAIN_PHASE) {
1700 shader_defs.push("MAIN_PHASE".into());
1701 }
1702
1703 let label = format!(
1704 "{} build {}indexed indirect parameters",
1705 if !key.contains(BuildIndirectParametersPipelineKey::OCCLUSION_CULLING) {
1706 "frustum culling"
1707 } else if key.contains(BuildIndirectParametersPipelineKey::EARLY_PHASE) {
1708 "early occlusion culling"
1709 } else if key.contains(BuildIndirectParametersPipelineKey::LATE_PHASE) {
1710 "late occlusion culling"
1711 } else {
1712 "main occlusion culling"
1713 },
1714 if key.contains(BuildIndirectParametersPipelineKey::INDEXED) {
1715 ""
1716 } else {
1717 "non-"
1718 }
1719 );
1720
1721 ComputePipelineDescriptor {
1722 label: Some(label.into()),
1723 layout: vec![self.bind_group_layout.clone()],
1724 shader: self.shader.clone(),
1725 shader_defs,
1726 ..default()
1727 }
1728 }
1729}
1730
1731impl SpecializedComputePipeline for BinUnpackingPipeline {
1732 type Key = ();
1733
1734 fn specialize(&self, _: Self::Key) -> ComputePipelineDescriptor {
1735 ComputePipelineDescriptor {
1736 label: Some("bin unpacking".into()),
1737 layout: vec![self.bind_group_layout.clone()],
1738 shader: self.shader.clone(),
1739 shader_defs: vec![],
1740 ..default()
1741 }
1742 }
1743}
1744
1745impl ResetIndirectBatchSetsPipeline {
1746 fn prepare(
1747 &mut self,
1748 pipeline_cache: &PipelineCache,
1749 pipelines: &mut SpecializedComputePipelines<ResetIndirectBatchSetsPipeline>,
1750 ) {
1751 if self.pipeline_id.is_some() {
1752 return;
1753 }
1754
1755 let reset_indirect_batch_sets_pipeline_id = pipelines.specialize(pipeline_cache, self, ());
1756 self.pipeline_id = Some(reset_indirect_batch_sets_pipeline_id);
1757 }
1758}
1759
1760impl BuildIndirectParametersPipeline {
1761 fn prepare(
1762 &mut self,
1763 pipeline_cache: &PipelineCache,
1764 pipelines: &mut SpecializedComputePipelines<BuildIndirectParametersPipeline>,
1765 key: BuildIndirectParametersPipelineKey,
1766 ) {
1767 if self.pipeline_id.is_some() {
1768 return;
1769 }
1770
1771 let build_indirect_parameters_pipeline_id = pipelines.specialize(pipeline_cache, self, key);
1772 self.pipeline_id = Some(build_indirect_parameters_pipeline_id);
1773 }
1774}
1775
1776impl BinUnpackingPipeline {
1777 fn prepare(
1779 &mut self,
1780 pipeline_cache: &PipelineCache,
1781 pipelines: &mut SpecializedComputePipelines<BinUnpackingPipeline>,
1782 ) {
1783 if self.pipeline_id.is_some() {
1784 return;
1785 }
1786
1787 let bin_unpacking_pipeline_id = pipelines.specialize(pipeline_cache, self, ());
1788 self.pipeline_id = Some(bin_unpacking_pipeline_id);
1789 }
1790}
1791
1792#[expect(
1795 clippy::too_many_arguments,
1796 reason = "it's a system that needs a lot of arguments"
1797)]
1798pub fn prepare_preprocess_bind_groups(
1799 mut commands: Commands,
1800 views: Query<(Entity, &ExtractedView)>,
1801 view_depth_pyramids: Query<(&ViewDepthPyramid, &PreviousViewUniformOffset)>,
1802 render_device: Res<RenderDevice>,
1803 pipeline_cache: Res<PipelineCache>,
1804 batched_instance_buffers: Res<BatchedInstanceBuffers<MeshUniform, MeshInputUniform>>,
1805 indirect_parameters_buffers: Res<IndirectParametersBuffers>,
1806 bin_unpacking_buffers: Res<BinUnpackingBuffers>,
1807 mesh_culling_data_buffer: Res<MeshCullingDataBuffer>,
1808 visibility_ranges: Res<RenderVisibilityRanges>,
1809 view_uniforms: Res<ViewUniforms>,
1810 previous_view_uniforms: Res<PreviousViewUniforms>,
1811 pipelines: Res<PreprocessPipelines>,
1812 mut bin_unpacking_bind_groups: ResMut<BinUnpackingBindGroups>,
1813) {
1814 let BatchedInstanceBuffers {
1816 current_input_buffer: current_input_buffer_vec,
1817 previous_input_buffer: previous_input_buffer_vec,
1818 phase_instance_buffers,
1819 } = batched_instance_buffers.into_inner();
1820
1821 let (Some(current_input_buffer), Some(previous_input_buffer)) = (
1822 current_input_buffer_vec.buffer().buffer(),
1823 previous_input_buffer_vec.buffer(),
1824 ) else {
1825 return;
1826 };
1827
1828 let mut any_indirect = false;
1831
1832 for (view_entity, view) in &views {
1834 let mut bind_groups = TypeIdMap::default();
1835
1836 for (phase_type_id, phase_instance_buffers) in phase_instance_buffers {
1838 let UntypedPhaseBatchedInstanceBuffers {
1839 data_buffer: ref data_buffer_vec,
1840 ref work_item_buffers,
1841 ref late_indexed_indirect_parameters_buffer,
1842 ref late_non_indexed_indirect_parameters_buffer,
1843 } = *phase_instance_buffers;
1844
1845 let Some(data_buffer) = data_buffer_vec.buffer() else {
1846 continue;
1847 };
1848
1849 let Some(phase_indirect_parameters_buffers) =
1851 indirect_parameters_buffers.get(phase_type_id)
1852 else {
1853 continue;
1854 };
1855
1856 let Some(work_item_buffers) = work_item_buffers.get(&view.retained_view_entity) else {
1857 continue;
1858 };
1859
1860 let preprocess_bind_group_builder = PreprocessBindGroupBuilder {
1862 view: view_entity,
1863 late_indexed_indirect_parameters_buffer,
1864 late_non_indexed_indirect_parameters_buffer,
1865 render_device: &render_device,
1866 pipeline_cache: &pipeline_cache,
1867 phase_indirect_parameters_buffers,
1868 mesh_culling_data_buffer: &mesh_culling_data_buffer,
1869 visibility_range_data_buffer: visibility_ranges.buffer(),
1870 view_uniforms: &view_uniforms,
1871 previous_view_uniforms: &previous_view_uniforms,
1872 pipelines: &pipelines,
1873 current_input_buffer,
1874 previous_input_buffer,
1875 data_buffer,
1876 };
1877
1878 let (was_indirect, bind_group) = match *work_item_buffers {
1881 PreprocessWorkItemBuffers::Direct(ref work_item_buffer) => (
1882 false,
1883 preprocess_bind_group_builder
1884 .create_direct_preprocess_bind_groups(work_item_buffer),
1885 ),
1886
1887 PreprocessWorkItemBuffers::Indirect {
1888 indexed: ref indexed_work_item_buffer,
1889 non_indexed: ref non_indexed_work_item_buffer,
1890 gpu_occlusion_culling: Some(ref gpu_occlusion_culling_work_item_buffers),
1891 } => (
1892 true,
1893 preprocess_bind_group_builder
1894 .create_indirect_occlusion_culling_preprocess_bind_groups(
1895 &view_depth_pyramids,
1896 indexed_work_item_buffer,
1897 non_indexed_work_item_buffer,
1898 gpu_occlusion_culling_work_item_buffers,
1899 ),
1900 ),
1901
1902 PreprocessWorkItemBuffers::Indirect {
1903 indexed: ref indexed_work_item_buffer,
1904 non_indexed: ref non_indexed_work_item_buffer,
1905 gpu_occlusion_culling: None,
1906 } => (
1907 true,
1908 preprocess_bind_group_builder
1909 .create_indirect_frustum_culling_preprocess_bind_groups(
1910 indexed_work_item_buffer,
1911 non_indexed_work_item_buffer,
1912 ),
1913 ),
1914 };
1915
1916 if let Some(bind_group) = bind_group {
1918 any_indirect = any_indirect || was_indirect;
1919 bind_groups.insert(*phase_type_id, bind_group);
1920 }
1921 }
1922
1923 commands
1925 .entity(view_entity)
1926 .insert(PreprocessBindGroups(bind_groups));
1927 }
1928
1929 if any_indirect {
1932 create_build_indirect_parameters_bind_groups(
1933 &mut commands,
1934 &render_device,
1935 &pipeline_cache,
1936 &pipelines,
1937 current_input_buffer,
1938 &indirect_parameters_buffers,
1939 );
1940 }
1941
1942 for (_, view) in &views {
1945 create_bin_unpacking_bind_groups(
1946 &mut bin_unpacking_bind_groups,
1947 &render_device,
1948 &pipeline_cache,
1949 &pipelines,
1950 &indirect_parameters_buffers,
1951 phase_instance_buffers,
1952 &bin_unpacking_buffers,
1953 &view.retained_view_entity,
1954 );
1955 }
1956}
1957
1958struct PreprocessBindGroupBuilder<'a> {
1961 view: Entity,
1963 late_indexed_indirect_parameters_buffer:
1966 &'a RawBufferVec<LatePreprocessWorkItemIndirectParameters>,
1967 late_non_indexed_indirect_parameters_buffer:
1970 &'a RawBufferVec<LatePreprocessWorkItemIndirectParameters>,
1971 render_device: &'a RenderDevice,
1973 pipeline_cache: &'a PipelineCache,
1975 phase_indirect_parameters_buffers: &'a UntypedPhaseIndirectParametersBuffers,
1977 mesh_culling_data_buffer: &'a MeshCullingDataBuffer,
1979 visibility_range_data_buffer: &'a BufferVec<Vec4>,
1982 view_uniforms: &'a ViewUniforms,
1984 previous_view_uniforms: &'a PreviousViewUniforms,
1986 pipelines: &'a PreprocessPipelines,
1988 current_input_buffer: &'a Buffer,
1991 previous_input_buffer: &'a Buffer,
1994 data_buffer: &'a Buffer,
2000}
2001
2002impl<'a> PreprocessBindGroupBuilder<'a> {
2003 fn create_direct_preprocess_bind_groups(
2006 &self,
2007 work_item_buffer: &RawBufferVec<PreprocessWorkItem>,
2008 ) -> Option<PhasePreprocessBindGroups> {
2009 let work_item_buffer_size = NonZero::<u64>::try_from(
2013 work_item_buffer.len() as u64 * u64::from(PreprocessWorkItem::min_size()),
2014 )
2015 .ok();
2016
2017 Some(PhasePreprocessBindGroups::Direct(
2018 self.render_device.create_bind_group(
2019 "preprocess_direct_bind_group",
2020 &self
2021 .pipeline_cache
2022 .get_bind_group_layout(&self.pipelines.direct_preprocess.bind_group_layout),
2023 &BindGroupEntries::with_indices((
2024 (0, self.view_uniforms.uniforms.binding()?),
2025 (3, self.current_input_buffer.as_entire_binding()),
2026 (4, self.previous_input_buffer.as_entire_binding()),
2027 (
2028 5,
2029 BindingResource::Buffer(BufferBinding {
2030 buffer: work_item_buffer.buffer()?,
2031 offset: 0,
2032 size: work_item_buffer_size,
2033 }),
2034 ),
2035 (6, self.data_buffer.as_entire_binding()),
2036 )),
2037 ),
2038 ))
2039 }
2040
2041 fn create_indirect_occlusion_culling_preprocess_bind_groups(
2044 &self,
2045 view_depth_pyramids: &Query<(&ViewDepthPyramid, &PreviousViewUniformOffset)>,
2046 indexed_work_item_buffer: &PartialBufferVec<PreprocessWorkItem>,
2047 non_indexed_work_item_buffer: &PartialBufferVec<PreprocessWorkItem>,
2048 gpu_occlusion_culling_work_item_buffers: &GpuOcclusionCullingWorkItemBuffers,
2049 ) -> Option<PhasePreprocessBindGroups> {
2050 let GpuOcclusionCullingWorkItemBuffers {
2051 late_indexed: ref late_indexed_work_item_buffer,
2052 late_non_indexed: ref late_non_indexed_work_item_buffer,
2053 ..
2054 } = *gpu_occlusion_culling_work_item_buffers;
2055
2056 let (view_depth_pyramid, previous_view_uniform_offset) =
2057 view_depth_pyramids.get(self.view).ok()?;
2058
2059 Some(PhasePreprocessBindGroups::IndirectOcclusionCulling {
2060 early_indexed: self.create_indirect_occlusion_culling_early_indexed_bind_group(
2061 view_depth_pyramid,
2062 previous_view_uniform_offset,
2063 indexed_work_item_buffer,
2064 late_indexed_work_item_buffer,
2065 ),
2066
2067 early_non_indexed: self.create_indirect_occlusion_culling_early_non_indexed_bind_group(
2068 view_depth_pyramid,
2069 previous_view_uniform_offset,
2070 non_indexed_work_item_buffer,
2071 late_non_indexed_work_item_buffer,
2072 ),
2073
2074 late_indexed: self.create_indirect_occlusion_culling_late_indexed_bind_group(
2075 view_depth_pyramid,
2076 previous_view_uniform_offset,
2077 late_indexed_work_item_buffer,
2078 ),
2079
2080 late_non_indexed: self.create_indirect_occlusion_culling_late_non_indexed_bind_group(
2081 view_depth_pyramid,
2082 previous_view_uniform_offset,
2083 late_non_indexed_work_item_buffer,
2084 ),
2085 })
2086 }
2087
2088 fn create_indirect_occlusion_culling_early_indexed_bind_group(
2091 &self,
2092 view_depth_pyramid: &ViewDepthPyramid,
2093 previous_view_uniform_offset: &PreviousViewUniformOffset,
2094 indexed_work_item_buffer: &PartialBufferVec<PreprocessWorkItem>,
2095 late_indexed_work_item_buffer: &UninitBufferVec<PreprocessWorkItem>,
2096 ) -> Option<BindGroup> {
2097 let mesh_culling_data_buffer = self.mesh_culling_data_buffer.buffer()?;
2098 let visibility_range_binding = self.visibility_range_data_buffer.binding()?;
2099 let view_uniforms_binding = self.view_uniforms.uniforms.binding()?;
2100 let previous_view_buffer = self.previous_view_uniforms.uniforms.buffer()?;
2101
2102 match (
2103 self.phase_indirect_parameters_buffers
2104 .indexed
2105 .cpu_metadata_buffer(),
2106 self.phase_indirect_parameters_buffers
2107 .indexed
2108 .gpu_metadata_buffer(),
2109 indexed_work_item_buffer.buffer(),
2110 late_indexed_work_item_buffer.buffer(),
2111 self.late_indexed_indirect_parameters_buffer.buffer(),
2112 ) {
2113 (
2114 Some(indexed_cpu_metadata_buffer),
2115 Some(indexed_gpu_metadata_buffer),
2116 Some(indexed_work_item_gpu_buffer),
2117 Some(late_indexed_work_item_gpu_buffer),
2118 Some(late_indexed_indirect_parameters_buffer),
2119 ) => {
2120 let indexed_work_item_buffer_size = NonZero::<u64>::try_from(
2124 indexed_work_item_buffer.len() as u64
2125 * u64::from(PreprocessWorkItem::min_size()),
2126 )
2127 .ok();
2128
2129 Some(
2130 self.render_device.create_bind_group(
2131 "preprocess_early_indexed_gpu_occlusion_culling_bind_group",
2132 &self.pipeline_cache.get_bind_group_layout(
2133 &self
2134 .pipelines
2135 .early_gpu_occlusion_culling_preprocess
2136 .bind_group_layout,
2137 ),
2138 &BindGroupEntries::with_indices((
2139 (3, self.current_input_buffer.as_entire_binding()),
2140 (4, self.previous_input_buffer.as_entire_binding()),
2141 (
2142 5,
2143 BindingResource::Buffer(BufferBinding {
2144 buffer: indexed_work_item_gpu_buffer,
2145 offset: 0,
2146 size: indexed_work_item_buffer_size,
2147 }),
2148 ),
2149 (6, self.data_buffer.as_entire_binding()),
2150 (7, indexed_cpu_metadata_buffer.as_entire_binding()),
2151 (8, indexed_gpu_metadata_buffer.as_entire_binding()),
2152 (9, mesh_culling_data_buffer.as_entire_binding()),
2153 (10, visibility_range_binding.clone()),
2154 (0, view_uniforms_binding.clone()),
2155 (11, &view_depth_pyramid.all_mips),
2156 (
2157 2,
2158 BufferBinding {
2159 buffer: previous_view_buffer,
2160 offset: previous_view_uniform_offset.offset as u64,
2161 size: NonZeroU64::new(size_of::<PreviousViewData>() as u64),
2162 },
2163 ),
2164 (
2165 12,
2166 BufferBinding {
2167 buffer: late_indexed_work_item_gpu_buffer,
2168 offset: 0,
2169 size: indexed_work_item_buffer_size,
2170 },
2171 ),
2172 (
2173 13,
2174 BufferBinding {
2175 buffer: late_indexed_indirect_parameters_buffer,
2176 offset: 0,
2177 size: NonZeroU64::new(
2178 late_indexed_indirect_parameters_buffer.size(),
2179 ),
2180 },
2181 ),
2182 )),
2183 ),
2184 )
2185 }
2186 _ => None,
2187 }
2188 }
2189
2190 fn create_indirect_occlusion_culling_early_non_indexed_bind_group(
2193 &self,
2194 view_depth_pyramid: &ViewDepthPyramid,
2195 previous_view_uniform_offset: &PreviousViewUniformOffset,
2196 non_indexed_work_item_buffer: &PartialBufferVec<PreprocessWorkItem>,
2197 late_non_indexed_work_item_buffer: &UninitBufferVec<PreprocessWorkItem>,
2198 ) -> Option<BindGroup> {
2199 let mesh_culling_data_buffer = self.mesh_culling_data_buffer.buffer()?;
2200 let visibility_range_binding = self.visibility_range_data_buffer.binding()?;
2201 let view_uniforms_binding = self.view_uniforms.uniforms.binding()?;
2202 let previous_view_buffer = self.previous_view_uniforms.uniforms.buffer()?;
2203
2204 match (
2205 self.phase_indirect_parameters_buffers
2206 .non_indexed
2207 .cpu_metadata_buffer(),
2208 self.phase_indirect_parameters_buffers
2209 .non_indexed
2210 .gpu_metadata_buffer(),
2211 non_indexed_work_item_buffer.buffer(),
2212 late_non_indexed_work_item_buffer.buffer(),
2213 self.late_non_indexed_indirect_parameters_buffer.buffer(),
2214 ) {
2215 (
2216 Some(non_indexed_cpu_metadata_buffer),
2217 Some(non_indexed_gpu_metadata_buffer),
2218 Some(non_indexed_work_item_gpu_buffer),
2219 Some(late_non_indexed_work_item_buffer),
2220 Some(late_non_indexed_indirect_parameters_buffer),
2221 ) => {
2222 let non_indexed_work_item_buffer_size = NonZero::<u64>::try_from(
2226 non_indexed_work_item_buffer.len() as u64
2227 * u64::from(PreprocessWorkItem::min_size()),
2228 )
2229 .ok();
2230
2231 Some(
2232 self.render_device.create_bind_group(
2233 "preprocess_early_non_indexed_gpu_occlusion_culling_bind_group",
2234 &self.pipeline_cache.get_bind_group_layout(
2235 &self
2236 .pipelines
2237 .early_gpu_occlusion_culling_preprocess
2238 .bind_group_layout,
2239 ),
2240 &BindGroupEntries::with_indices((
2241 (3, self.current_input_buffer.as_entire_binding()),
2242 (4, self.previous_input_buffer.as_entire_binding()),
2243 (
2244 5,
2245 BindingResource::Buffer(BufferBinding {
2246 buffer: non_indexed_work_item_gpu_buffer,
2247 offset: 0,
2248 size: non_indexed_work_item_buffer_size,
2249 }),
2250 ),
2251 (6, self.data_buffer.as_entire_binding()),
2252 (7, non_indexed_cpu_metadata_buffer.as_entire_binding()),
2253 (8, non_indexed_gpu_metadata_buffer.as_entire_binding()),
2254 (9, mesh_culling_data_buffer.as_entire_binding()),
2255 (10, visibility_range_binding.clone()),
2256 (0, view_uniforms_binding.clone()),
2257 (11, &view_depth_pyramid.all_mips),
2258 (
2259 2,
2260 BufferBinding {
2261 buffer: previous_view_buffer,
2262 offset: previous_view_uniform_offset.offset as u64,
2263 size: NonZeroU64::new(size_of::<PreviousViewData>() as u64),
2264 },
2265 ),
2266 (
2267 12,
2268 BufferBinding {
2269 buffer: late_non_indexed_work_item_buffer,
2270 offset: 0,
2271 size: non_indexed_work_item_buffer_size,
2272 },
2273 ),
2274 (
2275 13,
2276 BufferBinding {
2277 buffer: late_non_indexed_indirect_parameters_buffer,
2278 offset: 0,
2279 size: NonZeroU64::new(
2280 late_non_indexed_indirect_parameters_buffer.size(),
2281 ),
2282 },
2283 ),
2284 )),
2285 ),
2286 )
2287 }
2288 _ => None,
2289 }
2290 }
2291
2292 fn create_indirect_occlusion_culling_late_indexed_bind_group(
2295 &self,
2296 view_depth_pyramid: &ViewDepthPyramid,
2297 previous_view_uniform_offset: &PreviousViewUniformOffset,
2298 late_indexed_work_item_buffer: &UninitBufferVec<PreprocessWorkItem>,
2299 ) -> Option<BindGroup> {
2300 let mesh_culling_data_buffer = self.mesh_culling_data_buffer.buffer()?;
2301 let visibility_range_binding = self.visibility_range_data_buffer.binding()?;
2302 let view_uniforms_binding = self.view_uniforms.uniforms.binding()?;
2303 let previous_view_buffer = self.previous_view_uniforms.uniforms.buffer()?;
2304
2305 match (
2306 self.phase_indirect_parameters_buffers
2307 .indexed
2308 .cpu_metadata_buffer(),
2309 self.phase_indirect_parameters_buffers
2310 .indexed
2311 .gpu_metadata_buffer(),
2312 late_indexed_work_item_buffer.buffer(),
2313 self.late_indexed_indirect_parameters_buffer.buffer(),
2314 ) {
2315 (
2316 Some(indexed_cpu_metadata_buffer),
2317 Some(indexed_gpu_metadata_buffer),
2318 Some(late_indexed_work_item_gpu_buffer),
2319 Some(late_indexed_indirect_parameters_buffer),
2320 ) => {
2321 let late_indexed_work_item_buffer_size = NonZero::<u64>::try_from(
2325 late_indexed_work_item_buffer.len() as u64
2326 * u64::from(PreprocessWorkItem::min_size()),
2327 )
2328 .ok();
2329
2330 Some(
2331 self.render_device.create_bind_group(
2332 "preprocess_late_indexed_gpu_occlusion_culling_bind_group",
2333 &self.pipeline_cache.get_bind_group_layout(
2334 &self
2335 .pipelines
2336 .late_gpu_occlusion_culling_preprocess
2337 .bind_group_layout,
2338 ),
2339 &BindGroupEntries::with_indices((
2340 (3, self.current_input_buffer.as_entire_binding()),
2341 (4, self.previous_input_buffer.as_entire_binding()),
2342 (
2343 5,
2344 BindingResource::Buffer(BufferBinding {
2345 buffer: late_indexed_work_item_gpu_buffer,
2346 offset: 0,
2347 size: late_indexed_work_item_buffer_size,
2348 }),
2349 ),
2350 (6, self.data_buffer.as_entire_binding()),
2351 (7, indexed_cpu_metadata_buffer.as_entire_binding()),
2352 (8, indexed_gpu_metadata_buffer.as_entire_binding()),
2353 (9, mesh_culling_data_buffer.as_entire_binding()),
2354 (10, visibility_range_binding.clone()),
2355 (0, view_uniforms_binding.clone()),
2356 (11, &view_depth_pyramid.all_mips),
2357 (
2358 2,
2359 BufferBinding {
2360 buffer: previous_view_buffer,
2361 offset: previous_view_uniform_offset.offset as u64,
2362 size: NonZeroU64::new(size_of::<PreviousViewData>() as u64),
2363 },
2364 ),
2365 (
2366 13,
2367 BufferBinding {
2368 buffer: late_indexed_indirect_parameters_buffer,
2369 offset: 0,
2370 size: NonZeroU64::new(
2371 late_indexed_indirect_parameters_buffer.size(),
2372 ),
2373 },
2374 ),
2375 )),
2376 ),
2377 )
2378 }
2379 _ => None,
2380 }
2381 }
2382
2383 fn create_indirect_occlusion_culling_late_non_indexed_bind_group(
2386 &self,
2387 view_depth_pyramid: &ViewDepthPyramid,
2388 previous_view_uniform_offset: &PreviousViewUniformOffset,
2389 late_non_indexed_work_item_buffer: &UninitBufferVec<PreprocessWorkItem>,
2390 ) -> Option<BindGroup> {
2391 let mesh_culling_data_buffer = self.mesh_culling_data_buffer.buffer()?;
2392 let visibility_range_binding = self.visibility_range_data_buffer.binding()?;
2393 let view_uniforms_binding = self.view_uniforms.uniforms.binding()?;
2394 let previous_view_buffer = self.previous_view_uniforms.uniforms.buffer()?;
2395
2396 match (
2397 self.phase_indirect_parameters_buffers
2398 .non_indexed
2399 .cpu_metadata_buffer(),
2400 self.phase_indirect_parameters_buffers
2401 .non_indexed
2402 .gpu_metadata_buffer(),
2403 late_non_indexed_work_item_buffer.buffer(),
2404 self.late_non_indexed_indirect_parameters_buffer.buffer(),
2405 ) {
2406 (
2407 Some(non_indexed_cpu_metadata_buffer),
2408 Some(non_indexed_gpu_metadata_buffer),
2409 Some(non_indexed_work_item_gpu_buffer),
2410 Some(late_non_indexed_indirect_parameters_buffer),
2411 ) => {
2412 let non_indexed_work_item_buffer_size = NonZero::<u64>::try_from(
2416 late_non_indexed_work_item_buffer.len() as u64
2417 * u64::from(PreprocessWorkItem::min_size()),
2418 )
2419 .ok();
2420
2421 Some(
2422 self.render_device.create_bind_group(
2423 "preprocess_late_non_indexed_gpu_occlusion_culling_bind_group",
2424 &self.pipeline_cache.get_bind_group_layout(
2425 &self
2426 .pipelines
2427 .late_gpu_occlusion_culling_preprocess
2428 .bind_group_layout,
2429 ),
2430 &BindGroupEntries::with_indices((
2431 (3, self.current_input_buffer.as_entire_binding()),
2432 (4, self.previous_input_buffer.as_entire_binding()),
2433 (
2434 5,
2435 BindingResource::Buffer(BufferBinding {
2436 buffer: non_indexed_work_item_gpu_buffer,
2437 offset: 0,
2438 size: non_indexed_work_item_buffer_size,
2439 }),
2440 ),
2441 (6, self.data_buffer.as_entire_binding()),
2442 (7, non_indexed_cpu_metadata_buffer.as_entire_binding()),
2443 (8, non_indexed_gpu_metadata_buffer.as_entire_binding()),
2444 (9, mesh_culling_data_buffer.as_entire_binding()),
2445 (10, visibility_range_binding.clone()),
2446 (0, view_uniforms_binding.clone()),
2447 (11, &view_depth_pyramid.all_mips),
2448 (
2449 2,
2450 BufferBinding {
2451 buffer: previous_view_buffer,
2452 offset: previous_view_uniform_offset.offset as u64,
2453 size: NonZeroU64::new(size_of::<PreviousViewData>() as u64),
2454 },
2455 ),
2456 (
2457 13,
2458 BufferBinding {
2459 buffer: late_non_indexed_indirect_parameters_buffer,
2460 offset: 0,
2461 size: NonZeroU64::new(
2462 late_non_indexed_indirect_parameters_buffer.size(),
2463 ),
2464 },
2465 ),
2466 )),
2467 ),
2468 )
2469 }
2470 _ => None,
2471 }
2472 }
2473
2474 fn create_indirect_frustum_culling_preprocess_bind_groups(
2477 &self,
2478 indexed_work_item_buffer: &PartialBufferVec<PreprocessWorkItem>,
2479 non_indexed_work_item_buffer: &PartialBufferVec<PreprocessWorkItem>,
2480 ) -> Option<PhasePreprocessBindGroups> {
2481 Some(PhasePreprocessBindGroups::IndirectFrustumCulling {
2482 indexed: self
2483 .create_indirect_frustum_culling_indexed_bind_group(indexed_work_item_buffer),
2484 non_indexed: self.create_indirect_frustum_culling_non_indexed_bind_group(
2485 non_indexed_work_item_buffer,
2486 ),
2487 })
2488 }
2489
2490 fn create_indirect_frustum_culling_indexed_bind_group(
2493 &self,
2494 indexed_work_item_buffer: &PartialBufferVec<PreprocessWorkItem>,
2495 ) -> Option<BindGroup> {
2496 let mesh_culling_data_buffer = self.mesh_culling_data_buffer.buffer()?;
2497 let visibility_range_binding = self.visibility_range_data_buffer.binding()?;
2498 let view_uniforms_binding = self.view_uniforms.uniforms.binding()?;
2499
2500 match (
2501 self.phase_indirect_parameters_buffers
2502 .indexed
2503 .cpu_metadata_buffer(),
2504 self.phase_indirect_parameters_buffers
2505 .indexed
2506 .gpu_metadata_buffer(),
2507 indexed_work_item_buffer.buffer(),
2508 ) {
2509 (
2510 Some(indexed_cpu_metadata_buffer),
2511 Some(indexed_gpu_metadata_buffer),
2512 Some(indexed_work_item_gpu_buffer),
2513 ) => {
2514 let indexed_work_item_buffer_size = NonZero::<u64>::try_from(
2518 indexed_work_item_buffer.len() as u64
2519 * u64::from(PreprocessWorkItem::min_size()),
2520 )
2521 .ok();
2522
2523 Some(
2524 self.render_device.create_bind_group(
2525 "preprocess_gpu_indexed_frustum_culling_bind_group",
2526 &self.pipeline_cache.get_bind_group_layout(
2527 &self
2528 .pipelines
2529 .gpu_frustum_culling_preprocess
2530 .bind_group_layout,
2531 ),
2532 &BindGroupEntries::with_indices((
2533 (3, self.current_input_buffer.as_entire_binding()),
2534 (4, self.previous_input_buffer.as_entire_binding()),
2535 (
2536 5,
2537 BindingResource::Buffer(BufferBinding {
2538 buffer: indexed_work_item_gpu_buffer,
2539 offset: 0,
2540 size: indexed_work_item_buffer_size,
2541 }),
2542 ),
2543 (6, self.data_buffer.as_entire_binding()),
2544 (7, indexed_cpu_metadata_buffer.as_entire_binding()),
2545 (8, indexed_gpu_metadata_buffer.as_entire_binding()),
2546 (9, mesh_culling_data_buffer.as_entire_binding()),
2547 (10, visibility_range_binding.clone()),
2548 (0, view_uniforms_binding.clone()),
2549 )),
2550 ),
2551 )
2552 }
2553 _ => None,
2554 }
2555 }
2556
2557 fn create_indirect_frustum_culling_non_indexed_bind_group(
2560 &self,
2561 non_indexed_work_item_buffer: &PartialBufferVec<PreprocessWorkItem>,
2562 ) -> Option<BindGroup> {
2563 let mesh_culling_data_buffer = self.mesh_culling_data_buffer.buffer()?;
2564 let visibility_range_binding = self.visibility_range_data_buffer.binding()?;
2565 let view_uniforms_binding = self.view_uniforms.uniforms.binding()?;
2566
2567 match (
2568 self.phase_indirect_parameters_buffers
2569 .non_indexed
2570 .cpu_metadata_buffer(),
2571 self.phase_indirect_parameters_buffers
2572 .non_indexed
2573 .gpu_metadata_buffer(),
2574 non_indexed_work_item_buffer.buffer(),
2575 ) {
2576 (
2577 Some(non_indexed_cpu_metadata_buffer),
2578 Some(non_indexed_gpu_metadata_buffer),
2579 Some(non_indexed_work_item_gpu_buffer),
2580 ) => {
2581 let non_indexed_work_item_buffer_size = NonZero::<u64>::try_from(
2585 non_indexed_work_item_buffer.len() as u64
2586 * u64::from(PreprocessWorkItem::min_size()),
2587 )
2588 .ok();
2589
2590 Some(
2591 self.render_device.create_bind_group(
2592 "preprocess_gpu_non_indexed_frustum_culling_bind_group",
2593 &self.pipeline_cache.get_bind_group_layout(
2594 &self
2595 .pipelines
2596 .gpu_frustum_culling_preprocess
2597 .bind_group_layout,
2598 ),
2599 &BindGroupEntries::with_indices((
2600 (3, self.current_input_buffer.as_entire_binding()),
2601 (4, self.previous_input_buffer.as_entire_binding()),
2602 (
2603 5,
2604 BindingResource::Buffer(BufferBinding {
2605 buffer: non_indexed_work_item_gpu_buffer,
2606 offset: 0,
2607 size: non_indexed_work_item_buffer_size,
2608 }),
2609 ),
2610 (6, self.data_buffer.as_entire_binding()),
2611 (7, non_indexed_cpu_metadata_buffer.as_entire_binding()),
2612 (8, non_indexed_gpu_metadata_buffer.as_entire_binding()),
2613 (9, mesh_culling_data_buffer.as_entire_binding()),
2614 (10, visibility_range_binding.clone()),
2615 (0, view_uniforms_binding.clone()),
2616 )),
2617 ),
2618 )
2619 }
2620 _ => None,
2621 }
2622 }
2623}
2624
2625fn create_build_indirect_parameters_bind_groups(
2629 commands: &mut Commands,
2630 render_device: &RenderDevice,
2631 pipeline_cache: &PipelineCache,
2632 pipelines: &PreprocessPipelines,
2633 current_input_buffer: &Buffer,
2634 indirect_parameters_buffers: &IndirectParametersBuffers,
2635) {
2636 let mut build_indirect_parameters_bind_groups = BuildIndirectParametersBindGroups::new();
2637
2638 for (phase_type_id, phase_indirect_parameters_buffer) in indirect_parameters_buffers.iter() {
2639 build_indirect_parameters_bind_groups.insert(
2640 *phase_type_id,
2641 PhaseBuildIndirectParametersBindGroups {
2642 reset_indexed_indirect_batch_sets: phase_indirect_parameters_buffer
2643 .indexed
2644 .batch_sets_buffer()
2645 .map(|indexed_batch_sets_buffer| {
2646 render_device.create_bind_group(
2647 "reset_indexed_indirect_batch_sets_bind_group",
2648 &pipeline_cache.get_bind_group_layout(
2651 &pipelines
2652 .early_phase
2653 .reset_indirect_batch_sets
2654 .bind_group_layout,
2655 ),
2656 &BindGroupEntries::sequential((
2657 indexed_batch_sets_buffer.as_entire_binding(),
2658 )),
2659 )
2660 }),
2661
2662 reset_non_indexed_indirect_batch_sets: phase_indirect_parameters_buffer
2663 .non_indexed
2664 .batch_sets_buffer()
2665 .map(|non_indexed_batch_sets_buffer| {
2666 render_device.create_bind_group(
2667 "reset_non_indexed_indirect_batch_sets_bind_group",
2668 &pipeline_cache.get_bind_group_layout(
2671 &pipelines
2672 .early_phase
2673 .reset_indirect_batch_sets
2674 .bind_group_layout,
2675 ),
2676 &BindGroupEntries::sequential((
2677 non_indexed_batch_sets_buffer.as_entire_binding(),
2678 )),
2679 )
2680 }),
2681
2682 build_indexed_indirect: match (
2683 phase_indirect_parameters_buffer
2684 .indexed
2685 .cpu_metadata_buffer(),
2686 phase_indirect_parameters_buffer
2687 .indexed
2688 .gpu_metadata_buffer(),
2689 phase_indirect_parameters_buffer.indexed.data_buffer(),
2690 phase_indirect_parameters_buffer.indexed.batch_sets_buffer(),
2691 ) {
2692 (
2693 Some(indexed_indirect_parameters_cpu_metadata_buffer),
2694 Some(indexed_indirect_parameters_gpu_metadata_buffer),
2695 Some(indexed_indirect_parameters_data_buffer),
2696 Some(indexed_batch_sets_buffer),
2697 ) => Some(
2698 render_device.create_bind_group(
2699 "build_indexed_indirect_parameters_bind_group",
2700 &pipeline_cache.get_bind_group_layout(
2703 &pipelines
2704 .gpu_frustum_culling_build_indexed_indirect_params
2705 .bind_group_layout,
2706 ),
2707 &BindGroupEntries::sequential((
2708 current_input_buffer.as_entire_binding(),
2709 BufferBinding {
2712 buffer: indexed_indirect_parameters_cpu_metadata_buffer,
2713 offset: 0,
2714 size: NonZeroU64::new(
2715 phase_indirect_parameters_buffer.indexed.batch_count()
2716 as u64
2717 * size_of::<IndirectParametersCpuMetadata>() as u64,
2718 ),
2719 },
2720 BufferBinding {
2721 buffer: indexed_indirect_parameters_gpu_metadata_buffer,
2722 offset: 0,
2723 size: NonZeroU64::new(
2724 phase_indirect_parameters_buffer.indexed.batch_count()
2725 as u64
2726 * size_of::<IndirectParametersGpuMetadata>() as u64,
2727 ),
2728 },
2729 indexed_batch_sets_buffer.as_entire_binding(),
2730 indexed_indirect_parameters_data_buffer.as_entire_binding(),
2731 )),
2732 ),
2733 ),
2734 _ => None,
2735 },
2736
2737 build_non_indexed_indirect: match (
2738 phase_indirect_parameters_buffer
2739 .non_indexed
2740 .cpu_metadata_buffer(),
2741 phase_indirect_parameters_buffer
2742 .non_indexed
2743 .gpu_metadata_buffer(),
2744 phase_indirect_parameters_buffer.non_indexed.data_buffer(),
2745 phase_indirect_parameters_buffer
2746 .non_indexed
2747 .batch_sets_buffer(),
2748 ) {
2749 (
2750 Some(non_indexed_indirect_parameters_cpu_metadata_buffer),
2751 Some(non_indexed_indirect_parameters_gpu_metadata_buffer),
2752 Some(non_indexed_indirect_parameters_data_buffer),
2753 Some(non_indexed_batch_sets_buffer),
2754 ) => Some(
2755 render_device.create_bind_group(
2756 "build_non_indexed_indirect_parameters_bind_group",
2757 &pipeline_cache.get_bind_group_layout(
2760 &pipelines
2761 .gpu_frustum_culling_build_non_indexed_indirect_params
2762 .bind_group_layout,
2763 ),
2764 &BindGroupEntries::sequential((
2765 current_input_buffer.as_entire_binding(),
2766 BufferBinding {
2769 buffer: non_indexed_indirect_parameters_cpu_metadata_buffer,
2770 offset: 0,
2771 size: NonZeroU64::new(
2772 phase_indirect_parameters_buffer.non_indexed.batch_count()
2773 as u64
2774 * size_of::<IndirectParametersCpuMetadata>() as u64,
2775 ),
2776 },
2777 BufferBinding {
2778 buffer: non_indexed_indirect_parameters_gpu_metadata_buffer,
2779 offset: 0,
2780 size: NonZeroU64::new(
2781 phase_indirect_parameters_buffer.non_indexed.batch_count()
2782 as u64
2783 * size_of::<IndirectParametersGpuMetadata>() as u64,
2784 ),
2785 },
2786 non_indexed_batch_sets_buffer.as_entire_binding(),
2787 non_indexed_indirect_parameters_data_buffer.as_entire_binding(),
2788 )),
2789 ),
2790 ),
2791 _ => None,
2792 },
2793 },
2794 );
2795 }
2796
2797 commands.insert_resource(build_indirect_parameters_bind_groups);
2798}
2799
2800fn create_bin_unpacking_bind_groups(
2803 bin_unpacking_bind_groups: &mut BinUnpackingBindGroups,
2804 render_device: &RenderDevice,
2805 pipeline_cache: &PipelineCache,
2806 preprocess_pipelines: &PreprocessPipelines,
2807 indirect_parameters_buffers: &IndirectParametersBuffers,
2808 phase_instance_buffers: &TypeIdMap<UntypedPhaseBatchedInstanceBuffers<MeshUniform>>,
2809 bin_unpacking_buffers: &BinUnpackingBuffers,
2810 view_entity: &RetainedViewEntity,
2811) {
2812 let Some(bin_unpacking_metadata_buffer) = bin_unpacking_buffers.bin_unpacking_metadata.buffer()
2813 else {
2814 return;
2815 };
2816
2817 for phase_type_id in indirect_parameters_buffers.keys() {
2819 let bin_unpacking_buffers_key = BinUnpackingBuffersKey {
2820 phase: *phase_type_id,
2821 view: *view_entity,
2822 };
2823
2824 let Some(phase_batched_instance_buffers) = phase_instance_buffers.get(phase_type_id) else {
2826 continue;
2827 };
2828 let Some(work_item_buffers) = phase_batched_instance_buffers
2829 .work_item_buffers
2830 .get(view_entity)
2831 else {
2832 continue;
2833 };
2834 let Some(view_phase_bin_unpacking_buffers) = bin_unpacking_buffers
2835 .view_phase_buffers
2836 .get(&bin_unpacking_buffers_key)
2837 else {
2838 continue;
2839 };
2840
2841 let maybe_indexed_work_item_buffer = match *work_item_buffers {
2843 PreprocessWorkItemBuffers::Direct(ref raw_buffer_vec) => raw_buffer_vec.buffer(),
2844 PreprocessWorkItemBuffers::Indirect { ref indexed, .. } => indexed.buffer(),
2845 };
2846 let maybe_non_indexed_work_item_buffer = match *work_item_buffers {
2847 PreprocessWorkItemBuffers::Direct(ref raw_buffer_vec) => raw_buffer_vec.buffer(),
2848 PreprocessWorkItemBuffers::Indirect {
2849 ref non_indexed, ..
2850 } => non_indexed.buffer(),
2851 };
2852
2853 bin_unpacking_bind_groups.insert(
2855 bin_unpacking_buffers_key,
2856 ViewPhaseBinUnpackingBindGroups {
2857 indexed: match maybe_indexed_work_item_buffer {
2858 Some(indexed_work_item_buffer) => view_phase_bin_unpacking_buffers
2859 .indexed_unpacking_jobs
2860 .iter()
2861 .map(|job| {
2862 create_bin_unpacking_bind_group(
2863 render_device,
2864 preprocess_pipelines,
2865 pipeline_cache,
2866 job,
2867 bin_unpacking_metadata_buffer,
2868 indexed_work_item_buffer,
2869 true,
2870 )
2871 })
2872 .collect(),
2873 None => vec![],
2874 },
2875 non_indexed: match maybe_non_indexed_work_item_buffer {
2876 Some(non_indexed_work_item_buffer) => view_phase_bin_unpacking_buffers
2877 .non_indexed_unpacking_jobs
2878 .iter()
2879 .map(|job| {
2880 create_bin_unpacking_bind_group(
2881 render_device,
2882 preprocess_pipelines,
2883 pipeline_cache,
2884 job,
2885 bin_unpacking_metadata_buffer,
2886 non_indexed_work_item_buffer,
2887 false,
2888 )
2889 })
2890 .collect(),
2891 None => vec![],
2892 },
2893 },
2894 );
2895 }
2896}
2897
2898fn create_bin_unpacking_bind_group(
2901 render_device: &RenderDevice,
2902 preprocess_pipelines: &PreprocessPipelines,
2903 pipeline_cache: &PipelineCache,
2904 job: &BinUnpackingJob,
2905 bin_unpacking_metadata_buffer: &Buffer,
2906 work_item_buffer: &Buffer,
2907 indexed: bool,
2908) -> ViewPhaseBinUnpackingBindGroup {
2909 let bind_group = render_device.create_bind_group(
2910 if indexed {
2911 "bin unpacking indexed bind group"
2912 } else {
2913 "bin unpacking non-indexed bind group"
2914 },
2915 &pipeline_cache
2916 .get_bind_group_layout(&preprocess_pipelines.bin_unpacking.bind_group_layout),
2917 &BindGroupEntries::sequential((
2918 BindingResource::Buffer(BufferBinding {
2922 buffer: bin_unpacking_metadata_buffer,
2923 offset: job.bin_unpacking_metadata_index.uniform_offset() as u64,
2924 size: NonZeroU64::new(size_of::<GpuBinUnpackingMetadata>() as u64),
2925 }),
2926 job.render_binned_mesh_instance_buffer.as_entire_binding(),
2930 work_item_buffer.as_entire_binding(),
2934 job.bin_index_to_indirect_parameters_offset_buffer
2938 .as_entire_binding(),
2939 )),
2940 );
2941 ViewPhaseBinUnpackingBindGroup {
2942 metadata_index: job.bin_unpacking_metadata_index,
2943 bind_group,
2944 mesh_instance_count: job.mesh_instance_count,
2945 }
2946}
2947
2948pub fn write_mesh_culling_data_buffer(
2950 render_device: Res<RenderDevice>,
2951 render_queue: Res<RenderQueue>,
2952 mut mesh_culling_data_buffer: ResMut<MeshCullingDataBuffer>,
2953 pipeline_cache: Res<PipelineCache>,
2954 mut sparse_buffer_update_jobs: ResMut<SparseBufferUpdateJobs>,
2955 mut sparse_buffer_update_bind_groups: ResMut<SparseBufferUpdateBindGroups>,
2956 sparse_buffer_update_pipelines: Res<SparseBufferUpdatePipelines>,
2957) {
2958 mesh_culling_data_buffer.write_buffers(&render_device, &render_queue);
2959 mesh_culling_data_buffer.prepare_to_populate_buffers(
2960 &render_device,
2961 &pipeline_cache,
2962 &mut sparse_buffer_update_jobs,
2963 &mut sparse_buffer_update_bind_groups,
2964 &sparse_buffer_update_pipelines,
2965 );
2966}