diff --git a/crates/bevy_render/macros/src/specializer.rs b/crates/bevy_render/macros/src/specializer.rs index d755c73736..d367722993 100644 --- a/crates/bevy_render/macros/src/specializer.rs +++ b/crates/bevy_render/macros/src/specializer.rs @@ -87,7 +87,6 @@ struct FieldInfo { ty: Type, member: Member, key: Key, - use_base_descriptor: bool, } impl FieldInfo { @@ -117,15 +116,6 @@ impl FieldInfo { parse_quote!(#ty: #specialize_path::Specializer<#target_path>) } } - - fn get_base_descriptor_predicate( - &self, - specialize_path: &Path, - target_path: &Path, - ) -> WherePredicate { - let ty = &self.ty; - parse_quote!(#ty: #specialize_path::GetBaseDescriptor<#target_path>) - } } fn get_field_info( @@ -190,7 +180,6 @@ fn get_field_info( ty: field_ty, member: field_member, key, - use_base_descriptor, }); } @@ -261,41 +250,18 @@ pub fn impl_specializer(input: TokenStream) -> TokenStream { }) .collect(); - let base_descriptor_fields = field_info - .iter() - .filter(|field| field.use_base_descriptor) - .collect::>(); - - if base_descriptor_fields.len() > 1 { - return syn::Error::new( - Span::call_site(), - "Too many #[base_descriptor] attributes found. It must be present on exactly one field", - ) - .into_compile_error() - .into(); - } - - let base_descriptor_field = base_descriptor_fields.first().copied(); - match targets { - SpecializeImplTargets::All => { - let specialize_impl = impl_specialize_all( - &specialize_path, - &ecs_path, - &ast, - &field_info, - &key_patterns, - &key_tuple_idents, - ); - let get_base_descriptor_impl = base_descriptor_field - .map(|field_info| impl_get_base_descriptor_all(&specialize_path, &ast, field_info)) - .unwrap_or_default(); - [specialize_impl, get_base_descriptor_impl] - .into_iter() - .collect() - } - SpecializeImplTargets::Specific(targets) => { - let specialize_impls = targets.iter().map(|target| { + SpecializeImplTargets::All => impl_specialize_all( + &specialize_path, + &ecs_path, + &ast, + &field_info, + &key_patterns, + &key_tuple_idents, + ), + SpecializeImplTargets::Specific(targets) => targets + .iter() + .map(|target| { impl_specialize_specific( &specialize_path, &ecs_path, @@ -305,14 +271,8 @@ pub fn impl_specializer(input: TokenStream) -> TokenStream { &key_patterns, &key_tuple_idents, ) - }); - let get_base_descriptor_impls = targets.iter().filter_map(|target| { - base_descriptor_field.map(|field_info| { - impl_get_base_descriptor_specific(&specialize_path, &ast, field_info, target) - }) - }); - specialize_impls.chain(get_base_descriptor_impls).collect() - } + }) + .collect(), } } @@ -406,56 +366,6 @@ fn impl_specialize_specific( }) } -fn impl_get_base_descriptor_specific( - specialize_path: &Path, - ast: &DeriveInput, - base_descriptor_field_info: &FieldInfo, - target_path: &Path, -) -> TokenStream { - let struct_name = &ast.ident; - let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); - let field_ty = &base_descriptor_field_info.ty; - let field_member = &base_descriptor_field_info.member; - TokenStream::from(quote!( - impl #impl_generics #specialize_path::GetBaseDescriptor<#target_path> for #struct_name #type_generics #where_clause { - fn get_base_descriptor(&self) -> <#target_path as #specialize_path::Specializable>::Descriptor { - <#field_ty as #specialize_path::GetBaseDescriptor<#target_path>>::get_base_descriptor(&self.#field_member) - } - } - )) -} - -fn impl_get_base_descriptor_all( - specialize_path: &Path, - ast: &DeriveInput, - base_descriptor_field_info: &FieldInfo, -) -> TokenStream { - let target_path = Path::from(format_ident!("T")); - let struct_name = &ast.ident; - let mut generics = ast.generics.clone(); - generics.params.insert( - 0, - parse_quote!(#target_path: #specialize_path::Specializable), - ); - - let where_clause = generics.make_where_clause(); - where_clause.predicates.push( - base_descriptor_field_info.get_base_descriptor_predicate(specialize_path, &target_path), - ); - - let (_, type_generics, _) = ast.generics.split_for_impl(); - let (impl_generics, _, where_clause) = &generics.split_for_impl(); - let field_ty = &base_descriptor_field_info.ty; - let field_member = &base_descriptor_field_info.member; - TokenStream::from(quote! { - impl #impl_generics #specialize_path::GetBaseDescriptor<#target_path> for #struct_name #type_generics #where_clause { - fn get_base_descriptor(&self) -> <#target_path as #specialize_path::Specializable>::Descriptor { - <#field_ty as #specialize_path::GetBaseDescriptor<#target_path>>::get_base_descriptor(&self.#field_member) - } - } - }) -} - pub fn impl_specializer_key(input: TokenStream) -> TokenStream { let bevy_render_path: Path = crate::bevy_render_path(); let specialize_path = { diff --git a/crates/bevy_render/src/render_resource/specializer.rs b/crates/bevy_render/src/render_resource/specializer.rs index d7a2f3aca1..762458145a 100644 --- a/crates/bevy_render/src/render_resource/specializer.rs +++ b/crates/bevy_render/src/render_resource/specializer.rs @@ -2,11 +2,7 @@ use super::{ CachedComputePipelineId, CachedRenderPipelineId, ComputePipeline, ComputePipelineDescriptor, PipelineCache, RenderPipeline, RenderPipelineDescriptor, }; -use bevy_ecs::{ - error::BevyError, - resource::Resource, - world::{FromWorld, World}, -}; +use bevy_ecs::error::BevyError; use bevy_platform::{ collections::{ hash_map::{Entry, VacantEntry}, @@ -260,91 +256,12 @@ macro_rules! impl_specialization_key_tuple { // TODO: How to we fake_variadics this? all_tuples!(impl_specialization_key_tuple, 0, 12, T); -/// Defines a specializer that can also provide a "base descriptor". -/// -/// In order to be composable, [`Specializer`] implementers don't create full -/// descriptors, only transform them. However, [`SpecializedCache`]s need a -/// "base descriptor" at creation time in order to have something for the -/// [`Specializer`] to work off of. This trait allows [`SpecializedCache`] -/// to impl [`FromWorld`] for [`Specializer`]s that also satisfy [`FromWorld`] -/// and [`GetBaseDescriptor`]. -/// -/// This trait can be also derived with `#[derive(Specializer)]`, by marking -/// a field with `#[base_descriptor]` to use its [`GetBaseDescriptor`] implementation. -/// -/// Example: -/// ```rust -/// # use bevy_ecs::error::BevyError; -/// # use bevy_render::render_resource::Specializer; -/// # use bevy_render::render_resource::GetBaseDescriptor; -/// # use bevy_render::render_resource::SpecializerKey; -/// # use bevy_render::render_resource::RenderPipeline; -/// # use bevy_render::render_resource::RenderPipelineDescriptor; -/// struct A; -/// struct B; -/// -/// impl Specializer for A { -/// # type Key = (); -/// # -/// # fn specialize( -/// # &self, -/// # key: (), -/// # _descriptor: &mut RenderPipelineDescriptor -/// # ) -> Result<(), BevyError> { -/// # Ok(key) -/// # } -/// // ... -/// } -/// -/// impl Specializer for B { -/// # type Key = (); -/// # -/// # fn specialize( -/// # &self, -/// # key: (), -/// # _descriptor: &mut RenderPipelineDescriptor -/// # ) -> Result<(), BevyError> { -/// # Ok(key) -/// # } -/// // ... -/// } -/// -/// impl GetBaseDescriptor for B { -/// fn get_base_descriptor(&self) -> RenderPipelineDescriptor { -/// # todo!() -/// // ... -/// } -/// } -/// -/// -/// #[derive(Specializer)] -/// #[specialize(RenderPipeline)] -/// struct C { -/// a: A, -/// #[base_descriptor] -/// b: B, -/// } -/// -/// /* -/// The generated implementation: -/// impl GetBaseDescriptor for C { -/// fn get_base_descriptor(&self) -> RenderPipelineDescriptor { -/// self.b.base_descriptor() -/// } -/// } -/// */ -/// ``` -pub trait GetBaseDescriptor: Specializer { - fn get_base_descriptor(&self) -> T::Descriptor; -} - pub type SpecializerFn = fn(>::Key, &mut ::Descriptor) -> Result<(), BevyError>; /// A cache for specializable resources. For a given key type the resulting /// resource will only be created if it is missing, retrieving it from the /// cache otherwise. -#[derive(Resource)] pub struct SpecializedCache> { specializer: S, user_specializer: Option>, @@ -447,19 +364,3 @@ impl> SpecializedCache { Ok(id) } } - -/// [`SpecializedCache`] implements [`FromWorld`] for [`Specializer`]s -/// that also satisfy [`FromWorld`] and [`GetBaseDescriptor`]. This will -/// create a [`SpecializedCache`] with no user specializer, and the base -/// descriptor take from the specializer's [`GetBaseDescriptor`] implementation. -impl FromWorld for SpecializedCache -where - T: Specializable, - S: FromWorld + Specializer + GetBaseDescriptor, -{ - fn from_world(world: &mut World) -> Self { - let specializer = S::from_world(world); - let base_descriptor = specializer.get_base_descriptor(); - Self::new(specializer, None, base_descriptor) - } -} diff --git a/examples/shader/custom_phase_item.rs b/examples/shader/custom_phase_item.rs index fd8dc063d0..5e4ab594ac 100644 --- a/examples/shader/custom_phase_item.rs +++ b/examples/shader/custom_phase_item.rs @@ -25,8 +25,8 @@ use bevy::{ }, render_resource::{ BufferUsages, Canonical, ColorTargetState, ColorWrites, CompareFunction, - DepthStencilState, FragmentState, GetBaseDescriptor, IndexFormat, PipelineCache, - RawBufferVec, RenderPipeline, RenderPipelineDescriptor, SpecializedCache, Specializer, + DepthStencilState, FragmentState, IndexFormat, PipelineCache, RawBufferVec, + RenderPipeline, RenderPipelineDescriptor, SpecializedCache, Specializer, SpecializerKey, TextureFormat, VertexAttribute, VertexBufferLayout, VertexFormat, VertexState, VertexStepMode, }, @@ -165,9 +165,8 @@ fn main() { .add_systems(Startup, setup); // We make sure to add these to the render app, not the main app. - app.get_sub_app_mut(RenderApp) - .unwrap() - .init_resource::>() + app.sub_app_mut(RenderApp) + .init_resource::() .add_render_command::() .add_systems( Render, @@ -212,9 +211,9 @@ fn prepare_custom_phase_item_buffers(mut commands: Commands) { /// the opaque render phases of each view. fn queue_custom_phase_item( pipeline_cache: Res, + mut pipeline: ResMut, mut opaque_render_phases: ResMut>, opaque_draw_functions: Res>, - mut specializer: ResMut>, views: Query<(&ExtractedView, &RenderVisibleEntities, &Msaa)>, mut next_tick: Local, ) { @@ -237,7 +236,9 @@ fn queue_custom_phase_item( // some per-view settings, such as whether the view is HDR, but for // simplicity's sake we simply hard-code the view's characteristics, // with the exception of number of MSAA samples. - let Ok(pipeline_id) = specializer.specialize(&pipeline_cache, CustomPhaseKey(*msaa)) + let Ok(pipeline_id) = pipeline + .specialized_cache + .specialize(&pipeline_cache, CustomPhaseKey(*msaa)) else { continue; }; @@ -275,44 +276,24 @@ fn queue_custom_phase_item( } } -/// Holds a reference to our shader. -/// -/// This is loaded at app creation time. -struct CustomPhaseSpecializer { +struct CustomPhaseSpecializer; + +#[derive(Resource)] +struct CustomPhasePipeline { + /// Holds a reference to our shader. shader: Handle, + specialized_cache: SpecializedCache, } -impl FromWorld for CustomPhaseSpecializer { +impl FromWorld for CustomPhasePipeline { fn from_world(world: &mut World) -> Self { let asset_server = world.resource::(); - Self { - shader: asset_server.load("shaders/custom_phase_item.wgsl"), - } - } -} + let shader = asset_server.load("shaders/custom_phase_item.wgsl"); -#[derive(Copy, Clone, PartialEq, Eq, Hash, SpecializerKey)] -struct CustomPhaseKey(Msaa); - -impl Specializer for CustomPhaseSpecializer { - type Key = CustomPhaseKey; - - fn specialize( - &self, - key: Self::Key, - descriptor: &mut RenderPipelineDescriptor, - ) -> Result, BevyError> { - descriptor.multisample.count = key.0.samples(); - Ok(key) - } -} - -impl GetBaseDescriptor for CustomPhaseSpecializer { - fn get_base_descriptor(&self) -> RenderPipelineDescriptor { - RenderPipelineDescriptor { + let base_descriptor = RenderPipelineDescriptor { label: Some("custom render pipeline".into()), vertex: VertexState { - shader: self.shader.clone(), + shader: shader.clone(), buffers: vec![VertexBufferLayout { array_stride: size_of::() as u64, step_mode: VertexStepMode::Vertex, @@ -333,7 +314,7 @@ impl GetBaseDescriptor for CustomPhaseSpecializer { ..default() }, fragment: Some(FragmentState { - shader: self.shader.clone(), + shader: shader.clone(), targets: vec![Some(ColorTargetState { // Ordinarily, you'd want to check whether the view has the // HDR format and substitute the appropriate texture format @@ -354,10 +335,34 @@ impl GetBaseDescriptor for CustomPhaseSpecializer { bias: default(), }), ..default() + }; + + let specialized_cache = + SpecializedCache::new(CustomPhaseSpecializer, None, base_descriptor); + + Self { + shader, + specialized_cache, } } } +#[derive(Copy, Clone, PartialEq, Eq, Hash, SpecializerKey)] +struct CustomPhaseKey(Msaa); + +impl Specializer for CustomPhaseSpecializer { + type Key = CustomPhaseKey; + + fn specialize( + &self, + key: Self::Key, + descriptor: &mut RenderPipelineDescriptor, + ) -> Result, BevyError> { + descriptor.multisample.count = key.0.samples(); + Ok(key) + } +} + impl FromWorld for CustomPhaseItemBuffers { fn from_world(world: &mut World) -> Self { let render_device = world.resource::();