Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_pbr/src/render/gpu_preprocess.rs
6600 views
1
//! GPU mesh preprocessing.
2
//!
3
//! This is an optional pass that uses a compute shader to reduce the amount of
4
//! data that has to be transferred from the CPU to the GPU. When enabled,
5
//! instead of transferring [`MeshUniform`]s to the GPU, we transfer the smaller
6
//! [`MeshInputUniform`]s instead and use the GPU to calculate the remaining
7
//! derived fields in [`MeshUniform`].
8
9
use core::num::{NonZero, NonZeroU64};
10
11
use bevy_app::{App, Plugin};
12
use bevy_asset::{embedded_asset, load_embedded_asset, Handle};
13
use bevy_core_pipeline::{
14
core_3d::graph::{Core3d, Node3d},
15
experimental::mip_generation::ViewDepthPyramid,
16
prepass::{DepthPrepass, PreviousViewData, PreviousViewUniformOffset, PreviousViewUniforms},
17
};
18
use bevy_derive::{Deref, DerefMut};
19
use bevy_ecs::{
20
component::Component,
21
entity::Entity,
22
prelude::resource_exists,
23
query::{Has, Or, QueryState, With, Without},
24
resource::Resource,
25
schedule::IntoScheduleConfigs as _,
26
system::{lifetimeless::Read, Commands, Query, Res, ResMut},
27
world::{FromWorld, World},
28
};
29
use bevy_render::{
30
batching::gpu_preprocessing::{
31
BatchedInstanceBuffers, GpuOcclusionCullingWorkItemBuffers, GpuPreprocessingMode,
32
GpuPreprocessingSupport, IndirectBatchSet, IndirectParametersBuffers,
33
IndirectParametersCpuMetadata, IndirectParametersGpuMetadata, IndirectParametersIndexed,
34
IndirectParametersNonIndexed, LatePreprocessWorkItemIndirectParameters, PreprocessWorkItem,
35
PreprocessWorkItemBuffers, UntypedPhaseBatchedInstanceBuffers,
36
UntypedPhaseIndirectParametersBuffers,
37
},
38
diagnostic::RecordDiagnostics,
39
experimental::occlusion_culling::OcclusionCulling,
40
render_graph::{Node, NodeRunError, RenderGraphContext, RenderGraphExt},
41
render_resource::{
42
binding_types::{storage_buffer, storage_buffer_read_only, texture_2d, uniform_buffer},
43
BindGroup, BindGroupEntries, BindGroupLayout, BindingResource, Buffer, BufferBinding,
44
CachedComputePipelineId, ComputePassDescriptor, ComputePipelineDescriptor,
45
DynamicBindGroupLayoutEntries, PipelineCache, PushConstantRange, RawBufferVec,
46
ShaderStages, ShaderType, SpecializedComputePipeline, SpecializedComputePipelines,
47
TextureSampleType, UninitBufferVec,
48
},
49
renderer::{RenderContext, RenderDevice, RenderQueue},
50
settings::WgpuFeatures,
51
view::{ExtractedView, NoIndirectDrawing, ViewUniform, ViewUniformOffset, ViewUniforms},
52
Render, RenderApp, RenderSystems,
53
};
54
use bevy_shader::Shader;
55
use bevy_utils::{default, TypeIdMap};
56
use bitflags::bitflags;
57
use smallvec::{smallvec, SmallVec};
58
use tracing::warn;
59
60
use crate::{
61
graph::NodePbr, MeshCullingData, MeshCullingDataBuffer, MeshInputUniform, MeshUniform,
62
};
63
64
use super::{ShadowView, ViewLightEntities};
65
66
/// The GPU workgroup size.
67
const WORKGROUP_SIZE: usize = 64;
68
69
/// A plugin that builds mesh uniforms on GPU.
70
///
71
/// This will only be added if the platform supports compute shaders (e.g. not
72
/// on WebGL 2).
73
pub struct GpuMeshPreprocessPlugin {
74
/// Whether we're building [`MeshUniform`]s on GPU.
75
///
76
/// This requires compute shader support and so will be forcibly disabled if
77
/// the platform doesn't support those.
78
pub use_gpu_instance_buffer_builder: bool,
79
}
80
81
/// The render node that clears out the GPU-side indirect metadata buffers.
82
///
83
/// This is only used when indirect drawing is enabled.
84
#[derive(Default)]
85
pub struct ClearIndirectParametersMetadataNode;
86
87
/// The render node for the first mesh preprocessing pass.
88
///
89
/// This pass runs a compute shader to cull meshes outside the view frustum (if
90
/// that wasn't done by the CPU), cull meshes that weren't visible last frame
91
/// (if occlusion culling is on), transform them, and, if indirect drawing is
92
/// on, populate indirect draw parameter metadata for the subsequent
93
/// [`EarlyPrepassBuildIndirectParametersNode`].
94
pub struct EarlyGpuPreprocessNode {
95
view_query: QueryState<
96
(
97
Read<ExtractedView>,
98
Option<Read<PreprocessBindGroups>>,
99
Option<Read<ViewUniformOffset>>,
100
Has<NoIndirectDrawing>,
101
Has<OcclusionCulling>,
102
),
103
Without<SkipGpuPreprocess>,
104
>,
105
main_view_query: QueryState<Read<ViewLightEntities>>,
106
}
107
108
/// The render node for the second mesh preprocessing pass.
109
///
110
/// This pass runs a compute shader to cull meshes outside the view frustum (if
111
/// that wasn't done by the CPU), cull meshes that were neither visible last
112
/// frame nor visible this frame (if occlusion culling is on), transform them,
113
/// and, if indirect drawing is on, populate the indirect draw parameter
114
/// metadata for the subsequent [`LatePrepassBuildIndirectParametersNode`].
115
pub struct LateGpuPreprocessNode {
116
view_query: QueryState<
117
(
118
Read<ExtractedView>,
119
Read<PreprocessBindGroups>,
120
Read<ViewUniformOffset>,
121
),
122
(
123
Without<SkipGpuPreprocess>,
124
Without<NoIndirectDrawing>,
125
With<OcclusionCulling>,
126
With<DepthPrepass>,
127
),
128
>,
129
}
130
131
/// The render node for the part of the indirect parameter building pass that
132
/// draws the meshes visible from the previous frame.
133
///
134
/// This node runs a compute shader on the output of the
135
/// [`EarlyGpuPreprocessNode`] in order to transform the
136
/// [`IndirectParametersGpuMetadata`] into properly-formatted
137
/// [`IndirectParametersIndexed`] and [`IndirectParametersNonIndexed`].
138
pub struct EarlyPrepassBuildIndirectParametersNode {
139
view_query: QueryState<
140
Read<PreprocessBindGroups>,
141
(
142
Without<SkipGpuPreprocess>,
143
Without<NoIndirectDrawing>,
144
Or<(With<DepthPrepass>, With<ShadowView>)>,
145
),
146
>,
147
}
148
149
/// The render node for the part of the indirect parameter building pass that
150
/// draws the meshes that are potentially visible on this frame but weren't
151
/// visible on the previous frame.
152
///
153
/// This node runs a compute shader on the output of the
154
/// [`LateGpuPreprocessNode`] in order to transform the
155
/// [`IndirectParametersGpuMetadata`] into properly-formatted
156
/// [`IndirectParametersIndexed`] and [`IndirectParametersNonIndexed`].
157
pub struct LatePrepassBuildIndirectParametersNode {
158
view_query: QueryState<
159
Read<PreprocessBindGroups>,
160
(
161
Without<SkipGpuPreprocess>,
162
Without<NoIndirectDrawing>,
163
Or<(With<DepthPrepass>, With<ShadowView>)>,
164
With<OcclusionCulling>,
165
),
166
>,
167
}
168
169
/// The render node for the part of the indirect parameter building pass that
170
/// draws all meshes, both those that are newly-visible on this frame and those
171
/// that were visible last frame.
172
///
173
/// This node runs a compute shader on the output of the
174
/// [`EarlyGpuPreprocessNode`] and [`LateGpuPreprocessNode`] in order to
175
/// transform the [`IndirectParametersGpuMetadata`] into properly-formatted
176
/// [`IndirectParametersIndexed`] and [`IndirectParametersNonIndexed`].
177
pub struct MainBuildIndirectParametersNode {
178
view_query: QueryState<
179
Read<PreprocessBindGroups>,
180
(Without<SkipGpuPreprocess>, Without<NoIndirectDrawing>),
181
>,
182
}
183
184
/// The compute shader pipelines for the GPU mesh preprocessing and indirect
185
/// parameter building passes.
186
#[derive(Resource)]
187
pub struct PreprocessPipelines {
188
/// The pipeline used for CPU culling. This pipeline doesn't populate
189
/// indirect parameter metadata.
190
pub direct_preprocess: PreprocessPipeline,
191
/// The pipeline used for mesh preprocessing when GPU frustum culling is in
192
/// use, but occlusion culling isn't.
193
///
194
/// This pipeline populates indirect parameter metadata.
195
pub gpu_frustum_culling_preprocess: PreprocessPipeline,
196
/// The pipeline used for the first phase of occlusion culling.
197
///
198
/// This pipeline culls, transforms meshes, and populates indirect parameter
199
/// metadata.
200
pub early_gpu_occlusion_culling_preprocess: PreprocessPipeline,
201
/// The pipeline used for the second phase of occlusion culling.
202
///
203
/// This pipeline culls, transforms meshes, and populates indirect parameter
204
/// metadata.
205
pub late_gpu_occlusion_culling_preprocess: PreprocessPipeline,
206
/// The pipeline that builds indirect draw parameters for indexed meshes,
207
/// when frustum culling is enabled but occlusion culling *isn't* enabled.
208
pub gpu_frustum_culling_build_indexed_indirect_params: BuildIndirectParametersPipeline,
209
/// The pipeline that builds indirect draw parameters for non-indexed
210
/// meshes, when frustum culling is enabled but occlusion culling *isn't*
211
/// enabled.
212
pub gpu_frustum_culling_build_non_indexed_indirect_params: BuildIndirectParametersPipeline,
213
/// Compute shader pipelines for the early prepass phase that draws meshes
214
/// visible in the previous frame.
215
pub early_phase: PreprocessPhasePipelines,
216
/// Compute shader pipelines for the late prepass phase that draws meshes
217
/// that weren't visible in the previous frame, but became visible this
218
/// frame.
219
pub late_phase: PreprocessPhasePipelines,
220
/// Compute shader pipelines for the main color phase.
221
pub main_phase: PreprocessPhasePipelines,
222
}
223
224
/// Compute shader pipelines for a specific phase: early, late, or main.
225
///
226
/// The distinction between these phases is relevant for occlusion culling.
227
#[derive(Clone)]
228
pub struct PreprocessPhasePipelines {
229
/// The pipeline that resets the indirect draw counts used in
230
/// `multi_draw_indirect_count` to 0 in preparation for a new pass.
231
pub reset_indirect_batch_sets: ResetIndirectBatchSetsPipeline,
232
/// The pipeline used for indexed indirect parameter building.
233
///
234
/// This pipeline converts indirect parameter metadata into indexed indirect
235
/// parameters.
236
pub gpu_occlusion_culling_build_indexed_indirect_params: BuildIndirectParametersPipeline,
237
/// The pipeline used for non-indexed indirect parameter building.
238
///
239
/// This pipeline converts indirect parameter metadata into non-indexed
240
/// indirect parameters.
241
pub gpu_occlusion_culling_build_non_indexed_indirect_params: BuildIndirectParametersPipeline,
242
}
243
244
/// The pipeline for the GPU mesh preprocessing shader.
245
pub struct PreprocessPipeline {
246
/// The bind group layout for the compute shader.
247
pub bind_group_layout: BindGroupLayout,
248
/// The shader asset handle.
249
pub shader: Handle<Shader>,
250
/// The pipeline ID for the compute shader.
251
///
252
/// This gets filled in `prepare_preprocess_pipelines`.
253
pub pipeline_id: Option<CachedComputePipelineId>,
254
}
255
256
/// The pipeline for the batch set count reset shader.
257
///
258
/// This shader resets the indirect batch set count to 0 for each view. It runs
259
/// in between every phase (early, late, and main).
260
#[derive(Clone)]
261
pub struct ResetIndirectBatchSetsPipeline {
262
/// The bind group layout for the compute shader.
263
pub bind_group_layout: BindGroupLayout,
264
/// The shader asset handle.
265
pub shader: Handle<Shader>,
266
/// The pipeline ID for the compute shader.
267
///
268
/// This gets filled in `prepare_preprocess_pipelines`.
269
pub pipeline_id: Option<CachedComputePipelineId>,
270
}
271
272
/// The pipeline for the indirect parameter building shader.
273
#[derive(Clone)]
274
pub struct BuildIndirectParametersPipeline {
275
/// The bind group layout for the compute shader.
276
pub bind_group_layout: BindGroupLayout,
277
/// The shader asset handle.
278
pub shader: Handle<Shader>,
279
/// The pipeline ID for the compute shader.
280
///
281
/// This gets filled in `prepare_preprocess_pipelines`.
282
pub pipeline_id: Option<CachedComputePipelineId>,
283
}
284
285
bitflags! {
286
/// Specifies variants of the mesh preprocessing shader.
287
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
288
pub struct PreprocessPipelineKey: u8 {
289
/// Whether GPU frustum culling is in use.
290
///
291
/// This `#define`'s `FRUSTUM_CULLING` in the shader.
292
const FRUSTUM_CULLING = 1;
293
/// Whether GPU two-phase occlusion culling is in use.
294
///
295
/// This `#define`'s `OCCLUSION_CULLING` in the shader.
296
const OCCLUSION_CULLING = 2;
297
/// Whether this is the early phase of GPU two-phase occlusion culling.
298
///
299
/// This `#define`'s `EARLY_PHASE` in the shader.
300
const EARLY_PHASE = 4;
301
}
302
303
/// Specifies variants of the indirect parameter building shader.
304
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
305
pub struct BuildIndirectParametersPipelineKey: u8 {
306
/// Whether the indirect parameter building shader is processing indexed
307
/// meshes (those that have index buffers).
308
///
309
/// This defines `INDEXED` in the shader.
310
const INDEXED = 1;
311
/// Whether the GPU and driver supports `multi_draw_indirect_count`.
312
///
313
/// This defines `MULTI_DRAW_INDIRECT_COUNT_SUPPORTED` in the shader.
314
const MULTI_DRAW_INDIRECT_COUNT_SUPPORTED = 2;
315
/// Whether GPU two-phase occlusion culling is in use.
316
///
317
/// This `#define`'s `OCCLUSION_CULLING` in the shader.
318
const OCCLUSION_CULLING = 4;
319
/// Whether this is the early phase of GPU two-phase occlusion culling.
320
///
321
/// This `#define`'s `EARLY_PHASE` in the shader.
322
const EARLY_PHASE = 8;
323
/// Whether this is the late phase of GPU two-phase occlusion culling.
324
///
325
/// This `#define`'s `LATE_PHASE` in the shader.
326
const LATE_PHASE = 16;
327
/// Whether this is the phase that runs after the early and late phases,
328
/// and right before the main drawing logic, when GPU two-phase
329
/// occlusion culling is in use.
330
///
331
/// This `#define`'s `MAIN_PHASE` in the shader.
332
const MAIN_PHASE = 32;
333
}
334
}
335
336
/// The compute shader bind group for the mesh preprocessing pass for each
337
/// render phase.
338
///
339
/// This goes on the view. It maps the [`core::any::TypeId`] of a render phase
340
/// (e.g. [`bevy_core_pipeline::core_3d::Opaque3d`]) to the
341
/// [`PhasePreprocessBindGroups`] for that phase.
342
#[derive(Component, Clone, Deref, DerefMut)]
343
pub struct PreprocessBindGroups(pub TypeIdMap<PhasePreprocessBindGroups>);
344
345
/// The compute shader bind group for the mesh preprocessing step for a single
346
/// render phase on a single view.
347
#[derive(Clone)]
348
pub enum PhasePreprocessBindGroups {
349
/// The bind group used for the single invocation of the compute shader when
350
/// indirect drawing is *not* being used.
351
///
352
/// Because direct drawing doesn't require splitting the meshes into indexed
353
/// and non-indexed meshes, there's only one bind group in this case.
354
Direct(BindGroup),
355
356
/// The bind groups used for the compute shader when indirect drawing is
357
/// being used, but occlusion culling isn't being used.
358
///
359
/// Because indirect drawing requires splitting the meshes into indexed and
360
/// non-indexed meshes, there are two bind groups here.
361
IndirectFrustumCulling {
362
/// The bind group for indexed meshes.
363
indexed: Option<BindGroup>,
364
/// The bind group for non-indexed meshes.
365
non_indexed: Option<BindGroup>,
366
},
367
368
/// The bind groups used for the compute shader when indirect drawing is
369
/// being used, but occlusion culling isn't being used.
370
///
371
/// Because indirect drawing requires splitting the meshes into indexed and
372
/// non-indexed meshes, and because occlusion culling requires splitting
373
/// this phase into early and late versions, there are four bind groups
374
/// here.
375
IndirectOcclusionCulling {
376
/// The bind group for indexed meshes during the early mesh
377
/// preprocessing phase.
378
early_indexed: Option<BindGroup>,
379
/// The bind group for non-indexed meshes during the early mesh
380
/// preprocessing phase.
381
early_non_indexed: Option<BindGroup>,
382
/// The bind group for indexed meshes during the late mesh preprocessing
383
/// phase.
384
late_indexed: Option<BindGroup>,
385
/// The bind group for non-indexed meshes during the late mesh
386
/// preprocessing phase.
387
late_non_indexed: Option<BindGroup>,
388
},
389
}
390
391
/// The bind groups for the compute shaders that reset indirect draw counts and
392
/// build indirect parameters.
393
///
394
/// There's one set of bind group for each phase. Phases are keyed off their
395
/// [`core::any::TypeId`].
396
#[derive(Resource, Default, Deref, DerefMut)]
397
pub struct BuildIndirectParametersBindGroups(pub TypeIdMap<PhaseBuildIndirectParametersBindGroups>);
398
399
impl BuildIndirectParametersBindGroups {
400
/// Creates a new, empty [`BuildIndirectParametersBindGroups`] table.
401
pub fn new() -> BuildIndirectParametersBindGroups {
402
Self::default()
403
}
404
}
405
406
/// The per-phase set of bind groups for the compute shaders that reset indirect
407
/// draw counts and build indirect parameters.
408
pub struct PhaseBuildIndirectParametersBindGroups {
409
/// The bind group for the `reset_indirect_batch_sets.wgsl` shader, for
410
/// indexed meshes.
411
reset_indexed_indirect_batch_sets: Option<BindGroup>,
412
/// The bind group for the `reset_indirect_batch_sets.wgsl` shader, for
413
/// non-indexed meshes.
414
reset_non_indexed_indirect_batch_sets: Option<BindGroup>,
415
/// The bind group for the `build_indirect_params.wgsl` shader, for indexed
416
/// meshes.
417
build_indexed_indirect: Option<BindGroup>,
418
/// The bind group for the `build_indirect_params.wgsl` shader, for
419
/// non-indexed meshes.
420
build_non_indexed_indirect: Option<BindGroup>,
421
}
422
423
/// Stops the `GpuPreprocessNode` attempting to generate the buffer for this view
424
/// useful to avoid duplicating effort if the bind group is shared between views
425
#[derive(Component, Default)]
426
pub struct SkipGpuPreprocess;
427
428
impl Plugin for GpuMeshPreprocessPlugin {
429
fn build(&self, app: &mut App) {
430
embedded_asset!(app, "mesh_preprocess.wgsl");
431
embedded_asset!(app, "reset_indirect_batch_sets.wgsl");
432
embedded_asset!(app, "build_indirect_params.wgsl");
433
}
434
435
fn finish(&self, app: &mut App) {
436
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
437
return;
438
};
439
440
// This plugin does nothing if GPU instance buffer building isn't in
441
// use.
442
let gpu_preprocessing_support = render_app.world().resource::<GpuPreprocessingSupport>();
443
if !self.use_gpu_instance_buffer_builder || !gpu_preprocessing_support.is_available() {
444
return;
445
}
446
447
render_app
448
.init_resource::<PreprocessPipelines>()
449
.init_resource::<SpecializedComputePipelines<PreprocessPipeline>>()
450
.init_resource::<SpecializedComputePipelines<ResetIndirectBatchSetsPipeline>>()
451
.init_resource::<SpecializedComputePipelines<BuildIndirectParametersPipeline>>()
452
.add_systems(
453
Render,
454
(
455
prepare_preprocess_pipelines.in_set(RenderSystems::Prepare),
456
prepare_preprocess_bind_groups
457
.run_if(resource_exists::<BatchedInstanceBuffers<
458
MeshUniform,
459
MeshInputUniform
460
>>)
461
.in_set(RenderSystems::PrepareBindGroups),
462
write_mesh_culling_data_buffer.in_set(RenderSystems::PrepareResourcesFlush),
463
),
464
)
465
.add_render_graph_node::<ClearIndirectParametersMetadataNode>(
466
Core3d,
467
NodePbr::ClearIndirectParametersMetadata
468
)
469
.add_render_graph_node::<EarlyGpuPreprocessNode>(Core3d, NodePbr::EarlyGpuPreprocess)
470
.add_render_graph_node::<LateGpuPreprocessNode>(Core3d, NodePbr::LateGpuPreprocess)
471
.add_render_graph_node::<EarlyPrepassBuildIndirectParametersNode>(
472
Core3d,
473
NodePbr::EarlyPrepassBuildIndirectParameters,
474
)
475
.add_render_graph_node::<LatePrepassBuildIndirectParametersNode>(
476
Core3d,
477
NodePbr::LatePrepassBuildIndirectParameters,
478
)
479
.add_render_graph_node::<MainBuildIndirectParametersNode>(
480
Core3d,
481
NodePbr::MainBuildIndirectParameters,
482
)
483
.add_render_graph_edges(
484
Core3d,
485
(
486
NodePbr::ClearIndirectParametersMetadata,
487
NodePbr::EarlyGpuPreprocess,
488
NodePbr::EarlyPrepassBuildIndirectParameters,
489
Node3d::EarlyPrepass,
490
Node3d::EarlyDeferredPrepass,
491
Node3d::EarlyDownsampleDepth,
492
NodePbr::LateGpuPreprocess,
493
NodePbr::LatePrepassBuildIndirectParameters,
494
Node3d::LatePrepass,
495
Node3d::LateDeferredPrepass,
496
NodePbr::MainBuildIndirectParameters,
497
Node3d::StartMainPass,
498
),
499
).add_render_graph_edges(
500
Core3d,
501
(
502
NodePbr::EarlyPrepassBuildIndirectParameters,
503
NodePbr::EarlyShadowPass,
504
Node3d::EarlyDownsampleDepth,
505
)
506
).add_render_graph_edges(
507
Core3d,
508
(
509
NodePbr::LatePrepassBuildIndirectParameters,
510
NodePbr::LateShadowPass,
511
NodePbr::MainBuildIndirectParameters,
512
)
513
);
514
}
515
}
516
517
impl Node for ClearIndirectParametersMetadataNode {
518
fn run<'w>(
519
&self,
520
_: &mut RenderGraphContext,
521
render_context: &mut RenderContext<'w>,
522
world: &'w World,
523
) -> Result<(), NodeRunError> {
524
let Some(indirect_parameters_buffers) = world.get_resource::<IndirectParametersBuffers>()
525
else {
526
return Ok(());
527
};
528
529
// Clear out each indexed and non-indexed GPU-side buffer.
530
for phase_indirect_parameters_buffers in indirect_parameters_buffers.values() {
531
if let Some(indexed_gpu_metadata_buffer) = phase_indirect_parameters_buffers
532
.indexed
533
.gpu_metadata_buffer()
534
{
535
render_context.command_encoder().clear_buffer(
536
indexed_gpu_metadata_buffer,
537
0,
538
Some(
539
phase_indirect_parameters_buffers.indexed.batch_count() as u64
540
* size_of::<IndirectParametersGpuMetadata>() as u64,
541
),
542
);
543
}
544
545
if let Some(non_indexed_gpu_metadata_buffer) = phase_indirect_parameters_buffers
546
.non_indexed
547
.gpu_metadata_buffer()
548
{
549
render_context.command_encoder().clear_buffer(
550
non_indexed_gpu_metadata_buffer,
551
0,
552
Some(
553
phase_indirect_parameters_buffers.non_indexed.batch_count() as u64
554
* size_of::<IndirectParametersGpuMetadata>() as u64,
555
),
556
);
557
}
558
}
559
560
Ok(())
561
}
562
}
563
564
impl FromWorld for EarlyGpuPreprocessNode {
565
fn from_world(world: &mut World) -> Self {
566
Self {
567
view_query: QueryState::new(world),
568
main_view_query: QueryState::new(world),
569
}
570
}
571
}
572
573
impl Node for EarlyGpuPreprocessNode {
574
fn update(&mut self, world: &mut World) {
575
self.view_query.update_archetypes(world);
576
self.main_view_query.update_archetypes(world);
577
}
578
579
fn run<'w>(
580
&self,
581
graph: &mut RenderGraphContext,
582
render_context: &mut RenderContext<'w>,
583
world: &'w World,
584
) -> Result<(), NodeRunError> {
585
let diagnostics = render_context.diagnostic_recorder();
586
587
// Grab the [`BatchedInstanceBuffers`].
588
let batched_instance_buffers =
589
world.resource::<BatchedInstanceBuffers<MeshUniform, MeshInputUniform>>();
590
591
let pipeline_cache = world.resource::<PipelineCache>();
592
let preprocess_pipelines = world.resource::<PreprocessPipelines>();
593
594
let mut compute_pass =
595
render_context
596
.command_encoder()
597
.begin_compute_pass(&ComputePassDescriptor {
598
label: Some("early_mesh_preprocessing"),
599
timestamp_writes: None,
600
});
601
let pass_span = diagnostics.time_span(&mut compute_pass, "early_mesh_preprocessing");
602
603
let mut all_views: SmallVec<[_; 8]> = SmallVec::new();
604
all_views.push(graph.view_entity());
605
if let Ok(shadow_cascade_views) =
606
self.main_view_query.get_manual(world, graph.view_entity())
607
{
608
all_views.extend(shadow_cascade_views.lights.iter().copied());
609
}
610
611
// Run the compute passes.
612
613
for view_entity in all_views {
614
let Ok((
615
view,
616
bind_groups,
617
view_uniform_offset,
618
no_indirect_drawing,
619
occlusion_culling,
620
)) = self.view_query.get_manual(world, view_entity)
621
else {
622
continue;
623
};
624
625
let Some(bind_groups) = bind_groups else {
626
continue;
627
};
628
let Some(view_uniform_offset) = view_uniform_offset else {
629
continue;
630
};
631
632
// Select the right pipeline, depending on whether GPU culling is in
633
// use.
634
let maybe_pipeline_id = if no_indirect_drawing {
635
preprocess_pipelines.direct_preprocess.pipeline_id
636
} else if occlusion_culling {
637
preprocess_pipelines
638
.early_gpu_occlusion_culling_preprocess
639
.pipeline_id
640
} else {
641
preprocess_pipelines
642
.gpu_frustum_culling_preprocess
643
.pipeline_id
644
};
645
646
// Fetch the pipeline.
647
let Some(preprocess_pipeline_id) = maybe_pipeline_id else {
648
warn!("The build mesh uniforms pipeline wasn't ready");
649
continue;
650
};
651
652
let Some(preprocess_pipeline) =
653
pipeline_cache.get_compute_pipeline(preprocess_pipeline_id)
654
else {
655
// This will happen while the pipeline is being compiled and is fine.
656
continue;
657
};
658
659
compute_pass.set_pipeline(preprocess_pipeline);
660
661
// Loop over each render phase.
662
for (phase_type_id, batched_phase_instance_buffers) in
663
&batched_instance_buffers.phase_instance_buffers
664
{
665
// Grab the work item buffers for this view.
666
let Some(work_item_buffers) = batched_phase_instance_buffers
667
.work_item_buffers
668
.get(&view.retained_view_entity)
669
else {
670
continue;
671
};
672
673
// Fetch the bind group for the render phase.
674
let Some(phase_bind_groups) = bind_groups.get(phase_type_id) else {
675
continue;
676
};
677
678
// Make sure the mesh preprocessing shader has access to the
679
// view info it needs to do culling and motion vector
680
// computation.
681
let dynamic_offsets = [view_uniform_offset.offset];
682
683
// Are we drawing directly or indirectly?
684
match *phase_bind_groups {
685
PhasePreprocessBindGroups::Direct(ref bind_group) => {
686
// Invoke the mesh preprocessing shader to transform
687
// meshes only, but not cull.
688
let PreprocessWorkItemBuffers::Direct(work_item_buffer) = work_item_buffers
689
else {
690
continue;
691
};
692
compute_pass.set_bind_group(0, bind_group, &dynamic_offsets);
693
let workgroup_count = work_item_buffer.len().div_ceil(WORKGROUP_SIZE);
694
if workgroup_count > 0 {
695
compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
696
}
697
}
698
699
PhasePreprocessBindGroups::IndirectFrustumCulling {
700
indexed: ref maybe_indexed_bind_group,
701
non_indexed: ref maybe_non_indexed_bind_group,
702
}
703
| PhasePreprocessBindGroups::IndirectOcclusionCulling {
704
early_indexed: ref maybe_indexed_bind_group,
705
early_non_indexed: ref maybe_non_indexed_bind_group,
706
..
707
} => {
708
// Invoke the mesh preprocessing shader to transform and
709
// cull the meshes.
710
let PreprocessWorkItemBuffers::Indirect {
711
indexed: indexed_buffer,
712
non_indexed: non_indexed_buffer,
713
..
714
} = work_item_buffers
715
else {
716
continue;
717
};
718
719
// Transform and cull indexed meshes if there are any.
720
if let Some(indexed_bind_group) = maybe_indexed_bind_group {
721
if let PreprocessWorkItemBuffers::Indirect {
722
gpu_occlusion_culling:
723
Some(GpuOcclusionCullingWorkItemBuffers {
724
late_indirect_parameters_indexed_offset,
725
..
726
}),
727
..
728
} = *work_item_buffers
729
{
730
compute_pass.set_push_constants(
731
0,
732
bytemuck::bytes_of(&late_indirect_parameters_indexed_offset),
733
);
734
}
735
736
compute_pass.set_bind_group(0, indexed_bind_group, &dynamic_offsets);
737
let workgroup_count = indexed_buffer.len().div_ceil(WORKGROUP_SIZE);
738
if workgroup_count > 0 {
739
compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
740
}
741
}
742
743
// Transform and cull non-indexed meshes if there are any.
744
if let Some(non_indexed_bind_group) = maybe_non_indexed_bind_group {
745
if let PreprocessWorkItemBuffers::Indirect {
746
gpu_occlusion_culling:
747
Some(GpuOcclusionCullingWorkItemBuffers {
748
late_indirect_parameters_non_indexed_offset,
749
..
750
}),
751
..
752
} = *work_item_buffers
753
{
754
compute_pass.set_push_constants(
755
0,
756
bytemuck::bytes_of(
757
&late_indirect_parameters_non_indexed_offset,
758
),
759
);
760
}
761
762
compute_pass.set_bind_group(
763
0,
764
non_indexed_bind_group,
765
&dynamic_offsets,
766
);
767
let workgroup_count = non_indexed_buffer.len().div_ceil(WORKGROUP_SIZE);
768
if workgroup_count > 0 {
769
compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
770
}
771
}
772
}
773
}
774
}
775
}
776
777
pass_span.end(&mut compute_pass);
778
779
Ok(())
780
}
781
}
782
783
impl FromWorld for EarlyPrepassBuildIndirectParametersNode {
784
fn from_world(world: &mut World) -> Self {
785
Self {
786
view_query: QueryState::new(world),
787
}
788
}
789
}
790
791
impl FromWorld for LatePrepassBuildIndirectParametersNode {
792
fn from_world(world: &mut World) -> Self {
793
Self {
794
view_query: QueryState::new(world),
795
}
796
}
797
}
798
799
impl FromWorld for MainBuildIndirectParametersNode {
800
fn from_world(world: &mut World) -> Self {
801
Self {
802
view_query: QueryState::new(world),
803
}
804
}
805
}
806
807
impl FromWorld for LateGpuPreprocessNode {
808
fn from_world(world: &mut World) -> Self {
809
Self {
810
view_query: QueryState::new(world),
811
}
812
}
813
}
814
815
impl Node for LateGpuPreprocessNode {
816
fn update(&mut self, world: &mut World) {
817
self.view_query.update_archetypes(world);
818
}
819
820
fn run<'w>(
821
&self,
822
_: &mut RenderGraphContext,
823
render_context: &mut RenderContext<'w>,
824
world: &'w World,
825
) -> Result<(), NodeRunError> {
826
let diagnostics = render_context.diagnostic_recorder();
827
828
// Grab the [`BatchedInstanceBuffers`].
829
let batched_instance_buffers =
830
world.resource::<BatchedInstanceBuffers<MeshUniform, MeshInputUniform>>();
831
832
let pipeline_cache = world.resource::<PipelineCache>();
833
let preprocess_pipelines = world.resource::<PreprocessPipelines>();
834
835
let mut compute_pass =
836
render_context
837
.command_encoder()
838
.begin_compute_pass(&ComputePassDescriptor {
839
label: Some("late_mesh_preprocessing"),
840
timestamp_writes: None,
841
});
842
let pass_span = diagnostics.time_span(&mut compute_pass, "late_mesh_preprocessing");
843
844
// Run the compute passes.
845
for (view, bind_groups, view_uniform_offset) in self.view_query.iter_manual(world) {
846
let maybe_pipeline_id = preprocess_pipelines
847
.late_gpu_occlusion_culling_preprocess
848
.pipeline_id;
849
850
// Fetch the pipeline.
851
let Some(preprocess_pipeline_id) = maybe_pipeline_id else {
852
warn!("The build mesh uniforms pipeline wasn't ready");
853
return Ok(());
854
};
855
856
let Some(preprocess_pipeline) =
857
pipeline_cache.get_compute_pipeline(preprocess_pipeline_id)
858
else {
859
// This will happen while the pipeline is being compiled and is fine.
860
return Ok(());
861
};
862
863
compute_pass.set_pipeline(preprocess_pipeline);
864
865
// Loop over each phase. Because we built the phases in parallel,
866
// each phase has a separate set of instance buffers.
867
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
// Grab the work item buffers for this view.
878
let Some(phase_work_item_buffers) =
879
work_item_buffers.get(&view.retained_view_entity)
880
else {
881
continue;
882
};
883
884
let (
885
PreprocessWorkItemBuffers::Indirect {
886
gpu_occlusion_culling:
887
Some(GpuOcclusionCullingWorkItemBuffers {
888
late_indirect_parameters_indexed_offset,
889
late_indirect_parameters_non_indexed_offset,
890
..
891
}),
892
..
893
},
894
Some(PhasePreprocessBindGroups::IndirectOcclusionCulling {
895
late_indexed: maybe_late_indexed_bind_group,
896
late_non_indexed: maybe_late_non_indexed_bind_group,
897
..
898
}),
899
Some(late_indexed_indirect_parameters_buffer),
900
Some(late_non_indexed_indirect_parameters_buffer),
901
) = (
902
phase_work_item_buffers,
903
bind_groups.get(phase_type_id),
904
late_indexed_indirect_parameters_buffer.buffer(),
905
late_non_indexed_indirect_parameters_buffer.buffer(),
906
)
907
else {
908
continue;
909
};
910
911
let mut dynamic_offsets: SmallVec<[u32; 1]> = smallvec![];
912
dynamic_offsets.push(view_uniform_offset.offset);
913
914
// If there's no space reserved for work items, then don't
915
// bother doing the dispatch, as there can't possibly be any
916
// meshes of the given class (indexed or non-indexed) in this
917
// phase.
918
919
// Transform and cull indexed meshes if there are any.
920
if let Some(late_indexed_bind_group) = maybe_late_indexed_bind_group {
921
compute_pass.set_push_constants(
922
0,
923
bytemuck::bytes_of(late_indirect_parameters_indexed_offset),
924
);
925
926
compute_pass.set_bind_group(0, late_indexed_bind_group, &dynamic_offsets);
927
compute_pass.dispatch_workgroups_indirect(
928
late_indexed_indirect_parameters_buffer,
929
(*late_indirect_parameters_indexed_offset as u64)
930
* (size_of::<LatePreprocessWorkItemIndirectParameters>() as u64),
931
);
932
}
933
934
// Transform and cull non-indexed meshes if there are any.
935
if let Some(late_non_indexed_bind_group) = maybe_late_non_indexed_bind_group {
936
compute_pass.set_push_constants(
937
0,
938
bytemuck::bytes_of(late_indirect_parameters_non_indexed_offset),
939
);
940
941
compute_pass.set_bind_group(0, late_non_indexed_bind_group, &dynamic_offsets);
942
compute_pass.dispatch_workgroups_indirect(
943
late_non_indexed_indirect_parameters_buffer,
944
(*late_indirect_parameters_non_indexed_offset as u64)
945
* (size_of::<LatePreprocessWorkItemIndirectParameters>() as u64),
946
);
947
}
948
}
949
}
950
951
pass_span.end(&mut compute_pass);
952
953
Ok(())
954
}
955
}
956
957
impl Node for EarlyPrepassBuildIndirectParametersNode {
958
fn update(&mut self, world: &mut World) {
959
self.view_query.update_archetypes(world);
960
}
961
962
fn run<'w>(
963
&self,
964
_: &mut RenderGraphContext,
965
render_context: &mut RenderContext<'w>,
966
world: &'w World,
967
) -> Result<(), NodeRunError> {
968
let preprocess_pipelines = world.resource::<PreprocessPipelines>();
969
970
// If there are no views with a depth prepass enabled, we don't need to
971
// run this.
972
if self.view_query.iter_manual(world).next().is_none() {
973
return Ok(());
974
}
975
976
run_build_indirect_parameters_node(
977
render_context,
978
world,
979
&preprocess_pipelines.early_phase,
980
"early_prepass_indirect_parameters_building",
981
)
982
}
983
}
984
985
impl Node for LatePrepassBuildIndirectParametersNode {
986
fn update(&mut self, world: &mut World) {
987
self.view_query.update_archetypes(world);
988
}
989
990
fn run<'w>(
991
&self,
992
_: &mut RenderGraphContext,
993
render_context: &mut RenderContext<'w>,
994
world: &'w World,
995
) -> Result<(), NodeRunError> {
996
let preprocess_pipelines = world.resource::<PreprocessPipelines>();
997
998
// If there are no views with occlusion culling enabled, we don't need
999
// to run this.
1000
if self.view_query.iter_manual(world).next().is_none() {
1001
return Ok(());
1002
}
1003
1004
run_build_indirect_parameters_node(
1005
render_context,
1006
world,
1007
&preprocess_pipelines.late_phase,
1008
"late_prepass_indirect_parameters_building",
1009
)
1010
}
1011
}
1012
1013
impl Node for MainBuildIndirectParametersNode {
1014
fn update(&mut self, world: &mut World) {
1015
self.view_query.update_archetypes(world);
1016
}
1017
1018
fn run<'w>(
1019
&self,
1020
_: &mut RenderGraphContext,
1021
render_context: &mut RenderContext<'w>,
1022
world: &'w World,
1023
) -> Result<(), NodeRunError> {
1024
let preprocess_pipelines = world.resource::<PreprocessPipelines>();
1025
1026
run_build_indirect_parameters_node(
1027
render_context,
1028
world,
1029
&preprocess_pipelines.main_phase,
1030
"main_indirect_parameters_building",
1031
)
1032
}
1033
}
1034
1035
fn run_build_indirect_parameters_node(
1036
render_context: &mut RenderContext,
1037
world: &World,
1038
preprocess_phase_pipelines: &PreprocessPhasePipelines,
1039
label: &'static str,
1040
) -> Result<(), NodeRunError> {
1041
let Some(build_indirect_params_bind_groups) =
1042
world.get_resource::<BuildIndirectParametersBindGroups>()
1043
else {
1044
return Ok(());
1045
};
1046
1047
let diagnostics = render_context.diagnostic_recorder();
1048
1049
let pipeline_cache = world.resource::<PipelineCache>();
1050
let indirect_parameters_buffers = world.resource::<IndirectParametersBuffers>();
1051
1052
let mut compute_pass =
1053
render_context
1054
.command_encoder()
1055
.begin_compute_pass(&ComputePassDescriptor {
1056
label: Some(label),
1057
timestamp_writes: None,
1058
});
1059
let pass_span = diagnostics.time_span(&mut compute_pass, label);
1060
1061
// Fetch the pipeline.
1062
let (
1063
Some(reset_indirect_batch_sets_pipeline_id),
1064
Some(build_indexed_indirect_params_pipeline_id),
1065
Some(build_non_indexed_indirect_params_pipeline_id),
1066
) = (
1067
preprocess_phase_pipelines
1068
.reset_indirect_batch_sets
1069
.pipeline_id,
1070
preprocess_phase_pipelines
1071
.gpu_occlusion_culling_build_indexed_indirect_params
1072
.pipeline_id,
1073
preprocess_phase_pipelines
1074
.gpu_occlusion_culling_build_non_indexed_indirect_params
1075
.pipeline_id,
1076
)
1077
else {
1078
warn!("The build indirect parameters pipelines weren't ready");
1079
pass_span.end(&mut compute_pass);
1080
return Ok(());
1081
};
1082
1083
let (
1084
Some(reset_indirect_batch_sets_pipeline),
1085
Some(build_indexed_indirect_params_pipeline),
1086
Some(build_non_indexed_indirect_params_pipeline),
1087
) = (
1088
pipeline_cache.get_compute_pipeline(reset_indirect_batch_sets_pipeline_id),
1089
pipeline_cache.get_compute_pipeline(build_indexed_indirect_params_pipeline_id),
1090
pipeline_cache.get_compute_pipeline(build_non_indexed_indirect_params_pipeline_id),
1091
)
1092
else {
1093
// This will happen while the pipeline is being compiled and is fine.
1094
pass_span.end(&mut compute_pass);
1095
return Ok(());
1096
};
1097
1098
// Loop over each phase. As each has as separate set of buffers, we need to
1099
// build indirect parameters individually for each phase.
1100
for (phase_type_id, phase_build_indirect_params_bind_groups) in
1101
build_indirect_params_bind_groups.iter()
1102
{
1103
let Some(phase_indirect_parameters_buffers) =
1104
indirect_parameters_buffers.get(phase_type_id)
1105
else {
1106
continue;
1107
};
1108
1109
// Build indexed indirect parameters.
1110
if let (
1111
Some(reset_indexed_indirect_batch_sets_bind_group),
1112
Some(build_indirect_indexed_params_bind_group),
1113
) = (
1114
&phase_build_indirect_params_bind_groups.reset_indexed_indirect_batch_sets,
1115
&phase_build_indirect_params_bind_groups.build_indexed_indirect,
1116
) {
1117
compute_pass.set_pipeline(reset_indirect_batch_sets_pipeline);
1118
compute_pass.set_bind_group(0, reset_indexed_indirect_batch_sets_bind_group, &[]);
1119
let workgroup_count = phase_indirect_parameters_buffers
1120
.batch_set_count(true)
1121
.div_ceil(WORKGROUP_SIZE);
1122
if workgroup_count > 0 {
1123
compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
1124
}
1125
1126
compute_pass.set_pipeline(build_indexed_indirect_params_pipeline);
1127
compute_pass.set_bind_group(0, build_indirect_indexed_params_bind_group, &[]);
1128
let workgroup_count = phase_indirect_parameters_buffers
1129
.indexed
1130
.batch_count()
1131
.div_ceil(WORKGROUP_SIZE);
1132
if workgroup_count > 0 {
1133
compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
1134
}
1135
}
1136
1137
// Build non-indexed indirect parameters.
1138
if let (
1139
Some(reset_non_indexed_indirect_batch_sets_bind_group),
1140
Some(build_indirect_non_indexed_params_bind_group),
1141
) = (
1142
&phase_build_indirect_params_bind_groups.reset_non_indexed_indirect_batch_sets,
1143
&phase_build_indirect_params_bind_groups.build_non_indexed_indirect,
1144
) {
1145
compute_pass.set_pipeline(reset_indirect_batch_sets_pipeline);
1146
compute_pass.set_bind_group(0, reset_non_indexed_indirect_batch_sets_bind_group, &[]);
1147
let workgroup_count = phase_indirect_parameters_buffers
1148
.batch_set_count(false)
1149
.div_ceil(WORKGROUP_SIZE);
1150
if workgroup_count > 0 {
1151
compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
1152
}
1153
1154
compute_pass.set_pipeline(build_non_indexed_indirect_params_pipeline);
1155
compute_pass.set_bind_group(0, build_indirect_non_indexed_params_bind_group, &[]);
1156
let workgroup_count = phase_indirect_parameters_buffers
1157
.non_indexed
1158
.batch_count()
1159
.div_ceil(WORKGROUP_SIZE);
1160
if workgroup_count > 0 {
1161
compute_pass.dispatch_workgroups(workgroup_count as u32, 1, 1);
1162
}
1163
}
1164
}
1165
1166
pass_span.end(&mut compute_pass);
1167
1168
Ok(())
1169
}
1170
1171
impl PreprocessPipelines {
1172
/// Returns true if the preprocessing and indirect parameters pipelines have
1173
/// been loaded or false otherwise.
1174
pub(crate) fn pipelines_are_loaded(
1175
&self,
1176
pipeline_cache: &PipelineCache,
1177
preprocessing_support: &GpuPreprocessingSupport,
1178
) -> bool {
1179
match preprocessing_support.max_supported_mode {
1180
GpuPreprocessingMode::None => false,
1181
GpuPreprocessingMode::PreprocessingOnly => {
1182
self.direct_preprocess.is_loaded(pipeline_cache)
1183
&& self
1184
.gpu_frustum_culling_preprocess
1185
.is_loaded(pipeline_cache)
1186
}
1187
GpuPreprocessingMode::Culling => {
1188
self.direct_preprocess.is_loaded(pipeline_cache)
1189
&& self
1190
.gpu_frustum_culling_preprocess
1191
.is_loaded(pipeline_cache)
1192
&& self
1193
.early_gpu_occlusion_culling_preprocess
1194
.is_loaded(pipeline_cache)
1195
&& self
1196
.late_gpu_occlusion_culling_preprocess
1197
.is_loaded(pipeline_cache)
1198
&& self
1199
.gpu_frustum_culling_build_indexed_indirect_params
1200
.is_loaded(pipeline_cache)
1201
&& self
1202
.gpu_frustum_culling_build_non_indexed_indirect_params
1203
.is_loaded(pipeline_cache)
1204
&& self.early_phase.is_loaded(pipeline_cache)
1205
&& self.late_phase.is_loaded(pipeline_cache)
1206
&& self.main_phase.is_loaded(pipeline_cache)
1207
}
1208
}
1209
}
1210
}
1211
1212
impl PreprocessPhasePipelines {
1213
fn is_loaded(&self, pipeline_cache: &PipelineCache) -> bool {
1214
self.reset_indirect_batch_sets.is_loaded(pipeline_cache)
1215
&& self
1216
.gpu_occlusion_culling_build_indexed_indirect_params
1217
.is_loaded(pipeline_cache)
1218
&& self
1219
.gpu_occlusion_culling_build_non_indexed_indirect_params
1220
.is_loaded(pipeline_cache)
1221
}
1222
}
1223
1224
impl PreprocessPipeline {
1225
fn is_loaded(&self, pipeline_cache: &PipelineCache) -> bool {
1226
self.pipeline_id
1227
.is_some_and(|pipeline_id| pipeline_cache.get_compute_pipeline(pipeline_id).is_some())
1228
}
1229
}
1230
1231
impl ResetIndirectBatchSetsPipeline {
1232
fn is_loaded(&self, pipeline_cache: &PipelineCache) -> bool {
1233
self.pipeline_id
1234
.is_some_and(|pipeline_id| pipeline_cache.get_compute_pipeline(pipeline_id).is_some())
1235
}
1236
}
1237
1238
impl BuildIndirectParametersPipeline {
1239
/// Returns true if this pipeline has been loaded into the pipeline cache or
1240
/// false otherwise.
1241
fn is_loaded(&self, pipeline_cache: &PipelineCache) -> bool {
1242
self.pipeline_id
1243
.is_some_and(|pipeline_id| pipeline_cache.get_compute_pipeline(pipeline_id).is_some())
1244
}
1245
}
1246
1247
impl SpecializedComputePipeline for PreprocessPipeline {
1248
type Key = PreprocessPipelineKey;
1249
1250
fn specialize(&self, key: Self::Key) -> ComputePipelineDescriptor {
1251
let mut shader_defs = vec!["WRITE_INDIRECT_PARAMETERS_METADATA".into()];
1252
if key.contains(PreprocessPipelineKey::FRUSTUM_CULLING) {
1253
shader_defs.push("INDIRECT".into());
1254
shader_defs.push("FRUSTUM_CULLING".into());
1255
}
1256
if key.contains(PreprocessPipelineKey::OCCLUSION_CULLING) {
1257
shader_defs.push("OCCLUSION_CULLING".into());
1258
if key.contains(PreprocessPipelineKey::EARLY_PHASE) {
1259
shader_defs.push("EARLY_PHASE".into());
1260
} else {
1261
shader_defs.push("LATE_PHASE".into());
1262
}
1263
}
1264
1265
ComputePipelineDescriptor {
1266
label: Some(
1267
format!(
1268
"mesh preprocessing ({})",
1269
if key.contains(
1270
PreprocessPipelineKey::OCCLUSION_CULLING
1271
| PreprocessPipelineKey::EARLY_PHASE
1272
) {
1273
"early GPU occlusion culling"
1274
} else if key.contains(PreprocessPipelineKey::OCCLUSION_CULLING) {
1275
"late GPU occlusion culling"
1276
} else if key.contains(PreprocessPipelineKey::FRUSTUM_CULLING) {
1277
"GPU frustum culling"
1278
} else {
1279
"direct"
1280
}
1281
)
1282
.into(),
1283
),
1284
layout: vec![self.bind_group_layout.clone()],
1285
push_constant_ranges: if key.contains(PreprocessPipelineKey::OCCLUSION_CULLING) {
1286
vec![PushConstantRange {
1287
stages: ShaderStages::COMPUTE,
1288
range: 0..4,
1289
}]
1290
} else {
1291
vec![]
1292
},
1293
shader: self.shader.clone(),
1294
shader_defs,
1295
..default()
1296
}
1297
}
1298
}
1299
1300
impl FromWorld for PreprocessPipelines {
1301
fn from_world(world: &mut World) -> Self {
1302
let render_device = world.resource::<RenderDevice>();
1303
1304
// GPU culling bind group parameters are a superset of those in the CPU
1305
// culling (direct) shader.
1306
let direct_bind_group_layout_entries = preprocess_direct_bind_group_layout_entries();
1307
let gpu_frustum_culling_bind_group_layout_entries = gpu_culling_bind_group_layout_entries();
1308
let gpu_early_occlusion_culling_bind_group_layout_entries =
1309
gpu_occlusion_culling_bind_group_layout_entries().extend_with_indices(((
1310
11,
1311
storage_buffer::<PreprocessWorkItem>(/*has_dynamic_offset=*/ false),
1312
),));
1313
let gpu_late_occlusion_culling_bind_group_layout_entries =
1314
gpu_occlusion_culling_bind_group_layout_entries();
1315
1316
let reset_indirect_batch_sets_bind_group_layout_entries =
1317
DynamicBindGroupLayoutEntries::sequential(
1318
ShaderStages::COMPUTE,
1319
(storage_buffer::<IndirectBatchSet>(false),),
1320
);
1321
1322
// Indexed and non-indexed bind group parameters share all the bind
1323
// group layout entries except the final one.
1324
let build_indexed_indirect_params_bind_group_layout_entries =
1325
build_indirect_params_bind_group_layout_entries()
1326
.extend_sequential((storage_buffer::<IndirectParametersIndexed>(false),));
1327
let build_non_indexed_indirect_params_bind_group_layout_entries =
1328
build_indirect_params_bind_group_layout_entries()
1329
.extend_sequential((storage_buffer::<IndirectParametersNonIndexed>(false),));
1330
1331
// Create the bind group layouts.
1332
let direct_bind_group_layout = render_device.create_bind_group_layout(
1333
"build mesh uniforms direct bind group layout",
1334
&direct_bind_group_layout_entries,
1335
);
1336
let gpu_frustum_culling_bind_group_layout = render_device.create_bind_group_layout(
1337
"build mesh uniforms GPU frustum culling bind group layout",
1338
&gpu_frustum_culling_bind_group_layout_entries,
1339
);
1340
let gpu_early_occlusion_culling_bind_group_layout = render_device.create_bind_group_layout(
1341
"build mesh uniforms GPU early occlusion culling bind group layout",
1342
&gpu_early_occlusion_culling_bind_group_layout_entries,
1343
);
1344
let gpu_late_occlusion_culling_bind_group_layout = render_device.create_bind_group_layout(
1345
"build mesh uniforms GPU late occlusion culling bind group layout",
1346
&gpu_late_occlusion_culling_bind_group_layout_entries,
1347
);
1348
let reset_indirect_batch_sets_bind_group_layout = render_device.create_bind_group_layout(
1349
"reset indirect batch sets bind group layout",
1350
&reset_indirect_batch_sets_bind_group_layout_entries,
1351
);
1352
let build_indexed_indirect_params_bind_group_layout = render_device
1353
.create_bind_group_layout(
1354
"build indexed indirect parameters bind group layout",
1355
&build_indexed_indirect_params_bind_group_layout_entries,
1356
);
1357
let build_non_indexed_indirect_params_bind_group_layout = render_device
1358
.create_bind_group_layout(
1359
"build non-indexed indirect parameters bind group layout",
1360
&build_non_indexed_indirect_params_bind_group_layout_entries,
1361
);
1362
1363
let preprocess_shader = load_embedded_asset!(world, "mesh_preprocess.wgsl");
1364
let reset_indirect_batch_sets_shader =
1365
load_embedded_asset!(world, "reset_indirect_batch_sets.wgsl");
1366
let build_indirect_params_shader =
1367
load_embedded_asset!(world, "build_indirect_params.wgsl");
1368
1369
let preprocess_phase_pipelines = PreprocessPhasePipelines {
1370
reset_indirect_batch_sets: ResetIndirectBatchSetsPipeline {
1371
bind_group_layout: reset_indirect_batch_sets_bind_group_layout.clone(),
1372
shader: reset_indirect_batch_sets_shader,
1373
pipeline_id: None,
1374
},
1375
gpu_occlusion_culling_build_indexed_indirect_params: BuildIndirectParametersPipeline {
1376
bind_group_layout: build_indexed_indirect_params_bind_group_layout.clone(),
1377
shader: build_indirect_params_shader.clone(),
1378
pipeline_id: None,
1379
},
1380
gpu_occlusion_culling_build_non_indexed_indirect_params:
1381
BuildIndirectParametersPipeline {
1382
bind_group_layout: build_non_indexed_indirect_params_bind_group_layout.clone(),
1383
shader: build_indirect_params_shader.clone(),
1384
pipeline_id: None,
1385
},
1386
};
1387
1388
PreprocessPipelines {
1389
direct_preprocess: PreprocessPipeline {
1390
bind_group_layout: direct_bind_group_layout,
1391
shader: preprocess_shader.clone(),
1392
pipeline_id: None,
1393
},
1394
gpu_frustum_culling_preprocess: PreprocessPipeline {
1395
bind_group_layout: gpu_frustum_culling_bind_group_layout,
1396
shader: preprocess_shader.clone(),
1397
pipeline_id: None,
1398
},
1399
early_gpu_occlusion_culling_preprocess: PreprocessPipeline {
1400
bind_group_layout: gpu_early_occlusion_culling_bind_group_layout,
1401
shader: preprocess_shader.clone(),
1402
pipeline_id: None,
1403
},
1404
late_gpu_occlusion_culling_preprocess: PreprocessPipeline {
1405
bind_group_layout: gpu_late_occlusion_culling_bind_group_layout,
1406
shader: preprocess_shader,
1407
pipeline_id: None,
1408
},
1409
gpu_frustum_culling_build_indexed_indirect_params: BuildIndirectParametersPipeline {
1410
bind_group_layout: build_indexed_indirect_params_bind_group_layout.clone(),
1411
shader: build_indirect_params_shader.clone(),
1412
pipeline_id: None,
1413
},
1414
gpu_frustum_culling_build_non_indexed_indirect_params:
1415
BuildIndirectParametersPipeline {
1416
bind_group_layout: build_non_indexed_indirect_params_bind_group_layout.clone(),
1417
shader: build_indirect_params_shader,
1418
pipeline_id: None,
1419
},
1420
early_phase: preprocess_phase_pipelines.clone(),
1421
late_phase: preprocess_phase_pipelines.clone(),
1422
main_phase: preprocess_phase_pipelines.clone(),
1423
}
1424
}
1425
}
1426
1427
fn preprocess_direct_bind_group_layout_entries() -> DynamicBindGroupLayoutEntries {
1428
DynamicBindGroupLayoutEntries::new_with_indices(
1429
ShaderStages::COMPUTE,
1430
(
1431
// `view`
1432
(
1433
0,
1434
uniform_buffer::<ViewUniform>(/* has_dynamic_offset= */ true),
1435
),
1436
// `current_input`
1437
(3, storage_buffer_read_only::<MeshInputUniform>(false)),
1438
// `previous_input`
1439
(4, storage_buffer_read_only::<MeshInputUniform>(false)),
1440
// `indices`
1441
(5, storage_buffer_read_only::<PreprocessWorkItem>(false)),
1442
// `output`
1443
(6, storage_buffer::<MeshUniform>(false)),
1444
),
1445
)
1446
}
1447
1448
// Returns the first 4 bind group layout entries shared between all invocations
1449
// of the indirect parameters building shader.
1450
fn build_indirect_params_bind_group_layout_entries() -> DynamicBindGroupLayoutEntries {
1451
DynamicBindGroupLayoutEntries::new_with_indices(
1452
ShaderStages::COMPUTE,
1453
(
1454
(0, storage_buffer_read_only::<MeshInputUniform>(false)),
1455
(
1456
1,
1457
storage_buffer_read_only::<IndirectParametersCpuMetadata>(false),
1458
),
1459
(
1460
2,
1461
storage_buffer_read_only::<IndirectParametersGpuMetadata>(false),
1462
),
1463
(3, storage_buffer::<IndirectBatchSet>(false)),
1464
),
1465
)
1466
}
1467
1468
/// A system that specializes the `mesh_preprocess.wgsl` and
1469
/// `build_indirect_params.wgsl` pipelines if necessary.
1470
fn gpu_culling_bind_group_layout_entries() -> DynamicBindGroupLayoutEntries {
1471
// GPU culling bind group parameters are a superset of those in the CPU
1472
// culling (direct) shader.
1473
preprocess_direct_bind_group_layout_entries().extend_with_indices((
1474
// `indirect_parameters_cpu_metadata`
1475
(
1476
7,
1477
storage_buffer_read_only::<IndirectParametersCpuMetadata>(
1478
/* has_dynamic_offset= */ false,
1479
),
1480
),
1481
// `indirect_parameters_gpu_metadata`
1482
(
1483
8,
1484
storage_buffer::<IndirectParametersGpuMetadata>(/* has_dynamic_offset= */ false),
1485
),
1486
// `mesh_culling_data`
1487
(
1488
9,
1489
storage_buffer_read_only::<MeshCullingData>(/* has_dynamic_offset= */ false),
1490
),
1491
))
1492
}
1493
1494
fn gpu_occlusion_culling_bind_group_layout_entries() -> DynamicBindGroupLayoutEntries {
1495
gpu_culling_bind_group_layout_entries().extend_with_indices((
1496
(
1497
2,
1498
uniform_buffer::<PreviousViewData>(/*has_dynamic_offset=*/ false),
1499
),
1500
(
1501
10,
1502
texture_2d(TextureSampleType::Float { filterable: true }),
1503
),
1504
(
1505
12,
1506
storage_buffer::<LatePreprocessWorkItemIndirectParameters>(
1507
/*has_dynamic_offset=*/ false,
1508
),
1509
),
1510
))
1511
}
1512
1513
/// A system that specializes the `mesh_preprocess.wgsl` pipelines if necessary.
1514
pub fn prepare_preprocess_pipelines(
1515
pipeline_cache: Res<PipelineCache>,
1516
render_device: Res<RenderDevice>,
1517
mut specialized_preprocess_pipelines: ResMut<SpecializedComputePipelines<PreprocessPipeline>>,
1518
mut specialized_reset_indirect_batch_sets_pipelines: ResMut<
1519
SpecializedComputePipelines<ResetIndirectBatchSetsPipeline>,
1520
>,
1521
mut specialized_build_indirect_parameters_pipelines: ResMut<
1522
SpecializedComputePipelines<BuildIndirectParametersPipeline>,
1523
>,
1524
preprocess_pipelines: ResMut<PreprocessPipelines>,
1525
gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
1526
) {
1527
let preprocess_pipelines = preprocess_pipelines.into_inner();
1528
1529
preprocess_pipelines.direct_preprocess.prepare(
1530
&pipeline_cache,
1531
&mut specialized_preprocess_pipelines,
1532
PreprocessPipelineKey::empty(),
1533
);
1534
preprocess_pipelines.gpu_frustum_culling_preprocess.prepare(
1535
&pipeline_cache,
1536
&mut specialized_preprocess_pipelines,
1537
PreprocessPipelineKey::FRUSTUM_CULLING,
1538
);
1539
1540
if gpu_preprocessing_support.is_culling_supported() {
1541
preprocess_pipelines
1542
.early_gpu_occlusion_culling_preprocess
1543
.prepare(
1544
&pipeline_cache,
1545
&mut specialized_preprocess_pipelines,
1546
PreprocessPipelineKey::FRUSTUM_CULLING
1547
| PreprocessPipelineKey::OCCLUSION_CULLING
1548
| PreprocessPipelineKey::EARLY_PHASE,
1549
);
1550
preprocess_pipelines
1551
.late_gpu_occlusion_culling_preprocess
1552
.prepare(
1553
&pipeline_cache,
1554
&mut specialized_preprocess_pipelines,
1555
PreprocessPipelineKey::FRUSTUM_CULLING | PreprocessPipelineKey::OCCLUSION_CULLING,
1556
);
1557
}
1558
1559
let mut build_indirect_parameters_pipeline_key = BuildIndirectParametersPipelineKey::empty();
1560
1561
// If the GPU and driver support `multi_draw_indirect_count`, tell the
1562
// shader that.
1563
if render_device
1564
.wgpu_device()
1565
.features()
1566
.contains(WgpuFeatures::MULTI_DRAW_INDIRECT_COUNT)
1567
{
1568
build_indirect_parameters_pipeline_key
1569
.insert(BuildIndirectParametersPipelineKey::MULTI_DRAW_INDIRECT_COUNT_SUPPORTED);
1570
}
1571
1572
preprocess_pipelines
1573
.gpu_frustum_culling_build_indexed_indirect_params
1574
.prepare(
1575
&pipeline_cache,
1576
&mut specialized_build_indirect_parameters_pipelines,
1577
build_indirect_parameters_pipeline_key | BuildIndirectParametersPipelineKey::INDEXED,
1578
);
1579
preprocess_pipelines
1580
.gpu_frustum_culling_build_non_indexed_indirect_params
1581
.prepare(
1582
&pipeline_cache,
1583
&mut specialized_build_indirect_parameters_pipelines,
1584
build_indirect_parameters_pipeline_key,
1585
);
1586
1587
if !gpu_preprocessing_support.is_culling_supported() {
1588
return;
1589
}
1590
1591
for (preprocess_phase_pipelines, build_indirect_parameters_phase_pipeline_key) in [
1592
(
1593
&mut preprocess_pipelines.early_phase,
1594
BuildIndirectParametersPipelineKey::EARLY_PHASE,
1595
),
1596
(
1597
&mut preprocess_pipelines.late_phase,
1598
BuildIndirectParametersPipelineKey::LATE_PHASE,
1599
),
1600
(
1601
&mut preprocess_pipelines.main_phase,
1602
BuildIndirectParametersPipelineKey::MAIN_PHASE,
1603
),
1604
] {
1605
preprocess_phase_pipelines
1606
.reset_indirect_batch_sets
1607
.prepare(
1608
&pipeline_cache,
1609
&mut specialized_reset_indirect_batch_sets_pipelines,
1610
);
1611
preprocess_phase_pipelines
1612
.gpu_occlusion_culling_build_indexed_indirect_params
1613
.prepare(
1614
&pipeline_cache,
1615
&mut specialized_build_indirect_parameters_pipelines,
1616
build_indirect_parameters_pipeline_key
1617
| build_indirect_parameters_phase_pipeline_key
1618
| BuildIndirectParametersPipelineKey::INDEXED
1619
| BuildIndirectParametersPipelineKey::OCCLUSION_CULLING,
1620
);
1621
preprocess_phase_pipelines
1622
.gpu_occlusion_culling_build_non_indexed_indirect_params
1623
.prepare(
1624
&pipeline_cache,
1625
&mut specialized_build_indirect_parameters_pipelines,
1626
build_indirect_parameters_pipeline_key
1627
| build_indirect_parameters_phase_pipeline_key
1628
| BuildIndirectParametersPipelineKey::OCCLUSION_CULLING,
1629
);
1630
}
1631
}
1632
1633
impl PreprocessPipeline {
1634
fn prepare(
1635
&mut self,
1636
pipeline_cache: &PipelineCache,
1637
pipelines: &mut SpecializedComputePipelines<PreprocessPipeline>,
1638
key: PreprocessPipelineKey,
1639
) {
1640
if self.pipeline_id.is_some() {
1641
return;
1642
}
1643
1644
let preprocess_pipeline_id = pipelines.specialize(pipeline_cache, self, key);
1645
self.pipeline_id = Some(preprocess_pipeline_id);
1646
}
1647
}
1648
1649
impl SpecializedComputePipeline for ResetIndirectBatchSetsPipeline {
1650
type Key = ();
1651
1652
fn specialize(&self, _: Self::Key) -> ComputePipelineDescriptor {
1653
ComputePipelineDescriptor {
1654
label: Some("reset indirect batch sets".into()),
1655
layout: vec![self.bind_group_layout.clone()],
1656
shader: self.shader.clone(),
1657
..default()
1658
}
1659
}
1660
}
1661
1662
impl SpecializedComputePipeline for BuildIndirectParametersPipeline {
1663
type Key = BuildIndirectParametersPipelineKey;
1664
1665
fn specialize(&self, key: Self::Key) -> ComputePipelineDescriptor {
1666
let mut shader_defs = vec![];
1667
if key.contains(BuildIndirectParametersPipelineKey::INDEXED) {
1668
shader_defs.push("INDEXED".into());
1669
}
1670
if key.contains(BuildIndirectParametersPipelineKey::MULTI_DRAW_INDIRECT_COUNT_SUPPORTED) {
1671
shader_defs.push("MULTI_DRAW_INDIRECT_COUNT_SUPPORTED".into());
1672
}
1673
if key.contains(BuildIndirectParametersPipelineKey::OCCLUSION_CULLING) {
1674
shader_defs.push("OCCLUSION_CULLING".into());
1675
}
1676
if key.contains(BuildIndirectParametersPipelineKey::EARLY_PHASE) {
1677
shader_defs.push("EARLY_PHASE".into());
1678
}
1679
if key.contains(BuildIndirectParametersPipelineKey::LATE_PHASE) {
1680
shader_defs.push("LATE_PHASE".into());
1681
}
1682
if key.contains(BuildIndirectParametersPipelineKey::MAIN_PHASE) {
1683
shader_defs.push("MAIN_PHASE".into());
1684
}
1685
1686
let label = format!(
1687
"{} build {}indexed indirect parameters",
1688
if !key.contains(BuildIndirectParametersPipelineKey::OCCLUSION_CULLING) {
1689
"frustum culling"
1690
} else if key.contains(BuildIndirectParametersPipelineKey::EARLY_PHASE) {
1691
"early occlusion culling"
1692
} else if key.contains(BuildIndirectParametersPipelineKey::LATE_PHASE) {
1693
"late occlusion culling"
1694
} else {
1695
"main occlusion culling"
1696
},
1697
if key.contains(BuildIndirectParametersPipelineKey::INDEXED) {
1698
""
1699
} else {
1700
"non-"
1701
}
1702
);
1703
1704
ComputePipelineDescriptor {
1705
label: Some(label.into()),
1706
layout: vec![self.bind_group_layout.clone()],
1707
shader: self.shader.clone(),
1708
shader_defs,
1709
..default()
1710
}
1711
}
1712
}
1713
1714
impl ResetIndirectBatchSetsPipeline {
1715
fn prepare(
1716
&mut self,
1717
pipeline_cache: &PipelineCache,
1718
pipelines: &mut SpecializedComputePipelines<ResetIndirectBatchSetsPipeline>,
1719
) {
1720
if self.pipeline_id.is_some() {
1721
return;
1722
}
1723
1724
let reset_indirect_batch_sets_pipeline_id = pipelines.specialize(pipeline_cache, self, ());
1725
self.pipeline_id = Some(reset_indirect_batch_sets_pipeline_id);
1726
}
1727
}
1728
1729
impl BuildIndirectParametersPipeline {
1730
fn prepare(
1731
&mut self,
1732
pipeline_cache: &PipelineCache,
1733
pipelines: &mut SpecializedComputePipelines<BuildIndirectParametersPipeline>,
1734
key: BuildIndirectParametersPipelineKey,
1735
) {
1736
if self.pipeline_id.is_some() {
1737
return;
1738
}
1739
1740
let build_indirect_parameters_pipeline_id = pipelines.specialize(pipeline_cache, self, key);
1741
self.pipeline_id = Some(build_indirect_parameters_pipeline_id);
1742
}
1743
}
1744
1745
/// A system that attaches the mesh uniform buffers to the bind groups for the
1746
/// variants of the mesh preprocessing compute shader.
1747
#[expect(
1748
clippy::too_many_arguments,
1749
reason = "it's a system that needs a lot of arguments"
1750
)]
1751
pub fn prepare_preprocess_bind_groups(
1752
mut commands: Commands,
1753
views: Query<(Entity, &ExtractedView)>,
1754
view_depth_pyramids: Query<(&ViewDepthPyramid, &PreviousViewUniformOffset)>,
1755
render_device: Res<RenderDevice>,
1756
batched_instance_buffers: Res<BatchedInstanceBuffers<MeshUniform, MeshInputUniform>>,
1757
indirect_parameters_buffers: Res<IndirectParametersBuffers>,
1758
mesh_culling_data_buffer: Res<MeshCullingDataBuffer>,
1759
view_uniforms: Res<ViewUniforms>,
1760
previous_view_uniforms: Res<PreviousViewUniforms>,
1761
pipelines: Res<PreprocessPipelines>,
1762
) {
1763
// Grab the `BatchedInstanceBuffers`.
1764
let BatchedInstanceBuffers {
1765
current_input_buffer: current_input_buffer_vec,
1766
previous_input_buffer: previous_input_buffer_vec,
1767
phase_instance_buffers,
1768
} = batched_instance_buffers.into_inner();
1769
1770
let (Some(current_input_buffer), Some(previous_input_buffer)) = (
1771
current_input_buffer_vec.buffer().buffer(),
1772
previous_input_buffer_vec.buffer().buffer(),
1773
) else {
1774
return;
1775
};
1776
1777
// Record whether we have any meshes that are to be drawn indirectly. If we
1778
// don't, then we can skip building indirect parameters.
1779
let mut any_indirect = false;
1780
1781
// Loop over each view.
1782
for (view_entity, view) in &views {
1783
let mut bind_groups = TypeIdMap::default();
1784
1785
// Loop over each phase.
1786
for (phase_type_id, phase_instance_buffers) in phase_instance_buffers {
1787
let UntypedPhaseBatchedInstanceBuffers {
1788
data_buffer: ref data_buffer_vec,
1789
ref work_item_buffers,
1790
ref late_indexed_indirect_parameters_buffer,
1791
ref late_non_indexed_indirect_parameters_buffer,
1792
} = *phase_instance_buffers;
1793
1794
let Some(data_buffer) = data_buffer_vec.buffer() else {
1795
continue;
1796
};
1797
1798
// Grab the indirect parameters buffers for this phase.
1799
let Some(phase_indirect_parameters_buffers) =
1800
indirect_parameters_buffers.get(phase_type_id)
1801
else {
1802
continue;
1803
};
1804
1805
let Some(work_item_buffers) = work_item_buffers.get(&view.retained_view_entity) else {
1806
continue;
1807
};
1808
1809
// Create the `PreprocessBindGroupBuilder`.
1810
let preprocess_bind_group_builder = PreprocessBindGroupBuilder {
1811
view: view_entity,
1812
late_indexed_indirect_parameters_buffer,
1813
late_non_indexed_indirect_parameters_buffer,
1814
render_device: &render_device,
1815
phase_indirect_parameters_buffers,
1816
mesh_culling_data_buffer: &mesh_culling_data_buffer,
1817
view_uniforms: &view_uniforms,
1818
previous_view_uniforms: &previous_view_uniforms,
1819
pipelines: &pipelines,
1820
current_input_buffer,
1821
previous_input_buffer,
1822
data_buffer,
1823
};
1824
1825
// Depending on the type of work items we have, construct the
1826
// appropriate bind groups.
1827
let (was_indirect, bind_group) = match *work_item_buffers {
1828
PreprocessWorkItemBuffers::Direct(ref work_item_buffer) => (
1829
false,
1830
preprocess_bind_group_builder
1831
.create_direct_preprocess_bind_groups(work_item_buffer),
1832
),
1833
1834
PreprocessWorkItemBuffers::Indirect {
1835
indexed: ref indexed_work_item_buffer,
1836
non_indexed: ref non_indexed_work_item_buffer,
1837
gpu_occlusion_culling: Some(ref gpu_occlusion_culling_work_item_buffers),
1838
} => (
1839
true,
1840
preprocess_bind_group_builder
1841
.create_indirect_occlusion_culling_preprocess_bind_groups(
1842
&view_depth_pyramids,
1843
indexed_work_item_buffer,
1844
non_indexed_work_item_buffer,
1845
gpu_occlusion_culling_work_item_buffers,
1846
),
1847
),
1848
1849
PreprocessWorkItemBuffers::Indirect {
1850
indexed: ref indexed_work_item_buffer,
1851
non_indexed: ref non_indexed_work_item_buffer,
1852
gpu_occlusion_culling: None,
1853
} => (
1854
true,
1855
preprocess_bind_group_builder
1856
.create_indirect_frustum_culling_preprocess_bind_groups(
1857
indexed_work_item_buffer,
1858
non_indexed_work_item_buffer,
1859
),
1860
),
1861
};
1862
1863
// Write that bind group in.
1864
if let Some(bind_group) = bind_group {
1865
any_indirect = any_indirect || was_indirect;
1866
bind_groups.insert(*phase_type_id, bind_group);
1867
}
1868
}
1869
1870
// Save the bind groups.
1871
commands
1872
.entity(view_entity)
1873
.insert(PreprocessBindGroups(bind_groups));
1874
}
1875
1876
// Now, if there were any indirect draw commands, create the bind groups for
1877
// the indirect parameters building shader.
1878
if any_indirect {
1879
create_build_indirect_parameters_bind_groups(
1880
&mut commands,
1881
&render_device,
1882
&pipelines,
1883
current_input_buffer,
1884
&indirect_parameters_buffers,
1885
);
1886
}
1887
}
1888
1889
/// A temporary structure that stores all the information needed to construct
1890
/// bind groups for the mesh preprocessing shader.
1891
struct PreprocessBindGroupBuilder<'a> {
1892
/// The render-world entity corresponding to the current view.
1893
view: Entity,
1894
/// The indirect compute dispatch parameters buffer for indexed meshes in
1895
/// the late prepass.
1896
late_indexed_indirect_parameters_buffer:
1897
&'a RawBufferVec<LatePreprocessWorkItemIndirectParameters>,
1898
/// The indirect compute dispatch parameters buffer for non-indexed meshes
1899
/// in the late prepass.
1900
late_non_indexed_indirect_parameters_buffer:
1901
&'a RawBufferVec<LatePreprocessWorkItemIndirectParameters>,
1902
/// The device.
1903
render_device: &'a RenderDevice,
1904
/// The buffers that store indirect draw parameters.
1905
phase_indirect_parameters_buffers: &'a UntypedPhaseIndirectParametersBuffers,
1906
/// The GPU buffer that stores the information needed to cull each mesh.
1907
mesh_culling_data_buffer: &'a MeshCullingDataBuffer,
1908
/// The GPU buffer that stores information about the view.
1909
view_uniforms: &'a ViewUniforms,
1910
/// The GPU buffer that stores information about the view from last frame.
1911
previous_view_uniforms: &'a PreviousViewUniforms,
1912
/// The pipelines for the mesh preprocessing shader.
1913
pipelines: &'a PreprocessPipelines,
1914
/// The GPU buffer containing the list of [`MeshInputUniform`]s for the
1915
/// current frame.
1916
current_input_buffer: &'a Buffer,
1917
/// The GPU buffer containing the list of [`MeshInputUniform`]s for the
1918
/// previous frame.
1919
previous_input_buffer: &'a Buffer,
1920
/// The GPU buffer containing the list of [`MeshUniform`]s for the current
1921
/// frame.
1922
///
1923
/// This is the buffer containing the mesh's final transforms that the
1924
/// shaders will write to.
1925
data_buffer: &'a Buffer,
1926
}
1927
1928
impl<'a> PreprocessBindGroupBuilder<'a> {
1929
/// Creates the bind groups for mesh preprocessing when GPU frustum culling
1930
/// and GPU occlusion culling are both disabled.
1931
fn create_direct_preprocess_bind_groups(
1932
&self,
1933
work_item_buffer: &RawBufferVec<PreprocessWorkItem>,
1934
) -> Option<PhasePreprocessBindGroups> {
1935
// Don't use `as_entire_binding()` here; the shader reads the array
1936
// length and the underlying buffer may be longer than the actual size
1937
// of the vector.
1938
let work_item_buffer_size = NonZero::<u64>::try_from(
1939
work_item_buffer.len() as u64 * u64::from(PreprocessWorkItem::min_size()),
1940
)
1941
.ok();
1942
1943
Some(PhasePreprocessBindGroups::Direct(
1944
self.render_device.create_bind_group(
1945
"preprocess_direct_bind_group",
1946
&self.pipelines.direct_preprocess.bind_group_layout,
1947
&BindGroupEntries::with_indices((
1948
(0, self.view_uniforms.uniforms.binding()?),
1949
(3, self.current_input_buffer.as_entire_binding()),
1950
(4, self.previous_input_buffer.as_entire_binding()),
1951
(
1952
5,
1953
BindingResource::Buffer(BufferBinding {
1954
buffer: work_item_buffer.buffer()?,
1955
offset: 0,
1956
size: work_item_buffer_size,
1957
}),
1958
),
1959
(6, self.data_buffer.as_entire_binding()),
1960
)),
1961
),
1962
))
1963
}
1964
1965
/// Creates the bind groups for mesh preprocessing when GPU occlusion
1966
/// culling is enabled.
1967
fn create_indirect_occlusion_culling_preprocess_bind_groups(
1968
&self,
1969
view_depth_pyramids: &Query<(&ViewDepthPyramid, &PreviousViewUniformOffset)>,
1970
indexed_work_item_buffer: &RawBufferVec<PreprocessWorkItem>,
1971
non_indexed_work_item_buffer: &RawBufferVec<PreprocessWorkItem>,
1972
gpu_occlusion_culling_work_item_buffers: &GpuOcclusionCullingWorkItemBuffers,
1973
) -> Option<PhasePreprocessBindGroups> {
1974
let GpuOcclusionCullingWorkItemBuffers {
1975
late_indexed: ref late_indexed_work_item_buffer,
1976
late_non_indexed: ref late_non_indexed_work_item_buffer,
1977
..
1978
} = *gpu_occlusion_culling_work_item_buffers;
1979
1980
let (view_depth_pyramid, previous_view_uniform_offset) =
1981
view_depth_pyramids.get(self.view).ok()?;
1982
1983
Some(PhasePreprocessBindGroups::IndirectOcclusionCulling {
1984
early_indexed: self.create_indirect_occlusion_culling_early_indexed_bind_group(
1985
view_depth_pyramid,
1986
previous_view_uniform_offset,
1987
indexed_work_item_buffer,
1988
late_indexed_work_item_buffer,
1989
),
1990
1991
early_non_indexed: self.create_indirect_occlusion_culling_early_non_indexed_bind_group(
1992
view_depth_pyramid,
1993
previous_view_uniform_offset,
1994
non_indexed_work_item_buffer,
1995
late_non_indexed_work_item_buffer,
1996
),
1997
1998
late_indexed: self.create_indirect_occlusion_culling_late_indexed_bind_group(
1999
view_depth_pyramid,
2000
previous_view_uniform_offset,
2001
late_indexed_work_item_buffer,
2002
),
2003
2004
late_non_indexed: self.create_indirect_occlusion_culling_late_non_indexed_bind_group(
2005
view_depth_pyramid,
2006
previous_view_uniform_offset,
2007
late_non_indexed_work_item_buffer,
2008
),
2009
})
2010
}
2011
2012
/// Creates the bind group for the first phase of mesh preprocessing of
2013
/// indexed meshes when GPU occlusion culling is enabled.
2014
fn create_indirect_occlusion_culling_early_indexed_bind_group(
2015
&self,
2016
view_depth_pyramid: &ViewDepthPyramid,
2017
previous_view_uniform_offset: &PreviousViewUniformOffset,
2018
indexed_work_item_buffer: &RawBufferVec<PreprocessWorkItem>,
2019
late_indexed_work_item_buffer: &UninitBufferVec<PreprocessWorkItem>,
2020
) -> Option<BindGroup> {
2021
let mesh_culling_data_buffer = self.mesh_culling_data_buffer.buffer()?;
2022
let view_uniforms_binding = self.view_uniforms.uniforms.binding()?;
2023
let previous_view_buffer = self.previous_view_uniforms.uniforms.buffer()?;
2024
2025
match (
2026
self.phase_indirect_parameters_buffers
2027
.indexed
2028
.cpu_metadata_buffer(),
2029
self.phase_indirect_parameters_buffers
2030
.indexed
2031
.gpu_metadata_buffer(),
2032
indexed_work_item_buffer.buffer(),
2033
late_indexed_work_item_buffer.buffer(),
2034
self.late_indexed_indirect_parameters_buffer.buffer(),
2035
) {
2036
(
2037
Some(indexed_cpu_metadata_buffer),
2038
Some(indexed_gpu_metadata_buffer),
2039
Some(indexed_work_item_gpu_buffer),
2040
Some(late_indexed_work_item_gpu_buffer),
2041
Some(late_indexed_indirect_parameters_buffer),
2042
) => {
2043
// Don't use `as_entire_binding()` here; the shader reads the array
2044
// length and the underlying buffer may be longer than the actual size
2045
// of the vector.
2046
let indexed_work_item_buffer_size = NonZero::<u64>::try_from(
2047
indexed_work_item_buffer.len() as u64
2048
* u64::from(PreprocessWorkItem::min_size()),
2049
)
2050
.ok();
2051
2052
Some(
2053
self.render_device.create_bind_group(
2054
"preprocess_early_indexed_gpu_occlusion_culling_bind_group",
2055
&self
2056
.pipelines
2057
.early_gpu_occlusion_culling_preprocess
2058
.bind_group_layout,
2059
&BindGroupEntries::with_indices((
2060
(3, self.current_input_buffer.as_entire_binding()),
2061
(4, self.previous_input_buffer.as_entire_binding()),
2062
(
2063
5,
2064
BindingResource::Buffer(BufferBinding {
2065
buffer: indexed_work_item_gpu_buffer,
2066
offset: 0,
2067
size: indexed_work_item_buffer_size,
2068
}),
2069
),
2070
(6, self.data_buffer.as_entire_binding()),
2071
(7, indexed_cpu_metadata_buffer.as_entire_binding()),
2072
(8, indexed_gpu_metadata_buffer.as_entire_binding()),
2073
(9, mesh_culling_data_buffer.as_entire_binding()),
2074
(0, view_uniforms_binding.clone()),
2075
(10, &view_depth_pyramid.all_mips),
2076
(
2077
2,
2078
BufferBinding {
2079
buffer: previous_view_buffer,
2080
offset: previous_view_uniform_offset.offset as u64,
2081
size: NonZeroU64::new(size_of::<PreviousViewData>() as u64),
2082
},
2083
),
2084
(
2085
11,
2086
BufferBinding {
2087
buffer: late_indexed_work_item_gpu_buffer,
2088
offset: 0,
2089
size: indexed_work_item_buffer_size,
2090
},
2091
),
2092
(
2093
12,
2094
BufferBinding {
2095
buffer: late_indexed_indirect_parameters_buffer,
2096
offset: 0,
2097
size: NonZeroU64::new(
2098
late_indexed_indirect_parameters_buffer.size(),
2099
),
2100
},
2101
),
2102
)),
2103
),
2104
)
2105
}
2106
_ => None,
2107
}
2108
}
2109
2110
/// Creates the bind group for the first phase of mesh preprocessing of
2111
/// non-indexed meshes when GPU occlusion culling is enabled.
2112
fn create_indirect_occlusion_culling_early_non_indexed_bind_group(
2113
&self,
2114
view_depth_pyramid: &ViewDepthPyramid,
2115
previous_view_uniform_offset: &PreviousViewUniformOffset,
2116
non_indexed_work_item_buffer: &RawBufferVec<PreprocessWorkItem>,
2117
late_non_indexed_work_item_buffer: &UninitBufferVec<PreprocessWorkItem>,
2118
) -> Option<BindGroup> {
2119
let mesh_culling_data_buffer = self.mesh_culling_data_buffer.buffer()?;
2120
let view_uniforms_binding = self.view_uniforms.uniforms.binding()?;
2121
let previous_view_buffer = self.previous_view_uniforms.uniforms.buffer()?;
2122
2123
match (
2124
self.phase_indirect_parameters_buffers
2125
.non_indexed
2126
.cpu_metadata_buffer(),
2127
self.phase_indirect_parameters_buffers
2128
.non_indexed
2129
.gpu_metadata_buffer(),
2130
non_indexed_work_item_buffer.buffer(),
2131
late_non_indexed_work_item_buffer.buffer(),
2132
self.late_non_indexed_indirect_parameters_buffer.buffer(),
2133
) {
2134
(
2135
Some(non_indexed_cpu_metadata_buffer),
2136
Some(non_indexed_gpu_metadata_buffer),
2137
Some(non_indexed_work_item_gpu_buffer),
2138
Some(late_non_indexed_work_item_buffer),
2139
Some(late_non_indexed_indirect_parameters_buffer),
2140
) => {
2141
// Don't use `as_entire_binding()` here; the shader reads the array
2142
// length and the underlying buffer may be longer than the actual size
2143
// of the vector.
2144
let non_indexed_work_item_buffer_size = NonZero::<u64>::try_from(
2145
non_indexed_work_item_buffer.len() as u64
2146
* u64::from(PreprocessWorkItem::min_size()),
2147
)
2148
.ok();
2149
2150
Some(
2151
self.render_device.create_bind_group(
2152
"preprocess_early_non_indexed_gpu_occlusion_culling_bind_group",
2153
&self
2154
.pipelines
2155
.early_gpu_occlusion_culling_preprocess
2156
.bind_group_layout,
2157
&BindGroupEntries::with_indices((
2158
(3, self.current_input_buffer.as_entire_binding()),
2159
(4, self.previous_input_buffer.as_entire_binding()),
2160
(
2161
5,
2162
BindingResource::Buffer(BufferBinding {
2163
buffer: non_indexed_work_item_gpu_buffer,
2164
offset: 0,
2165
size: non_indexed_work_item_buffer_size,
2166
}),
2167
),
2168
(6, self.data_buffer.as_entire_binding()),
2169
(7, non_indexed_cpu_metadata_buffer.as_entire_binding()),
2170
(8, non_indexed_gpu_metadata_buffer.as_entire_binding()),
2171
(9, mesh_culling_data_buffer.as_entire_binding()),
2172
(0, view_uniforms_binding.clone()),
2173
(10, &view_depth_pyramid.all_mips),
2174
(
2175
2,
2176
BufferBinding {
2177
buffer: previous_view_buffer,
2178
offset: previous_view_uniform_offset.offset as u64,
2179
size: NonZeroU64::new(size_of::<PreviousViewData>() as u64),
2180
},
2181
),
2182
(
2183
11,
2184
BufferBinding {
2185
buffer: late_non_indexed_work_item_buffer,
2186
offset: 0,
2187
size: non_indexed_work_item_buffer_size,
2188
},
2189
),
2190
(
2191
12,
2192
BufferBinding {
2193
buffer: late_non_indexed_indirect_parameters_buffer,
2194
offset: 0,
2195
size: NonZeroU64::new(
2196
late_non_indexed_indirect_parameters_buffer.size(),
2197
),
2198
},
2199
),
2200
)),
2201
),
2202
)
2203
}
2204
_ => None,
2205
}
2206
}
2207
2208
/// Creates the bind group for the second phase of mesh preprocessing of
2209
/// indexed meshes when GPU occlusion culling is enabled.
2210
fn create_indirect_occlusion_culling_late_indexed_bind_group(
2211
&self,
2212
view_depth_pyramid: &ViewDepthPyramid,
2213
previous_view_uniform_offset: &PreviousViewUniformOffset,
2214
late_indexed_work_item_buffer: &UninitBufferVec<PreprocessWorkItem>,
2215
) -> Option<BindGroup> {
2216
let mesh_culling_data_buffer = self.mesh_culling_data_buffer.buffer()?;
2217
let view_uniforms_binding = self.view_uniforms.uniforms.binding()?;
2218
let previous_view_buffer = self.previous_view_uniforms.uniforms.buffer()?;
2219
2220
match (
2221
self.phase_indirect_parameters_buffers
2222
.indexed
2223
.cpu_metadata_buffer(),
2224
self.phase_indirect_parameters_buffers
2225
.indexed
2226
.gpu_metadata_buffer(),
2227
late_indexed_work_item_buffer.buffer(),
2228
self.late_indexed_indirect_parameters_buffer.buffer(),
2229
) {
2230
(
2231
Some(indexed_cpu_metadata_buffer),
2232
Some(indexed_gpu_metadata_buffer),
2233
Some(late_indexed_work_item_gpu_buffer),
2234
Some(late_indexed_indirect_parameters_buffer),
2235
) => {
2236
// Don't use `as_entire_binding()` here; the shader reads the array
2237
// length and the underlying buffer may be longer than the actual size
2238
// of the vector.
2239
let late_indexed_work_item_buffer_size = NonZero::<u64>::try_from(
2240
late_indexed_work_item_buffer.len() as u64
2241
* u64::from(PreprocessWorkItem::min_size()),
2242
)
2243
.ok();
2244
2245
Some(
2246
self.render_device.create_bind_group(
2247
"preprocess_late_indexed_gpu_occlusion_culling_bind_group",
2248
&self
2249
.pipelines
2250
.late_gpu_occlusion_culling_preprocess
2251
.bind_group_layout,
2252
&BindGroupEntries::with_indices((
2253
(3, self.current_input_buffer.as_entire_binding()),
2254
(4, self.previous_input_buffer.as_entire_binding()),
2255
(
2256
5,
2257
BindingResource::Buffer(BufferBinding {
2258
buffer: late_indexed_work_item_gpu_buffer,
2259
offset: 0,
2260
size: late_indexed_work_item_buffer_size,
2261
}),
2262
),
2263
(6, self.data_buffer.as_entire_binding()),
2264
(7, indexed_cpu_metadata_buffer.as_entire_binding()),
2265
(8, indexed_gpu_metadata_buffer.as_entire_binding()),
2266
(9, mesh_culling_data_buffer.as_entire_binding()),
2267
(0, view_uniforms_binding.clone()),
2268
(10, &view_depth_pyramid.all_mips),
2269
(
2270
2,
2271
BufferBinding {
2272
buffer: previous_view_buffer,
2273
offset: previous_view_uniform_offset.offset as u64,
2274
size: NonZeroU64::new(size_of::<PreviousViewData>() as u64),
2275
},
2276
),
2277
(
2278
12,
2279
BufferBinding {
2280
buffer: late_indexed_indirect_parameters_buffer,
2281
offset: 0,
2282
size: NonZeroU64::new(
2283
late_indexed_indirect_parameters_buffer.size(),
2284
),
2285
},
2286
),
2287
)),
2288
),
2289
)
2290
}
2291
_ => None,
2292
}
2293
}
2294
2295
/// Creates the bind group for the second phase of mesh preprocessing of
2296
/// non-indexed meshes when GPU occlusion culling is enabled.
2297
fn create_indirect_occlusion_culling_late_non_indexed_bind_group(
2298
&self,
2299
view_depth_pyramid: &ViewDepthPyramid,
2300
previous_view_uniform_offset: &PreviousViewUniformOffset,
2301
late_non_indexed_work_item_buffer: &UninitBufferVec<PreprocessWorkItem>,
2302
) -> Option<BindGroup> {
2303
let mesh_culling_data_buffer = self.mesh_culling_data_buffer.buffer()?;
2304
let view_uniforms_binding = self.view_uniforms.uniforms.binding()?;
2305
let previous_view_buffer = self.previous_view_uniforms.uniforms.buffer()?;
2306
2307
match (
2308
self.phase_indirect_parameters_buffers
2309
.non_indexed
2310
.cpu_metadata_buffer(),
2311
self.phase_indirect_parameters_buffers
2312
.non_indexed
2313
.gpu_metadata_buffer(),
2314
late_non_indexed_work_item_buffer.buffer(),
2315
self.late_non_indexed_indirect_parameters_buffer.buffer(),
2316
) {
2317
(
2318
Some(non_indexed_cpu_metadata_buffer),
2319
Some(non_indexed_gpu_metadata_buffer),
2320
Some(non_indexed_work_item_gpu_buffer),
2321
Some(late_non_indexed_indirect_parameters_buffer),
2322
) => {
2323
// Don't use `as_entire_binding()` here; the shader reads the array
2324
// length and the underlying buffer may be longer than the actual size
2325
// of the vector.
2326
let non_indexed_work_item_buffer_size = NonZero::<u64>::try_from(
2327
late_non_indexed_work_item_buffer.len() as u64
2328
* u64::from(PreprocessWorkItem::min_size()),
2329
)
2330
.ok();
2331
2332
Some(
2333
self.render_device.create_bind_group(
2334
"preprocess_late_non_indexed_gpu_occlusion_culling_bind_group",
2335
&self
2336
.pipelines
2337
.late_gpu_occlusion_culling_preprocess
2338
.bind_group_layout,
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: non_indexed_work_item_gpu_buffer,
2346
offset: 0,
2347
size: non_indexed_work_item_buffer_size,
2348
}),
2349
),
2350
(6, self.data_buffer.as_entire_binding()),
2351
(7, non_indexed_cpu_metadata_buffer.as_entire_binding()),
2352
(8, non_indexed_gpu_metadata_buffer.as_entire_binding()),
2353
(9, mesh_culling_data_buffer.as_entire_binding()),
2354
(0, view_uniforms_binding.clone()),
2355
(10, &view_depth_pyramid.all_mips),
2356
(
2357
2,
2358
BufferBinding {
2359
buffer: previous_view_buffer,
2360
offset: previous_view_uniform_offset.offset as u64,
2361
size: NonZeroU64::new(size_of::<PreviousViewData>() as u64),
2362
},
2363
),
2364
(
2365
12,
2366
BufferBinding {
2367
buffer: late_non_indexed_indirect_parameters_buffer,
2368
offset: 0,
2369
size: NonZeroU64::new(
2370
late_non_indexed_indirect_parameters_buffer.size(),
2371
),
2372
},
2373
),
2374
)),
2375
),
2376
)
2377
}
2378
_ => None,
2379
}
2380
}
2381
2382
/// Creates the bind groups for mesh preprocessing when GPU frustum culling
2383
/// is enabled, but GPU occlusion culling is disabled.
2384
fn create_indirect_frustum_culling_preprocess_bind_groups(
2385
&self,
2386
indexed_work_item_buffer: &RawBufferVec<PreprocessWorkItem>,
2387
non_indexed_work_item_buffer: &RawBufferVec<PreprocessWorkItem>,
2388
) -> Option<PhasePreprocessBindGroups> {
2389
Some(PhasePreprocessBindGroups::IndirectFrustumCulling {
2390
indexed: self
2391
.create_indirect_frustum_culling_indexed_bind_group(indexed_work_item_buffer),
2392
non_indexed: self.create_indirect_frustum_culling_non_indexed_bind_group(
2393
non_indexed_work_item_buffer,
2394
),
2395
})
2396
}
2397
2398
/// Creates the bind group for mesh preprocessing of indexed meshes when GPU
2399
/// frustum culling is enabled, but GPU occlusion culling is disabled.
2400
fn create_indirect_frustum_culling_indexed_bind_group(
2401
&self,
2402
indexed_work_item_buffer: &RawBufferVec<PreprocessWorkItem>,
2403
) -> Option<BindGroup> {
2404
let mesh_culling_data_buffer = self.mesh_culling_data_buffer.buffer()?;
2405
let view_uniforms_binding = self.view_uniforms.uniforms.binding()?;
2406
2407
match (
2408
self.phase_indirect_parameters_buffers
2409
.indexed
2410
.cpu_metadata_buffer(),
2411
self.phase_indirect_parameters_buffers
2412
.indexed
2413
.gpu_metadata_buffer(),
2414
indexed_work_item_buffer.buffer(),
2415
) {
2416
(
2417
Some(indexed_cpu_metadata_buffer),
2418
Some(indexed_gpu_metadata_buffer),
2419
Some(indexed_work_item_gpu_buffer),
2420
) => {
2421
// Don't use `as_entire_binding()` here; the shader reads the array
2422
// length and the underlying buffer may be longer than the actual size
2423
// of the vector.
2424
let indexed_work_item_buffer_size = NonZero::<u64>::try_from(
2425
indexed_work_item_buffer.len() as u64
2426
* u64::from(PreprocessWorkItem::min_size()),
2427
)
2428
.ok();
2429
2430
Some(
2431
self.render_device.create_bind_group(
2432
"preprocess_gpu_indexed_frustum_culling_bind_group",
2433
&self
2434
.pipelines
2435
.gpu_frustum_culling_preprocess
2436
.bind_group_layout,
2437
&BindGroupEntries::with_indices((
2438
(3, self.current_input_buffer.as_entire_binding()),
2439
(4, self.previous_input_buffer.as_entire_binding()),
2440
(
2441
5,
2442
BindingResource::Buffer(BufferBinding {
2443
buffer: indexed_work_item_gpu_buffer,
2444
offset: 0,
2445
size: indexed_work_item_buffer_size,
2446
}),
2447
),
2448
(6, self.data_buffer.as_entire_binding()),
2449
(7, indexed_cpu_metadata_buffer.as_entire_binding()),
2450
(8, indexed_gpu_metadata_buffer.as_entire_binding()),
2451
(9, mesh_culling_data_buffer.as_entire_binding()),
2452
(0, view_uniforms_binding.clone()),
2453
)),
2454
),
2455
)
2456
}
2457
_ => None,
2458
}
2459
}
2460
2461
/// Creates the bind group for mesh preprocessing of non-indexed meshes when
2462
/// GPU frustum culling is enabled, but GPU occlusion culling is disabled.
2463
fn create_indirect_frustum_culling_non_indexed_bind_group(
2464
&self,
2465
non_indexed_work_item_buffer: &RawBufferVec<PreprocessWorkItem>,
2466
) -> Option<BindGroup> {
2467
let mesh_culling_data_buffer = self.mesh_culling_data_buffer.buffer()?;
2468
let view_uniforms_binding = self.view_uniforms.uniforms.binding()?;
2469
2470
match (
2471
self.phase_indirect_parameters_buffers
2472
.non_indexed
2473
.cpu_metadata_buffer(),
2474
self.phase_indirect_parameters_buffers
2475
.non_indexed
2476
.gpu_metadata_buffer(),
2477
non_indexed_work_item_buffer.buffer(),
2478
) {
2479
(
2480
Some(non_indexed_cpu_metadata_buffer),
2481
Some(non_indexed_gpu_metadata_buffer),
2482
Some(non_indexed_work_item_gpu_buffer),
2483
) => {
2484
// Don't use `as_entire_binding()` here; the shader reads the array
2485
// length and the underlying buffer may be longer than the actual size
2486
// of the vector.
2487
let non_indexed_work_item_buffer_size = NonZero::<u64>::try_from(
2488
non_indexed_work_item_buffer.len() as u64
2489
* u64::from(PreprocessWorkItem::min_size()),
2490
)
2491
.ok();
2492
2493
Some(
2494
self.render_device.create_bind_group(
2495
"preprocess_gpu_non_indexed_frustum_culling_bind_group",
2496
&self
2497
.pipelines
2498
.gpu_frustum_culling_preprocess
2499
.bind_group_layout,
2500
&BindGroupEntries::with_indices((
2501
(3, self.current_input_buffer.as_entire_binding()),
2502
(4, self.previous_input_buffer.as_entire_binding()),
2503
(
2504
5,
2505
BindingResource::Buffer(BufferBinding {
2506
buffer: non_indexed_work_item_gpu_buffer,
2507
offset: 0,
2508
size: non_indexed_work_item_buffer_size,
2509
}),
2510
),
2511
(6, self.data_buffer.as_entire_binding()),
2512
(7, non_indexed_cpu_metadata_buffer.as_entire_binding()),
2513
(8, non_indexed_gpu_metadata_buffer.as_entire_binding()),
2514
(9, mesh_culling_data_buffer.as_entire_binding()),
2515
(0, view_uniforms_binding.clone()),
2516
)),
2517
),
2518
)
2519
}
2520
_ => None,
2521
}
2522
}
2523
}
2524
2525
/// A system that creates bind groups from the indirect parameters metadata and
2526
/// data buffers for the indirect batch set reset shader and the indirect
2527
/// parameter building shader.
2528
fn create_build_indirect_parameters_bind_groups(
2529
commands: &mut Commands,
2530
render_device: &RenderDevice,
2531
pipelines: &PreprocessPipelines,
2532
current_input_buffer: &Buffer,
2533
indirect_parameters_buffers: &IndirectParametersBuffers,
2534
) {
2535
let mut build_indirect_parameters_bind_groups = BuildIndirectParametersBindGroups::new();
2536
2537
for (phase_type_id, phase_indirect_parameters_buffer) in indirect_parameters_buffers.iter() {
2538
build_indirect_parameters_bind_groups.insert(
2539
*phase_type_id,
2540
PhaseBuildIndirectParametersBindGroups {
2541
reset_indexed_indirect_batch_sets: match (phase_indirect_parameters_buffer
2542
.indexed
2543
.batch_sets_buffer(),)
2544
{
2545
(Some(indexed_batch_sets_buffer),) => Some(
2546
render_device.create_bind_group(
2547
"reset_indexed_indirect_batch_sets_bind_group",
2548
// The early bind group is good for the main phase and late
2549
// phase too. They bind the same buffers.
2550
&pipelines
2551
.early_phase
2552
.reset_indirect_batch_sets
2553
.bind_group_layout,
2554
&BindGroupEntries::sequential((
2555
indexed_batch_sets_buffer.as_entire_binding(),
2556
)),
2557
),
2558
),
2559
_ => None,
2560
},
2561
2562
reset_non_indexed_indirect_batch_sets: match (phase_indirect_parameters_buffer
2563
.non_indexed
2564
.batch_sets_buffer(),)
2565
{
2566
(Some(non_indexed_batch_sets_buffer),) => Some(
2567
render_device.create_bind_group(
2568
"reset_non_indexed_indirect_batch_sets_bind_group",
2569
// The early bind group is good for the main phase and late
2570
// phase too. They bind the same buffers.
2571
&pipelines
2572
.early_phase
2573
.reset_indirect_batch_sets
2574
.bind_group_layout,
2575
&BindGroupEntries::sequential((
2576
non_indexed_batch_sets_buffer.as_entire_binding(),
2577
)),
2578
),
2579
),
2580
_ => None,
2581
},
2582
2583
build_indexed_indirect: match (
2584
phase_indirect_parameters_buffer
2585
.indexed
2586
.cpu_metadata_buffer(),
2587
phase_indirect_parameters_buffer
2588
.indexed
2589
.gpu_metadata_buffer(),
2590
phase_indirect_parameters_buffer.indexed.data_buffer(),
2591
phase_indirect_parameters_buffer.indexed.batch_sets_buffer(),
2592
) {
2593
(
2594
Some(indexed_indirect_parameters_cpu_metadata_buffer),
2595
Some(indexed_indirect_parameters_gpu_metadata_buffer),
2596
Some(indexed_indirect_parameters_data_buffer),
2597
Some(indexed_batch_sets_buffer),
2598
) => Some(
2599
render_device.create_bind_group(
2600
"build_indexed_indirect_parameters_bind_group",
2601
// The frustum culling bind group is good for occlusion culling
2602
// too. They bind the same buffers.
2603
&pipelines
2604
.gpu_frustum_culling_build_indexed_indirect_params
2605
.bind_group_layout,
2606
&BindGroupEntries::sequential((
2607
current_input_buffer.as_entire_binding(),
2608
// Don't use `as_entire_binding` here; the shader reads
2609
// the length and `RawBufferVec` overallocates.
2610
BufferBinding {
2611
buffer: indexed_indirect_parameters_cpu_metadata_buffer,
2612
offset: 0,
2613
size: NonZeroU64::new(
2614
phase_indirect_parameters_buffer.indexed.batch_count()
2615
as u64
2616
* size_of::<IndirectParametersCpuMetadata>() as u64,
2617
),
2618
},
2619
BufferBinding {
2620
buffer: indexed_indirect_parameters_gpu_metadata_buffer,
2621
offset: 0,
2622
size: NonZeroU64::new(
2623
phase_indirect_parameters_buffer.indexed.batch_count()
2624
as u64
2625
* size_of::<IndirectParametersGpuMetadata>() as u64,
2626
),
2627
},
2628
indexed_batch_sets_buffer.as_entire_binding(),
2629
indexed_indirect_parameters_data_buffer.as_entire_binding(),
2630
)),
2631
),
2632
),
2633
_ => None,
2634
},
2635
2636
build_non_indexed_indirect: match (
2637
phase_indirect_parameters_buffer
2638
.non_indexed
2639
.cpu_metadata_buffer(),
2640
phase_indirect_parameters_buffer
2641
.non_indexed
2642
.gpu_metadata_buffer(),
2643
phase_indirect_parameters_buffer.non_indexed.data_buffer(),
2644
phase_indirect_parameters_buffer
2645
.non_indexed
2646
.batch_sets_buffer(),
2647
) {
2648
(
2649
Some(non_indexed_indirect_parameters_cpu_metadata_buffer),
2650
Some(non_indexed_indirect_parameters_gpu_metadata_buffer),
2651
Some(non_indexed_indirect_parameters_data_buffer),
2652
Some(non_indexed_batch_sets_buffer),
2653
) => Some(
2654
render_device.create_bind_group(
2655
"build_non_indexed_indirect_parameters_bind_group",
2656
// The frustum culling bind group is good for occlusion culling
2657
// too. They bind the same buffers.
2658
&pipelines
2659
.gpu_frustum_culling_build_non_indexed_indirect_params
2660
.bind_group_layout,
2661
&BindGroupEntries::sequential((
2662
current_input_buffer.as_entire_binding(),
2663
// Don't use `as_entire_binding` here; the shader reads
2664
// the length and `RawBufferVec` overallocates.
2665
BufferBinding {
2666
buffer: non_indexed_indirect_parameters_cpu_metadata_buffer,
2667
offset: 0,
2668
size: NonZeroU64::new(
2669
phase_indirect_parameters_buffer.non_indexed.batch_count()
2670
as u64
2671
* size_of::<IndirectParametersCpuMetadata>() as u64,
2672
),
2673
},
2674
BufferBinding {
2675
buffer: non_indexed_indirect_parameters_gpu_metadata_buffer,
2676
offset: 0,
2677
size: NonZeroU64::new(
2678
phase_indirect_parameters_buffer.non_indexed.batch_count()
2679
as u64
2680
* size_of::<IndirectParametersGpuMetadata>() as u64,
2681
),
2682
},
2683
non_indexed_batch_sets_buffer.as_entire_binding(),
2684
non_indexed_indirect_parameters_data_buffer.as_entire_binding(),
2685
)),
2686
),
2687
),
2688
_ => None,
2689
},
2690
},
2691
);
2692
}
2693
2694
commands.insert_resource(build_indirect_parameters_bind_groups);
2695
}
2696
2697
/// Writes the information needed to do GPU mesh culling to the GPU.
2698
pub fn write_mesh_culling_data_buffer(
2699
render_device: Res<RenderDevice>,
2700
render_queue: Res<RenderQueue>,
2701
mut mesh_culling_data_buffer: ResMut<MeshCullingDataBuffer>,
2702
) {
2703
mesh_culling_data_buffer.write_buffer(&render_device, &render_queue);
2704
}
2705
2706