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