
# Objective - Progress towards #19887. ## Solution - For cases that don't need to conditionally add systems, we can just replace FromWorld impls with systems and then add those systems to `RenderStartup`. ## Testing - I ran the `lightmaps`, `reflection_probes`, `deferred_rendering`, `volumetric_fog`, and `wireframe` examples.
3.8 KiB
title | pull_requests | |||||||
---|---|---|---|---|---|---|---|---|
Many render resources now initialized in `RenderStartup` |
|
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
VolumetricFogPipeline
DeferredLightingLayout
RenderLightmaps
PrepassPipeline
PrepassViewBindGroup
Wireframe3dPipeline
MaterialPipeline
The vast majority of cases for initializing render resources look like so (in Bevy 0.16):
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:
- The resource implements the
FromWorld
trait which collects all its dependent resources (most commonly,RenderDevice
), and then creates an instance of the resource. - 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:
// 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:
- Functions that accept
&RenderDevice
for example may no longer compile after switching toRes<RenderDevice>
. This can be resolved by passing&render_device
instead ofrender_device
. - If you are using
load_embedded_asset(world, "my_asset.png")
, you may need to first addasset_server
as a system param, then change this toload_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
:
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:
render_app.add_systems(RenderStartup, init_my_resource.after(init_ui_pipeline));