![]() Currently, our specialization API works through a series of wrapper structs and traits, which make things confusing to follow and difficult to generalize. This pr takes a different approach, where "specializers" (types that implement `Specialize`) are composable, but "flat" rather than composed of a series of wrappers. The key is that specializers don't *produce* pipeline descriptors, but instead *modify* existing ones: ```rs pub trait Specialize<T: Specializable> { type Key: SpecializeKey; fn specialize( &self, key: Self::Key, descriptor: &mut T::Descriptor ) -> Result<Canonical<Self::Key>, BevyError>; } ``` This lets us use some derive magic to stick multiple specializers together: ```rs pub struct A; pub struct B; impl Specialize<RenderPipeline> for A { ... } impl Specialize<RenderPipeline> for A { ... } #[derive(Specialize)] #[specialize(RenderPipeline)] struct C { // specialization is applied in struct field order applied_first: A, applied_second: B, } type C::Key = (A::Key, B::Key); ``` This approach is much easier to understand, IMO, and also lets us separate concerns better. Specializers can be placed in fully separate crates/modules, and key computation can be shared as well. The only real breaking change here is that since specializers only modify descriptors, we need a "base" descriptor to work off of. This can either be manually supplied when constructing a `Specializer` (the new collection replacing `Specialized[Render/Compute]Pipelines`), or supplied by implementing `HasBaseDescriptor` on a specializer. See `examples/shader/custom_phase_item.rs` for an example implementation. ## Testing - Did some simple manual testing of the derive macro, it seems robust. --- ## Showcase ```rs #[derive(Specialize, HasBaseDescriptor)] #[specialize(RenderPipeline)] pub struct SpecializeMeshMaterial<M: Material> { // set mesh bind group layout and shader defs mesh: SpecializeMesh, // set view bind group layout and shader defs view: SpecializeView, // since type SpecializeMaterial::Key = (), // we can hide it from the wrapper's external API #[key(default)] // defer to the GetBaseDescriptor impl of SpecializeMaterial, // since it carries the vertex and fragment handles #[base_descriptor] // set material bind group layout, etc material: SpecializeMaterial<M>, } // implementation generated by the derive macro impl <M: Material> Specialize<RenderPipeline> for SpecializeMeshMaterial<M> { type Key = (MeshKey, ViewKey); fn specialize( &self, key: Self::Key, descriptor: &mut RenderPipelineDescriptor ) -> Result<Canonical<Self::Key>, BevyError> { let mesh_key = self.mesh.specialize(key.0, descriptor)?; let view_key = self.view.specialize(key.1, descriptor)?; let _ = self.material.specialize((), descriptor)?; Ok((mesh_key, view_key)); } } impl <M: Material> HasBaseDescriptor<RenderPipeline> for SpecializeMeshMaterial<M> { fn base_descriptor(&self) -> RenderPipelineDescriptor { self.material.base_descriptor() } } ``` --------- Co-authored-by: Tim Overbeek <158390905+Bleachfuel@users.noreply.github.com> |
||
---|---|---|
.. | ||
migration-guides | ||
release-notes | ||
migration_guides_template.md | ||
migration_guides.md | ||
README.md | ||
release_notes_template.md | ||
release_notes.md |
Release Content
This directory contains drafts of documentation for the current development cycle, which will be published to the website during the next release. You can find more information in the release notes and migration guides files.