nuke most of stuff

TODO: migration guide
This commit is contained in:
Emerson Coskey 2025-07-09 21:09:06 -07:00
parent c9c8964857
commit 0d9f1c81ce
No known key found for this signature in database
3 changed files with 57 additions and 241 deletions

View File

@ -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::<Vec<_>>();
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 = {

View File

@ -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<RenderPipeline> for A {
/// # type Key = ();
/// #
/// # fn specialize(
/// # &self,
/// # key: (),
/// # _descriptor: &mut RenderPipelineDescriptor
/// # ) -> Result<(), BevyError> {
/// # Ok(key)
/// # }
/// // ...
/// }
///
/// impl Specializer<RenderPipeline> for B {
/// # type Key = ();
/// #
/// # fn specialize(
/// # &self,
/// # key: (),
/// # _descriptor: &mut RenderPipelineDescriptor
/// # ) -> Result<(), BevyError> {
/// # Ok(key)
/// # }
/// // ...
/// }
///
/// impl GetBaseDescriptor<RenderPipeline> 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<T: Specializable>: Specializer<T> {
fn get_base_descriptor(&self) -> T::Descriptor;
}
pub type SpecializerFn<T, S> =
fn(<S as Specializer<T>>::Key, &mut <T as Specializable>::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<T: Specializable, S: Specializer<T>> {
specializer: S,
user_specializer: Option<SpecializerFn<T, S>>,
@ -447,19 +364,3 @@ impl<T: Specializable, S: Specializer<T>> SpecializedCache<T, S> {
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<T, S> FromWorld for SpecializedCache<T, S>
where
T: Specializable,
S: FromWorld + Specializer<T> + GetBaseDescriptor<T>,
{
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)
}
}

View File

@ -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::<SpecializedCache<RenderPipeline, CustomPhaseSpecializer>>()
app.sub_app_mut(RenderApp)
.init_resource::<CustomPhasePipeline>()
.add_render_command::<Opaque3d, DrawCustomPhaseItemCommands>()
.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<PipelineCache>,
mut pipeline: ResMut<CustomPhasePipeline>,
mut opaque_render_phases: ResMut<ViewBinnedRenderPhases<Opaque3d>>,
opaque_draw_functions: Res<DrawFunctions<Opaque3d>>,
mut specializer: ResMut<SpecializedCache<RenderPipeline, CustomPhaseSpecializer>>,
views: Query<(&ExtractedView, &RenderVisibleEntities, &Msaa)>,
mut next_tick: Local<Tick>,
) {
@ -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<Shader>,
specialized_cache: SpecializedCache<RenderPipeline, CustomPhaseSpecializer>,
}
impl FromWorld for CustomPhaseSpecializer {
impl FromWorld for CustomPhasePipeline {
fn from_world(world: &mut World) -> Self {
let asset_server = world.resource::<AssetServer>();
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<RenderPipeline> for CustomPhaseSpecializer {
type Key = CustomPhaseKey;
fn specialize(
&self,
key: Self::Key,
descriptor: &mut RenderPipelineDescriptor,
) -> Result<Canonical<Self::Key>, BevyError> {
descriptor.multisample.count = key.0.samples();
Ok(key)
}
}
impl GetBaseDescriptor<RenderPipeline> 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::<Vertex>() as u64,
step_mode: VertexStepMode::Vertex,
@ -333,7 +314,7 @@ impl GetBaseDescriptor<RenderPipeline> 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<RenderPipeline> 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<RenderPipeline> for CustomPhaseSpecializer {
type Key = CustomPhaseKey;
fn specialize(
&self,
key: Self::Key,
descriptor: &mut RenderPipelineDescriptor,
) -> Result<Canonical<Self::Key>, 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::<RenderDevice>();