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
|
The existing pipeline specialization APIs (`SpecializedRenderPipeline` etc.) have
|
||||||
been replaced with a single `Specializer` trait and `SpecializedCache` collection:
|
been replaced with a single `Specializer` trait and `SpecializedCache` collection:
|
||||||
|
|
||||||
```rs
|
```rust
|
||||||
pub trait Specializer<T: Specializable>: Send + Sync + 'static {
|
pub trait Specializer<T: Specializable>: Send + Sync + 'static {
|
||||||
type Key: SpecializerKey;
|
type Key: SpecializerKey;
|
||||||
fn specialize(
|
fn specialize(
|
||||||
@ -19,20 +19,55 @@ pub trait Specializer<T: Specializable>: Send + Sync + 'static {
|
|||||||
pub struct SpecializedCache<T: Specializable, S: Specializer<T>>{ ... };
|
pub struct SpecializedCache<T: Specializable, S: Specializer<T>>{ ... };
|
||||||
```
|
```
|
||||||
|
|
||||||
The main difference is the change from *producing* a pipeline descriptor to
|
For more info on specialization, see the docs for `bevy_render::render_resources::Specializer`
|
||||||
*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.
|
|
||||||
|
|
||||||
Composing multiple different specializers together with the `derive(Specializer)`
|
## Mutation and Base Descriptors
|
||||||
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.
|
|
||||||
|
|
||||||
```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 {
|
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: BindGroupLayout,
|
||||||
layout_msaa: BindGroupLayout,
|
layout_msaa: BindGroupLayout,
|
||||||
vertex: Handle<Shader>,
|
vertex: Handle<Shader>,
|
||||||
@ -41,65 +76,134 @@ pub struct MySpecializer {
|
|||||||
|
|
||||||
// before
|
// before
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
// after
|
pub struct MyPipelineKey {
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, SpecializerKey)]
|
|
||||||
|
|
||||||
pub struct MyKey {
|
|
||||||
blend_state: BlendState,
|
|
||||||
msaa: Msaa,
|
msaa: Msaa,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromWorld for MySpecializer {
|
impl FromWorld for MyPipeline {
|
||||||
fn from_world(&mut World) -> Self {
|
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 MyPipeline {
|
||||||
impl SpecializedRenderPipeline for MySpecializer {
|
type Key = MyPipelineKey;
|
||||||
type Key = MyKey;
|
|
||||||
|
|
||||||
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
|
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
|
||||||
RenderPipelineDescriptor {
|
RenderPipelineDescriptor {
|
||||||
label: Some("my_pipeline".into()),
|
label: Some("my_pipeline".into()),
|
||||||
layout: vec![
|
layout: vec![
|
||||||
if key.msaa.samples() > 0 {
|
if key.msaa.samples() > 1 {
|
||||||
self.layout_msaa.clone()
|
self.layout_msaa.clone()
|
||||||
} else {
|
} else {
|
||||||
self.layout.clone()
|
self.layout.clone()
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
push_constant_ranges: vec![],
|
|
||||||
vertex: VertexState {
|
vertex: VertexState {
|
||||||
shader: self.vertex.clone(),
|
shader: self.vertex.clone(),
|
||||||
shader_defs: vec![],
|
..default()
|
||||||
entry_point: "vertex".into(),
|
|
||||||
buffers: vec![],
|
|
||||||
},
|
},
|
||||||
primitive: Default::default(),
|
|
||||||
depth_stencil: None,
|
|
||||||
multisample: MultisampleState {
|
multisample: MultisampleState {
|
||||||
count: key.msaa.samples(),
|
count: key.msaa.samples(),
|
||||||
..Default::default()
|
..default()
|
||||||
},
|
},
|
||||||
fragment: Some(FragmentState {
|
fragment: Some(FragmentState {
|
||||||
shader: self.fragment.clone(),
|
shader: self.fragment.clone(),
|
||||||
shader_defs: vec![],
|
|
||||||
entry_point: "fragment".into(),
|
|
||||||
targets: vec![Some(ColorTargetState {
|
targets: vec![Some(ColorTargetState {
|
||||||
format: TextureFormat::Rgba8Unorm,
|
format: TextureFormat::Rgba8Unorm,
|
||||||
blend: Some(key.blend_state),
|
blend: None,
|
||||||
write_mask: ColorWrites::all(),
|
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 {
|
impl Specializer<RenderPipeline> for MySpecializer {
|
||||||
type Key = MyKey;
|
type Key = MyKey;
|
||||||
|
|
||||||
@ -109,45 +213,19 @@ impl Specializer<RenderPipeline> for MySpecializer {
|
|||||||
descriptor: &mut RenderPipeline,
|
descriptor: &mut RenderPipeline,
|
||||||
) -> Result<Canonical<Self::Key>, BevyError> {
|
) -> Result<Canonical<Self::Key>, BevyError> {
|
||||||
descriptor.multisample.count = key.msaa.samples();
|
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()
|
self.layout_msaa.clone()
|
||||||
} else {
|
} else {
|
||||||
self.layout.clone()
|
self.layout.clone()
|
||||||
};
|
};
|
||||||
descriptor.fragment.targets[0].as_mut().unwrap().blend_mode = key.blend_state;
|
|
||||||
|
descriptor.set_layout(0, layout);
|
||||||
|
|
||||||
Ok(key)
|
Ok(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GetBaseDescriptor for MySpecializer {
|
render_app.init_resource::<MyPipeline>();
|
||||||
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,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
app.init_resource::<SpecializedCache<RenderPipeline, MySpecializer>>();
|
|
||||||
```
|
```
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user