bevy/examples/shader
Emerson Coskey bdd3ef71b8
Composable Pipeline Specialization (#17373)
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>
2025-07-01 01:32:44 +00:00
..
animate_shader.rs Migrate cameras to required components (#15641) 2024-10-05 01:59:52 +00:00
array_texture.rs Remove incorrect equality comparisons for asset load error types (#15890) 2024-10-14 01:00:45 +00:00
automatic_instancing.rs Add user supplied mesh tag (#17648) 2025-02-10 22:38:13 +00:00
compute_shader_game_of_life.rs Fix the game of life example panicking if the pipeline shader isn't ready on the first frame. (#19420) 2025-05-29 11:30:53 +00:00
custom_phase_item.rs Composable Pipeline Specialization (#17373) 2025-07-01 01:32:44 +00:00
custom_post_processing.rs Use RenderStartup in custom_post_processing example (#19886) 2025-06-30 23:54:05 +00:00
custom_render_phase.rs Resolution override (#19817) 2025-06-27 16:30:54 +00:00
custom_shader_instancing.rs Ugrade to wgpu version 25.0 (#19563) 2025-06-26 19:41:47 +00:00
custom_vertex_attribute.rs Type erased materials (#19667) 2025-06-27 22:57:24 +00:00
extended_material_bindless.rs Add bindless support back to ExtendedMaterial. (#18025) 2025-04-09 15:34:44 +00:00
extended_material.rs Place percentage-closer soft shadows behind a feature gate to save on samplers. (#16068) 2024-10-24 21:16:00 +00:00
fallback_image.rs Migrate cameras to required components (#15641) 2024-10-05 01:59:52 +00:00
gpu_readback.rs Add newlines before impl blocks (#19746) 2025-06-22 23:07:02 +00:00
shader_defs.rs Type erased materials (#19667) 2025-06-27 22:57:24 +00:00
shader_material_2d.rs Add alpha mode implementation to shader_material_2d (#16603) 2025-01-28 05:09:30 +00:00
shader_material_bindless.rs Use global binding arrays for bindless resources. (#17898) 2025-02-21 05:55:36 +00:00
shader_material_glsl.rs Type erased materials (#19667) 2025-06-27 22:57:24 +00:00
shader_material_screenspace_texture.rs aligning public apis of Time,Timer and Stopwatch (#15962) 2024-10-16 21:09:32 +00:00
shader_material_wesl.rs Type erased materials (#19667) 2025-06-27 22:57:24 +00:00
shader_material.rs Migrate cameras to required components (#15641) 2024-10-05 01:59:52 +00:00
shader_prepass.rs Update shader_prepass, testbed_2d, and first_person_view_model examples to use children! macro (#18270) 2025-03-22 22:35:20 +00:00
specialized_mesh_pipeline.rs Ugrade to wgpu version 25.0 (#19563) 2025-06-26 19:41:47 +00:00
storage_buffer.rs Add user supplied mesh tag (#17648) 2025-02-10 22:38:13 +00:00
texture_binding_array.rs Type erased materials (#19667) 2025-06-27 22:57:24 +00:00