diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index e50ffcc0fc..a93e3e4c58 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -1,5 +1,3 @@ -use core::ops::DerefMut; - use bevy_ecs::{ entity::{EntityHashMap, EntityHashSet}, prelude::*, @@ -19,6 +17,7 @@ use bevy_render::{ }; use bevy_transform::components::{GlobalTransform, Transform}; use bevy_utils::Parallel; +use core::{marker::PhantomData, ops::DerefMut}; use crate::*; @@ -91,6 +90,16 @@ pub mod light_consts { } } +/// Marker resource for whether shadows are enabled for this material type +#[derive(Resource, Debug)] +pub struct ShadowsEnabled(PhantomData); + +impl Default for ShadowsEnabled { + fn default() -> Self { + Self(PhantomData) + } +} + /// Controls the resolution of [`PointLight`] shadow maps. /// /// ``` diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 17603704e6..bb1f9afde3 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -372,6 +372,13 @@ where } if let Some(render_app) = app.get_sub_app_mut(RenderApp) { + if self.prepass_enabled { + render_app.init_resource::>(); + } + if self.shadows_enabled { + render_app.init_resource::>(); + } + render_app .add_systems(RenderStartup, setup_render_app::) .add_systems( @@ -1316,6 +1323,10 @@ pub struct MaterialProperties { /// The key for this material, typically a bitfield of flags that are used to modify /// the pipeline descriptor used for this material. pub material_key: SmallVec<[u8; 8]>, + /// Whether shadows are enabled for this material + pub shadows_enabled: bool, + /// Whether prepass is enabled for this material + pub prepass_enabled: bool, } impl MaterialProperties { @@ -1394,7 +1405,11 @@ where SRes>, SRes>, SRes, - M::Param, + ( + Option>>, + Option>>, + M::Param, + ), ); fn prepare_asset( @@ -1415,10 +1430,14 @@ where alpha_mask_deferred_draw_functions, shadow_draw_functions, asset_server, - material_param, + (shadows_enabled, prepass_enabled, material_param), ): &mut SystemParamItem, ) -> Result> { let material_layout = M::bind_group_layout(render_device); + + let shadows_enabled = shadows_enabled.is_some(); + let prepass_enabled = prepass_enabled.is_some(); + let draw_opaque_pbr = opaque_draw_functions.read().id::(); let draw_alpha_mask_pbr = alpha_mask_draw_functions.read().id::(); let draw_transmissive_pbr = transmissive_draw_functions.read().id::(); @@ -1584,6 +1603,8 @@ where bindless, specialize: Some(specialize::), material_key, + shadows_enabled, + prepass_enabled, }), }) } @@ -1621,6 +1642,8 @@ where bindless, specialize: Some(specialize::), material_key, + shadows_enabled, + prepass_enabled, }), }) } diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 42b2c28c15..b007242c76 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -4,11 +4,11 @@ use crate::{ alpha_mode_pipeline_key, binding_arrays_are_usable, buffer_layout, collect_meshes_for_gpu_building, set_mesh_motion_vector_flags, setup_morph_and_skinning_defs, skin, DeferredDrawFunction, DeferredFragmentShader, DeferredVertexShader, DrawMesh, - EntitySpecializationTicks, ErasedMaterialPipelineKey, MaterialPipeline, MaterialProperties, - MeshLayouts, MeshPipeline, MeshPipelineKey, OpaqueRendererMethod, PreparedMaterial, - PrepassDrawFunction, PrepassFragmentShader, PrepassVertexShader, RenderLightmaps, - RenderMaterialInstances, RenderMeshInstanceFlags, RenderMeshInstances, RenderPhaseType, - SetMaterialBindGroup, SetMeshBindGroup, ShadowView, + EntitySpecializationTicks, ErasedMaterialPipelineKey, Material, MaterialPipeline, + MaterialProperties, MeshLayouts, MeshPipeline, MeshPipelineKey, OpaqueRendererMethod, + PreparedMaterial, PrepassDrawFunction, PrepassFragmentShader, PrepassVertexShader, + RenderLightmaps, RenderMaterialInstances, RenderMeshInstanceFlags, RenderMeshInstances, + RenderPhaseType, SetMaterialBindGroup, SetMeshBindGroup, ShadowView, }; use bevy_app::{App, Plugin, PreUpdate}; use bevy_render::{ @@ -58,13 +58,15 @@ use crate::meshlet::{ use alloc::sync::Arc; use bevy_derive::{Deref, DerefMut}; -use bevy_ecs::component::Tick; -use bevy_ecs::system::SystemChangeTick; +use bevy_ecs::{component::Tick, system::SystemChangeTick}; use bevy_platform::collections::HashMap; -use bevy_render::erased_render_asset::ErasedRenderAssets; -use bevy_render::sync_world::MainEntityHashMap; -use bevy_render::view::RenderVisibleEntities; -use bevy_render::RenderSystems::{PrepareAssets, PrepareResources}; +use bevy_render::{ + erased_render_asset::ErasedRenderAssets, + sync_world::MainEntityHashMap, + view::RenderVisibleEntities, + RenderSystems::{PrepareAssets, PrepareResources}, +}; +use core::marker::PhantomData; /// Sets up everything required to use the prepass pipeline. /// @@ -188,6 +190,16 @@ impl Plugin for PrepassPlugin { } } +/// Marker resource for whether prepass is enabled globally for this material type +#[derive(Resource, Debug)] +pub struct PrepassEnabled(PhantomData); + +impl Default for PrepassEnabled { + fn default() -> Self { + PrepassEnabled(PhantomData) + } +} + #[derive(Resource)] struct AnyPrepassPluginLoaded; @@ -911,6 +923,11 @@ pub fn specialize_prepass_material_meshes( let Some(material) = render_materials.get(material_instance.asset_id) else { continue; }; + if !material.properties.prepass_enabled && !material.properties.shadows_enabled { + // If the material was previously specialized for prepass, remove it + view_specialized_material_pipeline_cache.remove(visible_entity); + continue; + } let Some(mesh) = render_meshes.get(mesh_instance.mesh_asset_id) else { continue; }; diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 5a19930842..1a55eb0926 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -1891,6 +1891,10 @@ pub fn specialize_shadows( let Some(material) = render_materials.get(material_instance.asset_id) else { continue; }; + if !material.properties.shadows_enabled { + // If the material is not a shadow caster, we don't need to specialize it. + continue; + } if !mesh_instance .flags .contains(RenderMeshInstanceFlags::SHADOW_CASTER)