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::change_detection::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 #[inline]
185 fn enable_prepass() -> bool {
186 true
187 }
188
189 #[inline]
191 fn enable_shadows() -> bool {
192 true
193 }
194
195 fn prepass_vertex_shader() -> ShaderRef {
201 ShaderRef::Default
202 }
203
204 fn prepass_fragment_shader() -> ShaderRef {
210 ShaderRef::Default
211 }
212
213 fn deferred_vertex_shader() -> ShaderRef {
216 ShaderRef::Default
217 }
218
219 fn deferred_fragment_shader() -> ShaderRef {
222 ShaderRef::Default
223 }
224
225 #[cfg(feature = "meshlet")]
232 fn meshlet_mesh_fragment_shader() -> ShaderRef {
233 ShaderRef::Default
234 }
235
236 #[cfg(feature = "meshlet")]
243 fn meshlet_mesh_prepass_fragment_shader() -> ShaderRef {
244 ShaderRef::Default
245 }
246
247 #[cfg(feature = "meshlet")]
254 fn meshlet_mesh_deferred_fragment_shader() -> ShaderRef {
255 ShaderRef::Default
256 }
257
258 #[expect(
261 unused_variables,
262 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."
263 )]
264 #[inline]
265 fn specialize(
266 pipeline: &MaterialPipeline,
267 descriptor: &mut RenderPipelineDescriptor,
268 layout: &MeshVertexBufferLayoutRef,
269 key: MaterialPipelineKey<Self>,
270 ) -> Result<(), SpecializedMeshPipelineError> {
271 Ok(())
272 }
273}
274
275#[derive(Default)]
276pub struct MaterialsPlugin {
277 pub debug_flags: RenderDebugFlags,
279}
280
281impl Plugin for MaterialsPlugin {
282 fn build(&self, app: &mut App) {
283 app.add_plugins((PrepassPipelinePlugin, PrepassPlugin::new(self.debug_flags)));
284 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
285 render_app
286 .init_resource::<EntitySpecializationTicks>()
287 .init_resource::<SpecializedMaterialPipelineCache>()
288 .init_resource::<SpecializedMeshPipelines<MaterialPipelineSpecializer>>()
289 .init_resource::<LightKeyCache>()
290 .init_resource::<LightSpecializationTicks>()
291 .init_resource::<SpecializedShadowMaterialPipelineCache>()
292 .init_resource::<DrawFunctions<Shadow>>()
293 .init_resource::<RenderMaterialInstances>()
294 .init_resource::<MaterialBindGroupAllocators>()
295 .add_render_command::<Shadow, DrawPrepass>()
296 .add_render_command::<Transmissive3d, DrawMaterial>()
297 .add_render_command::<Transparent3d, DrawMaterial>()
298 .add_render_command::<Opaque3d, DrawMaterial>()
299 .add_render_command::<AlphaMask3d, DrawMaterial>()
300 .add_systems(RenderStartup, init_material_pipeline)
301 .add_systems(
302 Render,
303 (
304 specialize_material_meshes
305 .in_set(RenderSystems::PrepareMeshes)
306 .after(prepare_assets::<RenderMesh>)
307 .after(collect_meshes_for_gpu_building)
308 .after(set_mesh_motion_vector_flags),
309 queue_material_meshes.in_set(RenderSystems::QueueMeshes),
310 ),
311 )
312 .add_systems(
313 Render,
314 (
315 prepare_material_bind_groups,
316 write_material_bind_group_buffers,
317 )
318 .chain()
319 .in_set(RenderSystems::PrepareBindGroups),
320 )
321 .add_systems(
322 Render,
323 (
324 check_views_lights_need_specialization.in_set(RenderSystems::PrepareAssets),
325 specialize_shadows
328 .in_set(RenderSystems::ManageViews)
329 .after(prepare_lights),
330 queue_shadows.in_set(RenderSystems::QueueMeshes),
331 ),
332 );
333 }
334 }
335}
336
337pub struct MaterialPlugin<M: Material> {
340 pub debug_flags: RenderDebugFlags,
342 pub _marker: PhantomData<M>,
343}
344
345impl<M: Material> Default for MaterialPlugin<M> {
346 fn default() -> Self {
347 Self {
348 debug_flags: RenderDebugFlags::default(),
349 _marker: Default::default(),
350 }
351 }
352}
353
354impl<M: Material> Plugin for MaterialPlugin<M>
355where
356 M::Data: PartialEq + Eq + Hash + Clone,
357{
358 fn build(&self, app: &mut App) {
359 app.init_asset::<M>()
360 .register_type::<MeshMaterial3d<M>>()
361 .init_resource::<EntitiesNeedingSpecialization<M>>()
362 .add_plugins((ErasedRenderAssetPlugin::<MeshMaterial3d<M>>::default(),))
363 .add_systems(
364 PostUpdate,
365 (
366 mark_meshes_as_changed_if_their_materials_changed::<M>.ambiguous_with_all(),
367 check_entities_needing_specialization::<M>.after(AssetEventSystems),
368 )
369 .after(mark_3d_meshes_as_changed_if_their_assets_changed),
370 );
371
372 if M::enable_shadows() {
373 app.add_systems(
374 PostUpdate,
375 check_light_entities_needing_specialization::<M>
376 .after(check_entities_needing_specialization::<M>),
377 );
378 }
379
380 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
381 render_app
382 .add_systems(RenderStartup, add_material_bind_group_allocator::<M>)
383 .add_systems(
384 ExtractSchedule,
385 (
386 extract_mesh_materials::<M>.in_set(MaterialExtractionSystems),
387 early_sweep_material_instances::<M>
388 .after(MaterialExtractionSystems)
389 .before(late_sweep_material_instances),
390 extract_entities_needs_specialization::<M>
394 .in_set(MaterialExtractEntitiesNeedingSpecializationSystems),
395 sweep_entities_needing_specialization::<M>
396 .after(MaterialExtractEntitiesNeedingSpecializationSystems)
397 .after(MaterialExtractionSystems)
398 .after(extract_cameras)
399 .before(late_sweep_material_instances),
400 ),
401 );
402 }
403 }
404}
405
406fn add_material_bind_group_allocator<M: Material>(
407 render_device: Res<RenderDevice>,
408 mut bind_group_allocators: ResMut<MaterialBindGroupAllocators>,
409) {
410 bind_group_allocators.insert(
411 TypeId::of::<M>(),
412 MaterialBindGroupAllocator::new(
413 &render_device,
414 M::label(),
415 material_uses_bindless_resources::<M>(&render_device)
416 .then(|| M::bindless_descriptor())
417 .flatten(),
418 M::bind_group_layout_descriptor(&render_device),
419 M::bindless_slot_count(),
420 ),
421 );
422}
423
424pub(crate) static DUMMY_MESH_MATERIAL: AssetId<StandardMaterial> =
430 AssetId::<StandardMaterial>::invalid();
431
432pub struct MaterialPipelineKey<M: Material> {
434 pub mesh_key: MeshPipelineKey,
435 pub bind_group_data: M::Data,
436}
437
438#[derive(Clone, Debug, PartialEq, Eq, Hash)]
439pub struct ErasedMaterialPipelineKey {
440 pub mesh_key: MeshPipelineKey,
441 pub material_key: ErasedMaterialKey,
442 pub type_id: TypeId,
443}
444
445#[derive(Resource, Clone)]
447pub struct MaterialPipeline {
448 pub mesh_pipeline: MeshPipeline,
449}
450
451pub struct MaterialPipelineSpecializer {
452 pub(crate) pipeline: MaterialPipeline,
453 pub(crate) properties: Arc<MaterialProperties>,
454}
455
456impl SpecializedMeshPipeline for MaterialPipelineSpecializer {
457 type Key = ErasedMaterialPipelineKey;
458
459 fn specialize(
460 &self,
461 key: Self::Key,
462 layout: &MeshVertexBufferLayoutRef,
463 ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
464 let mut descriptor = self
465 .pipeline
466 .mesh_pipeline
467 .specialize(key.mesh_key, layout)?;
468 descriptor.vertex.shader_defs.push(ShaderDefVal::UInt(
469 "MATERIAL_BIND_GROUP".into(),
470 MATERIAL_BIND_GROUP_INDEX as u32,
471 ));
472 if let Some(ref mut fragment) = descriptor.fragment {
473 fragment.shader_defs.push(ShaderDefVal::UInt(
474 "MATERIAL_BIND_GROUP".into(),
475 MATERIAL_BIND_GROUP_INDEX as u32,
476 ));
477 };
478 if let Some(vertex_shader) = self.properties.get_shader(MaterialVertexShader) {
479 descriptor.vertex.shader = vertex_shader.clone();
480 }
481
482 if let Some(fragment_shader) = self.properties.get_shader(MaterialFragmentShader) {
483 descriptor.fragment.as_mut().unwrap().shader = fragment_shader.clone();
484 }
485
486 descriptor
487 .layout
488 .insert(3, self.properties.material_layout.as_ref().unwrap().clone());
489
490 if let Some(specialize) = self.properties.specialize {
491 specialize(&self.pipeline, &mut descriptor, layout, key)?;
492 }
493
494 if self.properties.bindless {
496 descriptor.vertex.shader_defs.push("BINDLESS".into());
497 if let Some(ref mut fragment) = descriptor.fragment {
498 fragment.shader_defs.push("BINDLESS".into());
499 }
500 }
501
502 Ok(descriptor)
503 }
504}
505
506pub fn init_material_pipeline(mut commands: Commands, mesh_pipeline: Res<MeshPipeline>) {
507 commands.insert_resource(MaterialPipeline {
508 mesh_pipeline: mesh_pipeline.clone(),
509 });
510}
511
512pub type DrawMaterial = (
513 SetItemPipeline,
514 SetMeshViewBindGroup<0>,
515 SetMeshViewBindingArrayBindGroup<1>,
516 SetMeshBindGroup<2>,
517 SetMaterialBindGroup<MATERIAL_BIND_GROUP_INDEX>,
518 DrawMesh,
519);
520
521pub struct SetMaterialBindGroup<const I: usize>;
523impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMaterialBindGroup<I> {
524 type Param = (
525 SRes<ErasedRenderAssets<PreparedMaterial>>,
526 SRes<RenderMaterialInstances>,
527 SRes<MaterialBindGroupAllocators>,
528 );
529 type ViewQuery = ();
530 type ItemQuery = ();
531
532 #[inline]
533 fn render<'w>(
534 item: &P,
535 _view: (),
536 _item_query: Option<()>,
537 (materials, material_instances, material_bind_group_allocator): SystemParamItem<
538 'w,
539 '_,
540 Self::Param,
541 >,
542 pass: &mut TrackedRenderPass<'w>,
543 ) -> RenderCommandResult {
544 let materials = materials.into_inner();
545 let material_instances = material_instances.into_inner();
546 let material_bind_group_allocators = material_bind_group_allocator.into_inner();
547
548 let Some(material_instance) = material_instances.instances.get(&item.main_entity()) else {
549 return RenderCommandResult::Skip;
550 };
551 let Some(material_bind_group_allocator) =
552 material_bind_group_allocators.get(&material_instance.asset_id.type_id())
553 else {
554 return RenderCommandResult::Skip;
555 };
556 let Some(material) = materials.get(material_instance.asset_id) else {
557 return RenderCommandResult::Skip;
558 };
559 let Some(material_bind_group) = material_bind_group_allocator.get(material.binding.group)
560 else {
561 return RenderCommandResult::Skip;
562 };
563 let Some(bind_group) = material_bind_group.bind_group() else {
564 return RenderCommandResult::Skip;
565 };
566 pass.set_bind_group(I, bind_group, &[]);
567 RenderCommandResult::Success
568 }
569}
570
571#[derive(Resource, Default)]
573pub struct RenderMaterialInstances {
574 pub instances: MainEntityHashMap<RenderMaterialInstance>,
577 pub current_change_tick: Tick,
581}
582
583impl RenderMaterialInstances {
584 pub(crate) fn mesh_material(&self, entity: MainEntity) -> UntypedAssetId {
591 match self.instances.get(&entity) {
592 Some(render_instance) => render_instance.asset_id,
593 None => DUMMY_MESH_MATERIAL.into(),
594 }
595 }
596}
597
598pub struct RenderMaterialInstance {
603 pub asset_id: UntypedAssetId,
605 pub last_change_tick: Tick,
608}
609
610#[derive(SystemSet, Clone, PartialEq, Eq, Debug, Hash)]
612pub struct MaterialExtractionSystems;
613
614#[derive(SystemSet, Clone, PartialEq, Eq, Debug, Hash)]
617pub struct MaterialExtractEntitiesNeedingSpecializationSystems;
618
619pub const fn alpha_mode_pipeline_key(alpha_mode: AlphaMode, msaa: &Msaa) -> MeshPipelineKey {
620 match alpha_mode {
621 AlphaMode::Premultiplied | AlphaMode::Add => MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA,
624 AlphaMode::Blend => MeshPipelineKey::BLEND_ALPHA,
625 AlphaMode::Multiply => MeshPipelineKey::BLEND_MULTIPLY,
626 AlphaMode::Mask(_) => MeshPipelineKey::MAY_DISCARD,
627 AlphaMode::AlphaToCoverage => match *msaa {
628 Msaa::Off => MeshPipelineKey::MAY_DISCARD,
629 _ => MeshPipelineKey::BLEND_ALPHA_TO_COVERAGE,
630 },
631 _ => MeshPipelineKey::NONE,
632 }
633}
634
635pub const fn tonemapping_pipeline_key(tonemapping: Tonemapping) -> MeshPipelineKey {
636 match tonemapping {
637 Tonemapping::None => MeshPipelineKey::TONEMAP_METHOD_NONE,
638 Tonemapping::Reinhard => MeshPipelineKey::TONEMAP_METHOD_REINHARD,
639 Tonemapping::ReinhardLuminance => MeshPipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE,
640 Tonemapping::AcesFitted => MeshPipelineKey::TONEMAP_METHOD_ACES_FITTED,
641 Tonemapping::AgX => MeshPipelineKey::TONEMAP_METHOD_AGX,
642 Tonemapping::SomewhatBoringDisplayTransform => {
643 MeshPipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM
644 }
645 Tonemapping::TonyMcMapface => MeshPipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE,
646 Tonemapping::BlenderFilmic => MeshPipelineKey::TONEMAP_METHOD_BLENDER_FILMIC,
647 }
648}
649
650pub const fn screen_space_specular_transmission_pipeline_key(
651 screen_space_transmissive_blur_quality: ScreenSpaceTransmissionQuality,
652) -> MeshPipelineKey {
653 match screen_space_transmissive_blur_quality {
654 ScreenSpaceTransmissionQuality::Low => {
655 MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_LOW
656 }
657 ScreenSpaceTransmissionQuality::Medium => {
658 MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_MEDIUM
659 }
660 ScreenSpaceTransmissionQuality::High => {
661 MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_HIGH
662 }
663 ScreenSpaceTransmissionQuality::Ultra => {
664 MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_ULTRA
665 }
666 }
667}
668
669fn mark_meshes_as_changed_if_their_materials_changed<M>(
684 mut changed_meshes_query: Query<
685 &mut Mesh3d,
686 Or<(Changed<MeshMaterial3d<M>>, AssetChanged<MeshMaterial3d<M>>)>,
687 >,
688) where
689 M: Material,
690{
691 for mut mesh in &mut changed_meshes_query {
692 mesh.set_changed();
693 }
694}
695
696fn extract_mesh_materials<M: Material>(
699 mut material_instances: ResMut<RenderMaterialInstances>,
700 changed_meshes_query: Extract<
701 Query<
702 (Entity, &ViewVisibility, &MeshMaterial3d<M>),
703 Or<(Changed<ViewVisibility>, Changed<MeshMaterial3d<M>>)>,
704 >,
705 >,
706) {
707 let last_change_tick = material_instances.current_change_tick;
708
709 for (entity, view_visibility, material) in &changed_meshes_query {
710 if view_visibility.get() {
711 material_instances.instances.insert(
712 entity.into(),
713 RenderMaterialInstance {
714 asset_id: material.id().untyped(),
715 last_change_tick,
716 },
717 );
718 } else {
719 material_instances
720 .instances
721 .remove(&MainEntity::from(entity));
722 }
723 }
724}
725
726fn early_sweep_material_instances<M>(
741 mut material_instances: ResMut<RenderMaterialInstances>,
742 mut removed_materials_query: Extract<RemovedComponents<MeshMaterial3d<M>>>,
743) where
744 M: Material,
745{
746 let last_change_tick = material_instances.current_change_tick;
747
748 for entity in removed_materials_query.read() {
749 if let Entry::Occupied(occupied_entry) = material_instances.instances.entry(entity.into()) {
750 if occupied_entry.get().last_change_tick != last_change_tick {
752 occupied_entry.remove();
753 }
754 }
755 }
756}
757
758pub fn late_sweep_material_instances(
765 mut material_instances: ResMut<RenderMaterialInstances>,
766 mut removed_meshes_query: Extract<RemovedComponents<Mesh3d>>,
767) {
768 let last_change_tick = material_instances.current_change_tick;
769
770 for entity in removed_meshes_query.read() {
771 if let Entry::Occupied(occupied_entry) = material_instances.instances.entry(entity.into()) {
772 if occupied_entry.get().last_change_tick != last_change_tick {
776 occupied_entry.remove();
777 }
778 }
779 }
780
781 material_instances
782 .current_change_tick
783 .set(last_change_tick.get() + 1);
784}
785
786pub fn extract_entities_needs_specialization<M>(
787 entities_needing_specialization: Extract<Res<EntitiesNeedingSpecialization<M>>>,
788 mut entity_specialization_ticks: ResMut<EntitySpecializationTicks>,
789 render_material_instances: Res<RenderMaterialInstances>,
790 ticks: SystemChangeTick,
791) where
792 M: Material,
793{
794 for entity in entities_needing_specialization.iter() {
795 entity_specialization_ticks.insert(
797 (*entity).into(),
798 EntitySpecializationTickPair {
799 system_tick: ticks.this_run(),
800 material_instances_tick: render_material_instances.current_change_tick,
801 },
802 );
803 }
804}
805
806pub fn sweep_entities_needing_specialization<M>(
821 mut entity_specialization_ticks: ResMut<EntitySpecializationTicks>,
822 mut removed_mesh_material_components: Extract<RemovedComponents<MeshMaterial3d<M>>>,
823 mut specialized_material_pipeline_cache: ResMut<SpecializedMaterialPipelineCache>,
824 mut specialized_prepass_material_pipeline_cache: Option<
825 ResMut<SpecializedPrepassMaterialPipelineCache>,
826 >,
827 mut specialized_shadow_material_pipeline_cache: Option<
828 ResMut<SpecializedShadowMaterialPipelineCache>,
829 >,
830 render_material_instances: Res<RenderMaterialInstances>,
831 views: Query<&ExtractedView>,
832) where
833 M: Material,
834{
835 for entity in removed_mesh_material_components.read() {
847 if entity_specialization_ticks
851 .get(&MainEntity::from(entity))
852 .is_some_and(|ticks| {
853 ticks.material_instances_tick == render_material_instances.current_change_tick
854 })
855 {
856 continue;
857 }
858
859 entity_specialization_ticks.remove(&MainEntity::from(entity));
860 for view in views {
861 if let Some(cache) =
862 specialized_material_pipeline_cache.get_mut(&view.retained_view_entity)
863 {
864 cache.remove(&MainEntity::from(entity));
865 }
866 if let Some(cache) = specialized_prepass_material_pipeline_cache
867 .as_mut()
868 .and_then(|c| c.get_mut(&view.retained_view_entity))
869 {
870 cache.remove(&MainEntity::from(entity));
871 }
872 if let Some(cache) = specialized_shadow_material_pipeline_cache
873 .as_mut()
874 .and_then(|c| c.get_mut(&view.retained_view_entity))
875 {
876 cache.remove(&MainEntity::from(entity));
877 }
878 }
879 }
880}
881
882#[derive(Resource, Deref, DerefMut, Clone, Debug)]
883pub struct EntitiesNeedingSpecialization<M> {
884 #[deref]
885 pub entities: Vec<Entity>,
886 _marker: PhantomData<M>,
887}
888
889impl<M> Default for EntitiesNeedingSpecialization<M> {
890 fn default() -> Self {
891 Self {
892 entities: Default::default(),
893 _marker: Default::default(),
894 }
895 }
896}
897
898#[derive(Resource, Deref, DerefMut, Default, Clone, Debug)]
904pub struct EntitySpecializationTicks {
905 #[deref]
911 pub entities: MainEntityHashMap<EntitySpecializationTickPair>,
912}
913
914#[derive(Clone, Copy, Debug)]
944pub struct EntitySpecializationTickPair {
945 pub system_tick: Tick,
947 pub material_instances_tick: Tick,
950}
951
952#[derive(Resource, Deref, DerefMut, Default)]
954pub struct SpecializedMaterialPipelineCache {
955 #[deref]
957 map: HashMap<RetainedViewEntity, SpecializedMaterialViewPipelineCache>,
958}
959
960#[derive(Deref, DerefMut, Default)]
963pub struct SpecializedMaterialViewPipelineCache {
964 #[deref]
966 map: MainEntityHashMap<(Tick, CachedRenderPipelineId)>,
967}
968
969pub fn check_entities_needing_specialization<M>(
970 needs_specialization: Query<
971 Entity,
972 (
973 Or<(
974 Changed<Mesh3d>,
975 AssetChanged<Mesh3d>,
976 Changed<MeshMaterial3d<M>>,
977 AssetChanged<MeshMaterial3d<M>>,
978 )>,
979 With<MeshMaterial3d<M>>,
980 ),
981 >,
982 mut par_local: Local<Parallel<Vec<Entity>>>,
983 mut entities_needing_specialization: ResMut<EntitiesNeedingSpecialization<M>>,
984) where
985 M: Material,
986{
987 entities_needing_specialization.clear();
988
989 needs_specialization
990 .par_iter()
991 .for_each(|entity| par_local.borrow_local_mut().push(entity));
992
993 par_local.drain_into(&mut entities_needing_specialization);
994}
995
996pub fn specialize_material_meshes(
997 render_meshes: Res<RenderAssets<RenderMesh>>,
998 render_materials: Res<ErasedRenderAssets<PreparedMaterial>>,
999 render_mesh_instances: Res<RenderMeshInstances>,
1000 render_material_instances: Res<RenderMaterialInstances>,
1001 render_lightmaps: Res<RenderLightmaps>,
1002 render_visibility_ranges: Res<RenderVisibilityRanges>,
1003 (
1004 opaque_render_phases,
1005 alpha_mask_render_phases,
1006 transmissive_render_phases,
1007 transparent_render_phases,
1008 ): (
1009 Res<ViewBinnedRenderPhases<Opaque3d>>,
1010 Res<ViewBinnedRenderPhases<AlphaMask3d>>,
1011 Res<ViewSortedRenderPhases<Transmissive3d>>,
1012 Res<ViewSortedRenderPhases<Transparent3d>>,
1013 ),
1014 views: Query<(&ExtractedView, &RenderVisibleEntities)>,
1015 view_key_cache: Res<ViewKeyCache>,
1016 entity_specialization_ticks: Res<EntitySpecializationTicks>,
1017 view_specialization_ticks: Res<ViewSpecializationTicks>,
1018 mut specialized_material_pipeline_cache: ResMut<SpecializedMaterialPipelineCache>,
1019 mut pipelines: ResMut<SpecializedMeshPipelines<MaterialPipelineSpecializer>>,
1020 pipeline: Res<MaterialPipeline>,
1021 pipeline_cache: Res<PipelineCache>,
1022 ticks: SystemChangeTick,
1023) {
1024 let mut all_views: HashSet<RetainedViewEntity, FixedHasher> = HashSet::default();
1027
1028 for (view, visible_entities) in &views {
1029 all_views.insert(view.retained_view_entity);
1030
1031 if !transparent_render_phases.contains_key(&view.retained_view_entity)
1032 && !opaque_render_phases.contains_key(&view.retained_view_entity)
1033 && !alpha_mask_render_phases.contains_key(&view.retained_view_entity)
1034 && !transmissive_render_phases.contains_key(&view.retained_view_entity)
1035 {
1036 continue;
1037 }
1038
1039 let Some(view_key) = view_key_cache.get(&view.retained_view_entity) else {
1040 continue;
1041 };
1042
1043 let view_tick = view_specialization_ticks
1044 .get(&view.retained_view_entity)
1045 .unwrap();
1046 let view_specialized_material_pipeline_cache = specialized_material_pipeline_cache
1047 .entry(view.retained_view_entity)
1048 .or_default();
1049
1050 for (_, visible_entity) in visible_entities.iter::<Mesh3d>() {
1051 let Some(material_instance) = render_material_instances.instances.get(visible_entity)
1052 else {
1053 continue;
1054 };
1055 let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(*visible_entity)
1056 else {
1057 continue;
1058 };
1059 let entity_tick = entity_specialization_ticks
1060 .get(visible_entity)
1061 .unwrap()
1062 .system_tick;
1063 let last_specialized_tick = view_specialized_material_pipeline_cache
1064 .get(visible_entity)
1065 .map(|(tick, _)| *tick);
1066 let needs_specialization = last_specialized_tick.is_none_or(|tick| {
1067 view_tick.is_newer_than(tick, ticks.this_run())
1068 || entity_tick.is_newer_than(tick, ticks.this_run())
1069 });
1070 if !needs_specialization {
1071 continue;
1072 }
1073 let Some(mesh) = render_meshes.get(mesh_instance.mesh_asset_id) else {
1074 continue;
1075 };
1076 let Some(material) = render_materials.get(material_instance.asset_id) else {
1077 continue;
1078 };
1079
1080 let mut mesh_pipeline_key_bits = material.properties.mesh_pipeline_key_bits;
1081 mesh_pipeline_key_bits.insert(alpha_mode_pipeline_key(
1082 material.properties.alpha_mode,
1083 &Msaa::from_samples(view_key.msaa_samples()),
1084 ));
1085 let mut mesh_key = *view_key
1086 | MeshPipelineKey::from_bits_retain(mesh.key_bits.bits())
1087 | mesh_pipeline_key_bits;
1088
1089 if let Some(lightmap) = render_lightmaps.render_lightmaps.get(visible_entity) {
1090 mesh_key |= MeshPipelineKey::LIGHTMAPPED;
1091
1092 if lightmap.bicubic_sampling {
1093 mesh_key |= MeshPipelineKey::LIGHTMAP_BICUBIC_SAMPLING;
1094 }
1095 }
1096
1097 if render_visibility_ranges.entity_has_crossfading_visibility_ranges(*visible_entity) {
1098 mesh_key |= MeshPipelineKey::VISIBILITY_RANGE_DITHER;
1099 }
1100
1101 if view_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
1102 if mesh_instance
1104 .flags
1105 .contains(RenderMeshInstanceFlags::HAS_PREVIOUS_SKIN)
1106 {
1107 mesh_key |= MeshPipelineKey::HAS_PREVIOUS_SKIN;
1108 }
1109 if mesh_instance
1110 .flags
1111 .contains(RenderMeshInstanceFlags::HAS_PREVIOUS_MORPH)
1112 {
1113 mesh_key |= MeshPipelineKey::HAS_PREVIOUS_MORPH;
1114 }
1115 }
1116
1117 let erased_key = ErasedMaterialPipelineKey {
1118 type_id: material_instance.asset_id.type_id(),
1119 mesh_key,
1120 material_key: material.properties.material_key.clone(),
1121 };
1122 let material_pipeline_specializer = MaterialPipelineSpecializer {
1123 pipeline: pipeline.clone(),
1124 properties: material.properties.clone(),
1125 };
1126 let pipeline_id = pipelines.specialize(
1127 &pipeline_cache,
1128 &material_pipeline_specializer,
1129 erased_key,
1130 &mesh.layout,
1131 );
1132 let pipeline_id = match pipeline_id {
1133 Ok(id) => id,
1134 Err(err) => {
1135 error!("{}", err);
1136 continue;
1137 }
1138 };
1139
1140 view_specialized_material_pipeline_cache
1141 .insert(*visible_entity, (ticks.this_run(), pipeline_id));
1142 }
1143 }
1144
1145 specialized_material_pipeline_cache
1147 .retain(|retained_view_entity, _| all_views.contains(retained_view_entity));
1148}
1149
1150pub fn queue_material_meshes(
1153 render_materials: Res<ErasedRenderAssets<PreparedMaterial>>,
1154 render_mesh_instances: Res<RenderMeshInstances>,
1155 render_material_instances: Res<RenderMaterialInstances>,
1156 mesh_allocator: Res<MeshAllocator>,
1157 gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
1158 mut opaque_render_phases: ResMut<ViewBinnedRenderPhases<Opaque3d>>,
1159 mut alpha_mask_render_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3d>>,
1160 mut transmissive_render_phases: ResMut<ViewSortedRenderPhases<Transmissive3d>>,
1161 mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
1162 views: Query<(&ExtractedView, &RenderVisibleEntities)>,
1163 specialized_material_pipeline_cache: ResMut<SpecializedMaterialPipelineCache>,
1164) {
1165 for (view, visible_entities) in &views {
1166 let (
1167 Some(opaque_phase),
1168 Some(alpha_mask_phase),
1169 Some(transmissive_phase),
1170 Some(transparent_phase),
1171 ) = (
1172 opaque_render_phases.get_mut(&view.retained_view_entity),
1173 alpha_mask_render_phases.get_mut(&view.retained_view_entity),
1174 transmissive_render_phases.get_mut(&view.retained_view_entity),
1175 transparent_render_phases.get_mut(&view.retained_view_entity),
1176 )
1177 else {
1178 continue;
1179 };
1180
1181 let Some(view_specialized_material_pipeline_cache) =
1182 specialized_material_pipeline_cache.get(&view.retained_view_entity)
1183 else {
1184 continue;
1185 };
1186
1187 let rangefinder = view.rangefinder3d();
1188 for (render_entity, visible_entity) in visible_entities.iter::<Mesh3d>() {
1189 let Some((current_change_tick, pipeline_id)) = view_specialized_material_pipeline_cache
1190 .get(visible_entity)
1191 .map(|(current_change_tick, pipeline_id)| (*current_change_tick, *pipeline_id))
1192 else {
1193 continue;
1194 };
1195
1196 if opaque_phase.validate_cached_entity(*visible_entity, current_change_tick)
1198 || alpha_mask_phase.validate_cached_entity(*visible_entity, current_change_tick)
1199 {
1200 continue;
1201 }
1202
1203 let Some(material_instance) = render_material_instances.instances.get(visible_entity)
1204 else {
1205 continue;
1206 };
1207 let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(*visible_entity)
1208 else {
1209 continue;
1210 };
1211 let Some(material) = render_materials.get(material_instance.asset_id) else {
1212 continue;
1213 };
1214
1215 let (vertex_slab, index_slab) = mesh_allocator.mesh_slabs(&mesh_instance.mesh_asset_id);
1217
1218 match material.properties.render_phase_type {
1219 RenderPhaseType::Transmissive => {
1220 let distance = rangefinder.distance(&mesh_instance.center)
1221 + material.properties.depth_bias;
1222 let Some(draw_function) = material
1223 .properties
1224 .get_draw_function(MainPassTransmissiveDrawFunction)
1225 else {
1226 continue;
1227 };
1228 transmissive_phase.add(Transmissive3d {
1229 entity: (*render_entity, *visible_entity),
1230 draw_function,
1231 pipeline: pipeline_id,
1232 distance,
1233 batch_range: 0..1,
1234 extra_index: PhaseItemExtraIndex::None,
1235 indexed: index_slab.is_some(),
1236 });
1237 }
1238 RenderPhaseType::Opaque => {
1239 if material.properties.render_method == OpaqueRendererMethod::Deferred {
1240 opaque_phase.update_cache(*visible_entity, None, current_change_tick);
1245 continue;
1246 }
1247 let Some(draw_function) = material
1248 .properties
1249 .get_draw_function(MainPassOpaqueDrawFunction)
1250 else {
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 Some(draw_function) = material
1279 .properties
1280 .get_draw_function(MainPassAlphaMaskDrawFunction)
1281 else {
1282 continue;
1283 };
1284 let batch_set_key = OpaqueNoLightmap3dBatchSetKey {
1285 draw_function,
1286 pipeline: pipeline_id,
1287 material_bind_group_index: Some(material.binding.group.0),
1288 vertex_slab: vertex_slab.unwrap_or_default(),
1289 index_slab,
1290 };
1291 let bin_key = OpaqueNoLightmap3dBinKey {
1292 asset_id: mesh_instance.mesh_asset_id.into(),
1293 };
1294 alpha_mask_phase.add(
1295 batch_set_key,
1296 bin_key,
1297 (*render_entity, *visible_entity),
1298 mesh_instance.current_uniform_index,
1299 BinnedRenderPhaseType::mesh(
1300 mesh_instance.should_batch(),
1301 &gpu_preprocessing_support,
1302 ),
1303 current_change_tick,
1304 );
1305 }
1306 RenderPhaseType::Transparent => {
1307 let distance = rangefinder.distance(&mesh_instance.center)
1308 + material.properties.depth_bias;
1309 let Some(draw_function) = material
1310 .properties
1311 .get_draw_function(MainPassTransparentDrawFunction)
1312 else {
1313 continue;
1314 };
1315 transparent_phase.add(Transparent3d {
1316 entity: (*render_entity, *visible_entity),
1317 draw_function,
1318 pipeline: pipeline_id,
1319 distance,
1320 batch_range: 0..1,
1321 extra_index: PhaseItemExtraIndex::None,
1322 indexed: index_slab.is_some(),
1323 });
1324 }
1325 }
1326 }
1327 }
1328}
1329
1330#[derive(Default, Resource, Clone, Debug, ExtractResource, Reflect)]
1332#[reflect(Resource, Default, Debug, Clone)]
1333pub struct DefaultOpaqueRendererMethod(OpaqueRendererMethod);
1334
1335impl DefaultOpaqueRendererMethod {
1336 pub fn forward() -> Self {
1337 DefaultOpaqueRendererMethod(OpaqueRendererMethod::Forward)
1338 }
1339
1340 pub fn deferred() -> Self {
1341 DefaultOpaqueRendererMethod(OpaqueRendererMethod::Deferred)
1342 }
1343
1344 pub fn set_to_forward(&mut self) {
1345 self.0 = OpaqueRendererMethod::Forward;
1346 }
1347
1348 pub fn set_to_deferred(&mut self) {
1349 self.0 = OpaqueRendererMethod::Deferred;
1350 }
1351}
1352
1353#[derive(Default, Clone, Copy, Debug, PartialEq, Reflect)]
1372#[reflect(Default, Clone, PartialEq)]
1373pub enum OpaqueRendererMethod {
1374 #[default]
1375 Forward,
1376 Deferred,
1377 Auto,
1378}
1379
1380#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1381pub struct MaterialVertexShader;
1382
1383#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1384pub struct MaterialFragmentShader;
1385
1386#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1387pub struct PrepassVertexShader;
1388
1389#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1390pub struct PrepassFragmentShader;
1391
1392#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1393pub struct DeferredVertexShader;
1394
1395#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1396pub struct DeferredFragmentShader;
1397
1398#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1399pub struct MeshletFragmentShader;
1400
1401#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1402pub struct MeshletPrepassFragmentShader;
1403
1404#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1405pub struct MeshletDeferredFragmentShader;
1406
1407#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1408pub struct MainPassOpaqueDrawFunction;
1409#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1410pub struct MainPassAlphaMaskDrawFunction;
1411#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1412pub struct MainPassTransmissiveDrawFunction;
1413#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1414pub struct MainPassTransparentDrawFunction;
1415
1416#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1417pub struct PrepassOpaqueDrawFunction;
1418#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1419pub struct PrepassAlphaMaskDrawFunction;
1420
1421#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1422pub struct DeferredOpaqueDrawFunction;
1423#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1424pub struct DeferredAlphaMaskDrawFunction;
1425
1426#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1427pub struct ShadowsDrawFunction;
1428
1429#[derive(Debug)]
1430pub struct ErasedMaterialKey {
1431 type_id: TypeId,
1432 hash: u64,
1433 value: Box<dyn Any + Send + Sync>,
1434 vtable: Arc<ErasedMaterialKeyVTable>,
1435}
1436
1437#[derive(Debug)]
1438pub struct ErasedMaterialKeyVTable {
1439 clone_fn: fn(&dyn Any) -> Box<dyn Any + Send + Sync>,
1440 partial_eq_fn: fn(&dyn Any, &dyn Any) -> bool,
1441}
1442
1443impl ErasedMaterialKey {
1444 pub fn new<T>(material_key: T) -> Self
1445 where
1446 T: Clone + Hash + PartialEq + Send + Sync + 'static,
1447 {
1448 let type_id = TypeId::of::<T>();
1449 let hash = FixedHasher::hash_one(&FixedHasher, &material_key);
1450
1451 fn clone<T: Clone + Send + Sync + 'static>(any: &dyn Any) -> Box<dyn Any + Send + Sync> {
1452 Box::new(any.downcast_ref::<T>().unwrap().clone())
1453 }
1454 fn partial_eq<T: PartialEq + 'static>(a: &dyn Any, b: &dyn Any) -> bool {
1455 a.downcast_ref::<T>().unwrap() == b.downcast_ref::<T>().unwrap()
1456 }
1457
1458 Self {
1459 type_id,
1460 hash,
1461 value: Box::new(material_key),
1462 vtable: Arc::new(ErasedMaterialKeyVTable {
1463 clone_fn: clone::<T>,
1464 partial_eq_fn: partial_eq::<T>,
1465 }),
1466 }
1467 }
1468
1469 pub fn to_key<T: Clone + 'static>(&self) -> T {
1470 debug_assert_eq!(self.type_id, TypeId::of::<T>());
1471 self.value.downcast_ref::<T>().unwrap().clone()
1472 }
1473}
1474
1475impl PartialEq for ErasedMaterialKey {
1476 fn eq(&self, other: &Self) -> bool {
1477 self.type_id == other.type_id
1478 && (self.vtable.partial_eq_fn)(self.value.as_ref(), other.value.as_ref())
1479 }
1480}
1481
1482impl Eq for ErasedMaterialKey {}
1483
1484impl Clone for ErasedMaterialKey {
1485 fn clone(&self) -> Self {
1486 Self {
1487 type_id: self.type_id,
1488 hash: self.hash,
1489 value: (self.vtable.clone_fn)(self.value.as_ref()),
1490 vtable: self.vtable.clone(),
1491 }
1492 }
1493}
1494
1495impl Hash for ErasedMaterialKey {
1496 fn hash<H: Hasher>(&self, state: &mut H) {
1497 self.type_id.hash(state);
1498 self.hash.hash(state);
1499 }
1500}
1501
1502impl Default for ErasedMaterialKey {
1503 fn default() -> Self {
1504 Self::new(())
1505 }
1506}
1507
1508#[derive(Default)]
1510pub struct MaterialProperties {
1511 pub render_method: OpaqueRendererMethod,
1514 pub alpha_mode: AlphaMode,
1516 pub mesh_pipeline_key_bits: MeshPipelineKey,
1521 pub depth_bias: f32,
1525 pub reads_view_transmission_texture: bool,
1530 pub render_phase_type: RenderPhaseType,
1531 pub material_layout: Option<BindGroupLayoutDescriptor>,
1532 pub draw_functions: SmallVec<[(InternedDrawFunctionLabel, DrawFunctionId); 4]>,
1534 pub shaders: SmallVec<[(InternedShaderLabel, Handle<Shader>); 3]>,
1537 pub bindless: bool,
1540 pub specialize: Option<
1541 fn(
1542 &MaterialPipeline,
1543 &mut RenderPipelineDescriptor,
1544 &MeshVertexBufferLayoutRef,
1545 ErasedMaterialPipelineKey,
1546 ) -> Result<(), SpecializedMeshPipelineError>,
1547 >,
1548 pub material_key: ErasedMaterialKey,
1551 pub shadows_enabled: bool,
1553 pub prepass_enabled: bool,
1555}
1556
1557impl MaterialProperties {
1558 pub fn get_shader(&self, label: impl ShaderLabel) -> Option<Handle<Shader>> {
1559 self.shaders
1560 .iter()
1561 .find(|(inner_label, _)| inner_label == &label.intern())
1562 .map(|(_, shader)| shader)
1563 .cloned()
1564 }
1565
1566 pub fn add_shader(&mut self, label: impl ShaderLabel, shader: Handle<Shader>) {
1567 self.shaders.push((label.intern(), shader));
1568 }
1569
1570 pub fn get_draw_function(&self, label: impl DrawFunctionLabel) -> Option<DrawFunctionId> {
1571 self.draw_functions
1572 .iter()
1573 .find(|(inner_label, _)| inner_label == &label.intern())
1574 .map(|(_, shader)| shader)
1575 .cloned()
1576 }
1577
1578 pub fn add_draw_function(
1579 &mut self,
1580 label: impl DrawFunctionLabel,
1581 draw_function: DrawFunctionId,
1582 ) {
1583 self.draw_functions.push((label.intern(), draw_function));
1584 }
1585}
1586
1587#[derive(Clone, Copy, Default)]
1588pub enum RenderPhaseType {
1589 #[default]
1590 Opaque,
1591 AlphaMask,
1592 Transmissive,
1593 Transparent,
1594}
1595
1596#[derive(Resource, Default, Deref, DerefMut)]
1602pub struct RenderMaterialBindings(HashMap<UntypedAssetId, MaterialBindingId>);
1603
1604pub struct PreparedMaterial {
1606 pub binding: MaterialBindingId,
1607 pub properties: Arc<MaterialProperties>,
1608}
1609
1610impl<M: Material> ErasedRenderAsset for MeshMaterial3d<M>
1612where
1613 M::Data: PartialEq + Eq + Hash + Clone,
1614{
1615 type SourceAsset = M;
1616 type ErasedAsset = PreparedMaterial;
1617
1618 type Param = (
1619 SRes<RenderDevice>,
1620 SRes<PipelineCache>,
1621 SRes<DefaultOpaqueRendererMethod>,
1622 SResMut<MaterialBindGroupAllocators>,
1623 SResMut<RenderMaterialBindings>,
1624 SRes<DrawFunctions<Opaque3d>>,
1625 SRes<DrawFunctions<AlphaMask3d>>,
1626 SRes<DrawFunctions<Transmissive3d>>,
1627 SRes<DrawFunctions<Transparent3d>>,
1628 SRes<DrawFunctions<Opaque3dPrepass>>,
1629 SRes<DrawFunctions<AlphaMask3dPrepass>>,
1630 SRes<DrawFunctions<Opaque3dDeferred>>,
1631 SRes<DrawFunctions<AlphaMask3dDeferred>>,
1632 SRes<DrawFunctions<Shadow>>,
1633 SRes<AssetServer>,
1634 M::Param,
1635 );
1636
1637 fn prepare_asset(
1638 material: Self::SourceAsset,
1639 material_id: AssetId<Self::SourceAsset>,
1640 (
1641 render_device,
1642 pipeline_cache,
1643 default_opaque_render_method,
1644 bind_group_allocators,
1645 render_material_bindings,
1646 opaque_draw_functions,
1647 alpha_mask_draw_functions,
1648 transmissive_draw_functions,
1649 transparent_draw_functions,
1650 opaque_prepass_draw_functions,
1651 alpha_mask_prepass_draw_functions,
1652 opaque_deferred_draw_functions,
1653 alpha_mask_deferred_draw_functions,
1654 shadow_draw_functions,
1655 asset_server,
1656 material_param,
1657 ): &mut SystemParamItem<Self::Param>,
1658 ) -> Result<Self::ErasedAsset, PrepareAssetError<Self::SourceAsset>> {
1659 let shadows_enabled = M::enable_shadows();
1660 let prepass_enabled = M::enable_prepass();
1661
1662 let draw_opaque_pbr = opaque_draw_functions.read().id::<DrawMaterial>();
1663 let draw_alpha_mask_pbr = alpha_mask_draw_functions.read().id::<DrawMaterial>();
1664 let draw_transmissive_pbr = transmissive_draw_functions.read().id::<DrawMaterial>();
1665 let draw_transparent_pbr = transparent_draw_functions.read().id::<DrawMaterial>();
1666 let draw_opaque_prepass = opaque_prepass_draw_functions.read().id::<DrawPrepass>();
1667 let draw_alpha_mask_prepass = alpha_mask_prepass_draw_functions.read().id::<DrawPrepass>();
1668 let draw_opaque_deferred = opaque_deferred_draw_functions.read().id::<DrawPrepass>();
1669 let draw_alpha_mask_deferred = alpha_mask_deferred_draw_functions
1670 .read()
1671 .id::<DrawPrepass>();
1672 let draw_shadows = shadow_draw_functions.read().id::<DrawPrepass>();
1673
1674 let draw_functions = SmallVec::from_iter([
1675 (MainPassOpaqueDrawFunction.intern(), draw_opaque_pbr),
1676 (MainPassAlphaMaskDrawFunction.intern(), draw_alpha_mask_pbr),
1677 (
1678 MainPassTransmissiveDrawFunction.intern(),
1679 draw_transmissive_pbr,
1680 ),
1681 (
1682 MainPassTransparentDrawFunction.intern(),
1683 draw_transparent_pbr,
1684 ),
1685 (PrepassOpaqueDrawFunction.intern(), draw_opaque_prepass),
1686 (
1687 PrepassAlphaMaskDrawFunction.intern(),
1688 draw_alpha_mask_prepass,
1689 ),
1690 (DeferredOpaqueDrawFunction.intern(), draw_opaque_deferred),
1691 (
1692 DeferredAlphaMaskDrawFunction.intern(),
1693 draw_alpha_mask_deferred,
1694 ),
1695 (ShadowsDrawFunction.intern(), draw_shadows),
1696 ]);
1697
1698 let render_method = match material.opaque_render_method() {
1699 OpaqueRendererMethod::Forward => OpaqueRendererMethod::Forward,
1700 OpaqueRendererMethod::Deferred => OpaqueRendererMethod::Deferred,
1701 OpaqueRendererMethod::Auto => default_opaque_render_method.0,
1702 };
1703
1704 let mut mesh_pipeline_key_bits = MeshPipelineKey::empty();
1705 mesh_pipeline_key_bits.set(
1706 MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE,
1707 material.reads_view_transmission_texture(),
1708 );
1709
1710 let reads_view_transmission_texture =
1711 mesh_pipeline_key_bits.contains(MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE);
1712
1713 let render_phase_type = match material.alpha_mode() {
1714 AlphaMode::Blend | AlphaMode::Premultiplied | AlphaMode::Add | AlphaMode::Multiply => {
1715 RenderPhaseType::Transparent
1716 }
1717 _ if reads_view_transmission_texture => RenderPhaseType::Transmissive,
1718 AlphaMode::Opaque | AlphaMode::AlphaToCoverage => RenderPhaseType::Opaque,
1719 AlphaMode::Mask(_) => RenderPhaseType::AlphaMask,
1720 };
1721
1722 let mut shaders = SmallVec::new();
1723 let mut add_shader = |label: InternedShaderLabel, shader_ref: ShaderRef| {
1724 let mayber_shader = match shader_ref {
1725 ShaderRef::Default => None,
1726 ShaderRef::Handle(handle) => Some(handle),
1727 ShaderRef::Path(path) => Some(asset_server.load(path)),
1728 };
1729 if let Some(shader) = mayber_shader {
1730 shaders.push((label, shader));
1731 }
1732 };
1733 add_shader(MaterialVertexShader.intern(), M::vertex_shader());
1734 add_shader(MaterialFragmentShader.intern(), M::fragment_shader());
1735 add_shader(PrepassVertexShader.intern(), M::prepass_vertex_shader());
1736 add_shader(PrepassFragmentShader.intern(), M::prepass_fragment_shader());
1737 add_shader(DeferredVertexShader.intern(), M::deferred_vertex_shader());
1738 add_shader(
1739 DeferredFragmentShader.intern(),
1740 M::deferred_fragment_shader(),
1741 );
1742
1743 #[cfg(feature = "meshlet")]
1744 {
1745 add_shader(
1746 MeshletFragmentShader.intern(),
1747 M::meshlet_mesh_fragment_shader(),
1748 );
1749 add_shader(
1750 MeshletPrepassFragmentShader.intern(),
1751 M::meshlet_mesh_prepass_fragment_shader(),
1752 );
1753 add_shader(
1754 MeshletDeferredFragmentShader.intern(),
1755 M::meshlet_mesh_deferred_fragment_shader(),
1756 );
1757 }
1758
1759 let bindless = material_uses_bindless_resources::<M>(render_device);
1760 let bind_group_data = material.bind_group_data();
1761 let material_key = ErasedMaterialKey::new(bind_group_data);
1762 fn specialize<M: Material>(
1763 pipeline: &MaterialPipeline,
1764 descriptor: &mut RenderPipelineDescriptor,
1765 mesh_layout: &MeshVertexBufferLayoutRef,
1766 erased_key: ErasedMaterialPipelineKey,
1767 ) -> Result<(), SpecializedMeshPipelineError>
1768 where
1769 M::Data: Hash + Clone,
1770 {
1771 let material_key = erased_key.material_key.to_key();
1772 M::specialize(
1773 pipeline,
1774 descriptor,
1775 mesh_layout,
1776 MaterialPipelineKey {
1777 mesh_key: erased_key.mesh_key,
1778 bind_group_data: material_key,
1779 },
1780 )
1781 }
1782
1783 let material_layout = M::bind_group_layout_descriptor(render_device);
1784 let actual_material_layout = pipeline_cache.get_bind_group_layout(&material_layout);
1785
1786 match material.unprepared_bind_group(
1787 &actual_material_layout,
1788 render_device,
1789 material_param,
1790 false,
1791 ) {
1792 Ok(unprepared) => {
1793 let bind_group_allocator =
1794 bind_group_allocators.get_mut(&TypeId::of::<M>()).unwrap();
1795 let binding = match render_material_bindings.entry(material_id.into()) {
1797 Entry::Occupied(mut occupied_entry) => {
1798 bind_group_allocator.free(*occupied_entry.get());
1803 let new_binding =
1804 bind_group_allocator.allocate_unprepared(unprepared, &material_layout);
1805 *occupied_entry.get_mut() = new_binding;
1806 new_binding
1807 }
1808 Entry::Vacant(vacant_entry) => *vacant_entry.insert(
1809 bind_group_allocator.allocate_unprepared(unprepared, &material_layout),
1810 ),
1811 };
1812
1813 Ok(PreparedMaterial {
1814 binding,
1815 properties: Arc::new(MaterialProperties {
1816 alpha_mode: material.alpha_mode(),
1817 depth_bias: material.depth_bias(),
1818 reads_view_transmission_texture,
1819 render_phase_type,
1820 render_method,
1821 mesh_pipeline_key_bits,
1822 material_layout: Some(material_layout),
1823 draw_functions,
1824 shaders,
1825 bindless,
1826 specialize: Some(specialize::<M>),
1827 material_key,
1828 shadows_enabled,
1829 prepass_enabled,
1830 }),
1831 })
1832 }
1833
1834 Err(AsBindGroupError::RetryNextUpdate) => {
1835 Err(PrepareAssetError::RetryNextUpdate(material))
1836 }
1837
1838 Err(AsBindGroupError::CreateBindGroupDirectly) => {
1839 match material.as_bind_group(
1844 &material_layout,
1845 render_device,
1846 pipeline_cache,
1847 material_param,
1848 ) {
1849 Ok(prepared_bind_group) => {
1850 let bind_group_allocator =
1851 bind_group_allocators.get_mut(&TypeId::of::<M>()).unwrap();
1852 let material_binding_id =
1854 bind_group_allocator.allocate_prepared(prepared_bind_group);
1855 render_material_bindings.insert(material_id.into(), material_binding_id);
1856
1857 Ok(PreparedMaterial {
1858 binding: material_binding_id,
1859 properties: Arc::new(MaterialProperties {
1860 alpha_mode: material.alpha_mode(),
1861 depth_bias: material.depth_bias(),
1862 reads_view_transmission_texture,
1863 render_phase_type,
1864 render_method,
1865 mesh_pipeline_key_bits,
1866 material_layout: Some(material_layout),
1867 draw_functions,
1868 shaders,
1869 bindless,
1870 specialize: Some(specialize::<M>),
1871 material_key,
1872 shadows_enabled,
1873 prepass_enabled,
1874 }),
1875 })
1876 }
1877
1878 Err(AsBindGroupError::RetryNextUpdate) => {
1879 Err(PrepareAssetError::RetryNextUpdate(material))
1880 }
1881
1882 Err(other) => Err(PrepareAssetError::AsBindGroupError(other)),
1883 }
1884 }
1885
1886 Err(other) => Err(PrepareAssetError::AsBindGroupError(other)),
1887 }
1888 }
1889
1890 fn unload_asset(
1891 source_asset: AssetId<Self::SourceAsset>,
1892 (_, _, _, bind_group_allocators, render_material_bindings, ..): &mut SystemParamItem<
1893 Self::Param,
1894 >,
1895 ) {
1896 let Some(material_binding_id) = render_material_bindings.remove(&source_asset.untyped())
1897 else {
1898 return;
1899 };
1900 let bind_group_allactor = bind_group_allocators.get_mut(&TypeId::of::<M>()).unwrap();
1901 bind_group_allactor.free(material_binding_id);
1902 }
1903}
1904
1905pub fn prepare_material_bind_groups(
1908 mut allocators: ResMut<MaterialBindGroupAllocators>,
1909 render_device: Res<RenderDevice>,
1910 pipeline_cache: Res<PipelineCache>,
1911 fallback_image: Res<FallbackImage>,
1912 fallback_resources: Res<FallbackBindlessResources>,
1913) {
1914 for (_, allocator) in allocators.iter_mut() {
1915 allocator.prepare_bind_groups(
1916 &render_device,
1917 &pipeline_cache,
1918 &fallback_resources,
1919 &fallback_image,
1920 );
1921 }
1922}
1923
1924pub fn write_material_bind_group_buffers(
1930 mut allocators: ResMut<MaterialBindGroupAllocators>,
1931 render_device: Res<RenderDevice>,
1932 render_queue: Res<RenderQueue>,
1933) {
1934 for (_, allocator) in allocators.iter_mut() {
1935 allocator.write_buffers(&render_device, &render_queue);
1936 }
1937}