-
-
Notifications
You must be signed in to change notification settings - Fork 4k
Add release notes and a migration guide for RenderStartup
.
#20024
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
alice-i-cecile
merged 4 commits into
bevyengine:main
from
andriyDev:render_startup_writing
Jul 10, 2025
Merged
Changes from 1 commit
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
99546fa
Add release notes and a migration guide for `RenderStartup`.
andriyDev 0a3e20b
Mention that we are talking about how code looked in Bevy 0.16.
andriyDev 2254805
Remove stray "a".
andriyDev 041e864
Use the correct form of verbs in a sentence.
andriyDev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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: | ||
|
||
```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 a slightly different. Two notes to be wary of: | ||
andriyDev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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)); | ||
``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 bring the renderer closer to regular ECS code will encourage that. Code that previously looked | ||
andriyDev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
like this: | ||
|
||
```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. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.