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