1mod prepass_bindings;
2
3use 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, MaterialPipeline,
8 MaterialPropertiesExt, MeshLayouts, MeshPipeline, MeshPipelineKey, PreparedMaterial,
9 PrepassAlphaMaskDrawFunction, PrepassFragmentShader, PrepassOpaqueDepthOnlyDrawFunction,
10 PrepassOpaqueDrawFunction, PrepassVertexShader, RenderLightmaps, RenderMaterialInstances,
11 RenderMeshInstanceFlags, RenderMeshInstances, SetMaterialBindGroup, SetMeshBindGroup,
12 ShadowView,
13};
14use bevy_app::{App, Plugin, PreUpdate};
15use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer, Handle};
16use bevy_camera::{Camera, Camera3d, MainPassResolutionOverride};
17use bevy_core_pipeline::{core_3d::CORE_3D_DEPTH_FORMAT, deferred::*, prepass::*};
18use bevy_ecs::{
19 prelude::*,
20 system::{
21 lifetimeless::{Read, SRes},
22 SystemParam, SystemParamItem, SystemState,
23 },
24};
25use bevy_material::{
26 key::{ErasedMaterialPipelineKey, ErasedMeshPipelineKey},
27 AlphaMode, MaterialProperties, OpaqueRendererMethod, RenderPhaseType,
28};
29use bevy_math::{Affine3A, Mat4, Vec2, Vec4};
30use bevy_mesh::{Mesh, Mesh3d, MeshAttributeCompressionFlags, MeshVertexBufferLayoutRef};
31use bevy_render::{
32 batching::gpu_preprocessing::GpuPreprocessingSupport,
33 camera::{DirtySpecializations, PendingQueues, TemporalJitter},
34 globals::{GlobalsBuffer, GlobalsUniform},
35 mesh::{allocator::MeshAllocator, RenderMesh},
36 render_asset::{prepare_assets, RenderAssets},
37 render_phase::*,
38 render_resource::{binding_types::uniform_buffer, *},
39 renderer::{RenderAdapter, RenderDevice, RenderQueue},
40 sync_world::{MainEntityHashSet, RenderEntity},
41 view::{
42 ExtractedView, Msaa, RenderVisibilityRanges, RenderVisibleEntities, RetainedViewEntity,
43 ViewUniform, ViewUniformOffset, ViewUniforms, VISIBILITY_RANGES_STORAGE_BUFFER_COUNT,
44 },
45 Extract, ExtractSchedule, GpuResourceAppExt, Render, RenderApp, RenderDebugFlags,
46 RenderStartup, RenderSystems,
47};
48use bevy_shader::{load_shader_library, Shader, ShaderDefVal};
49use bevy_transform::prelude::GlobalTransform;
50use core::any::TypeId;
51pub use prepass_bindings::*;
52use tracing::{error, warn};
53
54#[cfg(feature = "meshlet")]
55use crate::meshlet::{
56 prepare_material_meshlet_meshes_prepass, queue_material_meshlet_meshes, InstanceManager,
57 MeshletMesh3d,
58};
59
60use alloc::sync::Arc;
61use bevy_derive::{Deref, DerefMut};
62use bevy_ecs::{change_detection::Tick, system::SystemChangeTick};
63use bevy_platform::collections::{HashMap, HashSet};
64use bevy_platform::hash::FixedHasher;
65use bevy_render::{
66 erased_render_asset::ErasedRenderAssets,
67 sync_world::{MainEntity, MainEntityHashMap},
68 RenderSystems::{PrepareAssets, PrepareResources},
69};
70use bevy_utils::default;
71
72pub struct PrepassPipelinePlugin;
76
77impl Plugin for PrepassPipelinePlugin {
78 fn build(&self, app: &mut App) {
79 embedded_asset!(app, "prepass.wesl");
80
81 load_shader_library!(app, "bindings.wesl");
82 load_shader_library!(app, "utils.wesl");
83 load_shader_library!(app, "io.wesl");
84
85 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
86 return;
87 };
88
89 render_app
90 .add_systems(
91 RenderStartup,
92 (
93 init_prepass_pipeline.after(init_material_pipeline),
94 init_prepass_view_bind_group,
95 )
96 .chain(),
97 )
98 .add_systems(
99 Render,
100 prepare_prepass_view_bind_group.in_set(RenderSystems::PrepareBindGroups),
101 )
102 .init_gpu_resource::<SpecializedMeshPipelines<PrepassPipelineSpecializer>>();
103 }
104}
105
106pub struct PrepassPlugin {
110 pub debug_flags: RenderDebugFlags,
112}
113
114impl PrepassPlugin {
115 pub fn new(debug_flags: RenderDebugFlags) -> Self {
117 PrepassPlugin { debug_flags }
118 }
119}
120
121impl Plugin for PrepassPlugin {
122 fn build(&self, app: &mut App) {
123 let no_prepass_plugin_loaded = app
124 .world()
125 .get_resource::<AnyPrepassPluginLoaded>()
126 .is_none();
127
128 if no_prepass_plugin_loaded {
129 app.insert_resource(AnyPrepassPluginLoaded)
130 .add_systems(
133 PreUpdate,
134 (
135 update_mesh_previous_global_transforms,
136 update_previous_view_data,
137 ),
138 )
139 .add_plugins((
140 BinnedRenderPhasePlugin::<Opaque3dPrepass, MeshPipeline>::new(self.debug_flags),
141 BinnedRenderPhasePlugin::<AlphaMask3dPrepass, MeshPipeline>::new(
142 self.debug_flags,
143 ),
144 ));
145 }
146
147 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
148 return;
149 };
150
151 if no_prepass_plugin_loaded {
152 render_app
153 .add_systems(ExtractSchedule, extract_camera_previous_view_data)
154 .add_systems(
155 Render,
156 prepare_previous_view_uniforms.in_set(PrepareResources),
157 );
158 }
159
160 render_app
161 .init_gpu_resource::<ViewKeyPrepassCache>()
162 .init_gpu_resource::<SpecializedPrepassMaterialPipelineCache>()
163 .init_gpu_resource::<PendingPrepassMeshMaterialQueues>()
164 .add_render_command::<Opaque3dPrepass, DrawPrepass>()
165 .add_render_command::<Opaque3dPrepass, DrawDepthOnlyPrepass>()
166 .add_render_command::<AlphaMask3dPrepass, DrawPrepass>()
167 .add_render_command::<Opaque3dDeferred, DrawPrepass>()
168 .add_render_command::<AlphaMask3dDeferred, DrawPrepass>()
169 .add_systems(
170 Render,
171 (
172 check_prepass_views_need_specialization.in_set(PrepareAssets),
173 specialize_prepass_material_meshes
174 .in_set(RenderSystems::PrepareMeshes)
175 .after(prepare_assets::<RenderMesh>)
176 .after(collect_meshes_for_gpu_building)
177 .after(set_mesh_motion_vector_flags),
178 queue_prepass_material_meshes.in_set(RenderSystems::QueueMeshes),
179 ),
180 );
181
182 #[cfg(feature = "meshlet")]
183 render_app.add_systems(
184 Render,
185 prepare_material_meshlet_meshes_prepass
186 .in_set(RenderSystems::QueueMeshes)
187 .before(queue_material_meshlet_meshes)
188 .run_if(resource_exists::<InstanceManager>),
189 );
190 }
191}
192
193#[derive(Resource)]
194struct AnyPrepassPluginLoaded;
195
196pub fn update_previous_view_data(
197 mut commands: Commands,
198 query: Query<(Entity, &Camera, &GlobalTransform), Or<(With<Camera3d>, With<ShadowView>)>>,
199) {
200 for (entity, camera, camera_transform) in &query {
201 let world_from_view = camera_transform.affine();
202 let view_from_world = Mat4::from(world_from_view.inverse());
203 let view_from_clip = camera.clip_from_view().inverse();
204
205 commands.entity(entity).try_insert(PreviousViewData {
209 view_from_world,
210 clip_from_world: camera.clip_from_view() * view_from_world,
211 unjittered_clip_from_world: camera.clip_from_view() * view_from_world,
212 clip_from_view: camera.clip_from_view(),
213 world_from_clip: Mat4::from(world_from_view) * view_from_clip,
214 view_from_clip,
215 });
216 }
217}
218
219#[derive(Component, PartialEq, Clone, Default)]
220pub struct PreviousGlobalTransform(pub Affine3A);
221
222#[cfg(not(feature = "meshlet"))]
223type PreviousMeshFilter = With<Mesh3d>;
224#[cfg(feature = "meshlet")]
225type PreviousMeshFilter = Or<(With<Mesh3d>, With<MeshletMesh3d>)>;
226
227pub fn update_mesh_previous_global_transforms(
228 mut commands: Commands,
229 views: Query<&Camera, Or<(With<Camera3d>, With<ShadowView>)>>,
230 new_meshes: Query<
231 (Entity, &GlobalTransform),
232 (PreviousMeshFilter, Without<PreviousGlobalTransform>),
233 >,
234 mut meshes: Query<(Ref<GlobalTransform>, &mut PreviousGlobalTransform), PreviousMeshFilter>,
235) {
236 if !views.iter().any(|camera| camera.is_active) {
237 return;
238 }
239
240 for (entity, transform) in &new_meshes {
241 let new_previous_transform = PreviousGlobalTransform(transform.affine());
242 commands.entity(entity).try_insert(new_previous_transform);
243 }
244 meshes.par_iter_mut().for_each(|(transform, mut previous)| {
245 if transform.is_changed_after(previous.last_changed()) {
246 *previous = PreviousGlobalTransform(transform.affine());
247 }
248 });
249}
250
251#[derive(Resource, Clone)]
252pub struct PrepassPipeline {
253 pub view_layout_motion_vectors: BindGroupLayoutDescriptor,
254 pub view_layout_no_motion_vectors: BindGroupLayoutDescriptor,
255 pub mesh_layouts: MeshLayouts,
256 pub empty_layout: BindGroupLayoutDescriptor,
257 pub default_prepass_shader: Handle<Shader>,
258
259 pub skins_use_uniform_buffers: bool,
262
263 pub metadata_use_uniform_buffers: bool,
266
267 pub depth_clip_control_supported: bool,
268
269 pub binding_arrays_are_usable: bool,
272 pub material_pipeline: MaterialPipeline,
273}
274
275pub fn init_prepass_pipeline(
276 mut commands: Commands,
277 render_device: Res<RenderDevice>,
278 render_adapter: Res<RenderAdapter>,
279 mesh_pipeline: Res<MeshPipeline>,
280 material_pipeline: Res<MaterialPipeline>,
281 asset_server: Res<AssetServer>,
282) {
283 let visibility_ranges_buffer_binding_type =
284 render_device.get_supported_read_only_binding_type(VISIBILITY_RANGES_STORAGE_BUFFER_COUNT);
285
286 let view_layout_motion_vectors = BindGroupLayoutDescriptor::new(
287 "prepass_view_layout_motion_vectors",
288 &BindGroupLayoutEntries::with_indices(
289 ShaderStages::VERTEX_FRAGMENT,
290 (
291 (0, uniform_buffer::<ViewUniform>(true)),
293 (1, uniform_buffer::<GlobalsUniform>(false)),
295 (2, uniform_buffer::<PreviousViewData>(true)),
297 (
299 14,
300 buffer_layout(
301 visibility_ranges_buffer_binding_type,
302 false,
303 Some(Vec4::min_size()),
304 )
305 .visibility(ShaderStages::VERTEX),
306 ),
307 ),
308 ),
309 );
310
311 let view_layout_no_motion_vectors = BindGroupLayoutDescriptor::new(
312 "prepass_view_layout_no_motion_vectors",
313 &BindGroupLayoutEntries::with_indices(
314 ShaderStages::VERTEX_FRAGMENT,
315 (
316 (0, uniform_buffer::<ViewUniform>(true)),
318 (1, uniform_buffer::<GlobalsUniform>(false)),
320 (
322 14,
323 buffer_layout(
324 visibility_ranges_buffer_binding_type,
325 false,
326 Some(Vec4::min_size()),
327 )
328 .visibility(ShaderStages::VERTEX),
329 ),
330 ),
331 ),
332 );
333
334 let depth_clip_control_supported = render_device
335 .features()
336 .contains(WgpuFeatures::DEPTH_CLIP_CONTROL);
337 commands.insert_resource(PrepassPipeline {
338 view_layout_motion_vectors,
339 view_layout_no_motion_vectors,
340 mesh_layouts: mesh_pipeline.mesh_layouts.clone(),
341 default_prepass_shader: load_embedded_asset!(asset_server.as_ref(), "prepass.wesl"),
342 skins_use_uniform_buffers: skin::skins_use_uniform_buffers(&render_device.limits()),
343 metadata_use_uniform_buffers: bevy_render::storage_buffers_are_unsupported(
344 &render_device.limits(),
345 ),
346 depth_clip_control_supported,
347 binding_arrays_are_usable: binding_arrays_are_usable(&render_device, &render_adapter),
348 empty_layout: BindGroupLayoutDescriptor::new("prepass_empty_layout", &[]),
349 material_pipeline: material_pipeline.clone(),
350 });
351}
352
353pub struct PrepassPipelineSpecializer {
354 pub pipeline: PrepassPipeline,
355 pub properties: Arc<MaterialProperties>,
356}
357
358impl SpecializedMeshPipeline for PrepassPipelineSpecializer {
359 type Key = ErasedMaterialPipelineKey;
360
361 fn specialize(
362 &self,
363 key: Self::Key,
364 layout: &MeshVertexBufferLayoutRef,
365 ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
366 let mut shader_defs = Vec::new();
367 if self.properties.bindless {
368 shader_defs.push("BINDLESS".into());
369 }
370 let mut descriptor = self.pipeline.specialize(
371 key.mesh_key.downcast(),
372 shader_defs,
373 layout,
374 &self.properties,
375 )?;
376
377 if let Some(specialize) = self.properties.user_specialize {
381 specialize(
382 &self.pipeline.material_pipeline,
383 &mut descriptor,
384 layout,
385 key,
386 )?;
387 }
388
389 Ok(descriptor)
390 }
391}
392
393fn is_depth_only_opaque_prepass(mesh_key: MeshPipelineKey) -> bool {
394 !mesh_key.intersects(MeshPipelineKey::MAY_DISCARD | MeshPipelineKey::PREPASS_READS_MATERIAL)
395 && mesh_key.intersection(MeshPipelineKey::ALL_PREPASS_BITS)
396 == MeshPipelineKey::DEPTH_PREPASS
397}
398
399impl PrepassPipeline {
400 fn specialize(
401 &self,
402 mesh_key: MeshPipelineKey,
403 shader_defs: Vec<ShaderDefVal>,
404 layout: &MeshVertexBufferLayoutRef,
405 material_properties: &MaterialProperties,
406 ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
407 let mut shader_defs = shader_defs;
408 let mut bind_group_layouts = vec![
409 if mesh_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
410 self.view_layout_motion_vectors.clone()
411 } else {
412 self.view_layout_no_motion_vectors.clone()
413 },
414 self.empty_layout.clone(),
415 ];
416 let mut vertex_attributes = Vec::new();
417
418 shader_defs.push("PREPASS_PIPELINE".into());
422
423 shader_defs.push(ShaderDefVal::UInt(
424 "MATERIAL_BIND_GROUP".into(),
425 crate::MATERIAL_BIND_GROUP_INDEX as u32,
426 ));
427 let emulate_unclipped_depth = mesh_key.contains(MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO)
430 && !self.depth_clip_control_supported;
431 if is_depth_only_opaque_prepass(mesh_key)
432 && !emulate_unclipped_depth
433 && !material_properties.prepass_reads_material()
434 {
435 bind_group_layouts.push(self.empty_layout.clone());
438 } else {
439 bind_group_layouts.push(
440 material_properties
441 .material_layout
442 .as_ref()
443 .unwrap()
444 .clone(),
445 );
446 }
447 #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
448 shader_defs.push("WEBGL2".into());
449 shader_defs.push("VERTEX_OUTPUT_INSTANCE_INDEX".into());
450 let view_projection = mesh_key.intersection(MeshPipelineKey::VIEW_PROJECTION_RESERVED_BITS);
451 if view_projection == MeshPipelineKey::VIEW_PROJECTION_NONSTANDARD {
452 shader_defs.push("VIEW_PROJECTION_NONSTANDARD".into());
453 } else if view_projection == MeshPipelineKey::VIEW_PROJECTION_PERSPECTIVE {
454 shader_defs.push("VIEW_PROJECTION_PERSPECTIVE".into());
455 } else if view_projection == MeshPipelineKey::VIEW_PROJECTION_ORTHOGRAPHIC {
456 shader_defs.push("VIEW_PROJECTION_ORTHOGRAPHIC".into());
457 }
458 if mesh_key.contains(MeshPipelineKey::DEPTH_PREPASS) {
459 shader_defs.push("DEPTH_PREPASS".into());
460 }
461 if mesh_key.contains(MeshPipelineKey::MAY_DISCARD) {
462 shader_defs.push("MAY_DISCARD".into());
463 }
464 let blend_key = mesh_key.intersection(MeshPipelineKey::BLEND_RESERVED_BITS);
465 if blend_key == MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA {
466 shader_defs.push("BLEND_PREMULTIPLIED_ALPHA".into());
467 }
468 if blend_key == MeshPipelineKey::BLEND_ALPHA {
469 shader_defs.push("BLEND_ALPHA".into());
470 }
471 if layout.0.contains(Mesh::ATTRIBUTE_POSITION) {
472 shader_defs.push("VERTEX_POSITIONS".into());
473 if layout
474 .0
475 .get_attribute_compression()
476 .contains(MeshAttributeCompressionFlags::COMPRESS_POSITION)
477 {
478 shader_defs.push("VERTEX_POSITIONS_COMPRESSED".into());
479 }
480 vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
481 }
482 if emulate_unclipped_depth {
483 shader_defs.push("UNCLIPPED_DEPTH_ORTHO_EMULATION".into());
484 shader_defs.push("PREPASS_FRAGMENT".into());
491 }
492 let unclipped_depth = mesh_key.contains(MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO)
493 && self.depth_clip_control_supported;
494 if layout.0.contains(Mesh::ATTRIBUTE_UV_0) {
495 shader_defs.push("VERTEX_UVS".into());
496 shader_defs.push("VERTEX_UVS_A".into());
497 if layout
498 .0
499 .get_attribute_compression()
500 .contains(MeshAttributeCompressionFlags::COMPRESS_UV0)
501 {
502 shader_defs.push("VERTEX_UVS_A_COMPRESSED".into());
503 }
504 vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(1));
505 }
506 if layout.0.contains(Mesh::ATTRIBUTE_UV_1) {
507 shader_defs.push("VERTEX_UVS".into());
508 shader_defs.push("VERTEX_UVS_B".into());
509 if layout
510 .0
511 .get_attribute_compression()
512 .contains(MeshAttributeCompressionFlags::COMPRESS_UV1)
513 {
514 shader_defs.push("VERTEX_UVS_B_COMPRESSED".into());
515 }
516 vertex_attributes.push(Mesh::ATTRIBUTE_UV_1.at_shader_location(2));
517 }
518 if mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS) {
519 shader_defs.push("NORMAL_PREPASS".into());
520 }
521 if mesh_key.intersects(MeshPipelineKey::NORMAL_PREPASS | MeshPipelineKey::DEFERRED_PREPASS)
522 {
523 shader_defs.push("NORMAL_PREPASS_OR_DEFERRED_PREPASS".into());
524 if layout.0.contains(Mesh::ATTRIBUTE_NORMAL) {
525 shader_defs.push("VERTEX_NORMALS".into());
526 if layout
527 .0
528 .get_attribute_compression()
529 .contains(MeshAttributeCompressionFlags::COMPRESS_NORMAL)
530 {
531 shader_defs.push("VERTEX_NORMALS_COMPRESSED".into());
532 }
533 vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(3));
534 } else if mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS) {
535 warn!(
536 "The default normal prepass expects the mesh to have vertex normal attributes."
537 );
538 }
539 if layout.0.contains(Mesh::ATTRIBUTE_TANGENT) {
540 shader_defs.push("VERTEX_TANGENTS".into());
541 if layout
542 .0
543 .get_attribute_compression()
544 .contains(MeshAttributeCompressionFlags::COMPRESS_TANGENT)
545 {
546 shader_defs.push("VERTEX_TANGENTS_COMPRESSED".into());
547 }
548 vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(4));
549 }
550 }
551 if mesh_key
552 .intersects(MeshPipelineKey::MOTION_VECTOR_PREPASS | MeshPipelineKey::DEFERRED_PREPASS)
553 {
554 shader_defs.push("MOTION_VECTOR_PREPASS_OR_DEFERRED_PREPASS".into());
555 }
556 if mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) {
557 shader_defs.push("DEFERRED_PREPASS".into());
558 }
559 if mesh_key.contains(MeshPipelineKey::LIGHTMAPPED) {
560 shader_defs.push("LIGHTMAP".into());
561 }
562 if mesh_key.contains(MeshPipelineKey::LIGHTMAP_BICUBIC_SAMPLING) {
563 shader_defs.push("LIGHTMAP_BICUBIC_SAMPLING".into());
564 }
565 if layout.0.contains(Mesh::ATTRIBUTE_COLOR) {
566 shader_defs.push("VERTEX_COLORS".into());
567 vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(7));
568 }
569 if mesh_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
570 shader_defs.push("MOTION_VECTOR_PREPASS".into());
571 }
572 if mesh_key.contains(MeshPipelineKey::HAS_PREVIOUS_SKIN) {
573 shader_defs.push("HAS_PREVIOUS_SKIN".into());
574 }
575 if mesh_key.contains(MeshPipelineKey::HAS_PREVIOUS_MORPH) {
576 shader_defs.push("HAS_PREVIOUS_MORPH".into());
577 }
578 if self.binding_arrays_are_usable {
579 shader_defs.push("MULTIPLE_LIGHTMAPS_IN_ARRAY".into());
580 }
581 if mesh_key.contains(MeshPipelineKey::VISIBILITY_RANGE_DITHER) {
582 shader_defs.push("VISIBILITY_RANGE_DITHER".into());
583 }
584 if mesh_key.intersects(
585 MeshPipelineKey::NORMAL_PREPASS
586 | MeshPipelineKey::MOTION_VECTOR_PREPASS
587 | MeshPipelineKey::DEFERRED_PREPASS,
588 ) {
589 shader_defs.push("PREPASS_FRAGMENT".into());
590 }
591 if self.metadata_use_uniform_buffers {
592 shader_defs.push("METADATA_USE_UNIFORM_BUFFERS".into());
593 }
594 let bind_group = setup_morph_and_skinning_defs(
595 &self.mesh_layouts,
596 layout,
597 5,
598 &mesh_key,
599 &mut shader_defs,
600 &mut vertex_attributes,
601 self.skins_use_uniform_buffers,
602 );
603 bind_group_layouts.insert(2, bind_group);
604 let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?;
605 let mut targets = prepass_target_descriptors(
607 mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS),
608 mesh_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS),
609 mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS),
610 );
611
612 if targets.iter().all(Option::is_none) {
613 targets.clear();
616 }
617
618 let fragment_required = !targets.is_empty()
622 || emulate_unclipped_depth
623 || (mesh_key.contains(MeshPipelineKey::MAY_DISCARD)
624 && material_properties
625 .get_shader(PrepassFragmentShader)
626 .is_some());
627
628 let fragment = fragment_required.then(|| {
629 let frag_shader_handle = if mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) {
631 match material_properties.get_shader(DeferredFragmentShader) {
632 Some(frag_shader_handle) => frag_shader_handle,
633 None => self.default_prepass_shader.clone(),
634 }
635 } else {
636 match material_properties.get_shader(PrepassFragmentShader) {
637 Some(frag_shader_handle) => frag_shader_handle,
638 None => self.default_prepass_shader.clone(),
639 }
640 };
641
642 FragmentState {
643 shader: frag_shader_handle,
644 shader_defs: shader_defs.clone(),
645 targets,
646 ..default()
647 }
648 });
649
650 let vert_shader_handle = if mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) {
652 if let Some(handle) = material_properties.get_shader(DeferredVertexShader) {
653 handle
654 } else {
655 self.default_prepass_shader.clone()
656 }
657 } else if let Some(handle) = material_properties.get_shader(PrepassVertexShader) {
658 handle
659 } else {
660 self.default_prepass_shader.clone()
661 };
662 let descriptor = RenderPipelineDescriptor {
663 vertex: VertexState {
664 shader: vert_shader_handle,
665 shader_defs,
666 buffers: vec![vertex_buffer_layout],
667 ..default()
668 },
669 fragment,
670 layout: bind_group_layouts,
671 primitive: PrimitiveState {
672 topology: mesh_key.primitive_topology(),
673 strip_index_format: mesh_key.strip_index_format(),
674 unclipped_depth,
675 ..default()
676 },
677 depth_stencil: Some(DepthStencilState {
678 format: CORE_3D_DEPTH_FORMAT,
679 depth_write_enabled: Some(true),
680 depth_compare: Some(CompareFunction::GreaterEqual),
681 stencil: StencilState {
682 front: StencilFaceState::IGNORE,
683 back: StencilFaceState::IGNORE,
684 read_mask: 0,
685 write_mask: 0,
686 },
687 bias: DepthBiasState {
688 constant: 0,
689 slope_scale: 0.0,
690 clamp: 0.0,
691 },
692 }),
693 multisample: MultisampleState {
694 count: mesh_key.msaa_samples(),
695 mask: !0,
696 alpha_to_coverage_enabled: false,
697 },
698 label: Some("prepass_pipeline".into()),
699 ..default()
700 };
701 Ok(descriptor)
702 }
703}
704
705pub fn extract_camera_previous_view_data(
707 mut commands: Commands,
708 cameras_3d: Extract<Query<(RenderEntity, &Camera, Option<&PreviousViewData>), With<Camera3d>>>,
709) {
710 for (entity, camera, maybe_previous_view_data) in cameras_3d.iter() {
711 let mut entity = commands
712 .get_entity(entity)
713 .expect("Camera entity wasn't synced.");
714 if camera.is_active {
715 if let Some(previous_view_data) = maybe_previous_view_data {
716 entity.insert(previous_view_data.clone());
717 }
718 } else {
719 entity.remove::<PreviousViewData>();
720 }
721 }
722}
723
724pub fn prepare_previous_view_uniforms(
725 mut commands: Commands,
726 render_device: Res<RenderDevice>,
727 render_queue: Res<RenderQueue>,
728 mut previous_view_uniforms: ResMut<PreviousViewUniforms>,
729 views: Query<
730 (
731 Entity,
732 &ExtractedView,
733 Option<&PreviousViewData>,
734 Option<&PreviousTemporalJitter>,
735 Option<&TemporalJitter>,
736 Option<&MainPassResolutionOverride>,
737 ),
738 Or<(With<Camera3d>, With<ShadowView>)>,
739 >,
740) {
741 let views_iter = views.iter();
742 let view_count = views_iter.len();
743 let Some(mut writer) =
744 previous_view_uniforms
745 .uniforms
746 .get_writer(view_count, &render_device, &render_queue)
747 else {
748 return;
749 };
750
751 for (
752 entity,
753 camera,
754 maybe_previous_view_uniforms,
755 maybe_previous_jitter,
756 maybe_jitter,
757 resolution_override,
758 ) in views_iter
759 {
760 let mut prev_view_data = match maybe_previous_view_uniforms {
761 Some(previous_view) => previous_view.clone(),
762 None => {
763 let world_from_view = camera.world_from_view.affine();
764 let view_from_world = Mat4::from(world_from_view.inverse());
765 let view_from_clip = camera.clip_from_view.inverse();
766
767 PreviousViewData {
768 view_from_world,
769 clip_from_world: camera.clip_from_view * view_from_world,
770 unjittered_clip_from_world: camera.clip_from_view * view_from_world,
771 clip_from_view: camera.clip_from_view,
772 world_from_clip: Mat4::from(world_from_view) * view_from_clip,
773 view_from_clip,
774 }
775 }
776 };
777
778 if let Some(previous_jitter) = maybe_previous_jitter {
784 let mut clip_from_view = prev_view_data.clip_from_view;
785 previous_jitter
786 .jitter
787 .jitter_projection(&mut clip_from_view, previous_jitter.view_size);
788 let view_from_clip = clip_from_view.inverse();
789 let world_from_view = prev_view_data.view_from_world.inverse();
790
791 prev_view_data.clip_from_view = clip_from_view;
792 prev_view_data.view_from_clip = view_from_clip;
793 prev_view_data.clip_from_world = clip_from_view * prev_view_data.view_from_world;
794 prev_view_data.world_from_clip = world_from_view * view_from_clip;
795 }
796
797 commands.entity(entity).insert(PreviousViewUniformOffset {
798 offset: writer.write(&prev_view_data),
799 });
800
801 match maybe_jitter {
804 Some(jitter) => {
805 let mut view_size = Vec2::new(camera.viewport.z as f32, camera.viewport.w as f32);
806 if let Some(resolution_override) = resolution_override {
807 view_size = resolution_override.0.as_vec2();
808 }
809
810 commands.entity(entity).insert(PreviousTemporalJitter {
811 jitter: jitter.clone(),
812 view_size,
813 });
814 }
815 None => {
816 commands.entity(entity).remove::<PreviousTemporalJitter>();
817 }
818 }
819 }
820}
821
822#[derive(Component, Clone)]
826pub struct PreviousTemporalJitter {
827 pub jitter: TemporalJitter,
828 pub view_size: Vec2,
829}
830
831#[derive(Resource)]
832pub struct PrepassViewBindGroup {
833 pub motion_vectors: Option<BindGroup>,
834 pub no_motion_vectors: Option<BindGroup>,
835 pub empty_bind_group: BindGroup,
836}
837
838pub fn init_prepass_view_bind_group(
839 mut commands: Commands,
840 render_device: Res<RenderDevice>,
841 pipeline_cache: Res<PipelineCache>,
842 pipeline: Res<PrepassPipeline>,
843) {
844 let empty_bind_group = render_device.create_bind_group(
845 "prepass_view_empty_bind_group",
846 &pipeline_cache.get_bind_group_layout(&pipeline.empty_layout),
847 &[],
848 );
849 commands.insert_resource(PrepassViewBindGroup {
850 motion_vectors: None,
851 no_motion_vectors: None,
852 empty_bind_group,
853 });
854}
855
856pub fn prepare_prepass_view_bind_group(
857 render_device: Res<RenderDevice>,
858 pipeline_cache: Res<PipelineCache>,
859 prepass_pipeline: Res<PrepassPipeline>,
860 view_uniforms: Res<ViewUniforms>,
861 globals_buffer: Res<GlobalsBuffer>,
862 previous_view_uniforms: Res<PreviousViewUniforms>,
863 visibility_ranges: Res<RenderVisibilityRanges>,
864 mut prepass_view_bind_group: ResMut<PrepassViewBindGroup>,
865) {
866 if let (Some(view_binding), Some(globals_binding), Some(visibility_ranges_buffer)) = (
867 view_uniforms.uniforms.binding(),
868 globals_buffer.buffer.binding(),
869 visibility_ranges.buffer().buffer(),
870 ) {
871 prepass_view_bind_group.no_motion_vectors = Some(render_device.create_bind_group(
872 "prepass_view_no_motion_vectors_bind_group",
873 &pipeline_cache.get_bind_group_layout(&prepass_pipeline.view_layout_no_motion_vectors),
874 &BindGroupEntries::with_indices((
875 (0, view_binding.clone()),
876 (1, globals_binding.clone()),
877 (14, visibility_ranges_buffer.as_entire_binding()),
878 )),
879 ));
880
881 if let Some(previous_view_uniforms_binding) = previous_view_uniforms.uniforms.binding() {
882 prepass_view_bind_group.motion_vectors = Some(render_device.create_bind_group(
883 "prepass_view_motion_vectors_bind_group",
884 &pipeline_cache.get_bind_group_layout(&prepass_pipeline.view_layout_motion_vectors),
885 &BindGroupEntries::with_indices((
886 (0, view_binding),
887 (1, globals_binding),
888 (2, previous_view_uniforms_binding),
889 (14, visibility_ranges_buffer.as_entire_binding()),
890 )),
891 ));
892 }
893 }
894}
895
896#[derive(Resource, Deref, DerefMut, Default)]
898pub struct SpecializedPrepassMaterialPipelineCache {
899 #[deref]
901 map: HashMap<RetainedViewEntity, SpecializedPrepassMaterialViewPipelineCache>,
902}
903
904#[derive(Deref, DerefMut, Default)]
907pub struct SpecializedPrepassMaterialViewPipelineCache {
908 #[deref]
910 map: MainEntityHashMap<(Tick, CachedRenderPipelineId, DrawFunctionId)>,
911}
912
913#[derive(Resource, Deref, DerefMut, Default, Clone)]
914pub struct ViewKeyPrepassCache(HashMap<RetainedViewEntity, MeshPipelineKey>);
915
916pub fn check_prepass_views_need_specialization(
917 mut view_key_cache: ResMut<ViewKeyPrepassCache>,
918 mut dirty_specializations: ResMut<DirtySpecializations>,
919 mut views: Query<(
920 &ExtractedView,
921 &Msaa,
922 Option<&DepthPrepass>,
923 Option<&NormalPrepass>,
924 Option<&MotionVectorPrepass>,
925 )>,
926) {
927 for (view, msaa, depth_prepass, normal_prepass, motion_vector_prepass) in views.iter_mut() {
928 let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples());
929 if depth_prepass.is_some() {
930 view_key |= MeshPipelineKey::DEPTH_PREPASS;
931 }
932 if normal_prepass.is_some() {
933 view_key |= MeshPipelineKey::NORMAL_PREPASS;
934 }
935 if motion_vector_prepass.is_some() {
936 view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
937 }
938
939 if let Some(current_key) = view_key_cache.get_mut(&view.retained_view_entity) {
940 if *current_key != view_key {
941 view_key_cache.insert(view.retained_view_entity, view_key);
942 dirty_specializations
943 .views
944 .insert(view.retained_view_entity);
945 }
946 } else {
947 view_key_cache.insert(view.retained_view_entity, view_key);
948 dirty_specializations
949 .views
950 .insert(view.retained_view_entity);
951 }
952 }
953}
954
955pub(crate) struct PrepassSpecializationWorkItem {
956 visible_entity: MainEntity,
957 retained_view_entity: RetainedViewEntity,
958 mesh_key: MeshPipelineKey,
959 layout: MeshVertexBufferLayoutRef,
960 properties: Arc<MaterialProperties>,
961 material_type_id: TypeId,
962}
963
964#[derive(Default, Deref, DerefMut, Resource)]
969pub struct PendingPrepassMeshMaterialQueues(pub PendingQueues);
970
971#[derive(SystemParam)]
972pub(crate) struct SpecializePrepassSystemParam<'w, 's> {
973 render_meshes: Res<'w, RenderAssets<RenderMesh>>,
974 render_materials: Res<'w, ErasedRenderAssets<PreparedMaterial>>,
975 render_mesh_instances: Res<'w, RenderMeshInstances>,
976 render_material_instances: Res<'w, RenderMaterialInstances>,
977 render_lightmaps: Res<'w, RenderLightmaps>,
978 render_visibility_ranges: Res<'w, RenderVisibilityRanges>,
979 view_key_cache: Res<'w, ViewKeyPrepassCache>,
980 views: Query<
981 'w,
982 's,
983 (
984 &'static ExtractedView,
985 &'static RenderVisibleEntities,
986 &'static Msaa,
987 Option<&'static MotionVectorPrepass>,
988 Option<&'static DeferredPrepass>,
989 ),
990 >,
991 opaque_prepass_render_phases: Res<'w, ViewBinnedRenderPhases<Opaque3dPrepass>>,
992 alpha_mask_prepass_render_phases: Res<'w, ViewBinnedRenderPhases<AlphaMask3dPrepass>>,
993 opaque_deferred_render_phases: Res<'w, ViewBinnedRenderPhases<Opaque3dDeferred>>,
994 alpha_mask_deferred_render_phases: Res<'w, ViewBinnedRenderPhases<AlphaMask3dDeferred>>,
995 specialized_prepass_material_pipeline_cache:
996 ResMut<'w, SpecializedPrepassMaterialPipelineCache>,
997 pending_prepass_mesh_material_queues: ResMut<'w, PendingPrepassMeshMaterialQueues>,
998 dirty_specializations: Res<'w, DirtySpecializations>,
999 this_run: SystemChangeTick,
1000}
1001
1002pub(crate) fn specialize_prepass_material_meshes(
1003 world: &mut World,
1004 state: &mut SystemState<SpecializePrepassSystemParam>,
1005 mut work_items: Local<Vec<PrepassSpecializationWorkItem>>,
1006 mut removals: Local<Vec<(RetainedViewEntity, MainEntity)>>,
1007 mut all_views: Local<HashSet<RetainedViewEntity, FixedHasher>>,
1008) {
1009 work_items.clear();
1010 removals.clear();
1011 all_views.clear();
1012
1013 let this_run;
1014
1015 {
1016 let SpecializePrepassSystemParam {
1017 render_meshes,
1018 render_materials,
1019 render_mesh_instances,
1020 render_material_instances,
1021 render_lightmaps,
1022 render_visibility_ranges,
1023 view_key_cache,
1024 views,
1025 opaque_prepass_render_phases,
1026 alpha_mask_prepass_render_phases,
1027 opaque_deferred_render_phases,
1028 alpha_mask_deferred_render_phases,
1029 mut specialized_prepass_material_pipeline_cache,
1030 mut pending_prepass_mesh_material_queues,
1031 dirty_specializations,
1032 this_run: system_change_tick,
1033 } = state.get_mut(world).unwrap();
1034
1035 this_run = system_change_tick.this_run();
1036
1037 for (extracted_view, visible_entities, msaa, motion_vector_prepass, deferred_prepass) in
1038 &views
1039 {
1040 if !opaque_deferred_render_phases.contains_key(&extracted_view.retained_view_entity)
1041 && !alpha_mask_deferred_render_phases
1042 .contains_key(&extracted_view.retained_view_entity)
1043 && !opaque_prepass_render_phases.contains_key(&extracted_view.retained_view_entity)
1044 && !alpha_mask_prepass_render_phases
1045 .contains_key(&extracted_view.retained_view_entity)
1046 {
1047 continue;
1048 }
1049
1050 let Some(view_key) = view_key_cache.get(&extracted_view.retained_view_entity) else {
1051 continue;
1052 };
1053
1054 all_views.insert(extracted_view.retained_view_entity);
1055
1056 let Some(render_visible_mesh_entities) = visible_entities.get::<Mesh3d>() else {
1057 continue;
1058 };
1059
1060 let view_pending_prepass_mesh_material_queues = pending_prepass_mesh_material_queues
1062 .prepare_for_new_frame(extracted_view.retained_view_entity);
1063
1064 let mut maybe_specialized_prepass_material_pipeline_cache =
1066 specialized_prepass_material_pipeline_cache
1067 .get_mut(&extracted_view.retained_view_entity);
1068
1069 if let Some(ref mut specialized_prepass_material_pipeline_cache) =
1072 maybe_specialized_prepass_material_pipeline_cache
1073 {
1074 if dirty_specializations
1075 .must_wipe_specializations_for_view(extracted_view.retained_view_entity)
1076 {
1077 specialized_prepass_material_pipeline_cache.clear();
1078 } else {
1079 for &renderable_entity in dirty_specializations.iter_to_despecialize() {
1080 specialized_prepass_material_pipeline_cache.remove(&renderable_entity);
1081 }
1082 }
1083 }
1084
1085 for (render_entity, visible_entity) in dirty_specializations.iter_to_specialize(
1087 extracted_view.retained_view_entity,
1088 render_visible_mesh_entities,
1089 &view_pending_prepass_mesh_material_queues.prev_frame,
1090 ) {
1091 if maybe_specialized_prepass_material_pipeline_cache
1092 .as_ref()
1093 .is_some_and(|specialized_prepass_material_pipeline_cache| {
1094 specialized_prepass_material_pipeline_cache.contains_key(visible_entity)
1095 })
1096 {
1097 continue;
1098 }
1099
1100 let Some(material_instance) =
1105 render_material_instances.instances.get(visible_entity)
1106 else {
1107 view_pending_prepass_mesh_material_queues
1108 .current_frame
1109 .insert((*render_entity, *visible_entity));
1110 continue;
1111 };
1112 let Some(mesh_instance) =
1113 render_mesh_instances.render_mesh_queue_data(*visible_entity)
1114 else {
1115 view_pending_prepass_mesh_material_queues
1116 .current_frame
1117 .insert((*render_entity, *visible_entity));
1118 continue;
1119 };
1120 let Some(material) = render_materials.get(material_instance.asset_id) else {
1121 view_pending_prepass_mesh_material_queues
1122 .current_frame
1123 .insert((*render_entity, *visible_entity));
1124 continue;
1125 };
1126 if !material.properties.prepass_enabled {
1127 removals.push((extracted_view.retained_view_entity, *visible_entity));
1129 continue;
1130 }
1131 let Some(mesh) = render_meshes.get(mesh_instance.mesh_asset_id()) else {
1132 continue;
1133 };
1134
1135 let mut mesh_key =
1136 *view_key | MeshPipelineKey::from_bits_retain(mesh.key_bits.bits());
1137
1138 let alpha_mode = material.properties.alpha_mode;
1139 match alpha_mode {
1140 AlphaMode::Opaque | AlphaMode::AlphaToCoverage | AlphaMode::Mask(_) => {
1141 mesh_key |= alpha_mode_pipeline_key(alpha_mode, msaa);
1142 }
1143 AlphaMode::Blend
1144 | AlphaMode::Premultiplied
1145 | AlphaMode::Add
1146 | AlphaMode::Multiply => {
1147 removals.push((extracted_view.retained_view_entity, *visible_entity));
1150 continue;
1151 }
1152 }
1153
1154 if material.properties.reads_view_transmission_texture {
1155 removals.push((extracted_view.retained_view_entity, *visible_entity));
1158 continue;
1159 }
1160
1161 let forward = match material.properties.render_method {
1162 OpaqueRendererMethod::Forward => true,
1163 OpaqueRendererMethod::Deferred => false,
1164 OpaqueRendererMethod::Auto => unreachable!(),
1165 };
1166
1167 let deferred = deferred_prepass.is_some() && !forward;
1168
1169 if deferred {
1170 mesh_key |= MeshPipelineKey::DEFERRED_PREPASS;
1171 }
1172
1173 if let Some(lightmap) = render_lightmaps.render_lightmaps.get(visible_entity) {
1174 mesh_key |= MeshPipelineKey::LIGHTMAPPED;
1180
1181 if lightmap.bicubic_sampling && deferred {
1182 mesh_key |= MeshPipelineKey::LIGHTMAP_BICUBIC_SAMPLING;
1183 }
1184 }
1185
1186 if render_visibility_ranges
1187 .entity_has_crossfading_visibility_ranges(*visible_entity)
1188 {
1189 mesh_key |= MeshPipelineKey::VISIBILITY_RANGE_DITHER;
1190 }
1191
1192 if material.properties.prepass_reads_material() {
1193 mesh_key |= MeshPipelineKey::PREPASS_READS_MATERIAL;
1194 }
1195
1196 if motion_vector_prepass.is_some() {
1198 if mesh_instance
1199 .flags()
1200 .contains(RenderMeshInstanceFlags::HAS_PREVIOUS_SKIN)
1201 {
1202 mesh_key |= MeshPipelineKey::HAS_PREVIOUS_SKIN;
1203 }
1204 if mesh_instance
1205 .flags()
1206 .contains(RenderMeshInstanceFlags::HAS_PREVIOUS_MORPH)
1207 {
1208 mesh_key |= MeshPipelineKey::HAS_PREVIOUS_MORPH;
1209 }
1210 }
1211
1212 work_items.push(PrepassSpecializationWorkItem {
1213 visible_entity: *visible_entity,
1214 retained_view_entity: extracted_view.retained_view_entity,
1215 mesh_key,
1216 layout: mesh.layout.clone(),
1217 properties: material.properties.clone(),
1218 material_type_id: material_instance.asset_id.type_id(),
1219 });
1220 }
1221 }
1222
1223 pending_prepass_mesh_material_queues.expire_stale_views(&all_views);
1224 }
1225
1226 let depth_clip_control_supported = world
1227 .resource::<PrepassPipeline>()
1228 .depth_clip_control_supported;
1229
1230 for item in work_items.drain(..) {
1231 let Some(prepass_specialize) = item.properties.prepass_specialize else {
1232 continue;
1233 };
1234
1235 let key = ErasedMaterialPipelineKey {
1236 type_id: item.material_type_id,
1237 mesh_key: ErasedMeshPipelineKey::new(item.mesh_key),
1238 material_key: item.properties.material_key.clone(),
1239 };
1240
1241 let emulate_unclipped_depth = item
1242 .mesh_key
1243 .contains(MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO)
1244 && !depth_clip_control_supported;
1245 let deferred = item.mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS);
1246 let draw_function = match item.properties.render_phase_type {
1247 RenderPhaseType::Opaque => {
1248 if deferred {
1249 item.properties
1250 .get_draw_function(DeferredOpaqueDrawFunction)
1251 } else if is_depth_only_opaque_prepass(item.mesh_key) && !emulate_unclipped_depth {
1252 item.properties
1253 .get_draw_function(PrepassOpaqueDepthOnlyDrawFunction)
1254 } else {
1255 item.properties.get_draw_function(PrepassOpaqueDrawFunction)
1256 }
1257 }
1258 RenderPhaseType::AlphaMask => {
1259 if deferred {
1260 item.properties
1261 .get_draw_function(DeferredAlphaMaskDrawFunction)
1262 } else {
1263 item.properties
1264 .get_draw_function(PrepassAlphaMaskDrawFunction)
1265 }
1266 }
1267 RenderPhaseType::Transmissive | RenderPhaseType::Transparent => continue,
1268 };
1269
1270 let Some(draw_function) = draw_function else {
1271 continue;
1272 };
1273
1274 match prepass_specialize(world, &key, &item.layout, &item.properties) {
1275 Ok(pipeline_id) => {
1276 world
1277 .resource_mut::<SpecializedPrepassMaterialPipelineCache>()
1278 .entry(item.retained_view_entity)
1279 .or_default()
1280 .insert(item.visible_entity, (this_run, pipeline_id, draw_function));
1281 }
1282 Err(err) => error!("{}", err),
1283 }
1284 }
1285
1286 if !removals.is_empty() {
1287 let mut cache = world.resource_mut::<SpecializedPrepassMaterialPipelineCache>();
1288 for (view, entity) in removals.drain(..) {
1289 if let Some(view_cache) = cache.get_mut(&view) {
1290 view_cache.remove(&entity);
1291 }
1292 }
1293 }
1294
1295 world
1296 .resource_mut::<SpecializedPrepassMaterialPipelineCache>()
1297 .retain(|view, _| all_views.contains(view));
1298}
1299
1300pub fn queue_prepass_material_meshes(
1301 render_mesh_instances: Res<RenderMeshInstances>,
1302 render_materials: Res<ErasedRenderAssets<PreparedMaterial>>,
1303 render_material_instances: Res<RenderMaterialInstances>,
1304 mesh_allocator: Res<MeshAllocator>,
1305 gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
1306 mut opaque_prepass_render_phases: ResMut<ViewBinnedRenderPhases<Opaque3dPrepass>>,
1307 mut alpha_mask_prepass_render_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3dPrepass>>,
1308 mut opaque_deferred_render_phases: ResMut<ViewBinnedRenderPhases<Opaque3dDeferred>>,
1309 mut alpha_mask_deferred_render_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3dDeferred>>,
1310 views: Query<(&ExtractedView, &RenderVisibleEntities)>,
1311 specialized_material_pipeline_cache: Res<SpecializedPrepassMaterialPipelineCache>,
1312 mut pending_prepass_mesh_material_queues: ResMut<PendingPrepassMeshMaterialQueues>,
1313 dirty_specializations: Res<DirtySpecializations>,
1314 mut mesh_instances_queued_this_iteration_scratch_space: Local<MainEntityHashSet>,
1315) {
1316 for (extracted_view, visible_entities) in &views {
1317 let (
1318 mut opaque_phase,
1319 mut alpha_mask_phase,
1320 mut opaque_deferred_phase,
1321 mut alpha_mask_deferred_phase,
1322 ) = (
1323 opaque_prepass_render_phases.get_mut(&extracted_view.retained_view_entity),
1324 alpha_mask_prepass_render_phases.get_mut(&extracted_view.retained_view_entity),
1325 opaque_deferred_render_phases.get_mut(&extracted_view.retained_view_entity),
1326 alpha_mask_deferred_render_phases.get_mut(&extracted_view.retained_view_entity),
1327 );
1328
1329 let Some(view_specialized_material_pipeline_cache) =
1330 specialized_material_pipeline_cache.get(&extracted_view.retained_view_entity)
1331 else {
1332 continue;
1333 };
1334
1335 if opaque_phase.is_none()
1337 && alpha_mask_phase.is_none()
1338 && opaque_deferred_phase.is_none()
1339 && alpha_mask_deferred_phase.is_none()
1340 {
1341 continue;
1342 }
1343
1344 let Some(render_visible_mesh_entities) = visible_entities.get::<Mesh3d>() else {
1345 continue;
1346 };
1347
1348 let view_pending_prepass_mesh_material_queues = pending_prepass_mesh_material_queues
1350 .get_mut(&extracted_view.retained_view_entity)
1351 .expect(
1352 "View pending prepass mesh material queues should have been created in \
1353 `specialize_prepass_material_meshes`",
1354 );
1355
1356 for &main_entity in dirty_specializations.iter_to_dequeue(
1358 extracted_view.retained_view_entity,
1359 render_visible_mesh_entities,
1360 ) {
1361 if let Some(ref mut opaque_phase) = opaque_phase {
1362 opaque_phase.remove(main_entity);
1363 }
1364 if let Some(ref mut alpha_mask_phase) = alpha_mask_phase {
1365 alpha_mask_phase.remove(main_entity);
1366 }
1367 if let Some(ref mut opaque_deferred_phase) = opaque_deferred_phase {
1368 opaque_deferred_phase.remove(main_entity);
1369 }
1370 if let Some(ref mut alpha_mask_deferred_phase) = alpha_mask_deferred_phase {
1371 alpha_mask_deferred_phase.remove(main_entity);
1372 }
1373 }
1374
1375 for (render_entity, visible_entity) in dirty_specializations.iter_to_queue(
1377 extracted_view.retained_view_entity,
1378 render_visible_mesh_entities,
1379 &view_pending_prepass_mesh_material_queues.prev_frame,
1380 &mut mesh_instances_queued_this_iteration_scratch_space,
1381 ) {
1382 let Some(&(_, pipeline_id, draw_function)) =
1383 view_specialized_material_pipeline_cache.get(visible_entity)
1384 else {
1385 continue;
1386 };
1387
1388 let Some(material_instance) = render_material_instances.instances.get(visible_entity)
1393 else {
1394 view_pending_prepass_mesh_material_queues
1395 .current_frame
1396 .insert((*render_entity, *visible_entity));
1397 continue;
1398 };
1399 let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(*visible_entity)
1400 else {
1401 view_pending_prepass_mesh_material_queues
1402 .current_frame
1403 .insert((*render_entity, *visible_entity));
1404 continue;
1405 };
1406 let Some(material) = render_materials.get(material_instance.asset_id) else {
1407 view_pending_prepass_mesh_material_queues
1408 .current_frame
1409 .insert((*render_entity, *visible_entity));
1410 continue;
1411 };
1412 let Some(mesh_slabs) = mesh_allocator.mesh_slabs(&mesh_instance.mesh_asset_id()) else {
1413 continue;
1414 };
1415
1416 let deferred = match material.properties.render_method {
1417 OpaqueRendererMethod::Forward => false,
1418 OpaqueRendererMethod::Deferred => true,
1419 OpaqueRendererMethod::Auto => unreachable!(),
1420 };
1421
1422 match material.properties.render_phase_type {
1423 RenderPhaseType::Opaque => {
1424 if deferred {
1425 opaque_deferred_phase.as_mut().unwrap().add(
1426 OpaqueNoLightmap3dBatchSetKey {
1427 draw_function,
1428 pipeline: pipeline_id,
1429 material_bind_group_index: Some(material.binding.group.0),
1430 slabs: mesh_slabs,
1431 },
1432 OpaqueNoLightmap3dBinKey {
1433 asset_id: mesh_instance.mesh_asset_id().into(),
1434 },
1435 (*render_entity, *visible_entity),
1436 mesh_instance.current_uniform_index,
1437 BinnedRenderPhaseType::mesh(
1438 mesh_instance.should_batch(),
1439 &gpu_preprocessing_support,
1440 ),
1441 );
1442 } else if let Some(opaque_phase) = opaque_phase.as_mut() {
1443 let depth_only_draw_function = material
1444 .properties
1445 .get_draw_function(PrepassOpaqueDepthOnlyDrawFunction);
1446 let material_bind_group_index =
1447 if Some(draw_function) == depth_only_draw_function {
1448 None
1449 } else {
1450 Some(material.binding.group.0)
1451 };
1452 opaque_phase.add(
1453 OpaqueNoLightmap3dBatchSetKey {
1454 draw_function,
1455 pipeline: pipeline_id,
1456 material_bind_group_index,
1457 slabs: mesh_slabs,
1458 },
1459 OpaqueNoLightmap3dBinKey {
1460 asset_id: mesh_instance.mesh_asset_id().into(),
1461 },
1462 (*render_entity, *visible_entity),
1463 mesh_instance.current_uniform_index,
1464 BinnedRenderPhaseType::mesh(
1465 mesh_instance.should_batch(),
1466 &gpu_preprocessing_support,
1467 ),
1468 );
1469 }
1470 }
1471 RenderPhaseType::AlphaMask => {
1472 if deferred {
1473 alpha_mask_deferred_phase.as_mut().unwrap().add(
1474 OpaqueNoLightmap3dBatchSetKey {
1475 draw_function,
1476 pipeline: pipeline_id,
1477 material_bind_group_index: Some(material.binding.group.0),
1478 slabs: mesh_slabs,
1479 },
1480 OpaqueNoLightmap3dBinKey {
1481 asset_id: mesh_instance.mesh_asset_id().into(),
1482 },
1483 (*render_entity, *visible_entity),
1484 mesh_instance.current_uniform_index,
1485 BinnedRenderPhaseType::mesh(
1486 mesh_instance.should_batch(),
1487 &gpu_preprocessing_support,
1488 ),
1489 );
1490 } else if let Some(alpha_mask_phase) = alpha_mask_phase.as_mut() {
1491 alpha_mask_phase.add(
1492 OpaqueNoLightmap3dBatchSetKey {
1493 draw_function,
1494 pipeline: pipeline_id,
1495 material_bind_group_index: Some(material.binding.group.0),
1496 slabs: mesh_slabs,
1497 },
1498 OpaqueNoLightmap3dBinKey {
1499 asset_id: mesh_instance.mesh_asset_id().into(),
1500 },
1501 (*render_entity, *visible_entity),
1502 mesh_instance.current_uniform_index,
1503 BinnedRenderPhaseType::mesh(
1504 mesh_instance.should_batch(),
1505 &gpu_preprocessing_support,
1506 ),
1507 );
1508 }
1509 }
1510 _ => {}
1511 }
1512 }
1513 }
1514}
1515
1516pub struct SetPrepassViewBindGroup<const I: usize>;
1517impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetPrepassViewBindGroup<I> {
1518 type Param = SRes<PrepassViewBindGroup>;
1519 type ViewQuery = (
1520 Read<ViewUniformOffset>,
1521 Has<MotionVectorPrepass>,
1522 Option<Read<PreviousViewUniformOffset>>,
1523 );
1524 type ItemQuery = ();
1525
1526 #[inline]
1527 fn render<'w>(
1528 _item: &P,
1529 (view_uniform_offset, has_motion_vector_prepass, previous_view_uniform_offset): (
1530 &'_ ViewUniformOffset,
1531 bool,
1532 Option<&'_ PreviousViewUniformOffset>,
1533 ),
1534 _entity: Option<()>,
1535 prepass_view_bind_group: SystemParamItem<'w, '_, Self::Param>,
1536 pass: &mut TrackedRenderPass<'w>,
1537 ) -> RenderCommandResult {
1538 let prepass_view_bind_group = prepass_view_bind_group.into_inner();
1539
1540 match previous_view_uniform_offset {
1541 Some(previous_view_uniform_offset) if has_motion_vector_prepass => {
1542 pass.set_bind_group(
1543 I,
1544 prepass_view_bind_group.motion_vectors.as_ref().unwrap(),
1545 &[
1546 view_uniform_offset.offset,
1547 previous_view_uniform_offset.offset,
1548 ],
1549 );
1550 }
1551 _ => {
1552 pass.set_bind_group(
1553 I,
1554 prepass_view_bind_group.no_motion_vectors.as_ref().unwrap(),
1555 &[view_uniform_offset.offset],
1556 );
1557 }
1558 }
1559 RenderCommandResult::Success
1560 }
1561}
1562
1563pub struct SetPrepassViewEmptyBindGroup<const I: usize>;
1564impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetPrepassViewEmptyBindGroup<I> {
1565 type Param = SRes<PrepassViewBindGroup>;
1566 type ViewQuery = ();
1567 type ItemQuery = ();
1568
1569 #[inline]
1570 fn render<'w>(
1571 _item: &P,
1572 _view: (),
1573 _entity: Option<()>,
1574 prepass_view_bind_group: SystemParamItem<'w, '_, Self::Param>,
1575 pass: &mut TrackedRenderPass<'w>,
1576 ) -> RenderCommandResult {
1577 let prepass_view_bind_group = prepass_view_bind_group.into_inner();
1578 pass.set_bind_group(I, &prepass_view_bind_group.empty_bind_group, &[]);
1579 RenderCommandResult::Success
1580 }
1581}
1582
1583pub struct SetPrepassEmptyMaterialBindGroup<const I: usize>;
1584impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetPrepassEmptyMaterialBindGroup<I> {
1585 type Param = SRes<PrepassViewBindGroup>;
1586 type ViewQuery = ();
1587 type ItemQuery = ();
1588
1589 #[inline]
1590 fn render<'w>(
1591 _item: &P,
1592 _view: (),
1593 _entity: Option<()>,
1594 prepass_view_bind_group: SystemParamItem<'w, '_, Self::Param>,
1595 pass: &mut TrackedRenderPass<'w>,
1596 ) -> RenderCommandResult {
1597 let prepass_view_bind_group = prepass_view_bind_group.into_inner();
1598 pass.set_bind_group(I, &prepass_view_bind_group.empty_bind_group, &[]);
1599 RenderCommandResult::Success
1600 }
1601}
1602
1603pub type DrawPrepass = (
1604 SetItemPipeline,
1605 SetPrepassViewBindGroup<0>,
1606 SetPrepassViewEmptyBindGroup<1>,
1607 SetMeshBindGroup<2>,
1608 SetMaterialBindGroup<3>,
1609 DrawMesh,
1610);
1611
1612pub type DrawDepthOnlyPrepass = (
1613 SetItemPipeline,
1614 SetPrepassViewBindGroup<0>,
1615 SetPrepassViewEmptyBindGroup<1>,
1616 SetMeshBindGroup<2>,
1617 SetPrepassEmptyMaterialBindGroup<3>,
1618 DrawMesh,
1619);