Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_pbr/src/prepass/mod.rs
6600 views
1
mod prepass_bindings;
2
3
use crate::{
4
alpha_mode_pipeline_key, binding_arrays_are_usable, buffer_layout,
5
collect_meshes_for_gpu_building, init_material_pipeline, set_mesh_motion_vector_flags,
6
setup_morph_and_skinning_defs, skin, DeferredDrawFunction, DeferredFragmentShader,
7
DeferredVertexShader, DrawMesh, EntitySpecializationTicks, ErasedMaterialPipelineKey, Material,
8
MaterialPipeline, MaterialProperties, MeshLayouts, MeshPipeline, MeshPipelineKey,
9
OpaqueRendererMethod, PreparedMaterial, PrepassDrawFunction, PrepassFragmentShader,
10
PrepassVertexShader, RenderLightmaps, RenderMaterialInstances, RenderMeshInstanceFlags,
11
RenderMeshInstances, RenderPhaseType, SetMaterialBindGroup, SetMeshBindGroup, ShadowView,
12
};
13
use bevy_app::{App, Plugin, PreUpdate};
14
use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer, Handle};
15
use bevy_camera::{Camera, Camera3d};
16
use bevy_core_pipeline::{core_3d::CORE_3D_DEPTH_FORMAT, deferred::*, prepass::*};
17
use bevy_ecs::{
18
prelude::*,
19
system::{
20
lifetimeless::{Read, SRes},
21
SystemParamItem,
22
},
23
};
24
use bevy_math::{Affine3A, Mat4, Vec4};
25
use bevy_mesh::{Mesh, Mesh3d, MeshVertexBufferLayoutRef};
26
use bevy_render::{
27
alpha::AlphaMode,
28
batching::gpu_preprocessing::GpuPreprocessingSupport,
29
globals::{GlobalsBuffer, GlobalsUniform},
30
mesh::{allocator::MeshAllocator, RenderMesh},
31
render_asset::{prepare_assets, RenderAssets},
32
render_phase::*,
33
render_resource::{binding_types::uniform_buffer, *},
34
renderer::{RenderAdapter, RenderDevice, RenderQueue},
35
sync_world::RenderEntity,
36
view::{
37
ExtractedView, Msaa, RenderVisibilityRanges, RetainedViewEntity, ViewUniform,
38
ViewUniformOffset, ViewUniforms, VISIBILITY_RANGES_STORAGE_BUFFER_COUNT,
39
},
40
Extract, ExtractSchedule, Render, RenderApp, RenderDebugFlags, RenderStartup, RenderSystems,
41
};
42
use bevy_shader::{load_shader_library, Shader, ShaderDefVal};
43
use bevy_transform::prelude::GlobalTransform;
44
pub use prepass_bindings::*;
45
use tracing::{error, warn};
46
47
#[cfg(feature = "meshlet")]
48
use crate::meshlet::{
49
prepare_material_meshlet_meshes_prepass, queue_material_meshlet_meshes, InstanceManager,
50
MeshletMesh3d,
51
};
52
53
use alloc::sync::Arc;
54
use bevy_derive::{Deref, DerefMut};
55
use bevy_ecs::{component::Tick, system::SystemChangeTick};
56
use bevy_platform::collections::HashMap;
57
use bevy_render::{
58
erased_render_asset::ErasedRenderAssets,
59
sync_world::MainEntityHashMap,
60
view::RenderVisibleEntities,
61
RenderSystems::{PrepareAssets, PrepareResources},
62
};
63
use bevy_utils::default;
64
use core::marker::PhantomData;
65
66
/// Sets up everything required to use the prepass pipeline.
67
///
68
/// This does not add the actual prepasses, see [`PrepassPlugin`] for that.
69
pub struct PrepassPipelinePlugin;
70
71
impl Plugin for PrepassPipelinePlugin {
72
fn build(&self, app: &mut App) {
73
embedded_asset!(app, "prepass.wgsl");
74
75
load_shader_library!(app, "prepass_bindings.wgsl");
76
load_shader_library!(app, "prepass_utils.wgsl");
77
load_shader_library!(app, "prepass_io.wgsl");
78
79
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
80
return;
81
};
82
83
render_app
84
.add_systems(
85
RenderStartup,
86
(
87
init_prepass_pipeline.after(init_material_pipeline),
88
init_prepass_view_bind_group,
89
)
90
.chain(),
91
)
92
.add_systems(
93
Render,
94
prepare_prepass_view_bind_group.in_set(RenderSystems::PrepareBindGroups),
95
)
96
.init_resource::<SpecializedMeshPipelines<PrepassPipelineSpecializer>>();
97
}
98
}
99
100
/// Sets up the prepasses for a material.
101
///
102
/// This depends on the [`PrepassPipelinePlugin`].
103
pub struct PrepassPlugin {
104
/// Debugging flags that can optionally be set when constructing the renderer.
105
pub debug_flags: RenderDebugFlags,
106
}
107
108
impl PrepassPlugin {
109
/// Creates a new [`PrepassPlugin`] with the given debug flags.
110
pub fn new(debug_flags: RenderDebugFlags) -> Self {
111
PrepassPlugin { debug_flags }
112
}
113
}
114
115
impl Plugin for PrepassPlugin {
116
fn build(&self, app: &mut App) {
117
let no_prepass_plugin_loaded = app
118
.world()
119
.get_resource::<AnyPrepassPluginLoaded>()
120
.is_none();
121
122
if no_prepass_plugin_loaded {
123
app.insert_resource(AnyPrepassPluginLoaded)
124
// At the start of each frame, last frame's GlobalTransforms become this frame's PreviousGlobalTransforms
125
// and last frame's view projection matrices become this frame's PreviousViewProjections
126
.add_systems(
127
PreUpdate,
128
(
129
update_mesh_previous_global_transforms,
130
update_previous_view_data,
131
),
132
)
133
.add_plugins((
134
BinnedRenderPhasePlugin::<Opaque3dPrepass, MeshPipeline>::new(self.debug_flags),
135
BinnedRenderPhasePlugin::<AlphaMask3dPrepass, MeshPipeline>::new(
136
self.debug_flags,
137
),
138
));
139
}
140
141
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
142
return;
143
};
144
145
if no_prepass_plugin_loaded {
146
render_app
147
.add_systems(ExtractSchedule, extract_camera_previous_view_data)
148
.add_systems(
149
Render,
150
prepare_previous_view_uniforms.in_set(PrepareResources),
151
);
152
}
153
154
render_app
155
.init_resource::<ViewPrepassSpecializationTicks>()
156
.init_resource::<ViewKeyPrepassCache>()
157
.init_resource::<SpecializedPrepassMaterialPipelineCache>()
158
.add_render_command::<Opaque3dPrepass, DrawPrepass>()
159
.add_render_command::<AlphaMask3dPrepass, DrawPrepass>()
160
.add_render_command::<Opaque3dDeferred, DrawPrepass>()
161
.add_render_command::<AlphaMask3dDeferred, DrawPrepass>()
162
.add_systems(
163
Render,
164
(
165
check_prepass_views_need_specialization.in_set(PrepareAssets),
166
specialize_prepass_material_meshes
167
.in_set(RenderSystems::PrepareMeshes)
168
.after(prepare_assets::<RenderMesh>)
169
.after(collect_meshes_for_gpu_building)
170
.after(set_mesh_motion_vector_flags),
171
queue_prepass_material_meshes.in_set(RenderSystems::QueueMeshes),
172
),
173
);
174
175
#[cfg(feature = "meshlet")]
176
render_app.add_systems(
177
Render,
178
prepare_material_meshlet_meshes_prepass
179
.in_set(RenderSystems::QueueMeshes)
180
.before(queue_material_meshlet_meshes)
181
.run_if(resource_exists::<InstanceManager>),
182
);
183
}
184
}
185
186
/// Marker resource for whether prepass is enabled globally for this material type
187
#[derive(Resource, Debug)]
188
pub struct PrepassEnabled<M: Material>(PhantomData<M>);
189
190
impl<M: Material> Default for PrepassEnabled<M> {
191
fn default() -> Self {
192
PrepassEnabled(PhantomData)
193
}
194
}
195
196
#[derive(Resource)]
197
struct AnyPrepassPluginLoaded;
198
199
pub fn update_previous_view_data(
200
mut commands: Commands,
201
query: Query<(Entity, &Camera, &GlobalTransform), Or<(With<Camera3d>, With<ShadowView>)>>,
202
) {
203
for (entity, camera, camera_transform) in &query {
204
let world_from_view = camera_transform.affine();
205
let view_from_world = Mat4::from(world_from_view.inverse());
206
let view_from_clip = camera.clip_from_view().inverse();
207
208
commands.entity(entity).try_insert(PreviousViewData {
209
view_from_world,
210
clip_from_world: camera.clip_from_view() * view_from_world,
211
clip_from_view: camera.clip_from_view(),
212
world_from_clip: Mat4::from(world_from_view) * view_from_clip,
213
view_from_clip,
214
});
215
}
216
}
217
218
#[derive(Component, PartialEq, Default)]
219
pub struct PreviousGlobalTransform(pub Affine3A);
220
221
#[cfg(not(feature = "meshlet"))]
222
type PreviousMeshFilter = With<Mesh3d>;
223
#[cfg(feature = "meshlet")]
224
type PreviousMeshFilter = Or<(With<Mesh3d>, With<MeshletMesh3d>)>;
225
226
pub fn update_mesh_previous_global_transforms(
227
mut commands: Commands,
228
views: Query<&Camera, Or<(With<Camera3d>, With<ShadowView>)>>,
229
new_meshes: Query<
230
(Entity, &GlobalTransform),
231
(PreviousMeshFilter, Without<PreviousGlobalTransform>),
232
>,
233
mut meshes: Query<(&GlobalTransform, &mut PreviousGlobalTransform), PreviousMeshFilter>,
234
) {
235
let should_run = views.iter().any(|camera| camera.is_active);
236
237
if should_run {
238
for (entity, transform) in &new_meshes {
239
let new_previous_transform = PreviousGlobalTransform(transform.affine());
240
commands.entity(entity).try_insert(new_previous_transform);
241
}
242
meshes.par_iter_mut().for_each(|(transform, mut previous)| {
243
previous.set_if_neq(PreviousGlobalTransform(transform.affine()));
244
});
245
}
246
}
247
248
#[derive(Resource, Clone)]
249
pub struct PrepassPipeline {
250
pub view_layout_motion_vectors: BindGroupLayout,
251
pub view_layout_no_motion_vectors: BindGroupLayout,
252
pub mesh_layouts: MeshLayouts,
253
pub empty_layout: BindGroupLayout,
254
pub default_prepass_shader: Handle<Shader>,
255
256
/// Whether skins will use uniform buffers on account of storage buffers
257
/// being unavailable on this platform.
258
pub skins_use_uniform_buffers: bool,
259
260
pub depth_clip_control_supported: bool,
261
262
/// Whether binding arrays (a.k.a. bindless textures) are usable on the
263
/// current render device.
264
pub binding_arrays_are_usable: bool,
265
pub material_pipeline: MaterialPipeline,
266
}
267
268
pub fn init_prepass_pipeline(
269
mut commands: Commands,
270
render_device: Res<RenderDevice>,
271
render_adapter: Res<RenderAdapter>,
272
mesh_pipeline: Res<MeshPipeline>,
273
material_pipeline: Res<MaterialPipeline>,
274
asset_server: Res<AssetServer>,
275
) {
276
let visibility_ranges_buffer_binding_type =
277
render_device.get_supported_read_only_binding_type(VISIBILITY_RANGES_STORAGE_BUFFER_COUNT);
278
279
let view_layout_motion_vectors = render_device.create_bind_group_layout(
280
"prepass_view_layout_motion_vectors",
281
&BindGroupLayoutEntries::with_indices(
282
ShaderStages::VERTEX_FRAGMENT,
283
(
284
// View
285
(0, uniform_buffer::<ViewUniform>(true)),
286
// Globals
287
(1, uniform_buffer::<GlobalsUniform>(false)),
288
// PreviousViewUniforms
289
(2, uniform_buffer::<PreviousViewData>(true)),
290
// VisibilityRanges
291
(
292
14,
293
buffer_layout(
294
visibility_ranges_buffer_binding_type,
295
false,
296
Some(Vec4::min_size()),
297
)
298
.visibility(ShaderStages::VERTEX),
299
),
300
),
301
),
302
);
303
304
let view_layout_no_motion_vectors = render_device.create_bind_group_layout(
305
"prepass_view_layout_no_motion_vectors",
306
&BindGroupLayoutEntries::with_indices(
307
ShaderStages::VERTEX_FRAGMENT,
308
(
309
// View
310
(0, uniform_buffer::<ViewUniform>(true)),
311
// Globals
312
(1, uniform_buffer::<GlobalsUniform>(false)),
313
// VisibilityRanges
314
(
315
14,
316
buffer_layout(
317
visibility_ranges_buffer_binding_type,
318
false,
319
Some(Vec4::min_size()),
320
)
321
.visibility(ShaderStages::VERTEX),
322
),
323
),
324
),
325
);
326
327
let depth_clip_control_supported = render_device
328
.features()
329
.contains(WgpuFeatures::DEPTH_CLIP_CONTROL);
330
commands.insert_resource(PrepassPipeline {
331
view_layout_motion_vectors,
332
view_layout_no_motion_vectors,
333
mesh_layouts: mesh_pipeline.mesh_layouts.clone(),
334
default_prepass_shader: load_embedded_asset!(asset_server.as_ref(), "prepass.wgsl"),
335
skins_use_uniform_buffers: skin::skins_use_uniform_buffers(&render_device),
336
depth_clip_control_supported,
337
binding_arrays_are_usable: binding_arrays_are_usable(&render_device, &render_adapter),
338
empty_layout: render_device.create_bind_group_layout("prepass_empty_layout", &[]),
339
material_pipeline: material_pipeline.clone(),
340
});
341
}
342
343
pub struct PrepassPipelineSpecializer {
344
pub(crate) pipeline: PrepassPipeline,
345
pub(crate) properties: Arc<MaterialProperties>,
346
}
347
348
impl SpecializedMeshPipeline for PrepassPipelineSpecializer {
349
type Key = ErasedMaterialPipelineKey;
350
351
fn specialize(
352
&self,
353
key: Self::Key,
354
layout: &MeshVertexBufferLayoutRef,
355
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
356
let mut shader_defs = Vec::new();
357
if self.properties.bindless {
358
shader_defs.push("BINDLESS".into());
359
}
360
let mut descriptor =
361
self.pipeline
362
.specialize(key.mesh_key, shader_defs, layout, &self.properties)?;
363
364
// This is a bit risky because it's possible to change something that would
365
// break the prepass but be fine in the main pass.
366
// Since this api is pretty low-level it doesn't matter that much, but it is a potential issue.
367
if let Some(specialize) = self.properties.specialize {
368
specialize(
369
&self.pipeline.material_pipeline,
370
&mut descriptor,
371
layout,
372
key,
373
)?;
374
}
375
376
Ok(descriptor)
377
}
378
}
379
380
impl PrepassPipeline {
381
fn specialize(
382
&self,
383
mesh_key: MeshPipelineKey,
384
shader_defs: Vec<ShaderDefVal>,
385
layout: &MeshVertexBufferLayoutRef,
386
material_properties: &MaterialProperties,
387
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
388
let mut shader_defs = shader_defs;
389
let mut bind_group_layouts = vec![
390
if mesh_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
391
self.view_layout_motion_vectors.clone()
392
} else {
393
self.view_layout_no_motion_vectors.clone()
394
},
395
self.empty_layout.clone(),
396
];
397
let mut vertex_attributes = Vec::new();
398
399
// Let the shader code know that it's running in a prepass pipeline.
400
// (PBR code will use this to detect that it's running in deferred mode,
401
// since that's the only time it gets called from a prepass pipeline.)
402
shader_defs.push("PREPASS_PIPELINE".into());
403
404
shader_defs.push(ShaderDefVal::UInt(
405
"MATERIAL_BIND_GROUP".into(),
406
crate::MATERIAL_BIND_GROUP_INDEX as u32,
407
));
408
// NOTE: Eventually, it would be nice to only add this when the shaders are overloaded by the Material.
409
// The main limitation right now is that bind group order is hardcoded in shaders.
410
bind_group_layouts.push(
411
material_properties
412
.material_layout
413
.as_ref()
414
.unwrap()
415
.clone(),
416
);
417
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
418
shader_defs.push("WEBGL2".into());
419
shader_defs.push("VERTEX_OUTPUT_INSTANCE_INDEX".into());
420
if mesh_key.contains(MeshPipelineKey::DEPTH_PREPASS) {
421
shader_defs.push("DEPTH_PREPASS".into());
422
}
423
if mesh_key.contains(MeshPipelineKey::MAY_DISCARD) {
424
shader_defs.push("MAY_DISCARD".into());
425
}
426
let blend_key = mesh_key.intersection(MeshPipelineKey::BLEND_RESERVED_BITS);
427
if blend_key == MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA {
428
shader_defs.push("BLEND_PREMULTIPLIED_ALPHA".into());
429
}
430
if blend_key == MeshPipelineKey::BLEND_ALPHA {
431
shader_defs.push("BLEND_ALPHA".into());
432
}
433
if layout.0.contains(Mesh::ATTRIBUTE_POSITION) {
434
shader_defs.push("VERTEX_POSITIONS".into());
435
vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
436
}
437
// For directional light shadow map views, use unclipped depth via either the native GPU feature,
438
// or emulated by setting depth in the fragment shader for GPUs that don't support it natively.
439
let emulate_unclipped_depth = mesh_key.contains(MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO)
440
&& !self.depth_clip_control_supported;
441
if emulate_unclipped_depth {
442
shader_defs.push("UNCLIPPED_DEPTH_ORTHO_EMULATION".into());
443
// PERF: This line forces the "prepass fragment shader" to always run in
444
// common scenarios like "directional light calculation". Doing so resolves
445
// a pretty nasty depth clamping bug, but it also feels a bit excessive.
446
// We should try to find a way to resolve this without forcing the fragment
447
// shader to run.
448
// https://github.com/bevyengine/bevy/pull/8877
449
shader_defs.push("PREPASS_FRAGMENT".into());
450
}
451
let unclipped_depth = mesh_key.contains(MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO)
452
&& self.depth_clip_control_supported;
453
if layout.0.contains(Mesh::ATTRIBUTE_UV_0) {
454
shader_defs.push("VERTEX_UVS".into());
455
shader_defs.push("VERTEX_UVS_A".into());
456
vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(1));
457
}
458
if layout.0.contains(Mesh::ATTRIBUTE_UV_1) {
459
shader_defs.push("VERTEX_UVS".into());
460
shader_defs.push("VERTEX_UVS_B".into());
461
vertex_attributes.push(Mesh::ATTRIBUTE_UV_1.at_shader_location(2));
462
}
463
if mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS) {
464
shader_defs.push("NORMAL_PREPASS".into());
465
}
466
if mesh_key.intersects(MeshPipelineKey::NORMAL_PREPASS | MeshPipelineKey::DEFERRED_PREPASS)
467
{
468
shader_defs.push("NORMAL_PREPASS_OR_DEFERRED_PREPASS".into());
469
if layout.0.contains(Mesh::ATTRIBUTE_NORMAL) {
470
shader_defs.push("VERTEX_NORMALS".into());
471
vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(3));
472
} else if mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS) {
473
warn!(
474
"The default normal prepass expects the mesh to have vertex normal attributes."
475
);
476
}
477
if layout.0.contains(Mesh::ATTRIBUTE_TANGENT) {
478
shader_defs.push("VERTEX_TANGENTS".into());
479
vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(4));
480
}
481
}
482
if mesh_key
483
.intersects(MeshPipelineKey::MOTION_VECTOR_PREPASS | MeshPipelineKey::DEFERRED_PREPASS)
484
{
485
shader_defs.push("MOTION_VECTOR_PREPASS_OR_DEFERRED_PREPASS".into());
486
}
487
if mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) {
488
shader_defs.push("DEFERRED_PREPASS".into());
489
}
490
if mesh_key.contains(MeshPipelineKey::LIGHTMAPPED) {
491
shader_defs.push("LIGHTMAP".into());
492
}
493
if mesh_key.contains(MeshPipelineKey::LIGHTMAP_BICUBIC_SAMPLING) {
494
shader_defs.push("LIGHTMAP_BICUBIC_SAMPLING".into());
495
}
496
if layout.0.contains(Mesh::ATTRIBUTE_COLOR) {
497
shader_defs.push("VERTEX_COLORS".into());
498
vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(7));
499
}
500
if mesh_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
501
shader_defs.push("MOTION_VECTOR_PREPASS".into());
502
}
503
if mesh_key.contains(MeshPipelineKey::HAS_PREVIOUS_SKIN) {
504
shader_defs.push("HAS_PREVIOUS_SKIN".into());
505
}
506
if mesh_key.contains(MeshPipelineKey::HAS_PREVIOUS_MORPH) {
507
shader_defs.push("HAS_PREVIOUS_MORPH".into());
508
}
509
if self.binding_arrays_are_usable {
510
shader_defs.push("MULTIPLE_LIGHTMAPS_IN_ARRAY".into());
511
}
512
if mesh_key.contains(MeshPipelineKey::VISIBILITY_RANGE_DITHER) {
513
shader_defs.push("VISIBILITY_RANGE_DITHER".into());
514
}
515
if mesh_key.intersects(
516
MeshPipelineKey::NORMAL_PREPASS
517
| MeshPipelineKey::MOTION_VECTOR_PREPASS
518
| MeshPipelineKey::DEFERRED_PREPASS,
519
) {
520
shader_defs.push("PREPASS_FRAGMENT".into());
521
}
522
let bind_group = setup_morph_and_skinning_defs(
523
&self.mesh_layouts,
524
layout,
525
5,
526
&mesh_key,
527
&mut shader_defs,
528
&mut vertex_attributes,
529
self.skins_use_uniform_buffers,
530
);
531
bind_group_layouts.insert(2, bind_group);
532
let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?;
533
// Setup prepass fragment targets - normals in slot 0 (or None if not needed), motion vectors in slot 1
534
let mut targets = prepass_target_descriptors(
535
mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS),
536
mesh_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS),
537
mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS),
538
);
539
540
if targets.iter().all(Option::is_none) {
541
// if no targets are required then clear the list, so that no fragment shader is required
542
// (though one may still be used for discarding depth buffer writes)
543
targets.clear();
544
}
545
546
// The fragment shader is only used when the normal prepass or motion vectors prepass
547
// is enabled, the material uses alpha cutoff values and doesn't rely on the standard
548
// prepass shader, or we are emulating unclipped depth in the fragment shader.
549
let fragment_required = !targets.is_empty()
550
|| emulate_unclipped_depth
551
|| (mesh_key.contains(MeshPipelineKey::MAY_DISCARD)
552
&& material_properties
553
.get_shader(PrepassFragmentShader)
554
.is_some());
555
556
let fragment = fragment_required.then(|| {
557
// Use the fragment shader from the material
558
let frag_shader_handle = if mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) {
559
match material_properties.get_shader(DeferredFragmentShader) {
560
Some(frag_shader_handle) => frag_shader_handle,
561
None => self.default_prepass_shader.clone(),
562
}
563
} else {
564
match material_properties.get_shader(PrepassFragmentShader) {
565
Some(frag_shader_handle) => frag_shader_handle,
566
None => self.default_prepass_shader.clone(),
567
}
568
};
569
570
FragmentState {
571
shader: frag_shader_handle,
572
shader_defs: shader_defs.clone(),
573
targets,
574
..default()
575
}
576
});
577
578
// Use the vertex shader from the material if present
579
let vert_shader_handle = if mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) {
580
if let Some(handle) = material_properties.get_shader(DeferredVertexShader) {
581
handle
582
} else {
583
self.default_prepass_shader.clone()
584
}
585
} else if let Some(handle) = material_properties.get_shader(PrepassVertexShader) {
586
handle
587
} else {
588
self.default_prepass_shader.clone()
589
};
590
let descriptor = RenderPipelineDescriptor {
591
vertex: VertexState {
592
shader: vert_shader_handle,
593
shader_defs,
594
buffers: vec![vertex_buffer_layout],
595
..default()
596
},
597
fragment,
598
layout: bind_group_layouts,
599
primitive: PrimitiveState {
600
topology: mesh_key.primitive_topology(),
601
unclipped_depth,
602
..default()
603
},
604
depth_stencil: Some(DepthStencilState {
605
format: CORE_3D_DEPTH_FORMAT,
606
depth_write_enabled: true,
607
depth_compare: CompareFunction::GreaterEqual,
608
stencil: StencilState {
609
front: StencilFaceState::IGNORE,
610
back: StencilFaceState::IGNORE,
611
read_mask: 0,
612
write_mask: 0,
613
},
614
bias: DepthBiasState {
615
constant: 0,
616
slope_scale: 0.0,
617
clamp: 0.0,
618
},
619
}),
620
multisample: MultisampleState {
621
count: mesh_key.msaa_samples(),
622
mask: !0,
623
alpha_to_coverage_enabled: false,
624
},
625
label: Some("prepass_pipeline".into()),
626
..default()
627
};
628
Ok(descriptor)
629
}
630
}
631
632
// Extract the render phases for the prepass
633
pub fn extract_camera_previous_view_data(
634
mut commands: Commands,
635
cameras_3d: Extract<Query<(RenderEntity, &Camera, Option<&PreviousViewData>), With<Camera3d>>>,
636
) {
637
for (entity, camera, maybe_previous_view_data) in cameras_3d.iter() {
638
let mut entity = commands
639
.get_entity(entity)
640
.expect("Camera entity wasn't synced.");
641
if camera.is_active {
642
if let Some(previous_view_data) = maybe_previous_view_data {
643
entity.insert(previous_view_data.clone());
644
}
645
} else {
646
entity.remove::<PreviousViewData>();
647
}
648
}
649
}
650
651
pub fn prepare_previous_view_uniforms(
652
mut commands: Commands,
653
render_device: Res<RenderDevice>,
654
render_queue: Res<RenderQueue>,
655
mut previous_view_uniforms: ResMut<PreviousViewUniforms>,
656
views: Query<
657
(Entity, &ExtractedView, Option<&PreviousViewData>),
658
Or<(With<Camera3d>, With<ShadowView>)>,
659
>,
660
) {
661
let views_iter = views.iter();
662
let view_count = views_iter.len();
663
let Some(mut writer) =
664
previous_view_uniforms
665
.uniforms
666
.get_writer(view_count, &render_device, &render_queue)
667
else {
668
return;
669
};
670
671
for (entity, camera, maybe_previous_view_uniforms) in views_iter {
672
let prev_view_data = match maybe_previous_view_uniforms {
673
Some(previous_view) => previous_view.clone(),
674
None => {
675
let world_from_view = camera.world_from_view.affine();
676
let view_from_world = Mat4::from(world_from_view.inverse());
677
let view_from_clip = camera.clip_from_view.inverse();
678
679
PreviousViewData {
680
view_from_world,
681
clip_from_world: camera.clip_from_view * view_from_world,
682
clip_from_view: camera.clip_from_view,
683
world_from_clip: Mat4::from(world_from_view) * view_from_clip,
684
view_from_clip,
685
}
686
}
687
};
688
689
commands.entity(entity).insert(PreviousViewUniformOffset {
690
offset: writer.write(&prev_view_data),
691
});
692
}
693
}
694
695
#[derive(Resource)]
696
pub struct PrepassViewBindGroup {
697
pub motion_vectors: Option<BindGroup>,
698
pub no_motion_vectors: Option<BindGroup>,
699
pub empty_bind_group: BindGroup,
700
}
701
702
pub fn init_prepass_view_bind_group(
703
mut commands: Commands,
704
render_device: Res<RenderDevice>,
705
pipeline: Res<PrepassPipeline>,
706
) {
707
let empty_bind_group = render_device.create_bind_group(
708
"prepass_view_empty_bind_group",
709
&pipeline.empty_layout,
710
&[],
711
);
712
commands.insert_resource(PrepassViewBindGroup {
713
motion_vectors: None,
714
no_motion_vectors: None,
715
empty_bind_group,
716
});
717
}
718
719
pub fn prepare_prepass_view_bind_group(
720
render_device: Res<RenderDevice>,
721
prepass_pipeline: Res<PrepassPipeline>,
722
view_uniforms: Res<ViewUniforms>,
723
globals_buffer: Res<GlobalsBuffer>,
724
previous_view_uniforms: Res<PreviousViewUniforms>,
725
visibility_ranges: Res<RenderVisibilityRanges>,
726
mut prepass_view_bind_group: ResMut<PrepassViewBindGroup>,
727
) {
728
if let (Some(view_binding), Some(globals_binding), Some(visibility_ranges_buffer)) = (
729
view_uniforms.uniforms.binding(),
730
globals_buffer.buffer.binding(),
731
visibility_ranges.buffer().buffer(),
732
) {
733
prepass_view_bind_group.no_motion_vectors = Some(render_device.create_bind_group(
734
"prepass_view_no_motion_vectors_bind_group",
735
&prepass_pipeline.view_layout_no_motion_vectors,
736
&BindGroupEntries::with_indices((
737
(0, view_binding.clone()),
738
(1, globals_binding.clone()),
739
(14, visibility_ranges_buffer.as_entire_binding()),
740
)),
741
));
742
743
if let Some(previous_view_uniforms_binding) = previous_view_uniforms.uniforms.binding() {
744
prepass_view_bind_group.motion_vectors = Some(render_device.create_bind_group(
745
"prepass_view_motion_vectors_bind_group",
746
&prepass_pipeline.view_layout_motion_vectors,
747
&BindGroupEntries::with_indices((
748
(0, view_binding),
749
(1, globals_binding),
750
(2, previous_view_uniforms_binding),
751
(14, visibility_ranges_buffer.as_entire_binding()),
752
)),
753
));
754
}
755
}
756
}
757
758
/// Stores the [`SpecializedPrepassMaterialViewPipelineCache`] for each view.
759
#[derive(Resource, Deref, DerefMut, Default)]
760
pub struct SpecializedPrepassMaterialPipelineCache {
761
// view_entity -> view pipeline cache
762
#[deref]
763
map: HashMap<RetainedViewEntity, SpecializedPrepassMaterialViewPipelineCache>,
764
}
765
766
/// Stores the cached render pipeline ID for each entity in a single view, as
767
/// well as the last time it was changed.
768
#[derive(Deref, DerefMut, Default)]
769
pub struct SpecializedPrepassMaterialViewPipelineCache {
770
// material entity -> (tick, pipeline_id)
771
#[deref]
772
map: MainEntityHashMap<(Tick, CachedRenderPipelineId)>,
773
}
774
775
#[derive(Resource, Deref, DerefMut, Default, Clone)]
776
pub struct ViewKeyPrepassCache(HashMap<RetainedViewEntity, MeshPipelineKey>);
777
778
#[derive(Resource, Deref, DerefMut, Default, Clone)]
779
pub struct ViewPrepassSpecializationTicks(HashMap<RetainedViewEntity, Tick>);
780
781
pub fn check_prepass_views_need_specialization(
782
mut view_key_cache: ResMut<ViewKeyPrepassCache>,
783
mut view_specialization_ticks: ResMut<ViewPrepassSpecializationTicks>,
784
mut views: Query<(
785
&ExtractedView,
786
&Msaa,
787
Option<&DepthPrepass>,
788
Option<&NormalPrepass>,
789
Option<&MotionVectorPrepass>,
790
)>,
791
ticks: SystemChangeTick,
792
) {
793
for (view, msaa, depth_prepass, normal_prepass, motion_vector_prepass) in views.iter_mut() {
794
let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples());
795
if depth_prepass.is_some() {
796
view_key |= MeshPipelineKey::DEPTH_PREPASS;
797
}
798
if normal_prepass.is_some() {
799
view_key |= MeshPipelineKey::NORMAL_PREPASS;
800
}
801
if motion_vector_prepass.is_some() {
802
view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
803
}
804
805
if let Some(current_key) = view_key_cache.get_mut(&view.retained_view_entity) {
806
if *current_key != view_key {
807
view_key_cache.insert(view.retained_view_entity, view_key);
808
view_specialization_ticks.insert(view.retained_view_entity, ticks.this_run());
809
}
810
} else {
811
view_key_cache.insert(view.retained_view_entity, view_key);
812
view_specialization_ticks.insert(view.retained_view_entity, ticks.this_run());
813
}
814
}
815
}
816
817
pub fn specialize_prepass_material_meshes(
818
render_meshes: Res<RenderAssets<RenderMesh>>,
819
render_materials: Res<ErasedRenderAssets<PreparedMaterial>>,
820
render_mesh_instances: Res<RenderMeshInstances>,
821
render_material_instances: Res<RenderMaterialInstances>,
822
render_lightmaps: Res<RenderLightmaps>,
823
render_visibility_ranges: Res<RenderVisibilityRanges>,
824
view_key_cache: Res<ViewKeyPrepassCache>,
825
views: Query<(
826
&ExtractedView,
827
&RenderVisibleEntities,
828
&Msaa,
829
Option<&MotionVectorPrepass>,
830
Option<&DeferredPrepass>,
831
)>,
832
(
833
opaque_prepass_render_phases,
834
alpha_mask_prepass_render_phases,
835
opaque_deferred_render_phases,
836
alpha_mask_deferred_render_phases,
837
): (
838
Res<ViewBinnedRenderPhases<Opaque3dPrepass>>,
839
Res<ViewBinnedRenderPhases<AlphaMask3dPrepass>>,
840
Res<ViewBinnedRenderPhases<Opaque3dDeferred>>,
841
Res<ViewBinnedRenderPhases<AlphaMask3dDeferred>>,
842
),
843
(
844
mut specialized_material_pipeline_cache,
845
ticks,
846
prepass_pipeline,
847
mut pipelines,
848
pipeline_cache,
849
view_specialization_ticks,
850
entity_specialization_ticks,
851
): (
852
ResMut<SpecializedPrepassMaterialPipelineCache>,
853
SystemChangeTick,
854
Res<PrepassPipeline>,
855
ResMut<SpecializedMeshPipelines<PrepassPipelineSpecializer>>,
856
Res<PipelineCache>,
857
Res<ViewPrepassSpecializationTicks>,
858
Res<EntitySpecializationTicks>,
859
),
860
) {
861
for (extracted_view, visible_entities, msaa, motion_vector_prepass, deferred_prepass) in &views
862
{
863
if !opaque_deferred_render_phases.contains_key(&extracted_view.retained_view_entity)
864
&& !alpha_mask_deferred_render_phases.contains_key(&extracted_view.retained_view_entity)
865
&& !opaque_prepass_render_phases.contains_key(&extracted_view.retained_view_entity)
866
&& !alpha_mask_prepass_render_phases.contains_key(&extracted_view.retained_view_entity)
867
{
868
continue;
869
}
870
871
let Some(view_key) = view_key_cache.get(&extracted_view.retained_view_entity) else {
872
continue;
873
};
874
875
let view_tick = view_specialization_ticks
876
.get(&extracted_view.retained_view_entity)
877
.unwrap();
878
let view_specialized_material_pipeline_cache = specialized_material_pipeline_cache
879
.entry(extracted_view.retained_view_entity)
880
.or_default();
881
882
for (_, visible_entity) in visible_entities.iter::<Mesh3d>() {
883
let Some(material_instance) = render_material_instances.instances.get(visible_entity)
884
else {
885
continue;
886
};
887
let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(*visible_entity)
888
else {
889
continue;
890
};
891
let entity_tick = entity_specialization_ticks.get(visible_entity).unwrap();
892
let last_specialized_tick = view_specialized_material_pipeline_cache
893
.get(visible_entity)
894
.map(|(tick, _)| *tick);
895
let needs_specialization = last_specialized_tick.is_none_or(|tick| {
896
view_tick.is_newer_than(tick, ticks.this_run())
897
|| entity_tick.is_newer_than(tick, ticks.this_run())
898
});
899
if !needs_specialization {
900
continue;
901
}
902
let Some(material) = render_materials.get(material_instance.asset_id) else {
903
continue;
904
};
905
if !material.properties.prepass_enabled && !material.properties.shadows_enabled {
906
// If the material was previously specialized for prepass, remove it
907
view_specialized_material_pipeline_cache.remove(visible_entity);
908
continue;
909
}
910
let Some(mesh) = render_meshes.get(mesh_instance.mesh_asset_id) else {
911
continue;
912
};
913
914
let mut mesh_key = *view_key | MeshPipelineKey::from_bits_retain(mesh.key_bits.bits());
915
916
let alpha_mode = material.properties.alpha_mode;
917
match alpha_mode {
918
AlphaMode::Opaque | AlphaMode::AlphaToCoverage | AlphaMode::Mask(_) => {
919
mesh_key |= alpha_mode_pipeline_key(alpha_mode, msaa);
920
}
921
AlphaMode::Blend
922
| AlphaMode::Premultiplied
923
| AlphaMode::Add
924
| AlphaMode::Multiply => {
925
// In case this material was previously in a valid alpha_mode, remove it to
926
// stop the queue system from assuming its retained cache to be valid.
927
view_specialized_material_pipeline_cache.remove(visible_entity);
928
continue;
929
}
930
}
931
932
if material.properties.reads_view_transmission_texture {
933
// No-op: Materials reading from `ViewTransmissionTexture` are not rendered in the `Opaque3d`
934
// phase, and are therefore also excluded from the prepass much like alpha-blended materials.
935
view_specialized_material_pipeline_cache.remove(visible_entity);
936
continue;
937
}
938
939
let forward = match material.properties.render_method {
940
OpaqueRendererMethod::Forward => true,
941
OpaqueRendererMethod::Deferred => false,
942
OpaqueRendererMethod::Auto => unreachable!(),
943
};
944
945
let deferred = deferred_prepass.is_some() && !forward;
946
947
if deferred {
948
mesh_key |= MeshPipelineKey::DEFERRED_PREPASS;
949
}
950
951
if let Some(lightmap) = render_lightmaps.render_lightmaps.get(visible_entity) {
952
// Even though we don't use the lightmap in the forward prepass, the
953
// `SetMeshBindGroup` render command will bind the data for it. So
954
// we need to include the appropriate flag in the mesh pipeline key
955
// to ensure that the necessary bind group layout entries are
956
// present.
957
mesh_key |= MeshPipelineKey::LIGHTMAPPED;
958
959
if lightmap.bicubic_sampling && deferred {
960
mesh_key |= MeshPipelineKey::LIGHTMAP_BICUBIC_SAMPLING;
961
}
962
}
963
964
if render_visibility_ranges.entity_has_crossfading_visibility_ranges(*visible_entity) {
965
mesh_key |= MeshPipelineKey::VISIBILITY_RANGE_DITHER;
966
}
967
968
// If the previous frame has skins or morph targets, note that.
969
if motion_vector_prepass.is_some() {
970
if mesh_instance
971
.flags
972
.contains(RenderMeshInstanceFlags::HAS_PREVIOUS_SKIN)
973
{
974
mesh_key |= MeshPipelineKey::HAS_PREVIOUS_SKIN;
975
}
976
if mesh_instance
977
.flags
978
.contains(RenderMeshInstanceFlags::HAS_PREVIOUS_MORPH)
979
{
980
mesh_key |= MeshPipelineKey::HAS_PREVIOUS_MORPH;
981
}
982
}
983
984
let erased_key = ErasedMaterialPipelineKey {
985
mesh_key,
986
material_key: material.properties.material_key.clone(),
987
type_id: material_instance.asset_id.type_id(),
988
};
989
let prepass_pipeline_specializer = PrepassPipelineSpecializer {
990
pipeline: prepass_pipeline.clone(),
991
properties: material.properties.clone(),
992
};
993
let pipeline_id = pipelines.specialize(
994
&pipeline_cache,
995
&prepass_pipeline_specializer,
996
erased_key,
997
&mesh.layout,
998
);
999
let pipeline_id = match pipeline_id {
1000
Ok(id) => id,
1001
Err(err) => {
1002
error!("{}", err);
1003
continue;
1004
}
1005
};
1006
1007
view_specialized_material_pipeline_cache
1008
.insert(*visible_entity, (ticks.this_run(), pipeline_id));
1009
}
1010
}
1011
}
1012
1013
pub fn queue_prepass_material_meshes(
1014
render_mesh_instances: Res<RenderMeshInstances>,
1015
render_materials: Res<ErasedRenderAssets<PreparedMaterial>>,
1016
render_material_instances: Res<RenderMaterialInstances>,
1017
mesh_allocator: Res<MeshAllocator>,
1018
gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
1019
mut opaque_prepass_render_phases: ResMut<ViewBinnedRenderPhases<Opaque3dPrepass>>,
1020
mut alpha_mask_prepass_render_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3dPrepass>>,
1021
mut opaque_deferred_render_phases: ResMut<ViewBinnedRenderPhases<Opaque3dDeferred>>,
1022
mut alpha_mask_deferred_render_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3dDeferred>>,
1023
views: Query<(&ExtractedView, &RenderVisibleEntities)>,
1024
specialized_material_pipeline_cache: Res<SpecializedPrepassMaterialPipelineCache>,
1025
) {
1026
for (extracted_view, visible_entities) in &views {
1027
let (
1028
mut opaque_phase,
1029
mut alpha_mask_phase,
1030
mut opaque_deferred_phase,
1031
mut alpha_mask_deferred_phase,
1032
) = (
1033
opaque_prepass_render_phases.get_mut(&extracted_view.retained_view_entity),
1034
alpha_mask_prepass_render_phases.get_mut(&extracted_view.retained_view_entity),
1035
opaque_deferred_render_phases.get_mut(&extracted_view.retained_view_entity),
1036
alpha_mask_deferred_render_phases.get_mut(&extracted_view.retained_view_entity),
1037
);
1038
1039
let Some(view_specialized_material_pipeline_cache) =
1040
specialized_material_pipeline_cache.get(&extracted_view.retained_view_entity)
1041
else {
1042
continue;
1043
};
1044
1045
// Skip if there's no place to put the mesh.
1046
if opaque_phase.is_none()
1047
&& alpha_mask_phase.is_none()
1048
&& opaque_deferred_phase.is_none()
1049
&& alpha_mask_deferred_phase.is_none()
1050
{
1051
continue;
1052
}
1053
1054
for (render_entity, visible_entity) in visible_entities.iter::<Mesh3d>() {
1055
let Some((current_change_tick, pipeline_id)) =
1056
view_specialized_material_pipeline_cache.get(visible_entity)
1057
else {
1058
continue;
1059
};
1060
1061
// Skip the entity if it's cached in a bin and up to date.
1062
if opaque_phase.as_mut().is_some_and(|phase| {
1063
phase.validate_cached_entity(*visible_entity, *current_change_tick)
1064
}) || alpha_mask_phase.as_mut().is_some_and(|phase| {
1065
phase.validate_cached_entity(*visible_entity, *current_change_tick)
1066
}) || opaque_deferred_phase.as_mut().is_some_and(|phase| {
1067
phase.validate_cached_entity(*visible_entity, *current_change_tick)
1068
}) || alpha_mask_deferred_phase.as_mut().is_some_and(|phase| {
1069
phase.validate_cached_entity(*visible_entity, *current_change_tick)
1070
}) {
1071
continue;
1072
}
1073
1074
let Some(material_instance) = render_material_instances.instances.get(visible_entity)
1075
else {
1076
continue;
1077
};
1078
let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(*visible_entity)
1079
else {
1080
continue;
1081
};
1082
let Some(material) = render_materials.get(material_instance.asset_id) else {
1083
continue;
1084
};
1085
let (vertex_slab, index_slab) = mesh_allocator.mesh_slabs(&mesh_instance.mesh_asset_id);
1086
1087
let deferred = match material.properties.render_method {
1088
OpaqueRendererMethod::Forward => false,
1089
OpaqueRendererMethod::Deferred => true,
1090
OpaqueRendererMethod::Auto => unreachable!(),
1091
};
1092
1093
match material.properties.render_phase_type {
1094
RenderPhaseType::Opaque => {
1095
if deferred {
1096
opaque_deferred_phase.as_mut().unwrap().add(
1097
OpaqueNoLightmap3dBatchSetKey {
1098
draw_function: material
1099
.properties
1100
.get_draw_function(DeferredDrawFunction)
1101
.unwrap(),
1102
pipeline: *pipeline_id,
1103
material_bind_group_index: Some(material.binding.group.0),
1104
vertex_slab: vertex_slab.unwrap_or_default(),
1105
index_slab,
1106
},
1107
OpaqueNoLightmap3dBinKey {
1108
asset_id: mesh_instance.mesh_asset_id.into(),
1109
},
1110
(*render_entity, *visible_entity),
1111
mesh_instance.current_uniform_index,
1112
BinnedRenderPhaseType::mesh(
1113
mesh_instance.should_batch(),
1114
&gpu_preprocessing_support,
1115
),
1116
*current_change_tick,
1117
);
1118
} else if let Some(opaque_phase) = opaque_phase.as_mut() {
1119
let (vertex_slab, index_slab) =
1120
mesh_allocator.mesh_slabs(&mesh_instance.mesh_asset_id);
1121
opaque_phase.add(
1122
OpaqueNoLightmap3dBatchSetKey {
1123
draw_function: material
1124
.properties
1125
.get_draw_function(PrepassDrawFunction)
1126
.unwrap(),
1127
pipeline: *pipeline_id,
1128
material_bind_group_index: Some(material.binding.group.0),
1129
vertex_slab: vertex_slab.unwrap_or_default(),
1130
index_slab,
1131
},
1132
OpaqueNoLightmap3dBinKey {
1133
asset_id: mesh_instance.mesh_asset_id.into(),
1134
},
1135
(*render_entity, *visible_entity),
1136
mesh_instance.current_uniform_index,
1137
BinnedRenderPhaseType::mesh(
1138
mesh_instance.should_batch(),
1139
&gpu_preprocessing_support,
1140
),
1141
*current_change_tick,
1142
);
1143
}
1144
}
1145
RenderPhaseType::AlphaMask => {
1146
if deferred {
1147
let (vertex_slab, index_slab) =
1148
mesh_allocator.mesh_slabs(&mesh_instance.mesh_asset_id);
1149
let batch_set_key = OpaqueNoLightmap3dBatchSetKey {
1150
draw_function: material
1151
.properties
1152
.get_draw_function(DeferredDrawFunction)
1153
.unwrap(),
1154
pipeline: *pipeline_id,
1155
material_bind_group_index: Some(material.binding.group.0),
1156
vertex_slab: vertex_slab.unwrap_or_default(),
1157
index_slab,
1158
};
1159
let bin_key = OpaqueNoLightmap3dBinKey {
1160
asset_id: mesh_instance.mesh_asset_id.into(),
1161
};
1162
alpha_mask_deferred_phase.as_mut().unwrap().add(
1163
batch_set_key,
1164
bin_key,
1165
(*render_entity, *visible_entity),
1166
mesh_instance.current_uniform_index,
1167
BinnedRenderPhaseType::mesh(
1168
mesh_instance.should_batch(),
1169
&gpu_preprocessing_support,
1170
),
1171
*current_change_tick,
1172
);
1173
} else if let Some(alpha_mask_phase) = alpha_mask_phase.as_mut() {
1174
let (vertex_slab, index_slab) =
1175
mesh_allocator.mesh_slabs(&mesh_instance.mesh_asset_id);
1176
let batch_set_key = OpaqueNoLightmap3dBatchSetKey {
1177
draw_function: material
1178
.properties
1179
.get_draw_function(PrepassDrawFunction)
1180
.unwrap(),
1181
pipeline: *pipeline_id,
1182
material_bind_group_index: Some(material.binding.group.0),
1183
vertex_slab: vertex_slab.unwrap_or_default(),
1184
index_slab,
1185
};
1186
let bin_key = OpaqueNoLightmap3dBinKey {
1187
asset_id: mesh_instance.mesh_asset_id.into(),
1188
};
1189
alpha_mask_phase.add(
1190
batch_set_key,
1191
bin_key,
1192
(*render_entity, *visible_entity),
1193
mesh_instance.current_uniform_index,
1194
BinnedRenderPhaseType::mesh(
1195
mesh_instance.should_batch(),
1196
&gpu_preprocessing_support,
1197
),
1198
*current_change_tick,
1199
);
1200
}
1201
}
1202
_ => {}
1203
}
1204
}
1205
}
1206
}
1207
1208
pub struct SetPrepassViewBindGroup<const I: usize>;
1209
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetPrepassViewBindGroup<I> {
1210
type Param = SRes<PrepassViewBindGroup>;
1211
type ViewQuery = (
1212
Read<ViewUniformOffset>,
1213
Has<MotionVectorPrepass>,
1214
Option<Read<PreviousViewUniformOffset>>,
1215
);
1216
type ItemQuery = ();
1217
1218
#[inline]
1219
fn render<'w>(
1220
_item: &P,
1221
(view_uniform_offset, has_motion_vector_prepass, previous_view_uniform_offset): (
1222
&'_ ViewUniformOffset,
1223
bool,
1224
Option<&'_ PreviousViewUniformOffset>,
1225
),
1226
_entity: Option<()>,
1227
prepass_view_bind_group: SystemParamItem<'w, '_, Self::Param>,
1228
pass: &mut TrackedRenderPass<'w>,
1229
) -> RenderCommandResult {
1230
let prepass_view_bind_group = prepass_view_bind_group.into_inner();
1231
1232
match previous_view_uniform_offset {
1233
Some(previous_view_uniform_offset) if has_motion_vector_prepass => {
1234
pass.set_bind_group(
1235
I,
1236
prepass_view_bind_group.motion_vectors.as_ref().unwrap(),
1237
&[
1238
view_uniform_offset.offset,
1239
previous_view_uniform_offset.offset,
1240
],
1241
);
1242
}
1243
_ => {
1244
pass.set_bind_group(
1245
I,
1246
prepass_view_bind_group.no_motion_vectors.as_ref().unwrap(),
1247
&[view_uniform_offset.offset],
1248
);
1249
}
1250
}
1251
RenderCommandResult::Success
1252
}
1253
}
1254
1255
pub struct SetPrepassViewEmptyBindGroup<const I: usize>;
1256
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetPrepassViewEmptyBindGroup<I> {
1257
type Param = SRes<PrepassViewBindGroup>;
1258
type ViewQuery = ();
1259
type ItemQuery = ();
1260
1261
#[inline]
1262
fn render<'w>(
1263
_item: &P,
1264
_view: (),
1265
_entity: Option<()>,
1266
prepass_view_bind_group: SystemParamItem<'w, '_, Self::Param>,
1267
pass: &mut TrackedRenderPass<'w>,
1268
) -> RenderCommandResult {
1269
let prepass_view_bind_group = prepass_view_bind_group.into_inner();
1270
pass.set_bind_group(I, &prepass_view_bind_group.empty_bind_group, &[]);
1271
RenderCommandResult::Success
1272
}
1273
}
1274
1275
pub type DrawPrepass = (
1276
SetItemPipeline,
1277
SetPrepassViewBindGroup<0>,
1278
SetPrepassViewEmptyBindGroup<1>,
1279
SetMeshBindGroup<2>,
1280
SetMaterialBindGroup<3>,
1281
DrawMesh,
1282
);
1283
1284