edit migration guide with new patterns
This commit is contained in:
parent
0d9f1c81ce
commit
2db1ef9b69
@ -6,7 +6,7 @@ pull_requests: [17373]
|
||||
The existing pipeline specialization APIs (`SpecializedRenderPipeline` etc.) have
|
||||
been replaced with a single `Specializer` trait and `SpecializedCache` collection:
|
||||
|
||||
```rs
|
||||
```rust
|
||||
pub trait Specializer<T: Specializable>: Send + Sync + 'static {
|
||||
type Key: SpecializerKey;
|
||||
fn specialize(
|
||||
@ -19,20 +19,55 @@ pub trait Specializer<T: Specializable>: Send + Sync + 'static {
|
||||
pub struct SpecializedCache<T: Specializable, S: Specializer<T>>{ ... };
|
||||
```
|
||||
|
||||
The main difference is the change from *producing* a pipeline descriptor to
|
||||
*mutating* one based on a key. The "base descriptor" that the `SpecializedCache`
|
||||
passes to the `Specializer` can either be specified manually with `Specializer::new`
|
||||
or by implementing `GetBaseDescriptor`. There's also a new trait for specialization
|
||||
keys, `SpecializeKey`, that can be derived with the included macro in most cases.
|
||||
For more info on specialization, see the docs for `bevy_render::render_resources::Specializer`
|
||||
|
||||
Composing multiple different specializers together with the `derive(Specializer)`
|
||||
macro can be a lot more powerful (see the `Specialize` docs), but migrating
|
||||
individual specializers is fairly simple. All static parts of the pipeline
|
||||
should be specified in the base descriptor, while the `Specializer` impl
|
||||
should mutate the key as little as necessary to match the key.
|
||||
## Mutation and Base Descriptors
|
||||
|
||||
```rs
|
||||
The main difference between the old and new trait is that instead of
|
||||
*producing* a pipeline descriptor, `Specializer`s *mutate* existing descriptors
|
||||
based on a key. As such, `SpecializedCache::new` takes in a "base descriptor"
|
||||
to act as the template from which the specializer creates pipeline variants.
|
||||
|
||||
When migrating, the "static" parts of the pipeline (that don't depend
|
||||
on the key) should become part of the base descriptor, while the specializer
|
||||
itself should only change the parts demanded by the key. In the full example
|
||||
below, instead of creating the entire pipeline descriptor the specializer
|
||||
only changes the msaa sample count and the bind group layout.
|
||||
|
||||
## Composing Specializers
|
||||
|
||||
`Specializer`s can also be *composed* with the included derive macro to combine
|
||||
their effects! This is a great way to encapsulate and reuse specialization logic,
|
||||
though the rest of this guide will focus on migrating "standalone" specializers.
|
||||
|
||||
```rust
|
||||
pub struct MsaaSpecializer {...}
|
||||
impl Specialize<RenderPipeline> for MsaaSpecializer {...}
|
||||
|
||||
pub struct MeshLayoutSpecializer {...}
|
||||
impl Specialize<RenderPipeline> for MeshLayoutSpecializer {...}
|
||||
|
||||
#[derive(Specializer)]
|
||||
#[specialize(RenderPipeline)]
|
||||
pub struct MySpecializer {
|
||||
msaa: MsaaSpecializer,
|
||||
mesh_layout: MeshLayoutSpecializer,
|
||||
}
|
||||
```
|
||||
|
||||
## Misc Changes
|
||||
|
||||
The analogue of `SpecializedRenderPipelines`, `SpecializedCache`, is no longer a
|
||||
Bevy `Resource`. Instead, the cache should be stored in a user-created `Resource`
|
||||
(shown below) or even in a `Component` depending on the use case.
|
||||
|
||||
## Full Migration Example
|
||||
|
||||
Before:
|
||||
|
||||
```rust
|
||||
#[derive(Resource)]
|
||||
pub struct MyPipeline {
|
||||
layout: BindGroupLayout,
|
||||
layout_msaa: BindGroupLayout,
|
||||
vertex: Handle<Shader>,
|
||||
@ -41,65 +76,134 @@ pub struct MySpecializer {
|
||||
|
||||
// before
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
// after
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, SpecializerKey)]
|
||||
|
||||
pub struct MyKey {
|
||||
blend_state: BlendState,
|
||||
pub struct MyPipelineKey {
|
||||
msaa: Msaa,
|
||||
}
|
||||
|
||||
impl FromWorld for MySpecializer {
|
||||
fn from_world(&mut World) -> Self {
|
||||
...
|
||||
impl FromWorld for MyPipeline {
|
||||
fn from_world(world: &mut World) -> Self {
|
||||
let render_device = world.resource::<RenderDevice>();
|
||||
let asset_server = world.resource::<AssetServer>();
|
||||
|
||||
let layout = render_device.create_bind_group_layout(...);
|
||||
let layout_msaa = render_device.create_bind_group_layout(...);
|
||||
|
||||
let vertex = asset_server.load("vertex.wgsl");
|
||||
let fragment = asset_server.load("fragment.wgsl");
|
||||
|
||||
Self {
|
||||
layout,
|
||||
layout_msaa,
|
||||
vertex,
|
||||
fragment,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// before
|
||||
impl SpecializedRenderPipeline for MySpecializer {
|
||||
type Key = MyKey;
|
||||
impl SpecializedRenderPipeline for MyPipeline {
|
||||
type Key = MyPipelineKey;
|
||||
|
||||
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
|
||||
RenderPipelineDescriptor {
|
||||
label: Some("my_pipeline".into()),
|
||||
layout: vec![
|
||||
if key.msaa.samples() > 0 {
|
||||
if key.msaa.samples() > 1 {
|
||||
self.layout_msaa.clone()
|
||||
} else {
|
||||
self.layout.clone()
|
||||
}
|
||||
],
|
||||
push_constant_ranges: vec![],
|
||||
vertex: VertexState {
|
||||
shader: self.vertex.clone(),
|
||||
shader_defs: vec![],
|
||||
entry_point: "vertex".into(),
|
||||
buffers: vec![],
|
||||
..default()
|
||||
},
|
||||
primitive: Default::default(),
|
||||
depth_stencil: None,
|
||||
multisample: MultisampleState {
|
||||
count: key.msaa.samples(),
|
||||
..Default::default()
|
||||
..default()
|
||||
},
|
||||
fragment: Some(FragmentState {
|
||||
shader: self.fragment.clone(),
|
||||
shader_defs: vec![],
|
||||
entry_point: "fragment".into(),
|
||||
targets: vec![Some(ColorTargetState {
|
||||
format: TextureFormat::Rgba8Unorm,
|
||||
blend: Some(key.blend_state),
|
||||
blend: None,
|
||||
write_mask: ColorWrites::all(),
|
||||
})],
|
||||
..default()
|
||||
}),
|
||||
zero_initialize_workgroup_memory: false,
|
||||
..default()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
app.init_resource::<SpecializedRenderPipelines<MySpecializer>>();
|
||||
render_app
|
||||
.init_resource::<MyPipeline>();
|
||||
.init_resource::<SpecializedRenderPipelines<MySpecializer>>();
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```rust
|
||||
#[derive(Resource)]
|
||||
pub struct MyPipeline {
|
||||
layout: BindGroupLayout,
|
||||
layout_msaa: BindGroupLayout,
|
||||
vertex: Handle<Shader>,
|
||||
fragment: Handle<Shader>,
|
||||
specialized_cache: SpecializedCache<RenderPipeline, MySpecializer>,
|
||||
}
|
||||
|
||||
pub struct MySpecializer {
|
||||
layout: BindGroupLayout,
|
||||
layout_msaa: BindGroupLayout,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, SpecializerKey)]
|
||||
pub struct MyPipelineKey {
|
||||
msaa: Msaa,
|
||||
}
|
||||
|
||||
impl FromWorld for MyPipeline {
|
||||
fn from_world(world: &mut World) -> Self {
|
||||
let render_device = world.resource::<RenderDevice>();
|
||||
let asset_server = world.resource::<AssetServer>();
|
||||
|
||||
let layout = render_device.create_bind_group_layout(...);
|
||||
let layout_msaa = render_device.create_bind_group_layout(...);
|
||||
|
||||
let vertex = asset_server.load("vertex.wgsl");
|
||||
let fragment = asset_server.load("fragment.wgsl");
|
||||
|
||||
let base_descriptor = RenderPipelineDescriptor {
|
||||
label: Some("my_pipeline".into()),
|
||||
vertex: VertexState {
|
||||
shader: vertex.clone(),
|
||||
..default()
|
||||
},
|
||||
fragment: Some(FragmentState {
|
||||
shader: fragment.clone(),
|
||||
..default()
|
||||
}),
|
||||
..default()
|
||||
},
|
||||
|
||||
let specialized_cache = SpecializedCache::new(
|
||||
MySpecializer {
|
||||
layout: layout.clone(),
|
||||
layout_msaa: layout_msaa.clone(),
|
||||
},
|
||||
None,
|
||||
base_descriptor,
|
||||
);
|
||||
|
||||
Self {
|
||||
layout,
|
||||
layout_msaa,
|
||||
vertex,
|
||||
fragment,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// after
|
||||
impl Specializer<RenderPipeline> for MySpecializer {
|
||||
type Key = MyKey;
|
||||
|
||||
@ -109,45 +213,19 @@ impl Specializer<RenderPipeline> for MySpecializer {
|
||||
descriptor: &mut RenderPipeline,
|
||||
) -> Result<Canonical<Self::Key>, BevyError> {
|
||||
descriptor.multisample.count = key.msaa.samples();
|
||||
descriptor.layout[0] = if key.msaa.samples() > 0 {
|
||||
|
||||
let layout = if key.msaa.samples() > 1 {
|
||||
self.layout_msaa.clone()
|
||||
} else {
|
||||
self.layout.clone()
|
||||
};
|
||||
descriptor.fragment.targets[0].as_mut().unwrap().blend_mode = key.blend_state;
|
||||
|
||||
descriptor.set_layout(0, layout);
|
||||
|
||||
Ok(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl GetBaseDescriptor for MySpecializer {
|
||||
fn get_base_descriptor(&self) -> RenderPipelineDescriptor {
|
||||
RenderPipelineDescriptor {
|
||||
label: Some("my_pipeline".into()),
|
||||
layout: vec![self.layout.clone()],
|
||||
push_constant_ranges: vec![],
|
||||
vertex: VertexState {
|
||||
shader: self.vertex.clone(),
|
||||
shader_defs: vec![],
|
||||
entry_point: "vertex".into(),
|
||||
buffers: vec![],
|
||||
},
|
||||
primitive: Default::default(),
|
||||
depth_stencil: None,
|
||||
multisample: MultiSampleState::default(),
|
||||
fragment: Some(FragmentState {
|
||||
shader: self.fragment.clone(),
|
||||
shader_defs: vec![],
|
||||
entry_point: "fragment".into(),
|
||||
targets: vec![Some(ColorTargetState {
|
||||
format: TextureFormat::Rgba8Unorm,
|
||||
blend: None,
|
||||
write_mask: ColorWrites::all(),
|
||||
})],
|
||||
}),
|
||||
zero_initialize_workgroup_memory: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
render_app.init_resource::<MyPipeline>();
|
||||
|
||||
app.init_resource::<SpecializedCache<RenderPipeline, MySpecializer>>();
|
||||
```
|
||||
|
||||
Loading…
Reference in New Issue
Block a user