Add release notes and a migration guide for RenderStartup. (#20024)
# Objective - Document #19887 changes. ## Solution - Just some writing for Mwriting Monday! --------- Co-authored-by: atlv <email@atlasdostal.com>
This commit is contained in:
parent
6f6d8312f3
commit
c9c8964857
116
release-content/migration-guides/render_startup.md
Normal file
116
release-content/migration-guides/render_startup.md
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
---
|
||||||
|
title: Many render resources now initialized in `RenderStartup`
|
||||||
|
pull_requests: [19841, 19926, 19885, 19886, 19897, 19898, 19901]
|
||||||
|
---
|
||||||
|
|
||||||
|
Many render resources are **no longer present** during `Plugin::finish`. Instead they are
|
||||||
|
initialized during `RenderStartup` (which occurs once the app starts running). If you only access
|
||||||
|
the resource during the `Render` schedule, then there should be no change. However, if you need one
|
||||||
|
of these render resources to initialize your own resource, you will need to convert your resource
|
||||||
|
initialization into a system.
|
||||||
|
|
||||||
|
The following are the (public) resources that are now initialized in `RenderStartup`.
|
||||||
|
|
||||||
|
- `CasPipeline`
|
||||||
|
- `FxaaPipeline`
|
||||||
|
- `SmaaPipelines`
|
||||||
|
- `TaaPipeline`
|
||||||
|
- `BoxShadowPipeline`
|
||||||
|
- `GradientPipeline`
|
||||||
|
- `UiPipeline`
|
||||||
|
- `UiMaterialPipeline<M>`
|
||||||
|
- `UiTextureSlicePipeline`
|
||||||
|
|
||||||
|
The vast majority of cases for initializing render resources look like so (in Bevy 0.16):
|
||||||
|
|
||||||
|
```rust
|
||||||
|
impl Plugin for MyRenderingPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
// Do nothing??
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(&self, app: &mut App) {
|
||||||
|
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
render_app.init_resource::<MyRenderResource>();
|
||||||
|
render_app.add_systems(Render, my_render_system);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MyRenderResource {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWorld for MyRenderResource {
|
||||||
|
fn from_world(world: &mut World) -> Self {
|
||||||
|
let render_device = world.resource::<RenderDevice>();
|
||||||
|
let render_adapter = world.resource::<RenderAdapter>();
|
||||||
|
let asset_server = world.resource::<AssetServer>();
|
||||||
|
|
||||||
|
MyRenderResource {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The two main things to focus on are:
|
||||||
|
|
||||||
|
1. The resource implements the `FromWorld` trait which collects all its dependent resources (most
|
||||||
|
commonly, `RenderDevice`), and then creates an instance of the resource.
|
||||||
|
2. The plugin adds its systems and resources in `Plugin::finish`.
|
||||||
|
|
||||||
|
First, we need to rewrite our `FromWorld` implementation as a system. This generally means
|
||||||
|
converting calls to `World::resource` into system params, and then using `Commands` to insert the
|
||||||
|
resource. In the above case, that would look like:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Just a regular old system!!
|
||||||
|
fn init_my_resource(
|
||||||
|
mut commands: Commands,
|
||||||
|
render_device: Res<RenderDevice>,
|
||||||
|
render_adapter: Res<RenderAdapter>,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
) {
|
||||||
|
commands.insert_resource(MyRenderResource {
|
||||||
|
...
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Each case will be slightly different. Two notes to be wary of:
|
||||||
|
|
||||||
|
1. Functions that accept `&RenderDevice` for example may no longer compile after switching to
|
||||||
|
`Res<RenderDevice>`. This can be resolved by passing `&render_device` instead of
|
||||||
|
`render_device`.
|
||||||
|
2. If you are using `load_embedded_asset(world, "my_asset.png")`, you may need to first add
|
||||||
|
`asset_server` as a system param, then change this to
|
||||||
|
`load_embedded_asset(asset_server.as_ref(), "my_asset.png")`.
|
||||||
|
|
||||||
|
Now that we have our initialization system, we just need to add the system to `RenderStartup`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
impl Plugin for MyRenderingPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
render_app
|
||||||
|
.add_systems(RenderStartup, init_my_resource)
|
||||||
|
.add_systems(Render, my_render_system);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No more finish!!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In addition, if your resource requires one of the affected systems above, you will need to use
|
||||||
|
system ordering to ensure your resource initializes after the other system. For example, if your
|
||||||
|
system uses `Res<UiPipeline>`, you will need to add an ordering like:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
render_app.add_systems(RenderStartup, init_my_resource.after(init_ui_pipeline));
|
||||||
|
```
|
||||||
103
release-content/release-notes/render_startup.md
Normal file
103
release-content/release-notes/render_startup.md
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
---
|
||||||
|
title: `RenderStartup` and making the renderer my ECS-y
|
||||||
|
authors: ["@IceSentry", "@andriyDev"]
|
||||||
|
pull_requests: [19841, 19926, 19885, 19886, 19897, 19898, 19901]
|
||||||
|
---
|
||||||
|
|
||||||
|
Previous rendering code looked quite different from other Bevy code. In general, resources were
|
||||||
|
initialized with the `FromWorld` trait (where most Bevy code only uses the `Default` trait for most
|
||||||
|
resources) and systems/resources were added in `Plugin::finish` (where nearly all Bevy code does not
|
||||||
|
use `Plugin::finish` at all). This difference with Bevy code can make it harder for new developers
|
||||||
|
to learn rendering, and can result in "cargo cult" copying of rendering code (e.g., "is it important
|
||||||
|
to be using `FromWorld` here? Better to be safe and just do what the rendering code is doing!").
|
||||||
|
|
||||||
|
As a step towards making the renderer more accessible (and maintainable), we have introduced the
|
||||||
|
`RenderStartup` schedule and ported many rendering resources to be initialized in `RenderStartup`
|
||||||
|
with systems! This has several benefits:
|
||||||
|
|
||||||
|
1. Creating resources in systems makes it clearer that rendering resources **are just regular
|
||||||
|
resources**. Hopefully, this better communicates that how you initialize these resources is
|
||||||
|
totally up to you!
|
||||||
|
2. We can now use the system ordering API to ensure that resources are initialized in the correct
|
||||||
|
order. For example, we can do `init_material_pipeline.after(init_mesh_pipeline)` if we need the
|
||||||
|
mesh pipeline to initialize the material pipeline.
|
||||||
|
3. These initialization systems clearly describe what resources they require through their argument
|
||||||
|
list. If a system has an argument of `deferred_lighting_layout: Res<DeferredLightingLayout>`, it
|
||||||
|
clearly documents that this system needs to be run **after** we initialize the
|
||||||
|
`DeferredLightingLayout`.
|
||||||
|
|
||||||
|
We want developers to become more familiar and comfortable with Bevy's rendering stack, and hope
|
||||||
|
that bringing the renderer closer to regular ECS code will encourage that. Code that previously looked
|
||||||
|
like this (in Bevy 0.16):
|
||||||
|
|
||||||
|
```rust
|
||||||
|
impl Plugin for MyRenderingPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
// Do nothing??
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(&self, app: &mut App) {
|
||||||
|
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
render_app.init_resource::<MyRenderResource>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MyRenderResource {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWorld for MyRenderResource {
|
||||||
|
fn from_world(world: &mut World) -> Self {
|
||||||
|
let render_device = world.resource::<RenderDevice>();
|
||||||
|
let render_adapter = world.resource::<RenderAdapter>();
|
||||||
|
let asset_server = world.resource::<AssetServer>();
|
||||||
|
|
||||||
|
MyRenderResource {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Can now be written like:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
impl Plugin for MyRenderingPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
render_app.add_systems(RenderStartup, init_my_resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No more finish!!
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MyRenderResource {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just a regular old system!!
|
||||||
|
fn init_my_resource(
|
||||||
|
mut commands: Commands,
|
||||||
|
render_device: Res<RenderDevice>,
|
||||||
|
render_adapter: Res<RenderAdapter>,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
) {
|
||||||
|
commands.insert_resource(MyRenderResource {
|
||||||
|
...
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We highly encourage users to port their own rendering resources to this new system approach (and for
|
||||||
|
resources whose initialization depends on a Bevy core resource, it may be required). In fact, we
|
||||||
|
encourage users to abandon `Plugin::finish` entirely and move all their system and resource
|
||||||
|
initializations for rendering into `Plugin::build` instead.
|
||||||
|
|
||||||
|
As stated before, we've ported many resources to be initialized in `RenderStartup`. See the
|
||||||
|
migration guide for a full list of affected resources.
|
||||||
Loading…
Reference in New Issue
Block a user