|
| 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 | +``` |
0 commit comments