1use crate::*;
2use alloc::sync::Arc;
3use bevy_asset::prelude::AssetChanged;
4use bevy_asset::{Asset, AssetEventSystems, AssetId, AssetServer, UntypedAssetId};
5use bevy_camera::visibility::ViewVisibility;
6use bevy_core_pipeline::core_3d::TransparentSortingInfo3d;
7use bevy_core_pipeline::deferred::{AlphaMask3dDeferred, Opaque3dDeferred};
8use bevy_core_pipeline::prepass::{AlphaMask3dPrepass, Opaque3dPrepass};
9use bevy_core_pipeline::{
10 core_3d::{AlphaMask3d, Opaque3d, Opaque3dBatchSetKey, Opaque3dBinKey, Transparent3d},
11 prepass::{OpaqueNoLightmap3dBatchSetKey, OpaqueNoLightmap3dBinKey},
12 tonemapping::Tonemapping,
13};
14use bevy_derive::{Deref, DerefMut};
15use bevy_ecs::change_detection::Tick;
16use bevy_ecs::system::SystemParam;
17use bevy_ecs::{
18 prelude::*,
19 system::{
20 lifetimeless::{SRes, SResMut},
21 SystemParamItem, SystemState,
22 },
23};
24use bevy_material::{
25 key::{ErasedMaterialKey, ErasedMaterialPipelineKey, ErasedMeshPipelineKey},
26 labels::{DrawFunctionLabel, InternedShaderLabel, ShaderLabel},
27 MaterialProperties, OpaqueRendererMethod, RenderPhaseType,
28};
29use bevy_math::{Affine3, Affine3Ext as _};
30use bevy_mesh::{
31 mark_3d_meshes_as_changed_if_their_assets_changed, Mesh3d, MeshVertexBufferLayoutRef,
32};
33use bevy_platform::collections::hash_map::Entry;
34use bevy_platform::collections::{HashMap, HashSet};
35use bevy_platform::hash::FixedHasher;
36use bevy_reflect::std_traits::ReflectDefault;
37use bevy_reflect::Reflect;
38use bevy_render::batching::gpu_preprocessing::BatchedInstanceBuffers;
39use bevy_render::camera::{DirtySpecializationSystems, DirtySpecializations, PendingQueues};
40use bevy_render::erased_render_asset::{
41 ErasedRenderAsset, ErasedRenderAssetPlugin, ErasedRenderAssets, PrepareAssetError,
42};
43use bevy_render::material_bind_groups::{
44 material_uses_bindless_resources, FallbackBuffer, MaterialBindGroupAllocators,
45 MaterialBindingId, RenderMaterialBindings,
46};
47use bevy_render::render_asset::{prepare_assets, RenderAssets};
48use bevy_render::storage::GpuShaderBuffer;
49use bevy_render::sync_world::MainEntityHashSet;
50use bevy_render::view::RenderVisibleEntities;
51use bevy_render::GpuResourceAppExt;
52use bevy_render::RenderStartup;
53use bevy_render::{
54 batching::gpu_preprocessing::GpuPreprocessingSupport,
55 extract_resource::ExtractResource,
56 mesh::RenderMesh,
57 render_phase::*,
58 render_resource::*,
59 renderer::RenderDevice,
60 sync_world::MainEntity,
61 view::{ExtractedView, Msaa, RenderVisibilityRanges, RetainedViewEntity},
62 Extract,
63};
64use bevy_render::{mesh::allocator::MeshAllocator, sync_world::MainEntityHashMap};
65use bevy_shader::{Shader, ShaderDefVal};
66use bevy_utils::Parallel;
67use core::{
68 any::{Any, TypeId},
69 hash::Hash,
70 marker::PhantomData,
71};
72use smallvec::SmallVec;
73use tracing::error;
74
75pub const MATERIAL_BIND_GROUP_INDEX: usize = 3;
76
77pub trait Material: Asset + AsBindGroup + Clone + Sized {
148 fn vertex_shader() -> ShaderRef {
151 ShaderRef::Default
152 }
153
154 fn fragment_shader() -> ShaderRef {
157 ShaderRef::Default
158 }
159
160 #[inline]
162 fn alpha_mode(&self) -> AlphaMode {
163 AlphaMode::Opaque
164 }
165
166 #[inline]
170 fn opaque_render_method(&self) -> OpaqueRendererMethod {
171 OpaqueRendererMethod::Forward
172 }
173
174 #[inline]
175 fn depth_bias(&self) -> f32 {
179 0.0
180 }
181
182 #[inline]
183 fn reads_view_transmission_texture(&self) -> bool {
188 false
189 }
190
191 #[inline]
194 fn enable_prepass() -> bool {
195 true
196 }
197
198 #[inline]
200 fn enable_shadows() -> bool {
201 true
202 }
203
204 #[inline]
208 fn enable_oit() -> bool {
209 true
210 }
211
212 fn prepass_vertex_shader() -> ShaderRef {
218 ShaderRef::Default
219 }
220
221 fn prepass_fragment_shader() -> ShaderRef {
227 ShaderRef::Default
228 }
229
230 fn deferred_vertex_shader() -> ShaderRef {
233 ShaderRef::Default
234 }
235
236 fn deferred_fragment_shader() -> ShaderRef {
239 ShaderRef::Default
240 }
241
242 #[cfg(feature = "meshlet")]
249 fn meshlet_mesh_fragment_shader() -> ShaderRef {
250 ShaderRef::Default
251 }
252
253 #[cfg(feature = "meshlet")]
260 fn meshlet_mesh_prepass_fragment_shader() -> ShaderRef {
261 ShaderRef::Default
262 }
263
264 #[cfg(feature = "meshlet")]
271 fn meshlet_mesh_deferred_fragment_shader() -> ShaderRef {
272 ShaderRef::Default
273 }
274
275 #[expect(
278 unused_variables,
279 reason = "The parameters here are intentionally unused by the default implementation; however, putting underscores here will result in the underscores being copied by rust-analyzer's tab completion."
280 )]
281 #[inline]
282 fn specialize(
283 pipeline: &MaterialPipeline,
284 descriptor: &mut RenderPipelineDescriptor,
285 layout: &MeshVertexBufferLayoutRef,
286 key: MaterialPipelineKey<Self>,
287 ) -> Result<(), SpecializedMeshPipelineError> {
288 Ok(())
289 }
290}
291
292#[derive(Resource)]
294pub struct MaterialShaders<M: Material> {
295 shaders: SmallVec<[(InternedShaderLabel, Handle<Shader>); 6]>,
296 _marker: PhantomData<M>,
297}
298
299impl<M: Material> Default for MaterialShaders<M> {
300 fn default() -> Self {
301 Self {
302 shaders: SmallVec::new(),
303 _marker: PhantomData,
304 }
305 }
306}
307
308impl<M: Material> MaterialShaders<M> {
309 pub fn with_shader_cache(
310 shaders: SmallVec<[(InternedShaderLabel, Handle<Shader>); 6]>,
311 ) -> Self {
312 Self {
313 shaders,
314 _marker: PhantomData,
315 }
316 }
317}
318
319fn initialize_material_shaders<M: Material>(
320 render_world: &World,
321) -> SmallVec<[(InternedShaderLabel, Handle<Shader>); 6]> {
322 let asset_server = render_world.resource::<AssetServer>();
323 let mut shaders = SmallVec::new();
324
325 let mut add_shader = |label: InternedShaderLabel, shader_ref: ShaderRef| {
326 let maybe_shader = match shader_ref {
327 ShaderRef::Default => None,
328 ShaderRef::Handle(handle) => Some(handle),
329 ShaderRef::Path(path) => Some(asset_server.load(path)),
330 };
331 if let Some(shader) = maybe_shader {
332 shaders.push((label, shader));
333 }
334 };
335
336 add_shader(MaterialVertexShader.intern(), M::vertex_shader());
337 add_shader(MaterialFragmentShader.intern(), M::fragment_shader());
338 add_shader(PrepassVertexShader.intern(), M::prepass_vertex_shader());
339 add_shader(PrepassFragmentShader.intern(), M::prepass_fragment_shader());
340 add_shader(DeferredVertexShader.intern(), M::deferred_vertex_shader());
341 add_shader(
342 DeferredFragmentShader.intern(),
343 M::deferred_fragment_shader(),
344 );
345
346 #[cfg(feature = "meshlet")]
347 {
348 add_shader(
349 MeshletFragmentShader.intern(),
350 M::meshlet_mesh_fragment_shader(),
351 );
352 add_shader(
353 MeshletPrepassFragmentShader.intern(),
354 M::meshlet_mesh_prepass_fragment_shader(),
355 );
356 add_shader(
357 MeshletDeferredFragmentShader.intern(),
358 M::meshlet_mesh_deferred_fragment_shader(),
359 );
360 }
361
362 shaders
363}
364
365#[derive(Default)]
366pub struct MaterialsPlugin {
367 pub debug_flags: RenderDebugFlags,
369}
370
371impl Plugin for MaterialsPlugin {
372 fn build(&self, app: &mut App) {
373 app.add_plugins((PrepassPipelinePlugin, PrepassPlugin::new(self.debug_flags)));
374 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
375 render_app
376 .init_gpu_resource::<SpecializedMaterialPipelineCache>()
377 .init_gpu_resource::<SpecializedMeshPipelines<MaterialPipelineSpecializer>>()
378 .init_gpu_resource::<LightKeyCache>()
379 .init_gpu_resource::<SpecializedShadowMaterialPipelineCache>()
380 .init_resource::<DrawFunctions<Shadow>>()
381 .init_resource::<RenderMaterialInstances>()
382 .allow_ambiguous_resource::<RenderMaterialInstances>()
383 .init_gpu_resource::<PendingMeshMaterialQueues>()
384 .allow_ambiguous_resource::<PendingMeshMaterialQueues>()
385 .init_gpu_resource::<PendingShadowQueues>()
386 .allow_ambiguous_resource::<PendingShadowQueues>()
387 .add_render_command::<Shadow, DrawPrepass>()
388 .add_render_command::<Shadow, DrawDepthOnlyPrepass>()
389 .add_render_command::<Transparent3d, DrawMaterial>()
390 .add_render_command::<Opaque3d, DrawMaterial>()
391 .add_render_command::<AlphaMask3d, DrawMaterial>()
392 .add_systems(
393 RenderStartup,
394 init_material_pipeline.after(MeshPipelineSystems),
395 )
396 .add_systems(
397 Render,
398 (
399 specialize_material_meshes
400 .in_set(RenderSystems::Specialize)
401 .after(prepare_assets::<RenderMesh>)
402 .after(collect_meshes_for_gpu_building)
403 .after(set_mesh_motion_vector_flags),
404 queue_material_meshes.in_set(RenderSystems::QueueMeshes),
405 ),
406 )
407 .add_systems(
408 Render,
409 (
410 check_views_lights_need_specialization
411 .in_set(RenderSystems::Specialize)
412 .before(specialize_shadows),
413 specialize_shadows
416 .in_set(RenderSystems::Specialize)
417 .after(prepare_lights),
418 queue_shadows.in_set(RenderSystems::QueueMeshes),
419 ),
420 );
421 }
422 }
423}
424
425pub struct MaterialPlugin<M: Material> {
428 pub debug_flags: RenderDebugFlags,
430 pub _marker: PhantomData<M>,
431}
432
433impl<M: Material> Default for MaterialPlugin<M> {
434 fn default() -> Self {
435 Self {
436 debug_flags: RenderDebugFlags::default(),
437 _marker: Default::default(),
438 }
439 }
440}
441
442impl<M: Material> Plugin for MaterialPlugin<M>
443where
444 M::Data: PartialEq + Eq + Hash + Clone,
445{
446 fn build(&self, app: &mut App) {
447 app.init_asset::<M>()
448 .register_type::<MeshMaterial3d<M>>()
449 .init_resource::<EntitiesNeedingSpecialization<M>>()
450 .add_plugins((ErasedRenderAssetPlugin::<MeshMaterial3d<M>>::default(),))
451 .add_systems(
452 PostUpdate,
453 (
454 mark_meshes_as_changed_if_their_materials_changed::<M>.ambiguous_with_all(),
455 check_entities_needing_specialization::<M>.after(AssetEventSystems),
456 )
457 .after(mark_3d_meshes_as_changed_if_their_assets_changed),
458 );
459
460 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
461 let shaders = initialize_material_shaders::<M>(render_app.world());
462 render_app
463 .insert_resource(MaterialShaders::<M>::with_shader_cache(shaders))
464 .add_systems(RenderStartup, add_material_bind_group_allocator::<M>)
465 .add_systems(
466 ExtractSchedule,
467 (
468 extract_mesh_materials::<M>.in_set(MaterialExtractionSystems),
469 early_sweep_material_instances::<M>
470 .after(MaterialExtractionSystems)
471 .before(late_sweep_material_instances),
472 extract_entities_needs_specialization::<M>
473 .in_set(DirtySpecializationSystems::CheckForChanges),
474 extract_entities_that_need_specializations_removed::<M>
475 .in_set(DirtySpecializationSystems::CheckForRemovals),
476 ),
477 );
478 }
479 }
480}
481
482fn add_material_bind_group_allocator<M: Material>(
483 render_device: Res<RenderDevice>,
484 mut bind_group_allocators: ResMut<MaterialBindGroupAllocators>,
485) {
486 bind_group_allocators.add::<M>(&render_device);
487}
488
489pub struct MaterialPipelineKey<M: Material> {
491 pub mesh_key: MeshPipelineKey,
492 pub bind_group_data: M::Data,
493}
494
495#[derive(Resource, Clone)]
497pub struct MaterialPipeline {
498 pub mesh_pipeline: MeshPipeline,
499}
500
501pub struct MaterialPipelineSpecializer {
502 pub(crate) pipeline: MaterialPipeline,
503 pub(crate) properties: Arc<MaterialProperties>,
504}
505
506impl SpecializedMeshPipeline for MaterialPipelineSpecializer {
507 type Key = ErasedMaterialPipelineKey;
508
509 fn specialize(
510 &self,
511 key: Self::Key,
512 layout: &MeshVertexBufferLayoutRef,
513 ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
514 let concrete_mesh_key: MeshPipelineKey = key.mesh_key.downcast();
515 let mut descriptor = self
516 .pipeline
517 .mesh_pipeline
518 .specialize(concrete_mesh_key, layout)?;
519
520 descriptor.vertex.shader_defs.push(ShaderDefVal::UInt(
521 "MATERIAL_BIND_GROUP".into(),
522 MATERIAL_BIND_GROUP_INDEX as u32,
523 ));
524 if let Some(ref mut fragment) = descriptor.fragment {
525 fragment.shader_defs.push(ShaderDefVal::UInt(
526 "MATERIAL_BIND_GROUP".into(),
527 MATERIAL_BIND_GROUP_INDEX as u32,
528 ));
529 };
530 if let Some(vertex_shader) = self.properties.get_shader(MaterialVertexShader) {
531 descriptor.vertex.shader = vertex_shader.clone();
532 }
533
534 if let Some(fragment_shader) = self.properties.get_shader(MaterialFragmentShader) {
535 descriptor.fragment.as_mut().unwrap().shader = fragment_shader.clone();
536 }
537
538 descriptor
539 .layout
540 .insert(3, self.properties.material_layout.as_ref().unwrap().clone());
541
542 if key
543 .mesh_key
544 .downcast::<MeshPipelineKey>()
545 .contains(MeshPipelineKey::OIT_ENABLED)
546 && self.properties.oit_enabled
547 && matches!(
548 self.properties.alpha_mode,
549 AlphaMode::Blend | AlphaMode::Premultiplied | AlphaMode::Add
550 )
551 {
552 descriptor.label = Some("oit_mesh_pipeline".into());
553 let frag = descriptor.fragment.as_mut().unwrap();
554 frag.targets[0].as_mut().unwrap().blend = None;
556 frag.shader_defs.push("MATERIAL_OIT_ENABLED".into());
557 descriptor
560 .depth_stencil
561 .as_mut()
562 .unwrap()
563 .depth_write_enabled = Some(false);
564 }
565
566 if self.properties.bindless {
568 descriptor.vertex.shader_defs.push("BINDLESS".into());
569 if let Some(ref mut fragment) = descriptor.fragment {
570 fragment.shader_defs.push("BINDLESS".into());
571 }
572 }
573
574 if let Some(specialize) = self.properties.user_specialize {
575 specialize(&self.pipeline as &dyn Any, &mut descriptor, layout, key)?;
576 }
577
578 Ok(descriptor)
579 }
580}
581
582pub fn init_material_pipeline(mut commands: Commands, mesh_pipeline: Res<MeshPipeline>) {
583 commands.insert_resource(MaterialPipeline {
584 mesh_pipeline: mesh_pipeline.clone(),
585 });
586}
587
588pub type DrawMaterial = (
589 SetItemPipeline,
590 SetMeshViewBindGroup<0>,
591 SetMeshViewBindingArrayBindGroup<1>,
592 SetMeshBindGroup<2>,
593 SetMaterialBindGroup<MATERIAL_BIND_GROUP_INDEX>,
594 DrawMesh,
595);
596
597pub struct SetMaterialBindGroup<const I: usize>;
599impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMaterialBindGroup<I> {
600 type Param = (
601 SRes<ErasedRenderAssets<PreparedMaterial>>,
602 SRes<RenderMaterialInstances>,
603 SRes<MaterialBindGroupAllocators>,
604 );
605 type ViewQuery = ();
606 type ItemQuery = ();
607
608 #[inline]
609 fn render<'w>(
610 item: &P,
611 _view: (),
612 _item_query: Option<()>,
613 (materials, material_instances, material_bind_group_allocator): SystemParamItem<
614 'w,
615 '_,
616 Self::Param,
617 >,
618 pass: &mut TrackedRenderPass<'w>,
619 ) -> RenderCommandResult {
620 let materials = materials.into_inner();
621 let material_instances = material_instances.into_inner();
622 let material_bind_group_allocators = material_bind_group_allocator.into_inner();
623
624 let Some(material_instance) = material_instances.instances.get(&item.main_entity()) else {
625 return RenderCommandResult::Skip;
626 };
627 let Some(material_bind_group_allocator) =
628 material_bind_group_allocators.get(&material_instance.asset_id.type_id())
629 else {
630 return RenderCommandResult::Skip;
631 };
632 let Some(material) = materials.get(material_instance.asset_id) else {
633 return RenderCommandResult::Skip;
634 };
635 let Some(material_bind_group) = material_bind_group_allocator.get(material.binding.group)
636 else {
637 return RenderCommandResult::Skip;
638 };
639 let Some(bind_group) = material_bind_group.bind_group() else {
640 return RenderCommandResult::Skip;
641 };
642 pass.set_bind_group(I, bind_group, &[]);
643 RenderCommandResult::Success
644 }
645}
646
647#[derive(Resource, Default)]
649pub struct RenderMaterialInstances {
650 pub instances: MainEntityHashMap<RenderMaterialInstance>,
653 pub current_change_tick: Tick,
657}
658
659impl RenderMaterialInstances {
660 pub(crate) fn mesh_material(&self, entity: MainEntity) -> Option<UntypedAssetId> {
667 self.instances
668 .get(&entity)
669 .map(|instance| instance.asset_id)
670 }
671}
672
673pub struct RenderMaterialInstance {
678 pub asset_id: UntypedAssetId,
680 pub last_change_tick: Tick,
683}
684
685#[derive(SystemSet, Clone, PartialEq, Eq, Debug, Hash)]
687pub struct MaterialExtractionSystems;
688
689pub const fn alpha_mode_pipeline_key(alpha_mode: AlphaMode, msaa: &Msaa) -> MeshPipelineKey {
690 match alpha_mode {
691 AlphaMode::Premultiplied | AlphaMode::Add => MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA,
694 AlphaMode::Blend => MeshPipelineKey::BLEND_ALPHA,
695 AlphaMode::Multiply => MeshPipelineKey::BLEND_MULTIPLY,
696 AlphaMode::Mask(_) => MeshPipelineKey::MAY_DISCARD,
697 AlphaMode::AlphaToCoverage => match *msaa {
698 Msaa::Off => MeshPipelineKey::MAY_DISCARD,
699 _ => MeshPipelineKey::BLEND_ALPHA_TO_COVERAGE,
700 },
701 _ => MeshPipelineKey::NONE,
702 }
703}
704
705pub const fn tonemapping_pipeline_key(tonemapping: Tonemapping) -> MeshPipelineKey {
706 match tonemapping {
707 Tonemapping::None | Tonemapping::Linear => MeshPipelineKey::TONEMAP_METHOD_LINEAR,
708 Tonemapping::Reinhard => MeshPipelineKey::TONEMAP_METHOD_REINHARD,
709 Tonemapping::ReinhardLuminance => MeshPipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE,
710 Tonemapping::AcesFitted => MeshPipelineKey::TONEMAP_METHOD_ACES_FITTED,
711 Tonemapping::AgX => MeshPipelineKey::TONEMAP_METHOD_AGX,
712 Tonemapping::SomewhatBoringDisplayTransform => {
713 MeshPipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM
714 }
715 Tonemapping::TonyMcMapface => MeshPipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE,
716 Tonemapping::BlenderFilmic => MeshPipelineKey::TONEMAP_METHOD_BLENDER_FILMIC,
717 Tonemapping::KhronosPbrNeutral => MeshPipelineKey::TONEMAP_METHOD_PBR_NEUTRAL,
718 }
719}
720
721fn mark_meshes_as_changed_if_their_materials_changed<M>(
736 mut changed_meshes_query: Query<
737 &mut Mesh3d,
738 Or<(Changed<MeshMaterial3d<M>>, AssetChanged<MeshMaterial3d<M>>)>,
739 >,
740) where
741 M: Material,
742{
743 changed_meshes_query.par_iter_mut().for_each(|mut mesh| {
744 mesh.set_changed();
745 });
746}
747
748fn extract_mesh_materials<M: Material>(
751 mut material_instances: ResMut<RenderMaterialInstances>,
752 changed_meshes_query: Extract<
753 Query<
754 (Entity, &ViewVisibility, &MeshMaterial3d<M>),
755 Or<(Changed<ViewVisibility>, Changed<MeshMaterial3d<M>>)>,
756 >,
757 >,
758) {
759 let last_change_tick = material_instances.current_change_tick;
760
761 for (entity, view_visibility, material) in &changed_meshes_query {
762 if view_visibility.get() {
763 material_instances.instances.insert(
764 entity.into(),
765 RenderMaterialInstance {
766 asset_id: material.id().untyped(),
767 last_change_tick,
768 },
769 );
770 } else {
771 material_instances
772 .instances
773 .remove(&MainEntity::from(entity));
774 }
775 }
776}
777
778fn early_sweep_material_instances<M>(
793 mut material_instances: ResMut<RenderMaterialInstances>,
794 mut removed_materials_query: Extract<RemovedComponents<MeshMaterial3d<M>>>,
795) where
796 M: Material,
797{
798 let last_change_tick = material_instances.current_change_tick;
799
800 for entity in removed_materials_query.read() {
801 if let Entry::Occupied(occupied_entry) = material_instances.instances.entry(entity.into()) {
802 if occupied_entry.get().last_change_tick != last_change_tick {
804 occupied_entry.remove();
805 }
806 }
807 }
808}
809
810pub fn late_sweep_material_instances(
817 mut material_instances: ResMut<RenderMaterialInstances>,
818 mut removed_meshes_query: Extract<RemovedComponents<Mesh3d>>,
819) {
820 let last_change_tick = material_instances.current_change_tick;
821
822 for entity in removed_meshes_query.read() {
823 if let Entry::Occupied(occupied_entry) = material_instances.instances.entry(entity.into()) {
824 if occupied_entry.get().last_change_tick != last_change_tick {
828 occupied_entry.remove();
829 }
830 }
831 }
832
833 material_instances
834 .current_change_tick
835 .set(last_change_tick.get() + 1);
836}
837
838pub fn extract_entities_needs_specialization<M>(
839 entities_needing_specialization: Extract<Res<EntitiesNeedingSpecialization<M>>>,
840 mut dirty_specializations: ResMut<DirtySpecializations>,
841) where
842 M: Material,
843{
844 for entity in entities_needing_specialization.changed.iter() {
847 dirty_specializations
848 .changed_renderables
849 .insert(MainEntity::from(*entity));
850 }
851}
852
853pub fn extract_entities_that_need_specializations_removed<M>(
856 entities_needing_specialization: Extract<Res<EntitiesNeedingSpecialization<M>>>,
857 mut dirty_specializations: ResMut<DirtySpecializations>,
858) where
859 M: Material,
860{
861 for entity in entities_needing_specialization.removed.iter() {
862 dirty_specializations
863 .removed_renderables
864 .insert(MainEntity::from(*entity));
865 }
866}
867
868#[derive(Resource, Clone, Debug)]
872pub struct EntitiesNeedingSpecialization<M> {
873 pub changed: Vec<Entity>,
875 pub removed: Vec<Entity>,
880 _marker: PhantomData<M>,
881}
882
883impl<M> Default for EntitiesNeedingSpecialization<M> {
884 fn default() -> Self {
885 Self {
886 changed: Default::default(),
887 removed: Default::default(),
888 _marker: Default::default(),
889 }
890 }
891}
892
893#[derive(Resource, Deref, DerefMut, Default)]
895pub struct SpecializedMaterialPipelineCache {
896 #[deref]
898 map: HashMap<RetainedViewEntity, SpecializedMaterialViewPipelineCache>,
899}
900
901#[derive(Deref, DerefMut, Default)]
904pub struct SpecializedMaterialViewPipelineCache {
905 #[deref]
907 map: MainEntityHashMap<CachedRenderPipelineId>,
908}
909
910pub fn check_entities_needing_specialization<M>(
913 needs_specialization: Query<
914 Entity,
915 (
916 Or<(
917 Changed<Mesh3d>,
918 AssetChanged<Mesh3d>,
919 Changed<MeshMaterial3d<M>>,
920 AssetChanged<MeshMaterial3d<M>>,
921 )>,
922 With<MeshMaterial3d<M>>,
923 ),
924 >,
925 mut par_local: Local<Parallel<Vec<Entity>>>,
926 mut entities_needing_specialization: ResMut<EntitiesNeedingSpecialization<M>>,
927 mut removed_mesh_3d_components: RemovedComponents<Mesh3d>,
928 mut removed_mesh_material_3d_components: RemovedComponents<MeshMaterial3d<M>>,
929) where
930 M: Material,
931{
932 entities_needing_specialization.changed.clear();
933 entities_needing_specialization.removed.clear();
934
935 needs_specialization
937 .par_iter()
938 .for_each(|entity| par_local.borrow_local_mut().push(entity));
939 par_local.drain_into(&mut entities_needing_specialization.changed);
940
941 for entity in removed_mesh_3d_components
950 .read()
951 .chain(removed_mesh_material_3d_components.read())
952 {
953 entities_needing_specialization.removed.push(entity);
954 }
955}
956
957pub(crate) struct SpecializationWorkItem {
958 visible_entity: MainEntity,
959 retained_view_entity: RetainedViewEntity,
960 mesh_key: MeshPipelineKey,
961 layout: MeshVertexBufferLayoutRef,
962 properties: Arc<MaterialProperties>,
963 material_type_id: TypeId,
964}
965
966#[derive(Default, Deref, DerefMut, Resource)]
971pub struct PendingMeshMaterialQueues(pub PendingQueues);
972
973#[derive(SystemParam)]
974pub(crate) struct SpecializeMaterialMeshesSystemParam<'w, 's> {
975 render_meshes: Res<'w, RenderAssets<RenderMesh>>,
976 render_materials: Res<'w, ErasedRenderAssets<PreparedMaterial>>,
977 render_mesh_instances: Res<'w, RenderMeshInstances>,
978 render_material_instances: Res<'w, RenderMaterialInstances>,
979 render_lightmaps: Res<'w, RenderLightmaps>,
980 render_visibility_ranges: Res<'w, RenderVisibilityRanges>,
981 opaque_render_phases: Res<'w, ViewBinnedRenderPhases<Opaque3d>>,
982 alpha_mask_render_phases: Res<'w, ViewBinnedRenderPhases<AlphaMask3d>>,
983 transmissive_render_phases: Res<'w, ViewSortedRenderPhases<Transmissive3d>>,
984 transparent_render_phases: Res<'w, ViewSortedRenderPhases<Transparent3d>>,
985 views: Query<'w, 's, (&'static ExtractedView, &'static RenderVisibleEntities)>,
986 view_key_cache: Res<'w, ViewKeyCache>,
987 specialized_material_pipeline_cache: ResMut<'w, SpecializedMaterialPipelineCache>,
988 pending_mesh_material_queues: ResMut<'w, PendingMeshMaterialQueues>,
989 dirty_specializations: Res<'w, DirtySpecializations>,
990}
991
992pub(crate) fn specialize_material_meshes(
993 world: &mut World,
994 state: &mut SystemState<SpecializeMaterialMeshesSystemParam>,
995 mut work_items: Local<Vec<SpecializationWorkItem>>,
996 mut all_views: Local<HashSet<RetainedViewEntity, FixedHasher>>,
997) {
998 work_items.clear();
999 all_views.clear();
1000
1001 {
1002 let SpecializeMaterialMeshesSystemParam {
1003 render_meshes,
1004 render_materials,
1005 render_mesh_instances,
1006 render_material_instances,
1007 render_lightmaps,
1008 render_visibility_ranges,
1009 opaque_render_phases,
1010 alpha_mask_render_phases,
1011 transmissive_render_phases,
1012 transparent_render_phases,
1013 views,
1014 view_key_cache,
1015 mut specialized_material_pipeline_cache,
1016 mut pending_mesh_material_queues,
1017 dirty_specializations,
1018 } = state.get_mut(world).unwrap();
1019
1020 for (view, visible_entities) in &views {
1021 all_views.insert(view.retained_view_entity);
1022
1023 if !transparent_render_phases.contains_key(&view.retained_view_entity)
1024 && !opaque_render_phases.contains_key(&view.retained_view_entity)
1025 && !alpha_mask_render_phases.contains_key(&view.retained_view_entity)
1026 && !transmissive_render_phases.contains_key(&view.retained_view_entity)
1027 {
1028 continue;
1029 }
1030
1031 let Some(view_key) = view_key_cache.get(&view.retained_view_entity) else {
1032 continue;
1033 };
1034
1035 let Some(render_visible_mesh_entities) = visible_entities.get::<Mesh3d>() else {
1036 continue;
1037 };
1038
1039 let mut maybe_specialized_material_pipeline_cache =
1040 specialized_material_pipeline_cache.get_mut(&view.retained_view_entity);
1041
1042 if let Some(ref mut specialized_material_pipeline_cache) =
1045 maybe_specialized_material_pipeline_cache
1046 {
1047 if dirty_specializations
1048 .must_wipe_specializations_for_view(view.retained_view_entity)
1049 {
1050 specialized_material_pipeline_cache.clear();
1051 } else {
1052 for &renderable_entity in dirty_specializations.iter_to_despecialize() {
1053 specialized_material_pipeline_cache.remove(&renderable_entity);
1054 }
1055 }
1056 }
1057
1058 let view_pending_mesh_material_queues =
1060 pending_mesh_material_queues.prepare_for_new_frame(view.retained_view_entity);
1061
1062 for (render_entity, visible_entity) in dirty_specializations.iter_to_specialize(
1064 view.retained_view_entity,
1065 render_visible_mesh_entities,
1066 &view_pending_mesh_material_queues.prev_frame,
1067 ) {
1068 if maybe_specialized_material_pipeline_cache
1069 .as_ref()
1070 .is_some_and(|specialized_material_pipeline_cache| {
1071 specialized_material_pipeline_cache.contains_key(visible_entity)
1072 })
1073 {
1074 continue;
1075 }
1076
1077 let Some(material_instance) =
1082 render_material_instances.instances.get(visible_entity)
1083 else {
1084 view_pending_mesh_material_queues
1085 .current_frame
1086 .insert((*render_entity, *visible_entity));
1087 continue;
1088 };
1089 let Some(mesh_instance) =
1090 render_mesh_instances.render_mesh_queue_data(*visible_entity)
1091 else {
1092 view_pending_mesh_material_queues
1093 .current_frame
1094 .insert((*render_entity, *visible_entity));
1095 continue;
1096 };
1097 let Some(mesh) = render_meshes.get(mesh_instance.mesh_asset_id()) else {
1098 continue;
1099 };
1100 let Some(material) = render_materials.get(material_instance.asset_id) else {
1101 view_pending_mesh_material_queues
1102 .current_frame
1103 .insert((*render_entity, *visible_entity));
1104 continue;
1105 };
1106
1107 let mut mesh_pipeline_key_bits: MeshPipelineKey =
1108 material.properties.mesh_pipeline_key_bits.downcast();
1109 mesh_pipeline_key_bits.insert(alpha_mode_pipeline_key(
1110 material.properties.alpha_mode,
1111 &Msaa::from_samples(view_key.msaa_samples()),
1112 ));
1113 let mut mesh_key = *view_key
1114 | MeshPipelineKey::from_bits_retain(mesh.key_bits.bits())
1115 | mesh_pipeline_key_bits;
1116
1117 if let Some(lightmap) = render_lightmaps.render_lightmaps.get(visible_entity) {
1118 mesh_key |= MeshPipelineKey::LIGHTMAPPED;
1119
1120 if lightmap.bicubic_sampling {
1121 mesh_key |= MeshPipelineKey::LIGHTMAP_BICUBIC_SAMPLING;
1122 }
1123 }
1124
1125 if render_visibility_ranges
1126 .entity_has_crossfading_visibility_ranges(*visible_entity)
1127 {
1128 mesh_key |= MeshPipelineKey::VISIBILITY_RANGE_DITHER;
1129 }
1130
1131 if view_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
1132 if mesh_instance
1133 .flags()
1134 .contains(RenderMeshInstanceFlags::HAS_PREVIOUS_SKIN)
1135 {
1136 mesh_key |= MeshPipelineKey::HAS_PREVIOUS_SKIN;
1137 }
1138 if mesh_instance
1139 .flags()
1140 .contains(RenderMeshInstanceFlags::HAS_PREVIOUS_MORPH)
1141 {
1142 mesh_key |= MeshPipelineKey::HAS_PREVIOUS_MORPH;
1143 }
1144 }
1145
1146 work_items.push(SpecializationWorkItem {
1147 visible_entity: *visible_entity,
1148 retained_view_entity: view.retained_view_entity,
1149 mesh_key,
1150 layout: mesh.layout.clone(),
1151 properties: material.properties.clone(),
1152 material_type_id: material_instance.asset_id.type_id(),
1153 });
1154 }
1155 }
1156
1157 pending_mesh_material_queues.expire_stale_views(&all_views);
1158 }
1159
1160 for item in work_items.drain(..) {
1161 let key = ErasedMaterialPipelineKey {
1162 type_id: item.material_type_id,
1163 mesh_key: ErasedMeshPipelineKey::new(item.mesh_key),
1164 material_key: item.properties.material_key.clone(),
1165 };
1166
1167 let Some(base_specialize) = item.properties.base_specialize else {
1168 continue;
1169 };
1170 match base_specialize(world, key, &item.layout, &item.properties) {
1171 Ok(pipeline_id) => {
1172 world
1173 .resource_mut::<SpecializedMaterialPipelineCache>()
1174 .entry(item.retained_view_entity)
1175 .or_default()
1176 .insert(item.visible_entity, pipeline_id);
1177 }
1178 Err(err) => error!("{}", err),
1179 }
1180 }
1181
1182 world
1183 .resource_mut::<SpecializedMaterialPipelineCache>()
1184 .retain(|view, _| all_views.contains(view));
1185}
1186
1187pub fn queue_material_meshes(
1190 render_materials: Res<ErasedRenderAssets<PreparedMaterial>>,
1191 render_mesh_instances: Res<RenderMeshInstances>,
1192 render_material_instances: Res<RenderMaterialInstances>,
1193 mesh_assets: Res<RenderAssets<RenderMesh>>,
1194 mesh_allocator: Res<MeshAllocator>,
1195 gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
1196 maybe_batched_instance_buffers: Option<
1197 Res<BatchedInstanceBuffers<MeshUniform, MeshInputUniform>>,
1198 >,
1199 mut opaque_render_phases: ResMut<ViewBinnedRenderPhases<Opaque3d>>,
1200 mut alpha_mask_render_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3d>>,
1201 mut transmissive_render_phases: ResMut<ViewSortedRenderPhases<Transmissive3d>>,
1202 mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
1203 mut pending_mesh_material_queues: ResMut<PendingMeshMaterialQueues>,
1204 views: Query<(
1205 &ExtractedView,
1206 &RenderVisibleEntities,
1207 Has<ScreenSpaceTransmission>,
1208 )>,
1209 specialized_material_pipeline_cache: ResMut<SpecializedMaterialPipelineCache>,
1210 dirty_specializations: Res<DirtySpecializations>,
1211 mut mesh_instances_queued_this_iteration_scratch_space: Local<MainEntityHashSet>,
1212) {
1213 for (view, visible_entities, has_transmission) in &views {
1214 let (
1215 Some(opaque_phase),
1216 Some(alpha_mask_phase),
1217 Some(transmissive_phase),
1218 Some(transparent_phase),
1219 ) = (
1220 opaque_render_phases.get_mut(&view.retained_view_entity),
1221 alpha_mask_render_phases.get_mut(&view.retained_view_entity),
1222 transmissive_render_phases.get_mut(&view.retained_view_entity),
1223 transparent_render_phases.get_mut(&view.retained_view_entity),
1224 )
1225 else {
1226 continue;
1227 };
1228
1229 let Some(view_specialized_material_pipeline_cache) =
1230 specialized_material_pipeline_cache.get(&view.retained_view_entity)
1231 else {
1232 continue;
1233 };
1234
1235 let Some(render_visible_mesh_entities) = visible_entities.get::<Mesh3d>() else {
1236 continue;
1237 };
1238
1239 for &main_entity in dirty_specializations
1241 .iter_to_dequeue(view.retained_view_entity, render_visible_mesh_entities)
1242 {
1243 opaque_phase.remove(main_entity);
1244 alpha_mask_phase.remove(main_entity);
1245 transmissive_phase.remove(Entity::PLACEHOLDER, main_entity);
1246 transparent_phase.remove(Entity::PLACEHOLDER, main_entity);
1247 }
1248
1249 let view_pending_mesh_material_queues = pending_mesh_material_queues
1251 .get_mut(&view.retained_view_entity)
1252 .expect(
1253 "View pending mesh material queues should have been created in \
1254 `specialize_material_meshes`",
1255 );
1256
1257 for (render_entity, visible_entity) in dirty_specializations.iter_to_queue(
1259 view.retained_view_entity,
1260 render_visible_mesh_entities,
1261 &view_pending_mesh_material_queues.prev_frame,
1262 &mut mesh_instances_queued_this_iteration_scratch_space,
1263 ) {
1264 let Some(pipeline_id) = view_specialized_material_pipeline_cache
1265 .get(visible_entity)
1266 .copied()
1267 else {
1268 continue;
1269 };
1270
1271 let Some(material_instance) = render_material_instances.instances.get(visible_entity)
1276 else {
1277 view_pending_mesh_material_queues
1278 .current_frame
1279 .insert((*render_entity, *visible_entity));
1280 continue;
1281 };
1282 let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(*visible_entity)
1283 else {
1284 view_pending_mesh_material_queues
1285 .current_frame
1286 .insert((*render_entity, *visible_entity));
1287 continue;
1288 };
1289 let Some(material) = render_materials.get(material_instance.asset_id) else {
1290 view_pending_mesh_material_queues
1291 .current_frame
1292 .insert((*render_entity, *visible_entity));
1293 continue;
1294 };
1295
1296 let Some(mesh_slabs) = mesh_allocator.mesh_slabs(&mesh_instance.mesh_asset_id()) else {
1298 continue;
1299 };
1300
1301 let render_phase_type = if !has_transmission {
1302 match material.properties.alpha_mode {
1304 AlphaMode::Blend
1305 | AlphaMode::Premultiplied
1306 | AlphaMode::Add
1307 | AlphaMode::Multiply => RenderPhaseType::Transparent,
1308 AlphaMode::Opaque | AlphaMode::AlphaToCoverage => RenderPhaseType::Opaque,
1309 AlphaMode::Mask(_) => RenderPhaseType::AlphaMask,
1310 }
1311 } else {
1312 material.properties.render_phase_type
1313 };
1314
1315 match render_phase_type {
1316 RenderPhaseType::Transmissive => {
1317 let Some(draw_function) = material
1318 .properties
1319 .get_draw_function(MainPassTransmissiveDrawFunction)
1320 else {
1321 continue;
1322 };
1323 transmissive_phase.add_retained(Transmissive3d {
1324 sorting_info: TransparentSortingInfo3d::Sorted {
1325 mesh_center: get_mesh_instance_world_from_local(
1326 *visible_entity,
1327 mesh_instance.current_uniform_index,
1328 &render_mesh_instances,
1329 maybe_batched_instance_buffers.as_deref(),
1330 )
1331 .transform_point3(
1332 mesh_assets
1333 .get(mesh_instance.mesh_asset_id())
1334 .unwrap()
1335 .aabb_center,
1336 ),
1337 depth_bias: material.properties.depth_bias,
1338 },
1339 entity: (Entity::PLACEHOLDER, *visible_entity),
1340 draw_function,
1341 pipeline: pipeline_id,
1342 batch_range: 0..1,
1343 extra_index: PhaseItemExtraIndex::None,
1344 indexed: mesh_slabs.index_slab_id.is_some(),
1345 distance: 0.0,
1347 });
1348 }
1349 RenderPhaseType::Opaque => {
1350 if material.properties.render_method == OpaqueRendererMethod::Deferred {
1351 opaque_phase.update_cache(*visible_entity, None);
1356 continue;
1357 }
1358 let Some(draw_function) = material
1359 .properties
1360 .get_draw_function(MainPassOpaqueDrawFunction)
1361 else {
1362 continue;
1363 };
1364 let batch_set_key = Opaque3dBatchSetKey {
1365 pipeline: pipeline_id,
1366 draw_function,
1367 material_bind_group_index: Some(material.binding.group.0),
1368 slabs: mesh_slabs,
1369 lightmap_slab: mesh_instance
1370 .shared
1371 .lightmap_slab_index()
1372 .map(|index| *index),
1373 };
1374 let bin_key = Opaque3dBinKey {
1375 asset_id: mesh_instance.mesh_asset_id().into(),
1376 };
1377 opaque_phase.add(
1378 batch_set_key,
1379 bin_key,
1380 (Entity::PLACEHOLDER, *visible_entity),
1381 mesh_instance.current_uniform_index,
1382 BinnedRenderPhaseType::mesh(
1383 mesh_instance.should_batch(),
1384 &gpu_preprocessing_support,
1385 ),
1386 );
1387 }
1388 RenderPhaseType::AlphaMask => {
1390 if material.properties.render_method == OpaqueRendererMethod::Deferred {
1391 alpha_mask_phase.update_cache(*visible_entity, None);
1396 continue;
1397 }
1398 let Some(draw_function) = material
1399 .properties
1400 .get_draw_function(MainPassAlphaMaskDrawFunction)
1401 else {
1402 continue;
1403 };
1404 let batch_set_key = OpaqueNoLightmap3dBatchSetKey {
1405 draw_function,
1406 pipeline: pipeline_id,
1407 material_bind_group_index: Some(material.binding.group.0),
1408 slabs: mesh_slabs,
1409 };
1410 let bin_key = OpaqueNoLightmap3dBinKey {
1411 asset_id: mesh_instance.mesh_asset_id().into(),
1412 };
1413 alpha_mask_phase.add(
1414 batch_set_key,
1415 bin_key,
1416 (Entity::PLACEHOLDER, *visible_entity),
1417 mesh_instance.current_uniform_index,
1418 BinnedRenderPhaseType::mesh(
1419 mesh_instance.should_batch(),
1420 &gpu_preprocessing_support,
1421 ),
1422 );
1423 }
1424 RenderPhaseType::Transparent => {
1425 let Some(draw_function) = material
1426 .properties
1427 .get_draw_function(MainPassTransparentDrawFunction)
1428 else {
1429 continue;
1430 };
1431 transparent_phase.add_retained(Transparent3d {
1432 sorting_info: TransparentSortingInfo3d::Sorted {
1433 mesh_center: get_mesh_instance_world_from_local(
1434 *visible_entity,
1435 mesh_instance.current_uniform_index,
1436 &render_mesh_instances,
1437 maybe_batched_instance_buffers.as_deref(),
1438 )
1439 .transform_point3(
1440 mesh_assets
1441 .get(mesh_instance.mesh_asset_id())
1442 .unwrap()
1443 .aabb_center,
1444 ),
1445 depth_bias: material.properties.depth_bias,
1446 },
1447 entity: (Entity::PLACEHOLDER, *visible_entity),
1448 draw_function,
1449 pipeline: pipeline_id,
1450 batch_range: 0..1,
1451 extra_index: PhaseItemExtraIndex::None,
1452 indexed: mesh_slabs.index_slab_id.is_some(),
1453 distance: 0.0,
1455 });
1456 }
1457 }
1458 }
1459 }
1460}
1461
1462#[derive(Default, Resource, Clone, Debug, ExtractResource, Reflect)]
1464#[reflect(Resource, Default, Debug, Clone)]
1465#[extract_app(RenderApp)]
1466pub struct DefaultOpaqueRendererMethod(OpaqueRendererMethod);
1467
1468impl DefaultOpaqueRendererMethod {
1469 pub fn forward() -> Self {
1470 DefaultOpaqueRendererMethod(OpaqueRendererMethod::Forward)
1471 }
1472
1473 pub fn deferred() -> Self {
1474 DefaultOpaqueRendererMethod(OpaqueRendererMethod::Deferred)
1475 }
1476
1477 pub fn set_to_forward(&mut self) {
1478 self.0 = OpaqueRendererMethod::Forward;
1479 }
1480
1481 pub fn set_to_deferred(&mut self) {
1482 self.0 = OpaqueRendererMethod::Deferred;
1483 }
1484}
1485
1486#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1487pub struct MaterialVertexShader;
1488
1489#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1490pub struct MaterialFragmentShader;
1491
1492#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1493pub struct PrepassVertexShader;
1494
1495#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1496pub struct PrepassFragmentShader;
1497
1498#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1499pub struct DeferredVertexShader;
1500
1501#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1502pub struct DeferredFragmentShader;
1503
1504#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1505pub struct MeshletFragmentShader;
1506
1507#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1508pub struct MeshletPrepassFragmentShader;
1509
1510#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1511pub struct MeshletDeferredFragmentShader;
1512
1513#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1514pub struct MainPassOpaqueDrawFunction;
1515#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1516pub struct MainPassAlphaMaskDrawFunction;
1517#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1518pub struct MainPassTransmissiveDrawFunction;
1519#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1520pub struct MainPassTransparentDrawFunction;
1521
1522#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1523pub struct PrepassOpaqueDrawFunction;
1524#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1525pub struct PrepassAlphaMaskDrawFunction;
1526#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1527pub struct PrepassOpaqueDepthOnlyDrawFunction;
1528
1529#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1530pub struct DeferredOpaqueDrawFunction;
1531#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1532pub struct DeferredAlphaMaskDrawFunction;
1533
1534#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1535pub struct ShadowsDrawFunction;
1536#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1537pub struct ShadowsDepthOnlyDrawFunction;
1538
1539pub struct PreparedMaterial {
1541 pub binding: MaterialBindingId,
1542 pub properties: Arc<MaterialProperties>,
1543}
1544
1545pub fn base_specialize(
1546 world: &mut World,
1547 key: ErasedMaterialPipelineKey,
1548 layout: &MeshVertexBufferLayoutRef,
1549 properties: &Arc<MaterialProperties>,
1550) -> Result<CachedRenderPipelineId, SpecializedMeshPipelineError> {
1551 world.resource_scope(
1552 |world, mut pipelines: Mut<SpecializedMeshPipelines<MaterialPipelineSpecializer>>| {
1553 let mesh_pipeline = world.resource::<MeshPipeline>().clone();
1554 let pipeline_cache = world.resource::<PipelineCache>();
1555
1556 let specializer = MaterialPipelineSpecializer {
1557 pipeline: MaterialPipeline { mesh_pipeline },
1558 properties: properties.clone(),
1559 };
1560
1561 pipelines.specialize(pipeline_cache, &specializer, key, layout)
1562 },
1563 )
1564}
1565fn prepass_specialize(
1566 world: &mut World,
1567 key: &ErasedMaterialPipelineKey,
1568 layout: &MeshVertexBufferLayoutRef,
1569 properties: &Arc<MaterialProperties>,
1570) -> Result<CachedRenderPipelineId, SpecializedMeshPipelineError> {
1571 if let Some(pipelines) =
1572 world.get_resource::<SpecializedMeshPipelines<PrepassPipelineSpecializer>>()
1573 && let Some(id) = pipelines.get_pipeline(key, layout)
1574 {
1575 return Ok(id);
1576 }
1577
1578 world.resource_scope(
1579 |world, mut pipelines: Mut<SpecializedMeshPipelines<PrepassPipelineSpecializer>>| {
1580 let prepass_pipeline = world.resource::<PrepassPipeline>().clone();
1581 let pipeline_cache = world.resource::<PipelineCache>();
1582
1583 let specializer = PrepassPipelineSpecializer {
1584 pipeline: prepass_pipeline,
1585 properties: properties.clone(),
1586 };
1587
1588 pipelines.specialize(pipeline_cache, &specializer, key.clone(), layout)
1589 },
1590 )
1591}
1592
1593fn user_specialize<M: Material>(
1594 pipeline: &dyn Any,
1595 descriptor: &mut RenderPipelineDescriptor,
1596 mesh_layout: &MeshVertexBufferLayoutRef,
1597 erased_key: ErasedMaterialPipelineKey,
1598) -> Result<(), SpecializedMeshPipelineError>
1599where
1600 M::Data: Hash + Clone,
1601{
1602 let pipeline = pipeline.downcast_ref::<MaterialPipeline>().unwrap();
1603 let material_key = erased_key.material_key.to_key();
1604 let mesh_key: MeshPipelineKey = erased_key.mesh_key.downcast();
1605 M::specialize(
1606 pipeline,
1607 descriptor,
1608 mesh_layout,
1609 MaterialPipelineKey {
1610 mesh_key,
1611 bind_group_data: material_key,
1612 },
1613 )
1614}
1615
1616impl<M: Material> ErasedRenderAsset for MeshMaterial3d<M>
1618where
1619 M::Data: PartialEq + Eq + Hash + Clone,
1620{
1621 type SourceAsset = M;
1622 type ErasedAsset = PreparedMaterial;
1623
1624 type Param = (
1625 SRes<RenderDevice>,
1626 SRes<PipelineCache>,
1627 SRes<DefaultOpaqueRendererMethod>,
1628 SRes<FallbackBuffer>,
1629 SRes<RenderAssets<GpuShaderBuffer>>,
1630 SResMut<MaterialBindGroupAllocators>,
1631 SResMut<RenderMaterialBindings>,
1632 (
1633 SRes<DrawFunctions<Opaque3d>>,
1634 SRes<DrawFunctions<AlphaMask3d>>,
1635 SRes<DrawFunctions<Transmissive3d>>,
1636 SRes<DrawFunctions<Transparent3d>>,
1637 SRes<DrawFunctions<Opaque3dPrepass>>,
1638 SRes<DrawFunctions<AlphaMask3dPrepass>>,
1639 SRes<DrawFunctions<Opaque3dDeferred>>,
1640 SRes<DrawFunctions<AlphaMask3dDeferred>>,
1641 SRes<DrawFunctions<Shadow>>,
1642 ),
1643 SRes<MaterialShaders<M>>,
1644 M::Param,
1645 );
1646
1647 fn prepare_asset(
1648 material: Self::SourceAsset,
1649 material_id: AssetId<Self::SourceAsset>,
1650 (
1651 render_device,
1652 pipeline_cache,
1653 default_opaque_render_method,
1654 fallback_buffer,
1655 shader_buffer_assets,
1656 bind_group_allocators,
1657 render_material_bindings,
1658 (
1659 opaque_draw_functions,
1660 alpha_mask_draw_functions,
1661 transmissive_draw_functions,
1662 transparent_draw_functions,
1663 opaque_prepass_draw_functions,
1664 alpha_mask_prepass_draw_functions,
1665 opaque_deferred_draw_functions,
1666 alpha_mask_deferred_draw_functions,
1667 shadow_draw_functions,
1668 ),
1669 material_shaders,
1670 material_param,
1671 ): &mut SystemParamItem<Self::Param>,
1672 ) -> Result<Self::ErasedAsset, PrepareAssetError<Self::SourceAsset>> {
1673 let material_layout = M::bind_group_layout_descriptor(render_device);
1674
1675 let binding = render_material_bindings.prepare_material(
1676 &material,
1677 material_id,
1678 material_param,
1679 &material_layout,
1680 bind_group_allocators,
1681 render_device,
1682 pipeline_cache,
1683 fallback_buffer,
1684 shader_buffer_assets,
1685 )?;
1686
1687 let shadows_enabled = M::enable_shadows();
1688 let prepass_enabled = M::enable_prepass();
1689
1690 let draw_opaque_pbr = opaque_draw_functions.read().id::<DrawMaterial>();
1691 let draw_alpha_mask_pbr = alpha_mask_draw_functions.read().id::<DrawMaterial>();
1692 let draw_transmissive_pbr = transmissive_draw_functions.read().id::<DrawMaterial>();
1693 let draw_transparent_pbr = transparent_draw_functions.read().id::<DrawMaterial>();
1694 let draw_opaque_prepass = opaque_prepass_draw_functions.read().id::<DrawPrepass>();
1695 let draw_alpha_mask_prepass = alpha_mask_prepass_draw_functions.read().id::<DrawPrepass>();
1696 let draw_opaque_prepass_depth_only = opaque_prepass_draw_functions
1697 .read()
1698 .id::<DrawDepthOnlyPrepass>();
1699 let draw_opaque_deferred = opaque_deferred_draw_functions.read().id::<DrawPrepass>();
1700 let draw_alpha_mask_deferred = alpha_mask_deferred_draw_functions
1701 .read()
1702 .id::<DrawPrepass>();
1703 let draw_shadows = shadow_draw_functions.read().id::<DrawPrepass>();
1704 let draw_shadows_depth_only = shadow_draw_functions.read().id::<DrawDepthOnlyPrepass>();
1705
1706 let draw_functions = SmallVec::from_const([
1707 (MainPassOpaqueDrawFunction.intern(), draw_opaque_pbr),
1708 (MainPassAlphaMaskDrawFunction.intern(), draw_alpha_mask_pbr),
1709 (
1710 MainPassTransmissiveDrawFunction.intern(),
1711 draw_transmissive_pbr,
1712 ),
1713 (
1714 MainPassTransparentDrawFunction.intern(),
1715 draw_transparent_pbr,
1716 ),
1717 (PrepassOpaqueDrawFunction.intern(), draw_opaque_prepass),
1718 (
1719 PrepassAlphaMaskDrawFunction.intern(),
1720 draw_alpha_mask_prepass,
1721 ),
1722 (
1723 PrepassOpaqueDepthOnlyDrawFunction.intern(),
1724 draw_opaque_prepass_depth_only,
1725 ),
1726 (DeferredOpaqueDrawFunction.intern(), draw_opaque_deferred),
1727 (
1728 DeferredAlphaMaskDrawFunction.intern(),
1729 draw_alpha_mask_deferred,
1730 ),
1731 (ShadowsDrawFunction.intern(), draw_shadows),
1732 (
1733 ShadowsDepthOnlyDrawFunction.intern(),
1734 draw_shadows_depth_only,
1735 ),
1736 ]);
1737
1738 let render_method = match material.opaque_render_method() {
1739 OpaqueRendererMethod::Forward => OpaqueRendererMethod::Forward,
1740 OpaqueRendererMethod::Deferred => OpaqueRendererMethod::Deferred,
1741 OpaqueRendererMethod::Auto => default_opaque_render_method.0,
1742 };
1743 let alpha_mode = material.alpha_mode();
1744 let reads_view_transmission_texture = material.reads_view_transmission_texture();
1745
1746 let mesh_pipeline_key_bits = MeshPipelineKey::empty();
1747 let mesh_pipeline_key_bits = ErasedMeshPipelineKey::new(mesh_pipeline_key_bits);
1748
1749 let render_phase_type = match alpha_mode {
1750 _ if reads_view_transmission_texture => RenderPhaseType::Transmissive,
1751 AlphaMode::Blend | AlphaMode::Premultiplied | AlphaMode::Add | AlphaMode::Multiply => {
1752 RenderPhaseType::Transparent
1753 }
1754 AlphaMode::Opaque | AlphaMode::AlphaToCoverage => RenderPhaseType::Opaque,
1755 AlphaMode::Mask(_) => RenderPhaseType::AlphaMask,
1756 };
1757
1758 let shaders = material_shaders.shaders.clone();
1759
1760 let bindless = material_uses_bindless_resources::<M>(render_device);
1761 let bind_group_data = material.bind_group_data();
1762 let material_key = ErasedMaterialKey::new(bind_group_data);
1763
1764 Ok(PreparedMaterial {
1765 binding,
1766 properties: Arc::new(MaterialProperties {
1767 alpha_mode,
1768 depth_bias: material.depth_bias(),
1769 reads_view_transmission_texture,
1770 render_phase_type,
1771 render_method,
1772 mesh_pipeline_key_bits,
1773 material_layout: Some(material_layout),
1774 draw_functions,
1775 shaders,
1776 bindless,
1777 base_specialize: Some(base_specialize),
1778 prepass_specialize: Some(prepass_specialize),
1779 user_specialize: Some(user_specialize::<M>),
1780 material_key,
1781 shadows_enabled,
1782 prepass_enabled,
1783 oit_enabled: M::enable_oit(),
1784 }),
1785 })
1786 }
1787
1788 fn unload_asset(
1789 source_asset: AssetId<Self::SourceAsset>,
1790 (_, _, _, _, _, bind_group_allocators, render_material_bindings, ..): &mut SystemParamItem<
1791 Self::Param,
1792 >,
1793 ) {
1794 render_material_bindings.unload_material(source_asset, bind_group_allocators);
1795 }
1796}
1797
1798pub fn get_mesh_instance_world_from_local(
1800 entity: MainEntity,
1801 current_uniform_index: InputUniformIndex,
1802 render_mesh_instances: &RenderMeshInstances,
1803 maybe_batched_instance_buffers: Option<&BatchedInstanceBuffers<MeshUniform, MeshInputUniform>>,
1804) -> Affine3 {
1805 match *render_mesh_instances {
1811 RenderMeshInstances::CpuBuilding(ref render_mesh_instances_cpu) => {
1812 let Some(render_mesh_instance) = render_mesh_instances_cpu.get(&entity) else {
1813 return Affine3::IDENTITY;
1814 };
1815 render_mesh_instance.transforms.world_from_local
1816 }
1817 RenderMeshInstances::GpuBuilding(_) => {
1818 let Some(batched_instance_buffers) = maybe_batched_instance_buffers else {
1819 return Affine3::IDENTITY;
1820 };
1821 let Some(mesh_input_uniform) = batched_instance_buffers
1822 .current_input_buffer
1823 .get(current_uniform_index.0)
1824 else {
1825 return Affine3::IDENTITY;
1826 };
1827 Affine3::from_transpose(mesh_input_uniform.world_from_local)
1828 }
1829 }
1830}
1831
1832pub(crate) trait MaterialPropertiesExt {
1833 fn prepass_reads_material(&self) -> bool;
1834}
1835
1836impl MaterialPropertiesExt for MaterialProperties {
1837 fn prepass_reads_material(&self) -> bool {
1838 self.get_shader(PrepassVertexShader).is_some()
1844 || self.get_shader(PrepassFragmentShader).is_some()
1845 }
1846}