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>
395 .after(extract_cameras)
396 .after(MaterialExtractionSystems),
397 ),
398 );
399 }
400 }
401}
402
403fn add_material_bind_group_allocator<M: Material>(
404 render_device: Res<RenderDevice>,
405 mut bind_group_allocators: ResMut<MaterialBindGroupAllocators>,
406) {
407 bind_group_allocators.insert(
408 TypeId::of::<M>(),
409 MaterialBindGroupAllocator::new(
410 &render_device,
411 M::label(),
412 material_uses_bindless_resources::<M>(&render_device)
413 .then(|| M::bindless_descriptor())
414 .flatten(),
415 M::bind_group_layout(&render_device),
416 M::bindless_slot_count(),
417 ),
418 );
419}
420
421pub(crate) static DUMMY_MESH_MATERIAL: AssetId<StandardMaterial> =
427 AssetId::<StandardMaterial>::invalid();
428
429pub struct MaterialPipelineKey<M: Material> {
431 pub mesh_key: MeshPipelineKey,
432 pub bind_group_data: M::Data,
433}
434
435#[derive(Clone, Debug, PartialEq, Eq, Hash)]
436pub struct ErasedMaterialPipelineKey {
437 pub mesh_key: MeshPipelineKey,
438 pub material_key: ErasedMaterialKey,
439 pub type_id: TypeId,
440}
441
442#[derive(Resource, Clone)]
444pub struct MaterialPipeline {
445 pub mesh_pipeline: MeshPipeline,
446}
447
448pub struct MaterialPipelineSpecializer {
449 pub(crate) pipeline: MaterialPipeline,
450 pub(crate) properties: Arc<MaterialProperties>,
451}
452
453impl SpecializedMeshPipeline for MaterialPipelineSpecializer {
454 type Key = ErasedMaterialPipelineKey;
455
456 fn specialize(
457 &self,
458 key: Self::Key,
459 layout: &MeshVertexBufferLayoutRef,
460 ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
461 let mut descriptor = self
462 .pipeline
463 .mesh_pipeline
464 .specialize(key.mesh_key, layout)?;
465 descriptor.vertex.shader_defs.push(ShaderDefVal::UInt(
466 "MATERIAL_BIND_GROUP".into(),
467 MATERIAL_BIND_GROUP_INDEX as u32,
468 ));
469 if let Some(ref mut fragment) = descriptor.fragment {
470 fragment.shader_defs.push(ShaderDefVal::UInt(
471 "MATERIAL_BIND_GROUP".into(),
472 MATERIAL_BIND_GROUP_INDEX as u32,
473 ));
474 };
475 if let Some(vertex_shader) = self.properties.get_shader(MaterialVertexShader) {
476 descriptor.vertex.shader = vertex_shader.clone();
477 }
478
479 if let Some(fragment_shader) = self.properties.get_shader(MaterialFragmentShader) {
480 descriptor.fragment.as_mut().unwrap().shader = fragment_shader.clone();
481 }
482
483 descriptor
484 .layout
485 .insert(3, self.properties.material_layout.as_ref().unwrap().clone());
486
487 if let Some(specialize) = self.properties.specialize {
488 specialize(&self.pipeline, &mut descriptor, layout, key)?;
489 }
490
491 if self.properties.bindless {
493 descriptor.vertex.shader_defs.push("BINDLESS".into());
494 if let Some(ref mut fragment) = descriptor.fragment {
495 fragment.shader_defs.push("BINDLESS".into());
496 }
497 }
498
499 Ok(descriptor)
500 }
501}
502
503pub fn init_material_pipeline(mut commands: Commands, mesh_pipeline: Res<MeshPipeline>) {
504 commands.insert_resource(MaterialPipeline {
505 mesh_pipeline: mesh_pipeline.clone(),
506 });
507}
508
509pub type DrawMaterial = (
510 SetItemPipeline,
511 SetMeshViewBindGroup<0>,
512 SetMeshViewBindingArrayBindGroup<1>,
513 SetMeshBindGroup<2>,
514 SetMaterialBindGroup<MATERIAL_BIND_GROUP_INDEX>,
515 DrawMesh,
516);
517
518pub struct SetMaterialBindGroup<const I: usize>;
520impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMaterialBindGroup<I> {
521 type Param = (
522 SRes<ErasedRenderAssets<PreparedMaterial>>,
523 SRes<RenderMaterialInstances>,
524 SRes<MaterialBindGroupAllocators>,
525 );
526 type ViewQuery = ();
527 type ItemQuery = ();
528
529 #[inline]
530 fn render<'w>(
531 item: &P,
532 _view: (),
533 _item_query: Option<()>,
534 (materials, material_instances, material_bind_group_allocator): SystemParamItem<
535 'w,
536 '_,
537 Self::Param,
538 >,
539 pass: &mut TrackedRenderPass<'w>,
540 ) -> RenderCommandResult {
541 let materials = materials.into_inner();
542 let material_instances = material_instances.into_inner();
543 let material_bind_group_allocators = material_bind_group_allocator.into_inner();
544
545 let Some(material_instance) = material_instances.instances.get(&item.main_entity()) else {
546 return RenderCommandResult::Skip;
547 };
548 let Some(material_bind_group_allocator) =
549 material_bind_group_allocators.get(&material_instance.asset_id.type_id())
550 else {
551 return RenderCommandResult::Skip;
552 };
553 let Some(material) = materials.get(material_instance.asset_id) else {
554 return RenderCommandResult::Skip;
555 };
556 let Some(material_bind_group) = material_bind_group_allocator.get(material.binding.group)
557 else {
558 return RenderCommandResult::Skip;
559 };
560 let Some(bind_group) = material_bind_group.bind_group() else {
561 return RenderCommandResult::Skip;
562 };
563 pass.set_bind_group(I, bind_group, &[]);
564 RenderCommandResult::Success
565 }
566}
567
568#[derive(Resource, Default)]
570pub struct RenderMaterialInstances {
571 pub instances: MainEntityHashMap<RenderMaterialInstance>,
574 pub current_change_tick: Tick,
578}
579
580impl RenderMaterialInstances {
581 pub(crate) fn mesh_material(&self, entity: MainEntity) -> UntypedAssetId {
588 match self.instances.get(&entity) {
589 Some(render_instance) => render_instance.asset_id,
590 None => DUMMY_MESH_MATERIAL.into(),
591 }
592 }
593}
594
595pub struct RenderMaterialInstance {
600 pub asset_id: UntypedAssetId,
602 pub last_change_tick: Tick,
605}
606
607#[derive(SystemSet, Clone, PartialEq, Eq, Debug, Hash)]
609pub struct MaterialExtractionSystems;
610
611#[deprecated(since = "0.17.0", note = "Renamed to `MaterialExtractionSystems`.")]
613pub type ExtractMaterialsSet = MaterialExtractionSystems;
614
615pub const fn alpha_mode_pipeline_key(alpha_mode: AlphaMode, msaa: &Msaa) -> MeshPipelineKey {
616 match alpha_mode {
617 AlphaMode::Premultiplied | AlphaMode::Add => MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA,
620 AlphaMode::Blend => MeshPipelineKey::BLEND_ALPHA,
621 AlphaMode::Multiply => MeshPipelineKey::BLEND_MULTIPLY,
622 AlphaMode::Mask(_) => MeshPipelineKey::MAY_DISCARD,
623 AlphaMode::AlphaToCoverage => match *msaa {
624 Msaa::Off => MeshPipelineKey::MAY_DISCARD,
625 _ => MeshPipelineKey::BLEND_ALPHA_TO_COVERAGE,
626 },
627 _ => MeshPipelineKey::NONE,
628 }
629}
630
631pub const fn tonemapping_pipeline_key(tonemapping: Tonemapping) -> MeshPipelineKey {
632 match tonemapping {
633 Tonemapping::None => MeshPipelineKey::TONEMAP_METHOD_NONE,
634 Tonemapping::Reinhard => MeshPipelineKey::TONEMAP_METHOD_REINHARD,
635 Tonemapping::ReinhardLuminance => MeshPipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE,
636 Tonemapping::AcesFitted => MeshPipelineKey::TONEMAP_METHOD_ACES_FITTED,
637 Tonemapping::AgX => MeshPipelineKey::TONEMAP_METHOD_AGX,
638 Tonemapping::SomewhatBoringDisplayTransform => {
639 MeshPipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM
640 }
641 Tonemapping::TonyMcMapface => MeshPipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE,
642 Tonemapping::BlenderFilmic => MeshPipelineKey::TONEMAP_METHOD_BLENDER_FILMIC,
643 }
644}
645
646pub const fn screen_space_specular_transmission_pipeline_key(
647 screen_space_transmissive_blur_quality: ScreenSpaceTransmissionQuality,
648) -> MeshPipelineKey {
649 match screen_space_transmissive_blur_quality {
650 ScreenSpaceTransmissionQuality::Low => {
651 MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_LOW
652 }
653 ScreenSpaceTransmissionQuality::Medium => {
654 MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_MEDIUM
655 }
656 ScreenSpaceTransmissionQuality::High => {
657 MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_HIGH
658 }
659 ScreenSpaceTransmissionQuality::Ultra => {
660 MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_ULTRA
661 }
662 }
663}
664
665fn mark_meshes_as_changed_if_their_materials_changed<M>(
680 mut changed_meshes_query: Query<
681 &mut Mesh3d,
682 Or<(Changed<MeshMaterial3d<M>>, AssetChanged<MeshMaterial3d<M>>)>,
683 >,
684) where
685 M: Material,
686{
687 for mut mesh in &mut changed_meshes_query {
688 mesh.set_changed();
689 }
690}
691
692fn extract_mesh_materials<M: Material>(
695 mut material_instances: ResMut<RenderMaterialInstances>,
696 changed_meshes_query: Extract<
697 Query<
698 (Entity, &ViewVisibility, &MeshMaterial3d<M>),
699 Or<(Changed<ViewVisibility>, Changed<MeshMaterial3d<M>>)>,
700 >,
701 >,
702) {
703 let last_change_tick = material_instances.current_change_tick;
704
705 for (entity, view_visibility, material) in &changed_meshes_query {
706 if view_visibility.get() {
707 material_instances.instances.insert(
708 entity.into(),
709 RenderMaterialInstance {
710 asset_id: material.id().untyped(),
711 last_change_tick,
712 },
713 );
714 } else {
715 material_instances
716 .instances
717 .remove(&MainEntity::from(entity));
718 }
719 }
720}
721
722fn early_sweep_material_instances<M>(
737 mut material_instances: ResMut<RenderMaterialInstances>,
738 mut removed_materials_query: Extract<RemovedComponents<MeshMaterial3d<M>>>,
739) where
740 M: Material,
741{
742 let last_change_tick = material_instances.current_change_tick;
743
744 for entity in removed_materials_query.read() {
745 if let Entry::Occupied(occupied_entry) = material_instances.instances.entry(entity.into()) {
746 if occupied_entry.get().last_change_tick != last_change_tick {
748 occupied_entry.remove();
749 }
750 }
751 }
752}
753
754pub(crate) fn late_sweep_material_instances(
761 mut material_instances: ResMut<RenderMaterialInstances>,
762 mut removed_meshes_query: Extract<RemovedComponents<Mesh3d>>,
763) {
764 let last_change_tick = material_instances.current_change_tick;
765
766 for entity in removed_meshes_query.read() {
767 if let Entry::Occupied(occupied_entry) = material_instances.instances.entry(entity.into()) {
768 if occupied_entry.get().last_change_tick != last_change_tick {
772 occupied_entry.remove();
773 }
774 }
775 }
776
777 material_instances
778 .current_change_tick
779 .set(last_change_tick.get() + 1);
780}
781
782pub fn extract_entities_needs_specialization<M>(
783 entities_needing_specialization: Extract<Res<EntitiesNeedingSpecialization<M>>>,
784 material_instances: Res<RenderMaterialInstances>,
785 mut entity_specialization_ticks: ResMut<EntitySpecializationTicks>,
786 mut removed_mesh_material_components: Extract<RemovedComponents<MeshMaterial3d<M>>>,
787 mut specialized_material_pipeline_cache: ResMut<SpecializedMaterialPipelineCache>,
788 mut specialized_prepass_material_pipeline_cache: Option<
789 ResMut<SpecializedPrepassMaterialPipelineCache>,
790 >,
791 mut specialized_shadow_material_pipeline_cache: Option<
792 ResMut<SpecializedShadowMaterialPipelineCache>,
793 >,
794 views: Query<&ExtractedView>,
795 ticks: SystemChangeTick,
796) where
797 M: Material,
798{
799 for entity in removed_mesh_material_components.read() {
809 if material_instances
810 .instances
811 .contains_key(&MainEntity::from(entity))
812 {
813 continue;
814 }
815
816 entity_specialization_ticks.remove(&MainEntity::from(entity));
817 for view in views {
818 if let Some(cache) =
819 specialized_material_pipeline_cache.get_mut(&view.retained_view_entity)
820 {
821 cache.remove(&MainEntity::from(entity));
822 }
823 if let Some(cache) = specialized_prepass_material_pipeline_cache
824 .as_mut()
825 .and_then(|c| c.get_mut(&view.retained_view_entity))
826 {
827 cache.remove(&MainEntity::from(entity));
828 }
829 if let Some(cache) = specialized_shadow_material_pipeline_cache
830 .as_mut()
831 .and_then(|c| c.get_mut(&view.retained_view_entity))
832 {
833 cache.remove(&MainEntity::from(entity));
834 }
835 }
836 }
837
838 for entity in entities_needing_specialization.iter() {
839 entity_specialization_ticks.insert((*entity).into(), ticks.this_run());
841 }
842}
843
844#[derive(Resource, Deref, DerefMut, Clone, Debug)]
845pub struct EntitiesNeedingSpecialization<M> {
846 #[deref]
847 pub entities: Vec<Entity>,
848 _marker: PhantomData<M>,
849}
850
851impl<M> Default for EntitiesNeedingSpecialization<M> {
852 fn default() -> Self {
853 Self {
854 entities: Default::default(),
855 _marker: Default::default(),
856 }
857 }
858}
859
860#[derive(Resource, Deref, DerefMut, Default, Clone, Debug)]
861pub struct EntitySpecializationTicks {
862 #[deref]
863 pub entities: MainEntityHashMap<Tick>,
864}
865
866#[derive(Resource, Deref, DerefMut, Default)]
868pub struct SpecializedMaterialPipelineCache {
869 #[deref]
871 map: HashMap<RetainedViewEntity, SpecializedMaterialViewPipelineCache>,
872}
873
874#[derive(Deref, DerefMut, Default)]
877pub struct SpecializedMaterialViewPipelineCache {
878 #[deref]
880 map: MainEntityHashMap<(Tick, CachedRenderPipelineId)>,
881}
882
883pub fn check_entities_needing_specialization<M>(
884 needs_specialization: Query<
885 Entity,
886 (
887 Or<(
888 Changed<Mesh3d>,
889 AssetChanged<Mesh3d>,
890 Changed<MeshMaterial3d<M>>,
891 AssetChanged<MeshMaterial3d<M>>,
892 )>,
893 With<MeshMaterial3d<M>>,
894 ),
895 >,
896 mut par_local: Local<Parallel<Vec<Entity>>>,
897 mut entities_needing_specialization: ResMut<EntitiesNeedingSpecialization<M>>,
898) where
899 M: Material,
900{
901 entities_needing_specialization.clear();
902
903 needs_specialization
904 .par_iter()
905 .for_each(|entity| par_local.borrow_local_mut().push(entity));
906
907 par_local.drain_into(&mut entities_needing_specialization);
908}
909
910pub fn specialize_material_meshes(
911 render_meshes: Res<RenderAssets<RenderMesh>>,
912 render_materials: Res<ErasedRenderAssets<PreparedMaterial>>,
913 render_mesh_instances: Res<RenderMeshInstances>,
914 render_material_instances: Res<RenderMaterialInstances>,
915 render_lightmaps: Res<RenderLightmaps>,
916 render_visibility_ranges: Res<RenderVisibilityRanges>,
917 (
918 opaque_render_phases,
919 alpha_mask_render_phases,
920 transmissive_render_phases,
921 transparent_render_phases,
922 ): (
923 Res<ViewBinnedRenderPhases<Opaque3d>>,
924 Res<ViewBinnedRenderPhases<AlphaMask3d>>,
925 Res<ViewSortedRenderPhases<Transmissive3d>>,
926 Res<ViewSortedRenderPhases<Transparent3d>>,
927 ),
928 views: Query<(&ExtractedView, &RenderVisibleEntities)>,
929 view_key_cache: Res<ViewKeyCache>,
930 entity_specialization_ticks: Res<EntitySpecializationTicks>,
931 view_specialization_ticks: Res<ViewSpecializationTicks>,
932 mut specialized_material_pipeline_cache: ResMut<SpecializedMaterialPipelineCache>,
933 mut pipelines: ResMut<SpecializedMeshPipelines<MaterialPipelineSpecializer>>,
934 pipeline: Res<MaterialPipeline>,
935 pipeline_cache: Res<PipelineCache>,
936 ticks: SystemChangeTick,
937) {
938 let mut all_views: HashSet<RetainedViewEntity, FixedHasher> = HashSet::default();
941
942 for (view, visible_entities) in &views {
943 all_views.insert(view.retained_view_entity);
944
945 if !transparent_render_phases.contains_key(&view.retained_view_entity)
946 && !opaque_render_phases.contains_key(&view.retained_view_entity)
947 && !alpha_mask_render_phases.contains_key(&view.retained_view_entity)
948 && !transmissive_render_phases.contains_key(&view.retained_view_entity)
949 {
950 continue;
951 }
952
953 let Some(view_key) = view_key_cache.get(&view.retained_view_entity) else {
954 continue;
955 };
956
957 let view_tick = view_specialization_ticks
958 .get(&view.retained_view_entity)
959 .unwrap();
960 let view_specialized_material_pipeline_cache = specialized_material_pipeline_cache
961 .entry(view.retained_view_entity)
962 .or_default();
963
964 for (_, visible_entity) in visible_entities.iter::<Mesh3d>() {
965 let Some(material_instance) = render_material_instances.instances.get(visible_entity)
966 else {
967 continue;
968 };
969 let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(*visible_entity)
970 else {
971 continue;
972 };
973 let entity_tick = entity_specialization_ticks.get(visible_entity).unwrap();
974 let last_specialized_tick = view_specialized_material_pipeline_cache
975 .get(visible_entity)
976 .map(|(tick, _)| *tick);
977 let needs_specialization = last_specialized_tick.is_none_or(|tick| {
978 view_tick.is_newer_than(tick, ticks.this_run())
979 || entity_tick.is_newer_than(tick, ticks.this_run())
980 });
981 if !needs_specialization {
982 continue;
983 }
984 let Some(mesh) = render_meshes.get(mesh_instance.mesh_asset_id) else {
985 continue;
986 };
987 let Some(material) = render_materials.get(material_instance.asset_id) else {
988 continue;
989 };
990
991 let mut mesh_pipeline_key_bits = material.properties.mesh_pipeline_key_bits;
992 mesh_pipeline_key_bits.insert(alpha_mode_pipeline_key(
993 material.properties.alpha_mode,
994 &Msaa::from_samples(view_key.msaa_samples()),
995 ));
996 let mut mesh_key = *view_key
997 | MeshPipelineKey::from_bits_retain(mesh.key_bits.bits())
998 | mesh_pipeline_key_bits;
999
1000 if let Some(lightmap) = render_lightmaps.render_lightmaps.get(visible_entity) {
1001 mesh_key |= MeshPipelineKey::LIGHTMAPPED;
1002
1003 if lightmap.bicubic_sampling {
1004 mesh_key |= MeshPipelineKey::LIGHTMAP_BICUBIC_SAMPLING;
1005 }
1006 }
1007
1008 if render_visibility_ranges.entity_has_crossfading_visibility_ranges(*visible_entity) {
1009 mesh_key |= MeshPipelineKey::VISIBILITY_RANGE_DITHER;
1010 }
1011
1012 if view_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
1013 if mesh_instance
1015 .flags
1016 .contains(RenderMeshInstanceFlags::HAS_PREVIOUS_SKIN)
1017 {
1018 mesh_key |= MeshPipelineKey::HAS_PREVIOUS_SKIN;
1019 }
1020 if mesh_instance
1021 .flags
1022 .contains(RenderMeshInstanceFlags::HAS_PREVIOUS_MORPH)
1023 {
1024 mesh_key |= MeshPipelineKey::HAS_PREVIOUS_MORPH;
1025 }
1026 }
1027
1028 let erased_key = ErasedMaterialPipelineKey {
1029 type_id: material_instance.asset_id.type_id(),
1030 mesh_key,
1031 material_key: material.properties.material_key.clone(),
1032 };
1033 let material_pipeline_specializer = MaterialPipelineSpecializer {
1034 pipeline: pipeline.clone(),
1035 properties: material.properties.clone(),
1036 };
1037 let pipeline_id = pipelines.specialize(
1038 &pipeline_cache,
1039 &material_pipeline_specializer,
1040 erased_key,
1041 &mesh.layout,
1042 );
1043 let pipeline_id = match pipeline_id {
1044 Ok(id) => id,
1045 Err(err) => {
1046 error!("{}", err);
1047 continue;
1048 }
1049 };
1050
1051 view_specialized_material_pipeline_cache
1052 .insert(*visible_entity, (ticks.this_run(), pipeline_id));
1053 }
1054 }
1055
1056 specialized_material_pipeline_cache
1058 .retain(|retained_view_entity, _| all_views.contains(retained_view_entity));
1059}
1060
1061pub fn queue_material_meshes(
1064 render_materials: Res<ErasedRenderAssets<PreparedMaterial>>,
1065 render_mesh_instances: Res<RenderMeshInstances>,
1066 render_material_instances: Res<RenderMaterialInstances>,
1067 mesh_allocator: Res<MeshAllocator>,
1068 gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
1069 mut opaque_render_phases: ResMut<ViewBinnedRenderPhases<Opaque3d>>,
1070 mut alpha_mask_render_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3d>>,
1071 mut transmissive_render_phases: ResMut<ViewSortedRenderPhases<Transmissive3d>>,
1072 mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
1073 views: Query<(&ExtractedView, &RenderVisibleEntities)>,
1074 specialized_material_pipeline_cache: ResMut<SpecializedMaterialPipelineCache>,
1075) {
1076 for (view, visible_entities) in &views {
1077 let (
1078 Some(opaque_phase),
1079 Some(alpha_mask_phase),
1080 Some(transmissive_phase),
1081 Some(transparent_phase),
1082 ) = (
1083 opaque_render_phases.get_mut(&view.retained_view_entity),
1084 alpha_mask_render_phases.get_mut(&view.retained_view_entity),
1085 transmissive_render_phases.get_mut(&view.retained_view_entity),
1086 transparent_render_phases.get_mut(&view.retained_view_entity),
1087 )
1088 else {
1089 continue;
1090 };
1091
1092 let Some(view_specialized_material_pipeline_cache) =
1093 specialized_material_pipeline_cache.get(&view.retained_view_entity)
1094 else {
1095 continue;
1096 };
1097
1098 let rangefinder = view.rangefinder3d();
1099 for (render_entity, visible_entity) in visible_entities.iter::<Mesh3d>() {
1100 let Some((current_change_tick, pipeline_id)) = view_specialized_material_pipeline_cache
1101 .get(visible_entity)
1102 .map(|(current_change_tick, pipeline_id)| (*current_change_tick, *pipeline_id))
1103 else {
1104 continue;
1105 };
1106
1107 if opaque_phase.validate_cached_entity(*visible_entity, current_change_tick)
1109 || alpha_mask_phase.validate_cached_entity(*visible_entity, current_change_tick)
1110 {
1111 continue;
1112 }
1113
1114 let Some(material_instance) = render_material_instances.instances.get(visible_entity)
1115 else {
1116 continue;
1117 };
1118 let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(*visible_entity)
1119 else {
1120 continue;
1121 };
1122 let Some(material) = render_materials.get(material_instance.asset_id) else {
1123 continue;
1124 };
1125
1126 let (vertex_slab, index_slab) = mesh_allocator.mesh_slabs(&mesh_instance.mesh_asset_id);
1128 let Some(draw_function) = material.properties.get_draw_function(MaterialDrawFunction)
1129 else {
1130 continue;
1131 };
1132
1133 match material.properties.render_phase_type {
1134 RenderPhaseType::Transmissive => {
1135 let distance = rangefinder.distance_translation(&mesh_instance.translation)
1136 + material.properties.depth_bias;
1137 transmissive_phase.add(Transmissive3d {
1138 entity: (*render_entity, *visible_entity),
1139 draw_function,
1140 pipeline: pipeline_id,
1141 distance,
1142 batch_range: 0..1,
1143 extra_index: PhaseItemExtraIndex::None,
1144 indexed: index_slab.is_some(),
1145 });
1146 }
1147 RenderPhaseType::Opaque => {
1148 if material.properties.render_method == OpaqueRendererMethod::Deferred {
1149 opaque_phase.update_cache(*visible_entity, None, current_change_tick);
1154 continue;
1155 }
1156 let batch_set_key = Opaque3dBatchSetKey {
1157 pipeline: pipeline_id,
1158 draw_function,
1159 material_bind_group_index: Some(material.binding.group.0),
1160 vertex_slab: vertex_slab.unwrap_or_default(),
1161 index_slab,
1162 lightmap_slab: mesh_instance.shared.lightmap_slab_index.map(|index| *index),
1163 };
1164 let bin_key = Opaque3dBinKey {
1165 asset_id: mesh_instance.mesh_asset_id.into(),
1166 };
1167 opaque_phase.add(
1168 batch_set_key,
1169 bin_key,
1170 (*render_entity, *visible_entity),
1171 mesh_instance.current_uniform_index,
1172 BinnedRenderPhaseType::mesh(
1173 mesh_instance.should_batch(),
1174 &gpu_preprocessing_support,
1175 ),
1176 current_change_tick,
1177 );
1178 }
1179 RenderPhaseType::AlphaMask => {
1181 let batch_set_key = OpaqueNoLightmap3dBatchSetKey {
1182 draw_function,
1183 pipeline: pipeline_id,
1184 material_bind_group_index: Some(material.binding.group.0),
1185 vertex_slab: vertex_slab.unwrap_or_default(),
1186 index_slab,
1187 };
1188 let bin_key = OpaqueNoLightmap3dBinKey {
1189 asset_id: mesh_instance.mesh_asset_id.into(),
1190 };
1191 alpha_mask_phase.add(
1192 batch_set_key,
1193 bin_key,
1194 (*render_entity, *visible_entity),
1195 mesh_instance.current_uniform_index,
1196 BinnedRenderPhaseType::mesh(
1197 mesh_instance.should_batch(),
1198 &gpu_preprocessing_support,
1199 ),
1200 current_change_tick,
1201 );
1202 }
1203 RenderPhaseType::Transparent => {
1204 let distance = rangefinder.distance_translation(&mesh_instance.translation)
1205 + material.properties.depth_bias;
1206 transparent_phase.add(Transparent3d {
1207 entity: (*render_entity, *visible_entity),
1208 draw_function,
1209 pipeline: pipeline_id,
1210 distance,
1211 batch_range: 0..1,
1212 extra_index: PhaseItemExtraIndex::None,
1213 indexed: index_slab.is_some(),
1214 });
1215 }
1216 }
1217 }
1218 }
1219}
1220
1221#[derive(Default, Resource, Clone, Debug, ExtractResource, Reflect)]
1223#[reflect(Resource, Default, Debug, Clone)]
1224pub struct DefaultOpaqueRendererMethod(OpaqueRendererMethod);
1225
1226impl DefaultOpaqueRendererMethod {
1227 pub fn forward() -> Self {
1228 DefaultOpaqueRendererMethod(OpaqueRendererMethod::Forward)
1229 }
1230
1231 pub fn deferred() -> Self {
1232 DefaultOpaqueRendererMethod(OpaqueRendererMethod::Deferred)
1233 }
1234
1235 pub fn set_to_forward(&mut self) {
1236 self.0 = OpaqueRendererMethod::Forward;
1237 }
1238
1239 pub fn set_to_deferred(&mut self) {
1240 self.0 = OpaqueRendererMethod::Deferred;
1241 }
1242}
1243
1244#[derive(Default, Clone, Copy, Debug, PartialEq, Reflect)]
1263#[reflect(Default, Clone, PartialEq)]
1264pub enum OpaqueRendererMethod {
1265 #[default]
1266 Forward,
1267 Deferred,
1268 Auto,
1269}
1270
1271#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1272pub struct MaterialVertexShader;
1273
1274#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1275pub struct MaterialFragmentShader;
1276
1277#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1278pub struct PrepassVertexShader;
1279
1280#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1281pub struct PrepassFragmentShader;
1282
1283#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1284pub struct DeferredVertexShader;
1285
1286#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1287pub struct DeferredFragmentShader;
1288
1289#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1290pub struct MeshletFragmentShader;
1291
1292#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1293pub struct MeshletPrepassFragmentShader;
1294
1295#[derive(ShaderLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1296pub struct MeshletDeferredFragmentShader;
1297
1298#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1299pub struct MaterialDrawFunction;
1300
1301#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1302pub struct PrepassDrawFunction;
1303
1304#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1305pub struct DeferredDrawFunction;
1306
1307#[derive(DrawFunctionLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
1308pub struct ShadowsDrawFunction;
1309
1310#[derive(Debug)]
1311pub struct ErasedMaterialKey {
1312 type_id: TypeId,
1313 hash: u64,
1314 value: Box<dyn Any + Send + Sync>,
1315 vtable: Arc<ErasedMaterialKeyVTable>,
1316}
1317
1318#[derive(Debug)]
1319pub struct ErasedMaterialKeyVTable {
1320 clone_fn: fn(&dyn Any) -> Box<dyn Any + Send + Sync>,
1321 partial_eq_fn: fn(&dyn Any, &dyn Any) -> bool,
1322}
1323
1324impl ErasedMaterialKey {
1325 pub fn new<T>(material_key: T) -> Self
1326 where
1327 T: Clone + Hash + PartialEq + Send + Sync + 'static,
1328 {
1329 let type_id = TypeId::of::<T>();
1330 let hash = FixedHasher::hash_one(&FixedHasher, &material_key);
1331
1332 fn clone<T: Clone + Send + Sync + 'static>(any: &dyn Any) -> Box<dyn Any + Send + Sync> {
1333 Box::new(any.downcast_ref::<T>().unwrap().clone())
1334 }
1335 fn partial_eq<T: PartialEq + 'static>(a: &dyn Any, b: &dyn Any) -> bool {
1336 a.downcast_ref::<T>().unwrap() == b.downcast_ref::<T>().unwrap()
1337 }
1338
1339 Self {
1340 type_id,
1341 hash,
1342 value: Box::new(material_key),
1343 vtable: Arc::new(ErasedMaterialKeyVTable {
1344 clone_fn: clone::<T>,
1345 partial_eq_fn: partial_eq::<T>,
1346 }),
1347 }
1348 }
1349
1350 pub fn to_key<T: Clone + 'static>(&self) -> T {
1351 debug_assert_eq!(self.type_id, TypeId::of::<T>());
1352 self.value.downcast_ref::<T>().unwrap().clone()
1353 }
1354}
1355
1356impl PartialEq for ErasedMaterialKey {
1357 fn eq(&self, other: &Self) -> bool {
1358 self.type_id == other.type_id
1359 && (self.vtable.partial_eq_fn)(self.value.as_ref(), other.value.as_ref())
1360 }
1361}
1362
1363impl Eq for ErasedMaterialKey {}
1364
1365impl Clone for ErasedMaterialKey {
1366 fn clone(&self) -> Self {
1367 Self {
1368 type_id: self.type_id,
1369 hash: self.hash,
1370 value: (self.vtable.clone_fn)(self.value.as_ref()),
1371 vtable: self.vtable.clone(),
1372 }
1373 }
1374}
1375
1376impl Hash for ErasedMaterialKey {
1377 fn hash<H: Hasher>(&self, state: &mut H) {
1378 self.type_id.hash(state);
1379 self.hash.hash(state);
1380 }
1381}
1382
1383impl Default for ErasedMaterialKey {
1384 fn default() -> Self {
1385 Self::new(())
1386 }
1387}
1388
1389#[derive(Default)]
1391pub struct MaterialProperties {
1392 pub render_method: OpaqueRendererMethod,
1395 pub alpha_mode: AlphaMode,
1397 pub mesh_pipeline_key_bits: MeshPipelineKey,
1402 pub depth_bias: f32,
1406 pub reads_view_transmission_texture: bool,
1411 pub render_phase_type: RenderPhaseType,
1412 pub material_layout: Option<BindGroupLayout>,
1413 pub draw_functions: SmallVec<[(InternedDrawFunctionLabel, DrawFunctionId); 4]>,
1415 pub shaders: SmallVec<[(InternedShaderLabel, Handle<Shader>); 3]>,
1418 pub bindless: bool,
1421 pub specialize: Option<
1422 fn(
1423 &MaterialPipeline,
1424 &mut RenderPipelineDescriptor,
1425 &MeshVertexBufferLayoutRef,
1426 ErasedMaterialPipelineKey,
1427 ) -> Result<(), SpecializedMeshPipelineError>,
1428 >,
1429 pub material_key: ErasedMaterialKey,
1432 pub shadows_enabled: bool,
1434 pub prepass_enabled: bool,
1436}
1437
1438impl MaterialProperties {
1439 pub fn get_shader(&self, label: impl ShaderLabel) -> Option<Handle<Shader>> {
1440 self.shaders
1441 .iter()
1442 .find(|(inner_label, _)| inner_label == &label.intern())
1443 .map(|(_, shader)| shader)
1444 .cloned()
1445 }
1446
1447 pub fn add_shader(&mut self, label: impl ShaderLabel, shader: Handle<Shader>) {
1448 self.shaders.push((label.intern(), shader));
1449 }
1450
1451 pub fn get_draw_function(&self, label: impl DrawFunctionLabel) -> Option<DrawFunctionId> {
1452 self.draw_functions
1453 .iter()
1454 .find(|(inner_label, _)| inner_label == &label.intern())
1455 .map(|(_, shader)| shader)
1456 .cloned()
1457 }
1458
1459 pub fn add_draw_function(
1460 &mut self,
1461 label: impl DrawFunctionLabel,
1462 draw_function: DrawFunctionId,
1463 ) {
1464 self.draw_functions.push((label.intern(), draw_function));
1465 }
1466}
1467
1468#[derive(Clone, Copy, Default)]
1469pub enum RenderPhaseType {
1470 #[default]
1471 Opaque,
1472 AlphaMask,
1473 Transmissive,
1474 Transparent,
1475}
1476
1477#[derive(Resource, Default, Deref, DerefMut)]
1483pub struct RenderMaterialBindings(HashMap<UntypedAssetId, MaterialBindingId>);
1484
1485pub struct PreparedMaterial {
1487 pub binding: MaterialBindingId,
1488 pub properties: Arc<MaterialProperties>,
1489}
1490
1491impl<M: Material> ErasedRenderAsset for MeshMaterial3d<M>
1493where
1494 M::Data: PartialEq + Eq + Hash + Clone,
1495{
1496 type SourceAsset = M;
1497 type ErasedAsset = PreparedMaterial;
1498
1499 type Param = (
1500 SRes<RenderDevice>,
1501 SRes<DefaultOpaqueRendererMethod>,
1502 SResMut<MaterialBindGroupAllocators>,
1503 SResMut<RenderMaterialBindings>,
1504 SRes<DrawFunctions<Opaque3d>>,
1505 SRes<DrawFunctions<AlphaMask3d>>,
1506 SRes<DrawFunctions<Transmissive3d>>,
1507 SRes<DrawFunctions<Transparent3d>>,
1508 SRes<DrawFunctions<Opaque3dPrepass>>,
1509 SRes<DrawFunctions<AlphaMask3dPrepass>>,
1510 SRes<DrawFunctions<Opaque3dDeferred>>,
1511 SRes<DrawFunctions<AlphaMask3dDeferred>>,
1512 SRes<DrawFunctions<Shadow>>,
1513 SRes<AssetServer>,
1514 (
1515 Option<SRes<ShadowsEnabled<M>>>,
1516 Option<SRes<PrepassEnabled<M>>>,
1517 M::Param,
1518 ),
1519 );
1520
1521 fn prepare_asset(
1522 material: Self::SourceAsset,
1523 material_id: AssetId<Self::SourceAsset>,
1524 (
1525 render_device,
1526 default_opaque_render_method,
1527 bind_group_allocators,
1528 render_material_bindings,
1529 opaque_draw_functions,
1530 alpha_mask_draw_functions,
1531 transmissive_draw_functions,
1532 transparent_draw_functions,
1533 opaque_prepass_draw_functions,
1534 alpha_mask_prepass_draw_functions,
1535 opaque_deferred_draw_functions,
1536 alpha_mask_deferred_draw_functions,
1537 shadow_draw_functions,
1538 asset_server,
1539 (shadows_enabled, prepass_enabled, material_param),
1540 ): &mut SystemParamItem<Self::Param>,
1541 ) -> Result<Self::ErasedAsset, PrepareAssetError<Self::SourceAsset>> {
1542 let material_layout = M::bind_group_layout(render_device);
1543
1544 let shadows_enabled = shadows_enabled.is_some();
1545 let prepass_enabled = prepass_enabled.is_some();
1546
1547 let draw_opaque_pbr = opaque_draw_functions.read().id::<DrawMaterial>();
1548 let draw_alpha_mask_pbr = alpha_mask_draw_functions.read().id::<DrawMaterial>();
1549 let draw_transmissive_pbr = transmissive_draw_functions.read().id::<DrawMaterial>();
1550 let draw_transparent_pbr = transparent_draw_functions.read().id::<DrawMaterial>();
1551 let draw_opaque_prepass = opaque_prepass_draw_functions.read().get_id::<DrawPrepass>();
1552 let draw_alpha_mask_prepass = alpha_mask_prepass_draw_functions
1553 .read()
1554 .get_id::<DrawPrepass>();
1555 let draw_opaque_deferred = opaque_deferred_draw_functions
1556 .read()
1557 .get_id::<DrawPrepass>();
1558 let draw_alpha_mask_deferred = alpha_mask_deferred_draw_functions
1559 .read()
1560 .get_id::<DrawPrepass>();
1561 let shadow_draw_function_id = shadow_draw_functions.read().get_id::<DrawPrepass>();
1562
1563 let render_method = match material.opaque_render_method() {
1564 OpaqueRendererMethod::Forward => OpaqueRendererMethod::Forward,
1565 OpaqueRendererMethod::Deferred => OpaqueRendererMethod::Deferred,
1566 OpaqueRendererMethod::Auto => default_opaque_render_method.0,
1567 };
1568
1569 let mut mesh_pipeline_key_bits = MeshPipelineKey::empty();
1570 mesh_pipeline_key_bits.set(
1571 MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE,
1572 material.reads_view_transmission_texture(),
1573 );
1574
1575 let reads_view_transmission_texture =
1576 mesh_pipeline_key_bits.contains(MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE);
1577
1578 let render_phase_type = match material.alpha_mode() {
1579 AlphaMode::Blend | AlphaMode::Premultiplied | AlphaMode::Add | AlphaMode::Multiply => {
1580 RenderPhaseType::Transparent
1581 }
1582 _ if reads_view_transmission_texture => RenderPhaseType::Transmissive,
1583 AlphaMode::Opaque | AlphaMode::AlphaToCoverage => RenderPhaseType::Opaque,
1584 AlphaMode::Mask(_) => RenderPhaseType::AlphaMask,
1585 };
1586
1587 let draw_function_id = match render_phase_type {
1588 RenderPhaseType::Opaque => draw_opaque_pbr,
1589 RenderPhaseType::AlphaMask => draw_alpha_mask_pbr,
1590 RenderPhaseType::Transmissive => draw_transmissive_pbr,
1591 RenderPhaseType::Transparent => draw_transparent_pbr,
1592 };
1593 let prepass_draw_function_id = match render_phase_type {
1594 RenderPhaseType::Opaque => draw_opaque_prepass,
1595 RenderPhaseType::AlphaMask => draw_alpha_mask_prepass,
1596 _ => None,
1597 };
1598 let deferred_draw_function_id = match render_phase_type {
1599 RenderPhaseType::Opaque => draw_opaque_deferred,
1600 RenderPhaseType::AlphaMask => draw_alpha_mask_deferred,
1601 _ => None,
1602 };
1603
1604 let mut draw_functions = SmallVec::new();
1605 draw_functions.push((MaterialDrawFunction.intern(), draw_function_id));
1606 if let Some(prepass_draw_function_id) = prepass_draw_function_id {
1607 draw_functions.push((PrepassDrawFunction.intern(), prepass_draw_function_id));
1608 }
1609 if let Some(deferred_draw_function_id) = deferred_draw_function_id {
1610 draw_functions.push((DeferredDrawFunction.intern(), deferred_draw_function_id));
1611 }
1612 if let Some(shadow_draw_function_id) = shadow_draw_function_id {
1613 draw_functions.push((ShadowsDrawFunction.intern(), shadow_draw_function_id));
1614 }
1615
1616 let mut shaders = SmallVec::new();
1617 let mut add_shader = |label: InternedShaderLabel, shader_ref: ShaderRef| {
1618 let mayber_shader = match shader_ref {
1619 ShaderRef::Default => None,
1620 ShaderRef::Handle(handle) => Some(handle),
1621 ShaderRef::Path(path) => Some(asset_server.load(path)),
1622 };
1623 if let Some(shader) = mayber_shader {
1624 shaders.push((label, shader));
1625 }
1626 };
1627 add_shader(MaterialVertexShader.intern(), M::vertex_shader());
1628 add_shader(MaterialFragmentShader.intern(), M::fragment_shader());
1629 add_shader(PrepassVertexShader.intern(), M::prepass_vertex_shader());
1630 add_shader(PrepassFragmentShader.intern(), M::prepass_fragment_shader());
1631 add_shader(DeferredVertexShader.intern(), M::deferred_vertex_shader());
1632 add_shader(
1633 DeferredFragmentShader.intern(),
1634 M::deferred_fragment_shader(),
1635 );
1636
1637 #[cfg(feature = "meshlet")]
1638 {
1639 add_shader(
1640 MeshletFragmentShader.intern(),
1641 M::meshlet_mesh_fragment_shader(),
1642 );
1643 add_shader(
1644 MeshletPrepassFragmentShader.intern(),
1645 M::meshlet_mesh_prepass_fragment_shader(),
1646 );
1647 add_shader(
1648 MeshletDeferredFragmentShader.intern(),
1649 M::meshlet_mesh_deferred_fragment_shader(),
1650 );
1651 }
1652
1653 let bindless = material_uses_bindless_resources::<M>(render_device);
1654 let bind_group_data = material.bind_group_data();
1655 let material_key = ErasedMaterialKey::new(bind_group_data);
1656 fn specialize<M: Material>(
1657 pipeline: &MaterialPipeline,
1658 descriptor: &mut RenderPipelineDescriptor,
1659 mesh_layout: &MeshVertexBufferLayoutRef,
1660 erased_key: ErasedMaterialPipelineKey,
1661 ) -> Result<(), SpecializedMeshPipelineError>
1662 where
1663 M::Data: Hash + Clone,
1664 {
1665 let material_key = erased_key.material_key.to_key();
1666 M::specialize(
1667 pipeline,
1668 descriptor,
1669 mesh_layout,
1670 MaterialPipelineKey {
1671 mesh_key: erased_key.mesh_key,
1672 bind_group_data: material_key,
1673 },
1674 )
1675 }
1676
1677 match material.unprepared_bind_group(&material_layout, render_device, material_param, false)
1678 {
1679 Ok(unprepared) => {
1680 let bind_group_allocator =
1681 bind_group_allocators.get_mut(&TypeId::of::<M>()).unwrap();
1682 let binding = match render_material_bindings.entry(material_id.into()) {
1684 Entry::Occupied(mut occupied_entry) => {
1685 bind_group_allocator.free(*occupied_entry.get());
1690 let new_binding =
1691 bind_group_allocator.allocate_unprepared(unprepared, &material_layout);
1692 *occupied_entry.get_mut() = new_binding;
1693 new_binding
1694 }
1695 Entry::Vacant(vacant_entry) => *vacant_entry.insert(
1696 bind_group_allocator.allocate_unprepared(unprepared, &material_layout),
1697 ),
1698 };
1699
1700 Ok(PreparedMaterial {
1701 binding,
1702 properties: Arc::new(MaterialProperties {
1703 alpha_mode: material.alpha_mode(),
1704 depth_bias: material.depth_bias(),
1705 reads_view_transmission_texture,
1706 render_phase_type,
1707 render_method,
1708 mesh_pipeline_key_bits,
1709 material_layout: Some(material_layout),
1710 draw_functions,
1711 shaders,
1712 bindless,
1713 specialize: Some(specialize::<M>),
1714 material_key,
1715 shadows_enabled,
1716 prepass_enabled,
1717 }),
1718 })
1719 }
1720
1721 Err(AsBindGroupError::RetryNextUpdate) => {
1722 Err(PrepareAssetError::RetryNextUpdate(material))
1723 }
1724
1725 Err(AsBindGroupError::CreateBindGroupDirectly) => {
1726 match material.as_bind_group(&material_layout, render_device, material_param) {
1731 Ok(prepared_bind_group) => {
1732 let bind_group_allocator =
1733 bind_group_allocators.get_mut(&TypeId::of::<M>()).unwrap();
1734 let material_binding_id =
1736 bind_group_allocator.allocate_prepared(prepared_bind_group);
1737 render_material_bindings.insert(material_id.into(), material_binding_id);
1738
1739 Ok(PreparedMaterial {
1740 binding: material_binding_id,
1741 properties: Arc::new(MaterialProperties {
1742 alpha_mode: material.alpha_mode(),
1743 depth_bias: material.depth_bias(),
1744 reads_view_transmission_texture,
1745 render_phase_type,
1746 render_method,
1747 mesh_pipeline_key_bits,
1748 material_layout: Some(material_layout),
1749 draw_functions,
1750 shaders,
1751 bindless,
1752 specialize: Some(specialize::<M>),
1753 material_key,
1754 shadows_enabled,
1755 prepass_enabled,
1756 }),
1757 })
1758 }
1759
1760 Err(AsBindGroupError::RetryNextUpdate) => {
1761 Err(PrepareAssetError::RetryNextUpdate(material))
1762 }
1763
1764 Err(other) => Err(PrepareAssetError::AsBindGroupError(other)),
1765 }
1766 }
1767
1768 Err(other) => Err(PrepareAssetError::AsBindGroupError(other)),
1769 }
1770 }
1771
1772 fn unload_asset(
1773 source_asset: AssetId<Self::SourceAsset>,
1774 (_, _, bind_group_allocators, render_material_bindings, ..): &mut SystemParamItem<
1775 Self::Param,
1776 >,
1777 ) {
1778 let Some(material_binding_id) = render_material_bindings.remove(&source_asset.untyped())
1779 else {
1780 return;
1781 };
1782 let bind_group_allactor = bind_group_allocators.get_mut(&TypeId::of::<M>()).unwrap();
1783 bind_group_allactor.free(material_binding_id);
1784 }
1785}
1786
1787pub fn prepare_material_bind_groups(
1790 mut allocators: ResMut<MaterialBindGroupAllocators>,
1791 render_device: Res<RenderDevice>,
1792 fallback_image: Res<FallbackImage>,
1793 fallback_resources: Res<FallbackBindlessResources>,
1794) {
1795 for (_, allocator) in allocators.iter_mut() {
1796 allocator.prepare_bind_groups(&render_device, &fallback_resources, &fallback_image);
1797 }
1798}
1799
1800pub fn write_material_bind_group_buffers(
1806 mut allocators: ResMut<MaterialBindGroupAllocators>,
1807 render_device: Res<RenderDevice>,
1808 render_queue: Res<RenderQueue>,
1809) {
1810 for (_, allocator) in allocators.iter_mut() {
1811 allocator.write_buffers(&render_device, &render_queue);
1812 }
1813}
1814
1815#[derive(Resource, Debug)]
1817pub struct ShadowsEnabled<M: Material>(PhantomData<M>);
1818
1819impl<M: Material> Default for ShadowsEnabled<M> {
1820 fn default() -> Self {
1821 Self(PhantomData)
1822 }
1823}