bevy/crates/bevy_render/src/render_resource
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
..
batched_uniform_buffer.rs Fix *most* clippy lints (#15906) 2024-10-14 20:52:35 +00:00
bind_group_entries.rs Ugrade to wgpu version 25.0 (#19563) 2025-06-26 19:41:47 +00:00
bind_group_layout_entries.rs Ugrade to wgpu version 25.0 (#19563) 2025-06-26 19:41:47 +00:00
bind_group_layout.rs refactor(render): move WgpuWrapper into bevy_utils (#19303) 2025-05-27 03:43:49 +00:00
bind_group.rs Type erased materials (#19667) 2025-06-27 22:57:24 +00:00
bindless.rs Ugrade to wgpu version 25.0 (#19563) 2025-06-26 19:41:47 +00:00
buffer_vec.rs Add write_buffer_range to RawBufferVec and BufferVec (#19775) 2025-06-24 00:33:41 +00:00
buffer.rs refactor(render): move WgpuWrapper into bevy_utils (#19303) 2025-05-27 03:43:49 +00:00
gpu_array_buffer.rs Add newlines before impl blocks (#19746) 2025-06-22 23:07:02 +00:00
mod.rs Composable Pipeline Specialization (#17373) 2025-07-01 01:32:44 +00:00
pipeline_cache.rs bevy_render: fix clippy on wasm (#19872) 2025-06-29 22:09:37 +00:00
pipeline_specializer.rs Rename bevy_platform_support to bevy_platform (#18813) 2025-04-11 23:13:28 +00:00
pipeline.rs Composable Pipeline Specialization (#17373) 2025-07-01 01:32:44 +00:00
resource_macros.rs bevy_render: Apply #![deny(clippy::allow_attributes, clippy::allow_attributes_without_reason)] (#17194) 2025-01-06 23:10:58 +00:00
shader.rs Ugrade to wgpu version 25.0 (#19563) 2025-06-26 19:41:47 +00:00
specialize.rs Composable Pipeline Specialization (#17373) 2025-07-01 01:32:44 +00:00
storage_buffer.rs Update render_resource gpu buffer doc comments (#17118) 2025-01-28 05:13:04 +00:00
texture.rs refactor(render): move WgpuWrapper into bevy_utils (#19303) 2025-05-27 03:43:49 +00:00
uniform_buffer.rs Use target_abi = "sim" instead of ios_simulator feature (#17702) 2025-02-11 23:01:26 +00:00