Skip to content

Commit 73907a2

Browse files
committed
Add release notes and a migration guide for RenderStartup.
1 parent 5e3927b commit 73907a2

File tree

2 files changed

+219
-0
lines changed

2 files changed

+219
-0
lines changed
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
---
2+
title: Many render resources now initialized in `RenderStartup`
3+
pull_requests: [19841, 19926, 19885, 19886, 19897, 19898, 19901]
4+
---
5+
6+
Many render resources are **no longer present** during `Plugin::finish`. Instead they are
7+
initialized during `RenderStartup` (which occurs once the app starts running). If you only access
8+
the resource during the `Render` schedule, then there should be no change. However, if you need one
9+
of these render resources to initialize your own resource, you will need to convert your resource
10+
initialization into a system.
11+
12+
The following are the (public) resources that are now initialized in `RenderStartup`.
13+
14+
- `CasPipeline`
15+
- `FxaaPipeline`
16+
- `SmaaPipelines`
17+
- `TaaPipeline`
18+
- `BoxShadowPipeline`
19+
- `GradientPipeline`
20+
- `UiPipeline`
21+
- `UiMaterialPipeline<M>`
22+
- `UiTextureSlicePipeline`
23+
24+
The vast majority of cases for initializing render resources look like so:
25+
26+
```rust
27+
impl Plugin for MyRenderingPlugin {
28+
fn build(&self, app: &mut App) {
29+
// Do nothing??
30+
}
31+
32+
fn finish(&self, app: &mut App) {
33+
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
34+
return;
35+
};
36+
37+
render_app.init_resource::<MyRenderResource>();
38+
render_app.add_systems(Render, my_render_system);
39+
}
40+
}
41+
42+
pub struct MyRenderResource {
43+
...
44+
}
45+
46+
impl FromWorld for MyRenderResource {
47+
fn from_world(world: &mut World) -> Self {
48+
let render_device = world.resource::<RenderDevice>();
49+
let render_adapter = world.resource::<RenderAdapter>();
50+
let asset_server = world.resource::<AssetServer>();
51+
52+
MyRenderResource {
53+
...
54+
}
55+
}
56+
}
57+
```
58+
59+
The two main things to focus on are:
60+
61+
1. The resource implements the `FromWorld` trait which collects all its dependent resources (most
62+
commonly, `RenderDevice`), and then creates an instance of the resource.
63+
2. The plugin adds its systems and resources in `Plugin::finish`.
64+
65+
First, we need to rewrite our `FromWorld` implementation as a system. This generally means
66+
converting calls to `World::resource` into system params, and then using `Commands` to insert the
67+
resource. In the above case, that would look like:
68+
69+
```rust
70+
// Just a regular old system!!
71+
fn init_my_resource(
72+
mut commands: Commands,
73+
render_device: Res<RenderDevice>,
74+
render_adapter: Res<RenderAdapter>,
75+
asset_server: Res<AssetServer>,
76+
) {
77+
commands.insert_resource(MyRenderResource {
78+
...
79+
});
80+
}
81+
```
82+
83+
Each case will be a slightly different. Two notes to be wary of:
84+
85+
1. Functions that accept `&RenderDevice` for example may no longer compile after switching to
86+
`Res<RenderDevice>`. This can be resolved by passing `&render_device` instead of
87+
`render_device`.
88+
2. If you are using `load_embedded_asset(world, "my_asset.png")`, you may need to first add
89+
`asset_server` as a system param, then change this to
90+
`load_embedded_asset(asset_server.as_ref(), "my_asset.png")`.
91+
92+
Now that we have our initialization system, we just need to add the system to `RenderStartup`:
93+
94+
```rust
95+
impl Plugin for MyRenderingPlugin {
96+
fn build(&self, app: &mut App) {
97+
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
98+
return;
99+
};
100+
101+
render_app
102+
.add_systems(RenderStartup, init_my_resource)
103+
.add_systems(Render, my_render_system);
104+
}
105+
106+
// No more finish!!
107+
}
108+
```
109+
110+
In addition, if your resource requires one of the affected systems above, you will need to use
111+
system ordering to ensure your resource initializes after the other system. For example, if your
112+
system uses `Res<UiPipeline>`, you will need to add an ordering like:
113+
114+
```rust
115+
render_app.add_systems(RenderStartup, init_my_resource.after(init_ui_pipeline));
116+
```
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
---
2+
title: `RenderStartup` and making the renderer my ECS-y
3+
authors: ["@IceSentry", "@andriyDev"]
4+
pull_requests: [19841, 19926, 19885, 19886, 19897, 19898, 19901]
5+
---
6+
7+
Previous rendering code looked quite different from other Bevy code. In general, resources were
8+
initialized with the `FromWorld` trait (where most Bevy code only uses the `Default` trait for most
9+
resources) and systems/resources were added in `Plugin::finish` (where nearly all Bevy code does not
10+
use `Plugin::finish` at all). This difference with Bevy code can make it harder for new developers
11+
to learn rendering, and can result in "cargo cult" copying of rendering code (e.g., "is it important
12+
to be using `FromWorld` here? Better to be safe and just do what the rendering code is doing!").
13+
14+
As a step towards making the renderer more accessible (and maintainable), we have introduced the
15+
`RenderStartup` schedule and ported many rendering resources to be initialized in `RenderStartup`
16+
with systems! This has several benefits:
17+
18+
1. Creating resources in systems makes it clearer that rendering resources **are just regular
19+
resources**. Hopefully, this better communicates that how you initialize these resources is
20+
totally up to you!
21+
2. We can now use the system ordering API to ensure that resources are initialized in the correct
22+
order. For example, we can do `init_material_pipeline.after(init_mesh_pipeline)` if we need the
23+
mesh pipeline to initialize the material pipeline.
24+
3. These initialization systems clearly describe what resources they require through their argument
25+
list. If a system has an argument of `deferred_lighting_layout: Res<DeferredLightingLayout>`, it
26+
clearly documents that this system needs to be run **after** we initialize the
27+
`DeferredLightingLayout`.
28+
29+
We want developers to become more familiar and comfortable with Bevy's rendering stack, and hope
30+
that bring the renderer closer to regular ECS code will encourage that. Code that previously looked
31+
like this:
32+
33+
```rust
34+
impl Plugin for MyRenderingPlugin {
35+
fn build(&self, app: &mut App) {
36+
// Do nothing??
37+
}
38+
39+
fn finish(&self, app: &mut App) {
40+
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
41+
return;
42+
};
43+
44+
render_app.init_resource::<MyRenderResource>();
45+
}
46+
}
47+
48+
pub struct MyRenderResource {
49+
...
50+
}
51+
52+
impl FromWorld for MyRenderResource {
53+
fn from_world(world: &mut World) -> Self {
54+
let render_device = world.resource::<RenderDevice>();
55+
let render_adapter = world.resource::<RenderAdapter>();
56+
let asset_server = world.resource::<AssetServer>();
57+
58+
MyRenderResource {
59+
...
60+
}
61+
}
62+
}
63+
```
64+
65+
Can now be written like:
66+
67+
```rust
68+
impl Plugin for MyRenderingPlugin {
69+
fn build(&self, app: &mut App) {
70+
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
71+
return;
72+
};
73+
74+
render_app.add_systems(RenderStartup, init_my_resource);
75+
}
76+
77+
// No more finish!!
78+
}
79+
80+
pub struct MyRenderResource {
81+
...
82+
}
83+
84+
// Just a regular old system!!
85+
fn init_my_resource(
86+
mut commands: Commands,
87+
render_device: Res<RenderDevice>,
88+
render_adapter: Res<RenderAdapter>,
89+
asset_server: Res<AssetServer>,
90+
) {
91+
commands.insert_resource(MyRenderResource {
92+
...
93+
});
94+
}
95+
```
96+
97+
We highly encourage users to port their own rendering resources to this new system approach (and for
98+
resources whose initialization depends on a Bevy core resource, it may be required). In fact, we
99+
encourage users to abandon `Plugin::finish` entirely and move all their system and resource
100+
initializations for rendering into `Plugin::build` instead.
101+
102+
As stated before, we've ported many resources to be initialized in `RenderStartup`. See the
103+
migration guide for a full list of affected resources.

0 commit comments

Comments
 (0)