1use crate::material_bind_groups::{
2 FallbackBindlessResources, MaterialBindGroupAllocator, MaterialBindingId,
3};
4use crate::*;
5use alloc::sync::Arc;
6use bevy_asset::prelude::AssetChanged;
7use bevy_asset::{Asset, AssetEventSystems, AssetId, AssetServer, UntypedAssetId};
8use bevy_camera::visibility::ViewVisibility;
9use bevy_camera::ScreenSpaceTransmissionQuality;
10use bevy_core_pipeline::deferred::{AlphaMask3dDeferred, Opaque3dDeferred};
11use bevy_core_pipeline::prepass::{AlphaMask3dPrepass, Opaque3dPrepass};
12use bevy_core_pipeline::{
13 core_3d::{
14 AlphaMask3d, Opaque3d, Opaque3dBatchSetKey, Opaque3dBinKey, Transmissive3d, Transparent3d,
15 },
16 prepass::{OpaqueNoLightmap3dBatchSetKey, OpaqueNoLightmap3dBinKey},
17 tonemapping::Tonemapping,
18};
19use bevy_derive::{Deref, DerefMut};
20use bevy_ecs::component::Tick;
21use bevy_ecs::system::SystemChangeTick;
22use bevy_ecs::{
23 prelude::*,
24 system::{
25 lifetimeless::{SRes, SResMut},
26 SystemParamItem,
27 },
28};
29use bevy_mesh::{
30 mark_3d_meshes_as_changed_if_their_assets_changed, Mesh3d, MeshVertexBufferLayoutRef,
31};
32use bevy_platform::collections::hash_map::Entry;
33use bevy_platform::collections::{HashMap, HashSet};
34use bevy_platform::hash::FixedHasher;
35use bevy_reflect::std_traits::ReflectDefault;
36use bevy_reflect::Reflect;
37use bevy_render::camera::extract_cameras;
38use bevy_render::erased_render_asset::{
39 ErasedRenderAsset, ErasedRenderAssetPlugin, ErasedRenderAssets, PrepareAssetError,
40};
41use bevy_render::render_asset::{prepare_assets, RenderAssets};
42use bevy_render::renderer::RenderQueue;
43use bevy_render::RenderStartup;
44use bevy_render::{
45 batching::gpu_preprocessing::GpuPreprocessingSupport,
46 extract_resource::ExtractResource,
47 mesh::RenderMesh,
48 prelude::*,
49 render_phase::*,
50 render_resource::*,
51 renderer::RenderDevice,
52 sync_world::MainEntity,
53 view::{ExtractedView, Msaa, RenderVisibilityRanges, RetainedViewEntity},
54 Extract,
55};
56use bevy_render::{mesh::allocator::MeshAllocator, sync_world::MainEntityHashMap};
57use bevy_render::{texture::FallbackImage, view::RenderVisibleEntities};
58use bevy_shader::{Shader, ShaderDefVal};
59use bevy_utils::Parallel;
60use core::any::{Any, TypeId};
61use core::hash::{BuildHasher, Hasher};
62use core::{hash::Hash, marker::PhantomData};
63use smallvec::SmallVec;
64use tracing::error;
65
66pub const MATERIAL_BIND_GROUP_INDEX: usize = 3;
67
68pub trait Material: Asset + AsBindGroup + Clone + Sized {
139 fn vertex_shader() -> ShaderRef {
142 ShaderRef::Default
143 }
144
145 fn fragment_shader() -> ShaderRef {
148 ShaderRef::Default
149 }
150
151 #[inline]
153 fn alpha_mode(&self) -> AlphaMode {
154 AlphaMode::Opaque
155 }
156
157 #[inline]
161 fn opaque_render_method(&self) -> OpaqueRendererMethod {
162 OpaqueRendererMethod::Forward
163 }
164
165 #[inline]
166 fn depth_bias(&self) -> f32 {
170 0.0
171 }
172
173 #[inline]
174 fn reads_view_transmission_texture(&self) -> bool {
179 false
180 }
181
182 fn prepass_vertex_shader() -> ShaderRef {
188 ShaderRef::Default
189 }
190
191 fn prepass_fragment_shader() -> ShaderRef {
197 ShaderRef::Default
198 }
199
200 fn deferred_vertex_shader() -> ShaderRef {
203 ShaderRef::Default
204 }
205
206 fn deferred_fragment_shader() -> ShaderRef {
209 ShaderRef::Default
210 }
211
212 #[cfg(feature = "meshlet")]
219 fn meshlet_mesh_fragment_shader() -> ShaderRef {
220 ShaderRef::Default
221 }
222
223 #[cfg(feature = "meshlet")]
230 fn meshlet_mesh_prepass_fragment_shader() -> ShaderRef {
231 ShaderRef::Default
232 }
233
234 #[cfg(feature = "meshlet")]
241 fn meshlet_mesh_deferred_fragment_shader() -> ShaderRef {
242 ShaderRef::Default
243 }
244
245 #[expect(
248 unused_variables,
249 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."
250 )]
251 #[inline]
252 fn specialize(
253 pipeline: &MaterialPipeline,
254 descriptor: &mut RenderPipelineDescriptor,
255 layout: &MeshVertexBufferLayoutRef,
256 key: MaterialPipelineKey<Self>,
257 ) -> Result<(), SpecializedMeshPipelineError> {
258 Ok(())
259 }
260}
261
262#[derive(Default)]
263pub struct MaterialsPlugin {
264 pub debug_flags: RenderDebugFlags,
266}
267
268impl Plugin for MaterialsPlugin {
269 fn build(&self, app: &mut App) {
270 app.add_plugins((PrepassPipelinePlugin, PrepassPlugin::new(self.debug_flags)));
271 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
272 render_app
273 .init_resource::<EntitySpecializationTicks>()
274 .init_resource::<SpecializedMaterialPipelineCache>()
275 .init_resource::<SpecializedMeshPipelines<MaterialPipelineSpecializer>>()
276 .init_resource::<LightKeyCache>()
277 .init_resource::<LightSpecializationTicks>()
278 .init_resource::<SpecializedShadowMaterialPipelineCache>()
279 .init_resource::<DrawFunctions<Shadow>>()
280 .init_resource::<RenderMaterialInstances>()
281 .init_resource::<MaterialBindGroupAllocators>()
282 .add_render_command::<Shadow, DrawPrepass>()
283 .add_render_command::<Transmissive3d, DrawMaterial>()
284 .add_render_command::<Transparent3d, DrawMaterial>()
285 .add_render_command::<Opaque3d, DrawMaterial>()
286 .add_render_command::<AlphaMask3d, DrawMaterial>()
287 .add_systems(RenderStartup, init_material_pipeline)
288 .add_systems(
289 Render,
290 (
291 specialize_material_meshes
292 .in_set(RenderSystems::PrepareMeshes)
293 .after(prepare_assets::<RenderMesh>)
294 .after(collect_meshes_for_gpu_building)
295 .after(set_mesh_motion_vector_flags),
296 queue_material_meshes.in_set(RenderSystems::QueueMeshes),
297 ),
298 )
299 .add_systems(
300 Render,
301 (
302 prepare_material_bind_groups,
303 write_material_bind_group_buffers,
304 )
305 .chain()
306 .in_set(RenderSystems::PrepareBindGroups),
307 )
308 .add_systems(
309 Render,
310 (
311 check_views_lights_need_specialization.in_set(RenderSystems::PrepareAssets),
312 specialize_shadows
315 .in_set(RenderSystems::ManageViews)
316 .after(prepare_lights),
317 queue_shadows.in_set(RenderSystems::QueueMeshes),
318 ),
319 );
320 }
321 }
322}
323
324pub struct MaterialPlugin<M: Material> {
327 pub prepass_enabled: bool,
333 pub shadows_enabled: bool,
335 pub debug_flags: RenderDebugFlags,
337 pub _marker: PhantomData<M>,
338}
339
340impl<M: Material> Default for MaterialPlugin<M> {
341 fn default() -> Self {
342 Self {
343 prepass_enabled: true,
344 shadows_enabled: true,
345 debug_flags: RenderDebugFlags::default(),
346 _marker: Default::default(),
347 }
348 }
349}
350
351impl<M: Material> Plugin for MaterialPlugin<M>
352where
353 M::Data: PartialEq + Eq + Hash + Clone,
354{
355 fn build(&self, app: &mut App) {
356 app.init_asset::<M>()
357 .register_type::<MeshMaterial3d<M>>()
358 .init_resource::<EntitiesNeedingSpecialization<M>>()
359 .add_plugins((ErasedRenderAssetPlugin::<MeshMaterial3d<M>>::default(),))
360 .add_systems(
361 PostUpdate,
362 (
363 mark_meshes_as_changed_if_their_materials_changed::<M>.ambiguous_with_all(),
364 check_entities_needing_specialization::<M>.after(AssetEventSystems),
365 )
366 .after(mark_3d_meshes_as_changed_if_their_assets_changed),
367 );
368
369 if self.shadows_enabled {
370 app.add_systems(
371 PostUpdate,
372 check_light_entities_needing_specialization::<M>
373 .after(check_entities_needing_specialization::<M>),
374 );
375 }
376
377 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
378 if self.prepass_enabled {
379 render_app.init_resource::<PrepassEnabled<M>>();
380 }
381 if self.shadows_enabled {
382 render_app.init_resource::<ShadowsEnabled<M>>();
383 }
384
385 render_app
386 .add_systems(RenderStartup, add_material_bind_group_allocator::<M>)
387 .add_systems(
388 ExtractSchedule,
389 (
390 extract_mesh_materials::<M>.in_set(MaterialExtractionSystems),
391 early_sweep_material_instances::<M>
392 .after(MaterialExtractionSystems)
393 .before(late_sweep_material_instances),
394 extract_entities_needs_specialization::<M>
398 .in_set(MaterialExtractEntitiesNeedingSpecializationSystems),
399 sweep_entities_needing_specialization::<M>
400 .after(MaterialExtractEntitiesNeedingSpecializationSystems)
401 .after(MaterialExtractionSystems)
402 .after(extract_cameras)
403 .before(late_sweep_material_instances),
404 ),
405 );
406 }
407 }
408}
409
410fn add_material_bind_group_allocator<M: Material>(
411 render_device: Res<RenderDevice>,
412 mut bind_group_allocators: ResMut<MaterialBindGroupAllocators>,
413) {
414 bind_group_allocators.insert(
415 TypeId::of::<M>(),
416 MaterialBindGroupAllocator::new(
417 &render_device,
418 M::label(),
419 material_uses_bindless_resources::<M>(&render_device)
420 .then(|| M::bindless_descriptor())
421 .flatten(),
422 M::bind_group_layout(&render_device),
423 M::bindless_slot_count(),
424 ),
425 );
426}
427
428pub(crate) static DUMMY_MESH_MATERIAL: AssetId<StandardMaterial> =
434 AssetId::<StandardMaterial>::invalid();
435
436pub struct MaterialPipelineKey<M: Material> {
438 pub mesh_key: MeshPipelineKey,
439 pub bind_group_data: M::Data,
440}
441
442#[derive(Clone, Debug, PartialEq, Eq, Hash)]
443pub struct ErasedMaterialPipelineKey {
444 pub mesh_key: MeshPipelineKey,
445 pub material_key: ErasedMaterialKey,
446 pub type_id: TypeId,
447}
448
449#[derive(Resource, Clone)]
451pub struct MaterialPipeline {
452 pub mesh_pipeline: MeshPipeline,
453}
454
455pub struct MaterialPipelineSpecializer {
456 pub(crate) pipeline: MaterialPipeline,
457 pub(crate) properties: Arc<MaterialProperties>,
458}
459
460impl SpecializedMeshPipeline for MaterialPipelineSpecializer {
461 type Key = ErasedMaterialPipelineKey;
462
463 fn specialize(
464 &self,
465 key: Self::Key,
466 layout: &MeshVertexBufferLayoutRef,
467 ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
468 let mut descriptor = self
469 .pipeline
470 .mesh_pipeline
471 .specialize(key.mesh_key, layout)?;
472 descriptor.vertex.shader_defs.push(ShaderDefVal::UInt(
473 "MATERIAL_BIND_GROUP".into(),
474 MATERIAL_BIND_GROUP_INDEX as u32,
475 ));
476 if let Some(ref mut fragment) = descriptor.fragment {
477 fragment.shader_defs.push(ShaderDefVal::UInt(
478 "MATERIAL_BIND_GROUP".into(),
479 MATERIAL_BIND_GROUP_INDEX as u32,
480 ));
481 };
482 if let Some(vertex_shader) = self.properties.get_shader(MaterialVertexShader) {
483 descriptor.vertex.shader = vertex_shader.clone();
484 }
485
486 if let Some(fragment_shader) = self.properties.get_shader(MaterialFragmentShader) {
487 descriptor.fragment.as_mut().unwrap().shader = fragment_shader.clone();
488 }
489
490 descriptor
491 .layout
492 .insert(3, self.properties.material_layout.as_ref().unwrap().clone());
493
494 if let Some(specialize) = self.properties.specialize {
495 specialize(&self.pipeline, &mut descriptor, layout, key)?;
496 }
497
498 if self.properties.bindless {
500 descriptor.vertex.shader_defs.push("BINDLESS".into());
501 if let Some(ref mut fragment) = descriptor.fragment {
502 fragment.shader_defs.push("BINDLESS".into());
503 }
504 }
505
506 Ok(descriptor)
507 }
508}
509
510pub fn init_material_pipeline(mut commands: Commands, mesh_pipeline: Res<MeshPipeline>) {
511 commands.insert_resource(MaterialPipeline {
512 mesh_pipeline: mesh_pipeline.clone(),
513 });
514}
515
516pub type DrawMaterial = (
517 SetItemPipeline,
518 SetMeshViewBindGroup<0>,
519 SetMeshViewBindingArrayBindGroup<1>,
520 SetMeshBindGroup<2>,
521 SetMaterialBindGroup<MATERIAL_BIND_GROUP_INDEX>,
522 DrawMesh,
523);
524
525pub struct SetMaterialBindGroup<const I: usize>;
527impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMaterialBindGroup<I> {
528 type Param = (
529 SRes<ErasedRenderAssets<PreparedMaterial>>,
530 SRes<RenderMaterialInstances>,
531 SRes<MaterialBindGroupAllocators>,
532 );
533 type ViewQuery = ();
534 type ItemQuery = ();
535
536 #[inline]
537 fn render<'w>(
538 item: &P,
539 _view: (),
540 _item_query: Option<()>,
541 (materials, material_instances, material_bind_group_allocator): SystemParamItem<
542 'w,
543 '_,
544 Self::Param,
545 >,
546 pass: &mut TrackedRenderPass<'w>,
547 ) -> RenderCommandResult {
548 let materials = materials.into_inner();
549 let material_instances = material_instances.into_inner();
550 let material_bind_group_allocators = material_bind_group_allocator.into_inner();
551
552 let Some(material_instance) = material_instances.instances.get(&item.main_entity()) else {
553 return RenderCommandResult::Skip;
554 };
555 let Some(material_bind_group_allocator) =
556 material_bind_group_allocators.get(&material_instance.asset_id.type_id())
557 else {
558 return RenderCommandResult::Skip;
559 };
560 let Some(material) = materials.get(material_instance.asset_id) else {
561 return RenderCommandResult::Skip;
562 };
563 let Some(material_bind_group) = material_bind_group_allocator.get(material.binding.group)
564 else {
565 return RenderCommandResult::Skip;
566 };
567 let Some(bind_group) = material_bind_group.bind_group() else {
568 return RenderCommandResult::Skip;
569 };
570 pass.set_bind_group(I, bind_group, &[]);
571 RenderCommandResult::Success
572 }
573}
574
575#[derive(Resource, Default)]
577pub struct RenderMaterialInstances {
578 pub instances: MainEntityHashMap<RenderMaterialInstance>,
581 pub current_change_tick: Tick,
585}
586
587impl RenderMaterialInstances {
588 pub(crate) fn mesh_material(&self, entity: MainEntity) -> UntypedAssetId {
595 match self.instances.get(&entity) {
596 Some(render_instance) => render_instance.asset_id,
597 None => DUMMY_MESH_MATERIAL.into(),
598 }
599 }
600}
601
602pub struct RenderMaterialInstance {
607 pub asset_id: UntypedAssetId,
609 pub last_change_tick: Tick,
612}
613
614#[derive(SystemSet, Clone, PartialEq, Eq, Debug, Hash)]
616pub struct MaterialExtractionSystems;
617
618#[derive(SystemSet, Clone, PartialEq, Eq, Debug, Hash)]
621pub struct MaterialExtractEntitiesNeedingSpecializationSystems;
622
623#[deprecated(since = "0.17.0", note = "Renamed to `MaterialExtractionSystems`.")]
625pub type ExtractMaterialsSet = MaterialExtractionSystems;
626
627pub const fn alpha_mode_pipeline_key(alpha_mode: AlphaMode, msaa: &Msaa) -> MeshPipelineKey {
628 match alpha_mode {
629 AlphaMode::Premultiplied | AlphaMode::Add => MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA,
632 AlphaMode::Blend => MeshPipelineKey::BLEND_ALPHA,
633 AlphaMode::Multiply => MeshPipelineKey::BLEND_MULTIPLY,
634 AlphaMode::Mask(_) => MeshPipelineKey::MAY_DISCARD,
635 AlphaMode::AlphaToCoverage => match *msaa {
636 Msaa::Off => MeshPipelineKey::MAY_DISCARD,
637 _ => MeshPipelineKey::BLEND_ALPHA_TO_COVERAGE,
638 },
639 _ => MeshPipelineKey::NONE,
640 }
641}
642
643pub const fn tonemapping_pipeline_key(tonemapping: Tonemapping) -> MeshPipelineKey {
644 match tonemapping {
645 Tonemapping::None => MeshPipelineKey::TONEMAP_METHOD_NONE,
646 Tonemapping::Reinhard => MeshPipelineKey::TONEMAP_METHOD_REINHARD,
647 Tonemapping::ReinhardLuminance => MeshPipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE,
648 Tonemapping::AcesFitted => MeshPipelineKey::TONEMAP_METHOD_ACES_FITTED,
649 Tonemapping::AgX => MeshPipelineKey::TONEMAP_METHOD_AGX,
650 Tonemapping::SomewhatBoringDisplayTransform => {
651 MeshPipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM
652 }
653 Tonemapping::TonyMcMapface => MeshPipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE,
654 Tonemapping::BlenderFilmic => MeshPipelineKey::TONEMAP_METHOD_BLENDER_FILMIC,
655 }
656}
657
658pub const fn screen_space_specular_transmission_pipeline_key(
659 screen_space_transmissive_blur_quality: ScreenSpaceTransmissionQuality,
660) -> MeshPipelineKey {
661 match screen_space_transmissive_blur_quality {
662 ScreenSpaceTransmissionQuality::Low => {
663 MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_LOW
664 }
665 ScreenSpaceTransmissionQuality::Medium => {
666 MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_MEDIUM
667 }
668 ScreenSpaceTransmissionQuality::High => {
669 MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_HIGH
670 }
671 ScreenSpaceTransmissionQuality::Ultra => {
672 MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_ULTRA
673 }
674 }
675}
676
677fn mark_meshes_as_changed_if_their_materials_changed<M>(
692 mut changed_meshes_query: Query<
693 &mut Mesh3d,
694 Or<(Changed<MeshMaterial3d<M>>, AssetChanged<MeshMaterial3d<M>>)>,
695 >,
696) where
697 M: Material,
698{
699 for mut mesh in &mut changed_meshes_query {
700 mesh.set_changed();
701 }
702}
703
704fn extract_mesh_materials<M: Material>(
707 mut material_instances: ResMut<RenderMaterialInstances>,
708 changed_meshes_query: Extract<
709 Query<
710 (Entity, &ViewVisibility, &MeshMaterial3d<M>),
711 Or<(Changed<ViewVisibility>, Changed<MeshMaterial3d<M>>)>,
712 >,
713 >,
714) {
715 let last_change_tick = material_instances.current_change_tick;
716
717 for (entity, view_visibility, material) in &changed_meshes_query {
718 if view_visibility.get() {
719 material_instances.instances.insert(
720 entity.into(),
721 RenderMaterialInstance {
722 asset_id: material.id().untyped(),
723 last_change_tick,
724 },
725 );
726 } else {
727 material_instances
728 .instances
729 .remove(&MainEntity::from(entity));
730 }
731 }
732}
733
734fn early_sweep_material_instances<M>(
749 mut material_instances: ResMut<RenderMaterialInstances>,
750 mut removed_materials_query: Extract<RemovedComponents<MeshMaterial3d<M>>>,
751) where
752 M: Material,
753{
754 let last_change_tick = material_instances.current_change_tick;
755
756 for entity in removed_materials_query.read() {
757 if let Entry::Occupied(occupied_entry) = material_instances.instances.entry(entity.into()) {
758 if occupied_entry.get().last_change_tick != last_change_tick {
760 occupied_entry.remove();
761 }
762 }
763 }
764}
765
766pub fn late_sweep_material_instances(
773 mut material_instances: ResMut<RenderMaterialInstances>,
774 mut removed_meshes_query: Extract<RemovedComponents<Mesh3d>>,
775) {
776 let last_change_tick = material_instances.current_change_tick;
777
778 for entity in removed_meshes_query.read() {
779 if let Entry::Occupied(occupied_entry) = material_instances.instances.entry(entity.into()) {
780 if occupied_entry.get().last_change_tick != last_change_tick {
784 occupied_entry.remove();
785 }
786 }
787 }
788
789 material_instances
790 .current_change_tick
791 .set(last_change_tick.get() + 1);
792}
793
794pub fn extract_entities_needs_specialization<M>(
795 entities_needing_specialization: Extract<Res<EntitiesNeedingSpecialization<M>>>,
796 mut entity_specialization_ticks: ResMut<EntitySpecializationTicks>,
797 render_material_instances: Res<RenderMaterialInstances>,
798 ticks: SystemChangeTick,
799) where
800 M: Material,
801{
802 for entity in entities_needing_specialization.iter() {
803 entity_specialization_ticks.insert(
805 (*entity).into(),
806 EntitySpecializationTickPair {
807 system_tick: ticks.this_run(),
808 material_instances_tick: render_material_instances.current_change_tick,
809 },
810 );
811 }
812}
813
814pub fn sweep_entities_needing_specialization<M>(
829 mut entity_specialization_ticks: ResMut<EntitySpecializationTicks>,
830 mut removed_mesh_material_components: Extract<RemovedComponents<MeshMaterial3d<M>>>,
831 mut specialized_material_pipeline_cache: ResMut<SpecializedMaterialPipelineCache>,
832 mut specialized_prepass_material_pipeline_cache: Option<
833 ResMut<SpecializedPrepassMaterialPipelineCache>,
834 >,
835 mut specialized_shadow_material_pipeline_cache: Option<
836 ResMut<SpecializedShadowMaterialPipelineCache>,
837 >,
838 render_material_instances: Res<RenderMaterialInstances>,
839 views: Query<&ExtractedView>,
840) where
841 M: Material,
842{
843 for entity in removed_mesh_material_components.read() {
855 if entity_specialization_ticks
859 .get(&MainEntity::from(entity))
860 .is_some_and(|ticks| {
861 ticks.material_instances_tick == render_material_instances.current_change_tick
862 })
863 {
864 continue;
865 }
866
867 entity_specialization_ticks.remove(&MainEntity::from(entity));
868 for view in views {
869 if let Some(cache) =
870 specialized_material_pipeline_cache.get_mut(&view.retained_view_entity)
871 {
872 cache.remove(&MainEntity::from(entity));
873 }
874 if let Some(cache) = specialized_prepass_material_pipeline_cache
875 .as_mut()
876 .and_then(|c| c.get_mut(&view.retained_view_entity))
877 {
878 cache.remove(&MainEntity::from(entity));
879 }
880 if let Some(cache) = specialized_shadow_material_pipeline_cache
881 .as_mut()
882 .and_then(|c| c.get_mut(&view.retained_view_entity))
883 {
884 cache.remove(&MainEntity::from(entity));
885 }
886 }
887 }
888}
889
890#[derive(Resource, Deref, DerefMut, Clone, Debug)]
891pub struct EntitiesNeedingSpecialization<M> {
892 #[deref]
893 pub entities: Vec<Entity>,
894 _marker: PhantomData<M>,
895}
896
897impl<M> Default for EntitiesNeedingSpecialization<M> {
898 fn default() -> Self {
899 Self {
900 entities: Default::default(),
901 _marker: Default::default(),
902 }
903 }
904}
905
906#[derive(Resource, Deref, DerefMut, Default, Clone, Debug)]
912pub struct EntitySpecializationTicks {
913 #[deref]
919 pub entities: MainEntityHashMap<EntitySpecializationTickPair>,
920}
921
922#[derive(Clone, Copy, Debug)]
952pub struct EntitySpecializationTickPair {
953 pub system_tick: Tick,
955 pub material_instances_tick: Tick,
958}
959
960#[derive(Resource, Deref, DerefMut, Default)]
962pub struct SpecializedMaterialPipelineCache {
963 #[deref]
965 map: HashMap<RetainedViewEntity, SpecializedMaterialViewPipelineCache>,
966}
967
968#[derive(Deref, DerefMut, Default)]
971pub struct SpecializedMaterialViewPipelineCache {
972 #[deref]
974 map: MainEntityHashMap<(Tick, CachedRenderPipelineId)>,
975}
976
977pub fn check_entities_needing_specialization<M>(
978 needs_specialization: Query<
979 Entity,
980 (
981 Or<(
982 Changed<Mesh3d>,
983 AssetChanged<Mesh3d>,
984 Changed<MeshMaterial3d<M>>,
985 AssetChanged<MeshMaterial3d<M>>,
986 )>,
987 With<MeshMaterial3d<M>>,
988 ),
989 >,
990 mut par_local: Local<Parallel<Vec<Entity>>>,
991 mut entities_needing_specialization: ResMut<EntitiesNeedingSpecialization<M>>,
992) where
993 M: Material,
994{
995 entities_needing_specialization.clear();
996
997 needs_specialization
998 .par_iter()
999 .for_each(|entity| par_local.borrow_local_mut().push(entity));
1000
1001 par_local.drain_into(&mut entities_needing_specialization);
1002}
1003
1004pub fn specialize_material_meshes(
1005 render_meshes: Res<RenderAssets<RenderMesh>>,
1006 render_materials: Res<ErasedRenderAssets<PreparedMaterial>>,
1007 render_mesh_instances: Res<RenderMeshInstances>,
1008 render_material_instances: Res<RenderMaterialInstances>,
1009 render_lightmaps: Res<RenderLightmaps>,
1010 render_visibility_ranges: Res<RenderVisibilityRanges>,
1011 (
1012 opaque_render_phases,
1013 alpha_mask_render_phases,
1014 transmissive_render_phases,
1015 transparent_render_phases,
1016 ): (
1017 Res<ViewBinnedRenderPhases<Opaque3d>>,
1018 Res<ViewBinnedRenderPhases<AlphaMask3d>>,
1019 Res<ViewSortedRenderPhases<Transmissive3d>>,
1020 Res<ViewSortedRenderPhases<Transparent3d>>,
1021 ),
1022 views: Query<(&ExtractedView, &RenderVisibleEntities)>,
1023 view_key_cache: Res<ViewKeyCache>,
1024 entity_specialization_ticks: Res<EntitySpecializationTicks>,
1025 view_specialization_ticks: Res<ViewSpecializationTicks>,
1026 mut specialized_material_pipeline_cache: ResMut<SpecializedMaterialPipelineCache>,
1027 mut pipelines: ResMut<SpecializedMeshPipelines<MaterialPipelineSpecializer>>,
1028 pipeline: Res<MaterialPipeline>,
1029 pipeline_cache: Res<PipelineCache>,
1030 ticks: SystemChangeTick,
1031) {
1032 let mut all_views: HashSet<RetainedViewEntity, FixedHasher> = HashSet::default();
1035
1036 for (view, visible_entities) in &views {
1037 all_views.insert(view.retained_view_entity);
1038
1039 if !transparent_render_phases.contains_key(&view.retained_view_entity)
1040 && !opaque_render_phases.contains_key(&view.retained_view_entity)
1041 && !alpha_mask_render_phases.contains_key(&view.retained_view_entity)
1042 && !transmissive_render_phases.contains_key(&view.retained_view_entity)
1043 {
1044 continue;
1045 }
1046
1047 let Some(view_key) = view_key_cache.get(&view.retained_view_entity) else {
1048 continue;
1049 };
1050
1051 let view_tick = view_specialization_ticks
1052 .get(&view.retained_view_entity)
1053 .unwrap();
1054 let view_specialized_material_pipeline_cache = specialized_material_pipeline_cache
1055 .entry(view.retained_view_entity)
1056 .or_default();
1057
1058 for (_, visible_entity) in visible_entities.iter::<Mesh3d>() {
1059 let Some(material_instance) = render_material_instances.instances.get(visible_entity)
1060 else {
1061 continue;
1062 };
1063 let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(*visible_entity)
1064 else {
1065 continue;
1066 };
1067 let entity_tick = entity_specialization_ticks
1068 .get(visible_entity)
1069 .unwrap()
1070 .system_tick;
1071 let last_specialized_tick = view_specialized_material_pipeline_cache
1072 .get(visible_entity)
1073 .map(|(tick, _)| *tick);
1074 let needs_specialization = last_specialized_tick.is_none_or(|tick| {
1075 view_tick.is_newer_than(tick, ticks.this_run())
1076 || entity_tick.is_newer_than(tick, ticks.this_run())
1077 });
1078 if !needs_specialization {
1079 continue;
1080 }
1081 let Some(mesh) = render_meshes.get(mesh_instance.mesh_asset_id) else {
1082 continue;
1083 };
1084 let Some(material) = render_materials.get(material_instance.asset_id) else {
1085 continue;
1086 };
1087
1088 let mut mesh_pipeline_key_bits = material.properties.mesh_pipeline_key_bits;
1089 mesh_pipeline_key_bits.insert(alpha_mode_pipeline_key(
1090 material.properties.alpha_mode,
1091 &Msaa::from_samples(view_key.msaa_samples()),
1092 ));
1093 let mut mesh_key = *view_key
1094 | MeshPipelineKey::from_bits_retain(mesh.key_bits.bits())
1095 | mesh_pipeline_key_bits;
1096
1097 if let Some(lightmap) = render_lightmaps.render_lightmaps.get(visible_entity) {
1098 mesh_key |= MeshPipelineKey::LIGHTMAPPED;
1099
1100 if lightmap.bicubic_sampling {
1101 mesh_key |= MeshPipelineKey::LIGHTMAP_BICUBIC_SAMPLING;
1102 }
1103 }
1104
1105 if render_visibility_ranges.entity_has_crossfading_visibility_ranges(*visible_entity) {
1106 mesh_key |= MeshPipelineKey::VISIBILITY_RANGE_DITHER;
1107 }
1108
1109 if view_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
1110 if mesh_instance
1112 .flags
1113 .contains(RenderMeshInstanceFlags::HAS_PREVIOUS_SKIN)
1114 {
1115 mesh_key |= MeshPipelineKey::HAS_PREVIOUS_SKIN;
1116 }
1117 if mesh_instance
1118 .flags
1119 .contains(RenderMeshInstanceFlags::HAS_PREVIOUS_MORPH)
1120 {
1121 mesh_key |= MeshPipelineKey::HAS_PREVIOUS_MORPH;
1122 }
1123 }
1124
1125 let erased_key = ErasedMaterialPipelineKey {
1126 type_id: material_instance.asset_id.type_id(),
1127 mesh_key,
1128 material_key: material.properties.material_key.clone(),
1129 };
1130 let material_pipeline_specializer = MaterialPipelineSpecializer {
1131 pipeline: pipeline.clone(),
1132 properties: material.properties.clone(),
1133 };
1134 let pipeline_id = pipelines.specialize(
1135 &pipeline_cache,
1136 &material_pipeline_specializer,
1137 erased_key,
1138 &mesh.layout,
1139 );
1140 let pipeline_id = match pipeline_id {
1141 Ok(id) => id,
1142 Err(err) => {
1143 error!("{}", err);
1144 continue;
1145 }
1146 };
1147
1148 view_specialized_material_pipeline_cache
1149 .insert(*visible_entity, (ticks.this_run(), pipeline_id));
1150 }
1151 }
1152
1153 specialized_material_pipeline_cache
1155 .retain(|retained_view_entity, _| all_views.contains(retained_view_entity));
1156}
1157
1158pub fn queue_material_meshes(
1161 render_materials: Res<ErasedRenderAssets<PreparedMaterial>>,
1162 render_mesh_instances: Res<RenderMeshInstances>,
1163 render_material_instances: Res<RenderMaterialInstances>,
1164 mesh_allocator: Res<MeshAllocator>,
1165 gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
1166 mut opaque_render_phases: ResMut<ViewBinnedRenderPhases<Opaque3d>>,
1167 mut alpha_mask_render_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3d>>,
1168 mut transmissive_render_phases: ResMut<ViewSortedRenderPhases<Transmissive3d>>,
1169 mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
1170 views: Query<(&ExtractedView, &RenderVisibleEntities)>,
1171 specialized_material_pipeline_cache: ResMut<SpecializedMaterialPipelineCache>,
1172) {
1173 for (view, visible_entities) in &views {
1174 let (
1175 Some(opaque_phase),
1176 Some(alpha_mask_phase),
1177 Some(transmissive_phase),
1178 Some(transparent_phase),
1179 ) = (
1180 opaque_render_phases.get_mut(&view.retained_view_entity),
1181 alpha_mask_render_phases.get_mut(&view.retained_view_entity),
1182 transmissive_render_phases.get_mut(&view.retained_view_entity),
1183 transparent_render_phases.get_mut(&view.retained_view_entity),
1184 )
1185 else {
1186 continue;
1187 };
1188
1189 let Some(view_specialized_material_pipeline_cache) =
1190 specialized_material_pipeline_cache.get(&view.retained_view_entity)
1191 else {
1192 continue;
1193 };
1194
1195 let rangefinder = view.rangefinder3d();
1196 for (render_entity, visible_entity) in visible_entities.iter::<Mesh3d>() {
1197 let Some((current_change_tick, pipeline_id)) = view_specialized_material_pipeline_cache
1198 .get(visible_entity)
1199 .map(|(current_change_tick, pipeline_id)| (*current_change_tick, *pipeline_id))
1200 else {
1201 continue;
1202 };
1203
1204 if opaque_phase.validate_cached_entity(*visible_entity, current_change_tick)
1206 || alpha_mask_phase.validate_cached_entity(*visible_entity, current_change_tick)
1207 {
1208 continue;
1209 }
1210
1211 let Some(material_instance) = render_material_instances.instances.get(visible_entity)
1212 else {
1213 continue;
1214 };
1215 let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(*visible_entity)
1216 else {
1217 continue;
1218 };
1219 let Some(material) = render_materials.get(material_instance.asset_id) else {
1220 continue;
1221 };
1222
1223 let (vertex_slab, index_slab) = mesh_allocator.mesh_slabs(&mesh_instance.mesh_asset_id);
1225 let Some(draw_function) = material.properties.get_draw_function(MaterialDrawFunction)
1226 else {
1227 continue;
1228 };
1229
1230 match material.properties.render_phase_type {
1231 RenderPhaseType::Transmissive => {
1232 let distance = rangefinder.distance_translation(&mesh_instance.translation)
1233 + material.properties.depth_bias;
1234 transmissive_phase.add(Transmissive3d {
1235 entity: (*render_entity, *visible_entity),
1236 draw_function,
1237 pipeline: pipeline_id,
1238 distance,
1239 batch_range: 0..1,
1240 extra_index: PhaseItemExtraIndex::None,
1241 indexed: index_slab.is_some(),
1242 });
1243 }
1244 RenderPhaseType::Opaque => {
1245 if material.properties.render_method == OpaqueRendererMethod::Deferred {
1246 opaque_phase.update_cache(*visible_entity, None, current_change_tick);
1251 continue;
1252 }
1253 let batch_set_key = Opaque3dBatchSetKey {
1254 pipeline: pipeline_id,
1255 draw_function,
1256 material_bind_group_index: Some(material.binding.group.0),
1257 vertex_slab: vertex_slab.unwrap_or_default(),
1258 index_slab,
1259 lightmap_slab: mesh_instance.shared.lightmap_slab_index.map(|index| *index),
1260 };
1261 let bin_key = Opaque3dBinKey {
1262 asset_id: mesh_instance.mesh_asset_id.into(),
1263 };
1264 opaque_phase.add(
1265 batch_set_key,
1266 bin_key,
1267 (*render_entity, *visible_entity),
1268 mesh_instance.current_uniform_index,
1269 BinnedRenderPhaseType::mesh(
1270 mesh_instance.should_batch(),
1271 &gpu_preprocessing_support,
1272 ),
1273 current_change_tick,
1274 );
1275 }
1276 RenderPhaseType::AlphaMask => {
1278 let batch_set_key = OpaqueNoLightmap3dBatchSetKey {
1279 draw_function,
1280 pipeline: pipeline_id,
1281 material_bind_group_index: Some(material.binding.group.0),
1282 vertex_slab: vertex_slab.unwrap_or_default(),
1283 index_slab,
1284 };
1285 let bin_key = OpaqueNoLightmap3dBinKey {
1286 asset_id: mesh_instance.mesh_asset_id.into(),
1287 };
1288 alpha_mask_phase.add(
1289 batch_set_key,
1290 bin_key,
1291 (*render_entity, *visible_entity),
1292 mesh_instance.current_uniform_index,
1293 BinnedRenderPhaseType::mesh(
1294 mesh_instance.should_batch(),
1295 &gpu_preprocessing_support,
1296 ),
1297 current_change_tick,
1298 );
1299 }
1300 RenderPhaseType::Transparent => {
1301 let distance = rangefinder.distance_translation(&mesh_instance.translation)
1302 + material.properties.depth_bias;
1303 transparent_phase.add(Transparent3d {
1304 entity: (*render_entity, *visible_entity),
1305 draw_function,
1306 pipeline: pipeline_id,
1307 distance,
1308 batch_range: 0..1,
1309 extra_index: PhaseItemExtraIndex::None,
1310 indexed: index_slab.is_some(),
1311 });
1312 }
1313 }
1314 }
1315 }
1316}
1317
1318#[derive(Default, Resource, Clone, Debug, ExtractResource, Reflect)]
1320#[reflect(Resource, Default, Debug, Clone)]
1321pub struct DefaultOpaqueRendererMethod(OpaqueRendererMethod);
1322
1323impl DefaultOpaqueRendererMethod {
1324 pub fn forward() -> Self {
1325 DefaultOpaqueRendererMethod(OpaqueRendererMethod::Forward)
1326 }
1327
1328 pub fn deferred() -> Self {
1329 DefaultOpaqueRendererMethod(OpaqueRendererMethod::Deferred)
1330 }
1331
1332 pub fn set_to_forward(&mut self) {
1333 self.0 = OpaqueRendererMethod::Forward;
1334 }
1335
1336 pub fn set_to_deferred(&mut self) {
1337 self.0 = OpaqueRendererMethod::Deferred;
1338 }
1339}
1340
1341#[derive(Default, Clone, Copy, Debug, PartialEq, Reflect)]
1360#[reflect(Default, Clone, PartialEq)]
1361pub enum OpaqueRendererMethod {
1362 #[default]
1363 Forward,
1364 Deferred,
1365 Auto,
1366}
1367
1368#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1369pub struct MaterialVertexShader;
1370
1371#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1372pub struct MaterialFragmentShader;
1373
1374#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1375pub struct PrepassVertexShader;
1376
1377#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1378pub struct PrepassFragmentShader;
1379
1380#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1381pub struct DeferredVertexShader;
1382
1383#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1384pub struct DeferredFragmentShader;
1385
1386#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1387pub struct MeshletFragmentShader;
1388
1389#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1390pub struct MeshletPrepassFragmentShader;
1391
1392#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1393pub struct MeshletDeferredFragmentShader;
1394
1395#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1396pub struct MaterialDrawFunction;
1397
1398#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1399pub struct PrepassDrawFunction;
1400
1401#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1402pub struct DeferredDrawFunction;
1403
1404#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1405pub struct ShadowsDrawFunction;
1406
1407#[derive(Debug)]
1408pub struct ErasedMaterialKey {
1409 type_id: TypeId,
1410 hash: u64,
1411 value: Box<dyn Any + Send + Sync>,
1412 vtable: Arc<ErasedMaterialKeyVTable>,
1413}
1414
1415#[derive(Debug)]
1416pub struct ErasedMaterialKeyVTable {
1417 clone_fn: fn(&dyn Any) -> Box<dyn Any + Send + Sync>,
1418 partial_eq_fn: fn(&dyn Any, &dyn Any) -> bool,
1419}
1420
1421impl ErasedMaterialKey {
1422 pub fn new<T>(material_key: T) -> Self
1423 where
1424 T: Clone + Hash + PartialEq + Send + Sync + 'static,
1425 {
1426 let type_id = TypeId::of::<T>();
1427 let hash = FixedHasher::hash_one(&FixedHasher, &material_key);
1428
1429 fn clone<T: Clone + Send + Sync + 'static>(any: &dyn Any) -> Box<dyn Any + Send + Sync> {
1430 Box::new(any.downcast_ref::<T>().unwrap().clone())
1431 }
1432 fn partial_eq<T: PartialEq + 'static>(a: &dyn Any, b: &dyn Any) -> bool {
1433 a.downcast_ref::<T>().unwrap() == b.downcast_ref::<T>().unwrap()
1434 }
1435
1436 Self {
1437 type_id,
1438 hash,
1439 value: Box::new(material_key),
1440 vtable: Arc::new(ErasedMaterialKeyVTable {
1441 clone_fn: clone::<T>,
1442 partial_eq_fn: partial_eq::<T>,
1443 }),
1444 }
1445 }
1446
1447 pub fn to_key<T: Clone + 'static>(&self) -> T {
1448 debug_assert_eq!(self.type_id, TypeId::of::<T>());
1449 self.value.downcast_ref::<T>().unwrap().clone()
1450 }
1451}
1452
1453impl PartialEq for ErasedMaterialKey {
1454 fn eq(&self, other: &Self) -> bool {
1455 self.type_id == other.type_id
1456 && (self.vtable.partial_eq_fn)(self.value.as_ref(), other.value.as_ref())
1457 }
1458}
1459
1460impl Eq for ErasedMaterialKey {}
1461
1462impl Clone for ErasedMaterialKey {
1463 fn clone(&self) -> Self {
1464 Self {
1465 type_id: self.type_id,
1466 hash: self.hash,
1467 value: (self.vtable.clone_fn)(self.value.as_ref()),
1468 vtable: self.vtable.clone(),
1469 }
1470 }
1471}
1472
1473impl Hash for ErasedMaterialKey {
1474 fn hash<H: Hasher>(&self, state: &mut H) {
1475 self.type_id.hash(state);
1476 self.hash.hash(state);
1477 }
1478}
1479
1480impl Default for ErasedMaterialKey {
1481 fn default() -> Self {
1482 Self::new(())
1483 }
1484}
1485
1486#[derive(Default)]
1488pub struct MaterialProperties {
1489 pub render_method: OpaqueRendererMethod,
1492 pub alpha_mode: AlphaMode,
1494 pub mesh_pipeline_key_bits: MeshPipelineKey,
1499 pub depth_bias: f32,
1503 pub reads_view_transmission_texture: bool,
1508 pub render_phase_type: RenderPhaseType,
1509 pub material_layout: Option<BindGroupLayout>,
1510 pub draw_functions: SmallVec<[(InternedDrawFunctionLabel, DrawFunctionId); 4]>,
1512 pub shaders: SmallVec<[(InternedShaderLabel, Handle<Shader>); 3]>,
1515 pub bindless: bool,
1518 pub specialize: Option<
1519 fn(
1520 &MaterialPipeline,
1521 &mut RenderPipelineDescriptor,
1522 &MeshVertexBufferLayoutRef,
1523 ErasedMaterialPipelineKey,
1524 ) -> Result<(), SpecializedMeshPipelineError>,
1525 >,
1526 pub material_key: ErasedMaterialKey,
1529 pub shadows_enabled: bool,
1531 pub prepass_enabled: bool,
1533}
1534
1535impl MaterialProperties {
1536 pub fn get_shader(&self, label: impl ShaderLabel) -> Option<Handle<Shader>> {
1537 self.shaders
1538 .iter()
1539 .find(|(inner_label, _)| inner_label == &label.intern())
1540 .map(|(_, shader)| shader)
1541 .cloned()
1542 }
1543
1544 pub fn add_shader(&mut self, label: impl ShaderLabel, shader: Handle<Shader>) {
1545 self.shaders.push((label.intern(), shader));
1546 }
1547
1548 pub fn get_draw_function(&self, label: impl DrawFunctionLabel) -> Option<DrawFunctionId> {
1549 self.draw_functions
1550 .iter()
1551 .find(|(inner_label, _)| inner_label == &label.intern())
1552 .map(|(_, shader)| shader)
1553 .cloned()
1554 }
1555
1556 pub fn add_draw_function(
1557 &mut self,
1558 label: impl DrawFunctionLabel,
1559 draw_function: DrawFunctionId,
1560 ) {
1561 self.draw_functions.push((label.intern(), draw_function));
1562 }
1563}
1564
1565#[derive(Clone, Copy, Default)]
1566pub enum RenderPhaseType {
1567 #[default]
1568 Opaque,
1569 AlphaMask,
1570 Transmissive,
1571 Transparent,
1572}
1573
1574#[derive(Resource, Default, Deref, DerefMut)]
1580pub struct RenderMaterialBindings(HashMap<UntypedAssetId, MaterialBindingId>);
1581
1582pub struct PreparedMaterial {
1584 pub binding: MaterialBindingId,
1585 pub properties: Arc<MaterialProperties>,
1586}
1587
1588impl<M: Material> ErasedRenderAsset for MeshMaterial3d<M>
1590where
1591 M::Data: PartialEq + Eq + Hash + Clone,
1592{
1593 type SourceAsset = M;
1594 type ErasedAsset = PreparedMaterial;
1595
1596 type Param = (
1597 SRes<RenderDevice>,
1598 SRes<DefaultOpaqueRendererMethod>,
1599 SResMut<MaterialBindGroupAllocators>,
1600 SResMut<RenderMaterialBindings>,
1601 SRes<DrawFunctions<Opaque3d>>,
1602 SRes<DrawFunctions<AlphaMask3d>>,
1603 SRes<DrawFunctions<Transmissive3d>>,
1604 SRes<DrawFunctions<Transparent3d>>,
1605 SRes<DrawFunctions<Opaque3dPrepass>>,
1606 SRes<DrawFunctions<AlphaMask3dPrepass>>,
1607 SRes<DrawFunctions<Opaque3dDeferred>>,
1608 SRes<DrawFunctions<AlphaMask3dDeferred>>,
1609 SRes<DrawFunctions<Shadow>>,
1610 SRes<AssetServer>,
1611 (
1612 Option<SRes<ShadowsEnabled<M>>>,
1613 Option<SRes<PrepassEnabled<M>>>,
1614 M::Param,
1615 ),
1616 );
1617
1618 fn prepare_asset(
1619 material: Self::SourceAsset,
1620 material_id: AssetId<Self::SourceAsset>,
1621 (
1622 render_device,
1623 default_opaque_render_method,
1624 bind_group_allocators,
1625 render_material_bindings,
1626 opaque_draw_functions,
1627 alpha_mask_draw_functions,
1628 transmissive_draw_functions,
1629 transparent_draw_functions,
1630 opaque_prepass_draw_functions,
1631 alpha_mask_prepass_draw_functions,
1632 opaque_deferred_draw_functions,
1633 alpha_mask_deferred_draw_functions,
1634 shadow_draw_functions,
1635 asset_server,
1636 (shadows_enabled, prepass_enabled, material_param),
1637 ): &mut SystemParamItem<Self::Param>,
1638 ) -> Result<Self::ErasedAsset, PrepareAssetError<Self::SourceAsset>> {
1639 let material_layout = M::bind_group_layout(render_device);
1640
1641 let shadows_enabled = shadows_enabled.is_some();
1642 let prepass_enabled = prepass_enabled.is_some();
1643
1644 let draw_opaque_pbr = opaque_draw_functions.read().id::<DrawMaterial>();
1645 let draw_alpha_mask_pbr = alpha_mask_draw_functions.read().id::<DrawMaterial>();
1646 let draw_transmissive_pbr = transmissive_draw_functions.read().id::<DrawMaterial>();
1647 let draw_transparent_pbr = transparent_draw_functions.read().id::<DrawMaterial>();
1648 let draw_opaque_prepass = opaque_prepass_draw_functions.read().get_id::<DrawPrepass>();
1649 let draw_alpha_mask_prepass = alpha_mask_prepass_draw_functions
1650 .read()
1651 .get_id::<DrawPrepass>();
1652 let draw_opaque_deferred = opaque_deferred_draw_functions
1653 .read()
1654 .get_id::<DrawPrepass>();
1655 let draw_alpha_mask_deferred = alpha_mask_deferred_draw_functions
1656 .read()
1657 .get_id::<DrawPrepass>();
1658 let shadow_draw_function_id = shadow_draw_functions.read().get_id::<DrawPrepass>();
1659
1660 let render_method = match material.opaque_render_method() {
1661 OpaqueRendererMethod::Forward => OpaqueRendererMethod::Forward,
1662 OpaqueRendererMethod::Deferred => OpaqueRendererMethod::Deferred,
1663 OpaqueRendererMethod::Auto => default_opaque_render_method.0,
1664 };
1665
1666 let mut mesh_pipeline_key_bits = MeshPipelineKey::empty();
1667 mesh_pipeline_key_bits.set(
1668 MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE,
1669 material.reads_view_transmission_texture(),
1670 );
1671
1672 let reads_view_transmission_texture =
1673 mesh_pipeline_key_bits.contains(MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE);
1674
1675 let render_phase_type = match material.alpha_mode() {
1676 AlphaMode::Blend | AlphaMode::Premultiplied | AlphaMode::Add | AlphaMode::Multiply => {
1677 RenderPhaseType::Transparent
1678 }
1679 _ if reads_view_transmission_texture => RenderPhaseType::Transmissive,
1680 AlphaMode::Opaque | AlphaMode::AlphaToCoverage => RenderPhaseType::Opaque,
1681 AlphaMode::Mask(_) => RenderPhaseType::AlphaMask,
1682 };
1683
1684 let draw_function_id = match render_phase_type {
1685 RenderPhaseType::Opaque => draw_opaque_pbr,
1686 RenderPhaseType::AlphaMask => draw_alpha_mask_pbr,
1687 RenderPhaseType::Transmissive => draw_transmissive_pbr,
1688 RenderPhaseType::Transparent => draw_transparent_pbr,
1689 };
1690 let prepass_draw_function_id = match render_phase_type {
1691 RenderPhaseType::Opaque => draw_opaque_prepass,
1692 RenderPhaseType::AlphaMask => draw_alpha_mask_prepass,
1693 _ => None,
1694 };
1695 let deferred_draw_function_id = match render_phase_type {
1696 RenderPhaseType::Opaque => draw_opaque_deferred,
1697 RenderPhaseType::AlphaMask => draw_alpha_mask_deferred,
1698 _ => None,
1699 };
1700
1701 let mut draw_functions = SmallVec::new();
1702 draw_functions.push((MaterialDrawFunction.intern(), draw_function_id));
1703 if let Some(prepass_draw_function_id) = prepass_draw_function_id {
1704 draw_functions.push((PrepassDrawFunction.intern(), prepass_draw_function_id));
1705 }
1706 if let Some(deferred_draw_function_id) = deferred_draw_function_id {
1707 draw_functions.push((DeferredDrawFunction.intern(), deferred_draw_function_id));
1708 }
1709 if let Some(shadow_draw_function_id) = shadow_draw_function_id {
1710 draw_functions.push((ShadowsDrawFunction.intern(), shadow_draw_function_id));
1711 }
1712
1713 let mut shaders = SmallVec::new();
1714 let mut add_shader = |label: InternedShaderLabel, shader_ref: ShaderRef| {
1715 let mayber_shader = match shader_ref {
1716 ShaderRef::Default => None,
1717 ShaderRef::Handle(handle) => Some(handle),
1718 ShaderRef::Path(path) => Some(asset_server.load(path)),
1719 };
1720 if let Some(shader) = mayber_shader {
1721 shaders.push((label, shader));
1722 }
1723 };
1724 add_shader(MaterialVertexShader.intern(), M::vertex_shader());
1725 add_shader(MaterialFragmentShader.intern(), M::fragment_shader());
1726 add_shader(PrepassVertexShader.intern(), M::prepass_vertex_shader());
1727 add_shader(PrepassFragmentShader.intern(), M::prepass_fragment_shader());
1728 add_shader(DeferredVertexShader.intern(), M::deferred_vertex_shader());
1729 add_shader(
1730 DeferredFragmentShader.intern(),
1731 M::deferred_fragment_shader(),
1732 );
1733
1734 #[cfg(feature = "meshlet")]
1735 {
1736 add_shader(
1737 MeshletFragmentShader.intern(),
1738 M::meshlet_mesh_fragment_shader(),
1739 );
1740 add_shader(
1741 MeshletPrepassFragmentShader.intern(),
1742 M::meshlet_mesh_prepass_fragment_shader(),
1743 );
1744 add_shader(
1745 MeshletDeferredFragmentShader.intern(),
1746 M::meshlet_mesh_deferred_fragment_shader(),
1747 );
1748 }
1749
1750 let bindless = material_uses_bindless_resources::<M>(render_device);
1751 let bind_group_data = material.bind_group_data();
1752 let material_key = ErasedMaterialKey::new(bind_group_data);
1753 fn specialize<M: Material>(
1754 pipeline: &MaterialPipeline,
1755 descriptor: &mut RenderPipelineDescriptor,
1756 mesh_layout: &MeshVertexBufferLayoutRef,
1757 erased_key: ErasedMaterialPipelineKey,
1758 ) -> Result<(), SpecializedMeshPipelineError>
1759 where
1760 M::Data: Hash + Clone,
1761 {
1762 let material_key = erased_key.material_key.to_key();
1763 M::specialize(
1764 pipeline,
1765 descriptor,
1766 mesh_layout,
1767 MaterialPipelineKey {
1768 mesh_key: erased_key.mesh_key,
1769 bind_group_data: material_key,
1770 },
1771 )
1772 }
1773
1774 match material.unprepared_bind_group(&material_layout, render_device, material_param, false)
1775 {
1776 Ok(unprepared) => {
1777 let bind_group_allocator =
1778 bind_group_allocators.get_mut(&TypeId::of::<M>()).unwrap();
1779 let binding = match render_material_bindings.entry(material_id.into()) {
1781 Entry::Occupied(mut occupied_entry) => {
1782 bind_group_allocator.free(*occupied_entry.get());
1787 let new_binding =
1788 bind_group_allocator.allocate_unprepared(unprepared, &material_layout);
1789 *occupied_entry.get_mut() = new_binding;
1790 new_binding
1791 }
1792 Entry::Vacant(vacant_entry) => *vacant_entry.insert(
1793 bind_group_allocator.allocate_unprepared(unprepared, &material_layout),
1794 ),
1795 };
1796
1797 Ok(PreparedMaterial {
1798 binding,
1799 properties: Arc::new(MaterialProperties {
1800 alpha_mode: material.alpha_mode(),
1801 depth_bias: material.depth_bias(),
1802 reads_view_transmission_texture,
1803 render_phase_type,
1804 render_method,
1805 mesh_pipeline_key_bits,
1806 material_layout: Some(material_layout),
1807 draw_functions,
1808 shaders,
1809 bindless,
1810 specialize: Some(specialize::<M>),
1811 material_key,
1812 shadows_enabled,
1813 prepass_enabled,
1814 }),
1815 })
1816 }
1817
1818 Err(AsBindGroupError::RetryNextUpdate) => {
1819 Err(PrepareAssetError::RetryNextUpdate(material))
1820 }
1821
1822 Err(AsBindGroupError::CreateBindGroupDirectly) => {
1823 match material.as_bind_group(&material_layout, render_device, material_param) {
1828 Ok(prepared_bind_group) => {
1829 let bind_group_allocator =
1830 bind_group_allocators.get_mut(&TypeId::of::<M>()).unwrap();
1831 let material_binding_id =
1833 bind_group_allocator.allocate_prepared(prepared_bind_group);
1834 render_material_bindings.insert(material_id.into(), material_binding_id);
1835
1836 Ok(PreparedMaterial {
1837 binding: material_binding_id,
1838 properties: Arc::new(MaterialProperties {
1839 alpha_mode: material.alpha_mode(),
1840 depth_bias: material.depth_bias(),
1841 reads_view_transmission_texture,
1842 render_phase_type,
1843 render_method,
1844 mesh_pipeline_key_bits,
1845 material_layout: Some(material_layout),
1846 draw_functions,
1847 shaders,
1848 bindless,
1849 specialize: Some(specialize::<M>),
1850 material_key,
1851 shadows_enabled,
1852 prepass_enabled,
1853 }),
1854 })
1855 }
1856
1857 Err(AsBindGroupError::RetryNextUpdate) => {
1858 Err(PrepareAssetError::RetryNextUpdate(material))
1859 }
1860
1861 Err(other) => Err(PrepareAssetError::AsBindGroupError(other)),
1862 }
1863 }
1864
1865 Err(other) => Err(PrepareAssetError::AsBindGroupError(other)),
1866 }
1867 }
1868
1869 fn unload_asset(
1870 source_asset: AssetId<Self::SourceAsset>,
1871 (_, _, bind_group_allocators, render_material_bindings, ..): &mut SystemParamItem<
1872 Self::Param,
1873 >,
1874 ) {
1875 let Some(material_binding_id) = render_material_bindings.remove(&source_asset.untyped())
1876 else {
1877 return;
1878 };
1879 let bind_group_allactor = bind_group_allocators.get_mut(&TypeId::of::<M>()).unwrap();
1880 bind_group_allactor.free(material_binding_id);
1881 }
1882}
1883
1884pub fn prepare_material_bind_groups(
1887 mut allocators: ResMut<MaterialBindGroupAllocators>,
1888 render_device: Res<RenderDevice>,
1889 fallback_image: Res<FallbackImage>,
1890 fallback_resources: Res<FallbackBindlessResources>,
1891) {
1892 for (_, allocator) in allocators.iter_mut() {
1893 allocator.prepare_bind_groups(&render_device, &fallback_resources, &fallback_image);
1894 }
1895}
1896
1897pub fn write_material_bind_group_buffers(
1903 mut allocators: ResMut<MaterialBindGroupAllocators>,
1904 render_device: Res<RenderDevice>,
1905 render_queue: Res<RenderQueue>,
1906) {
1907 for (_, allocator) in allocators.iter_mut() {
1908 allocator.write_buffers(&render_device, &render_queue);
1909 }
1910}
1911
1912#[derive(Resource, Debug)]
1914pub struct ShadowsEnabled<M: Material>(PhantomData<M>);
1915
1916impl<M: Material> Default for ShadowsEnabled<M> {
1917 fn default() -> Self {
1918 Self(PhantomData)
1919 }
1920}