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