From 29779e1e181030b88f1fcb7837af7041824cee8f Mon Sep 17 00:00:00 2001 From: andriyDev Date: Fri, 4 Jul 2025 21:07:23 -0700 Subject: [PATCH] Use `RenderStartup` in `bevy_ui`. (#19901) # Objective - Progress towards #19887. ## Solution - Convert `FromWorld` impls into systems that run in `RenderStartup`. - Move `UiPipeline` init to `build_ui_render` instead of doing it separately in `finish`. Note: I am making several of these systems pub so that users could order their systems relative to them. This is to match the fact that these types previously were FromWorld so users could initialize them. ## Testing - Ran `ui_material`, `ui_texture_slice`, `box_shadow`, and `gradients` examples and it still worked. --- crates/bevy_ui_render/src/box_shadow.rs | 41 +++++------ crates/bevy_ui_render/src/gradient.rs | 41 +++++------ crates/bevy_ui_render/src/lib.rs | 11 +-- crates/bevy_ui_render/src/pipeline.rs | 54 +++++++------- .../src/ui_material_pipeline.rs | 72 +++++++++---------- .../src/ui_texture_slice_pipeline.rs | 61 ++++++++-------- 6 files changed, 126 insertions(+), 154 deletions(-) diff --git a/crates/bevy_ui_render/src/box_shadow.rs b/crates/bevy_ui_render/src/box_shadow.rs index 87c223ec8e..4c97e714cc 100644 --- a/crates/bevy_ui_render/src/box_shadow.rs +++ b/crates/bevy_ui_render/src/box_shadow.rs @@ -16,7 +16,6 @@ use bevy_ecs::{ use bevy_image::BevyDefault as _; use bevy_math::{vec2, Affine2, FloatOrd, Rect, Vec2}; use bevy_render::sync_world::{MainEntity, TemporaryRenderEntity}; -use bevy_render::RenderApp; use bevy_render::{ render_phase::*, render_resource::{binding_types::uniform_buffer, *}, @@ -24,6 +23,7 @@ use bevy_render::{ view::*, Extract, ExtractSchedule, Render, RenderSystems, }; +use bevy_render::{RenderApp, RenderStartup}; use bevy_ui::{ BoxShadow, CalculatedClip, ComputedNode, ComputedNodeTarget, ResolvedBorderRadius, UiGlobalTransform, Val, @@ -48,6 +48,7 @@ impl Plugin for BoxShadowPlugin { .init_resource::() .init_resource::() .init_resource::>() + .add_systems(RenderStartup, init_box_shadow_pipeline) .add_systems( ExtractSchedule, extract_shadows.in_set(RenderUiSystems::ExtractBoxShadows), @@ -61,12 +62,6 @@ impl Plugin for BoxShadowPlugin { ); } } - - fn finish(&self, app: &mut App) { - if let Some(render_app) = app.get_sub_app_mut(RenderApp) { - render_app.init_resource::(); - } - } } #[repr(C)] @@ -111,23 +106,23 @@ pub struct BoxShadowPipeline { pub shader: Handle, } -impl FromWorld for BoxShadowPipeline { - fn from_world(world: &mut World) -> Self { - let render_device = world.resource::(); +pub fn init_box_shadow_pipeline( + mut commands: Commands, + render_device: Res, + asset_server: Res, +) { + let view_layout = render_device.create_bind_group_layout( + "box_shadow_view_layout", + &BindGroupLayoutEntries::single( + ShaderStages::VERTEX_FRAGMENT, + uniform_buffer::(true), + ), + ); - let view_layout = render_device.create_bind_group_layout( - "box_shadow_view_layout", - &BindGroupLayoutEntries::single( - ShaderStages::VERTEX_FRAGMENT, - uniform_buffer::(true), - ), - ); - - BoxShadowPipeline { - view_layout, - shader: load_embedded_asset!(world, "box_shadow.wgsl"), - } - } + commands.insert_resource(BoxShadowPipeline { + view_layout, + shader: load_embedded_asset!(asset_server.as_ref(), "box_shadow.wgsl"), + }); } #[derive(Clone, Copy, Hash, PartialEq, Eq)] diff --git a/crates/bevy_ui_render/src/gradient.rs b/crates/bevy_ui_render/src/gradient.rs index 11de56edda..9bef5340cb 100644 --- a/crates/bevy_ui_render/src/gradient.rs +++ b/crates/bevy_ui_render/src/gradient.rs @@ -21,7 +21,6 @@ use bevy_math::{ FloatOrd, Rect, Vec2, }; use bevy_math::{Affine2, Vec2Swizzles}; -use bevy_render::sync_world::MainEntity; use bevy_render::{ render_phase::*, render_resource::{binding_types::uniform_buffer, *}, @@ -30,6 +29,7 @@ use bevy_render::{ view::*, Extract, ExtractSchedule, Render, RenderSystems, }; +use bevy_render::{sync_world::MainEntity, RenderStartup}; use bevy_sprite::BorderRect; use bevy_ui::{ BackgroundGradient, BorderGradient, ColorStop, ConicGradient, Gradient, @@ -51,6 +51,7 @@ impl Plugin for GradientPlugin { .init_resource::() .init_resource::() .init_resource::>() + .add_systems(RenderStartup, init_gradient_pipeline) .add_systems( ExtractSchedule, extract_gradients @@ -66,12 +67,6 @@ impl Plugin for GradientPlugin { ); } } - - fn finish(&self, app: &mut App) { - if let Some(render_app) = app.get_sub_app_mut(RenderApp) { - render_app.init_resource::(); - } - } } #[derive(Component)] @@ -102,23 +97,23 @@ pub struct GradientPipeline { pub shader: Handle, } -impl FromWorld for GradientPipeline { - fn from_world(world: &mut World) -> Self { - let render_device = world.resource::(); +pub fn init_gradient_pipeline( + mut commands: Commands, + render_device: Res, + asset_server: Res, +) { + let view_layout = render_device.create_bind_group_layout( + "ui_gradient_view_layout", + &BindGroupLayoutEntries::single( + ShaderStages::VERTEX_FRAGMENT, + uniform_buffer::(true), + ), + ); - let view_layout = render_device.create_bind_group_layout( - "ui_gradient_view_layout", - &BindGroupLayoutEntries::single( - ShaderStages::VERTEX_FRAGMENT, - uniform_buffer::(true), - ), - ); - - GradientPipeline { - view_layout, - shader: load_embedded_asset!(world, "gradient.wgsl"), - } - } + commands.insert_resource(GradientPipeline { + view_layout, + shader: load_embedded_asset!(asset_server.as_ref(), "gradient.wgsl"), + }); } pub fn compute_gradient_line_length(angle: f32, size: Vec2) -> f32 { diff --git a/crates/bevy_ui_render/src/lib.rs b/crates/bevy_ui_render/src/lib.rs index a06426c255..74617a7269 100644 --- a/crates/bevy_ui_render/src/lib.rs +++ b/crates/bevy_ui_render/src/lib.rs @@ -36,7 +36,6 @@ use bevy_ecs::prelude::*; use bevy_ecs::system::SystemParam; use bevy_image::prelude::*; use bevy_math::{Affine2, FloatOrd, Mat4, Rect, UVec4, Vec2}; -use bevy_render::load_shader_library; use bevy_render::render_graph::{NodeRunError, RenderGraphContext}; use bevy_render::render_phase::ViewSortedRenderPhases; use bevy_render::renderer::RenderContext; @@ -53,6 +52,7 @@ use bevy_render::{ view::{ExtractedView, ViewUniforms}, Extract, RenderApp, RenderSystems, }; +use bevy_render::{load_shader_library, RenderStartup}; use bevy_render::{ render_phase::{PhaseItem, PhaseItemExtraIndex}, sync_world::{RenderEntity, TemporaryRenderEntity}, @@ -243,6 +243,7 @@ impl Plugin for UiRenderPlugin { ) .chain(), ) + .add_systems(RenderStartup, init_ui_pipeline) .add_systems( ExtractSchedule, ( @@ -292,14 +293,6 @@ impl Plugin for UiRenderPlugin { app.add_plugins(GradientPlugin); app.add_plugins(BoxShadowPlugin); } - - fn finish(&self, app: &mut App) { - let Some(render_app) = app.get_sub_app_mut(RenderApp) else { - return; - }; - - render_app.init_resource::(); - } } fn get_ui_graph(render_app: &mut SubApp) -> RenderGraph { diff --git a/crates/bevy_ui_render/src/pipeline.rs b/crates/bevy_ui_render/src/pipeline.rs index 7440c5abad..6315091271 100644 --- a/crates/bevy_ui_render/src/pipeline.rs +++ b/crates/bevy_ui_render/src/pipeline.rs @@ -1,4 +1,4 @@ -use bevy_asset::{load_embedded_asset, Handle}; +use bevy_asset::{load_embedded_asset, AssetServer, Handle}; use bevy_ecs::prelude::*; use bevy_image::BevyDefault as _; use bevy_render::{ @@ -18,35 +18,35 @@ pub struct UiPipeline { pub shader: Handle, } -impl FromWorld for UiPipeline { - fn from_world(world: &mut World) -> Self { - let render_device = world.resource::(); +pub fn init_ui_pipeline( + mut commands: Commands, + render_device: Res, + asset_server: Res, +) { + let view_layout = render_device.create_bind_group_layout( + "ui_view_layout", + &BindGroupLayoutEntries::single( + ShaderStages::VERTEX_FRAGMENT, + uniform_buffer::(true), + ), + ); - let view_layout = render_device.create_bind_group_layout( - "ui_view_layout", - &BindGroupLayoutEntries::single( - ShaderStages::VERTEX_FRAGMENT, - uniform_buffer::(true), + let image_layout = render_device.create_bind_group_layout( + "ui_image_layout", + &BindGroupLayoutEntries::sequential( + ShaderStages::FRAGMENT, + ( + texture_2d(TextureSampleType::Float { filterable: true }), + sampler(SamplerBindingType::Filtering), ), - ); + ), + ); - let image_layout = render_device.create_bind_group_layout( - "ui_image_layout", - &BindGroupLayoutEntries::sequential( - ShaderStages::FRAGMENT, - ( - texture_2d(TextureSampleType::Float { filterable: true }), - sampler(SamplerBindingType::Filtering), - ), - ), - ); - - UiPipeline { - view_layout, - image_layout, - shader: load_embedded_asset!(world, "ui.wgsl"), - } - } + commands.insert_resource(UiPipeline { + view_layout, + image_layout, + shader: load_embedded_asset!(asset_server.as_ref(), "ui.wgsl"), + }); } #[derive(Clone, Copy, Hash, PartialEq, Eq)] diff --git a/crates/bevy_ui_render/src/ui_material_pipeline.rs b/crates/bevy_ui_render/src/ui_material_pipeline.rs index 5dc0453816..eb4f5050cf 100644 --- a/crates/bevy_ui_render/src/ui_material_pipeline.rs +++ b/crates/bevy_ui_render/src/ui_material_pipeline.rs @@ -8,11 +8,9 @@ use bevy_ecs::{ lifetimeless::{Read, SRes}, *, }, - world::{FromWorld, World}, }; use bevy_image::BevyDefault as _; use bevy_math::{Affine2, FloatOrd, Rect, Vec2}; -use bevy_render::RenderApp; use bevy_render::{ globals::{GlobalsBuffer, GlobalsUniform}, load_shader_library, @@ -24,6 +22,7 @@ use bevy_render::{ view::*, Extract, ExtractSchedule, Render, RenderSystems, }; +use bevy_render::{RenderApp, RenderStartup}; use bevy_sprite::BorderRect; use bevy_utils::default; use bytemuck::{Pod, Zeroable}; @@ -61,6 +60,7 @@ where .init_resource::>() .init_resource::>() .init_resource::>>() + .add_systems(RenderStartup, init_ui_material_pipeline::) .add_systems( ExtractSchedule, extract_ui_material_nodes::.in_set(RenderUiSystems::ExtractBackgrounds), @@ -74,12 +74,6 @@ where ); } } - - fn finish(&self, app: &mut App) { - if let Some(render_app) = app.get_sub_app_mut(RenderApp) { - render_app.init_resource::>(); - } - } } #[derive(Resource)] @@ -185,41 +179,41 @@ where } } -impl FromWorld for UiMaterialPipeline { - fn from_world(world: &mut World) -> Self { - let asset_server = world.resource::(); - let render_device = world.resource::(); - let ui_layout = M::bind_group_layout(render_device); +pub fn init_ui_material_pipeline( + mut commands: Commands, + render_device: Res, + asset_server: Res, +) { + let ui_layout = M::bind_group_layout(&render_device); - let view_layout = render_device.create_bind_group_layout( - "ui_view_layout", - &BindGroupLayoutEntries::sequential( - ShaderStages::VERTEX_FRAGMENT, - ( - uniform_buffer::(true), - uniform_buffer::(false), - ), + let view_layout = render_device.create_bind_group_layout( + "ui_view_layout", + &BindGroupLayoutEntries::sequential( + ShaderStages::VERTEX_FRAGMENT, + ( + uniform_buffer::(true), + uniform_buffer::(false), ), - ); + ), + ); - let load_default = || load_embedded_asset!(asset_server, "ui_material.wgsl"); + let load_default = || load_embedded_asset!(asset_server.as_ref(), "ui_material.wgsl"); - UiMaterialPipeline { - ui_layout, - view_layout, - vertex_shader: match M::vertex_shader() { - ShaderRef::Default => load_default(), - ShaderRef::Handle(handle) => handle, - ShaderRef::Path(path) => asset_server.load(path), - }, - fragment_shader: match M::fragment_shader() { - ShaderRef::Default => load_default(), - ShaderRef::Handle(handle) => handle, - ShaderRef::Path(path) => asset_server.load(path), - }, - marker: PhantomData, - } - } + commands.insert_resource(UiMaterialPipeline:: { + ui_layout, + view_layout, + vertex_shader: match M::vertex_shader() { + ShaderRef::Default => load_default(), + ShaderRef::Handle(handle) => handle, + ShaderRef::Path(path) => asset_server.load(path), + }, + fragment_shader: match M::fragment_shader() { + ShaderRef::Default => load_default(), + ShaderRef::Handle(handle) => handle, + ShaderRef::Path(path) => asset_server.load(path), + }, + marker: PhantomData, + }); } pub type DrawUiMaterial = ( diff --git a/crates/bevy_ui_render/src/ui_texture_slice_pipeline.rs b/crates/bevy_ui_render/src/ui_texture_slice_pipeline.rs index 547db6b06c..aa05f106ff 100644 --- a/crates/bevy_ui_render/src/ui_texture_slice_pipeline.rs +++ b/crates/bevy_ui_render/src/ui_texture_slice_pipeline.rs @@ -13,7 +13,6 @@ use bevy_ecs::{ use bevy_image::prelude::*; use bevy_math::{Affine2, FloatOrd, Rect, Vec2}; use bevy_platform::collections::HashMap; -use bevy_render::sync_world::MainEntity; use bevy_render::{ render_asset::RenderAssets, render_phase::*, @@ -23,6 +22,7 @@ use bevy_render::{ view::*, Extract, ExtractSchedule, Render, RenderSystems, }; +use bevy_render::{sync_world::MainEntity, RenderStartup}; use bevy_sprite::{SliceScaleMode, SpriteAssetEvents, SpriteImageMode, TextureSlicer}; use bevy_ui::widget; use bevy_utils::default; @@ -42,6 +42,7 @@ impl Plugin for UiTextureSlicerPlugin { .init_resource::() .init_resource::() .init_resource::>() + .add_systems(RenderStartup, init_ui_texture_slice_pipeline) .add_systems( ExtractSchedule, extract_ui_texture_slices.in_set(RenderUiSystems::ExtractTextureSlice), @@ -55,12 +56,6 @@ impl Plugin for UiTextureSlicerPlugin { ); } } - - fn finish(&self, app: &mut App) { - if let Some(render_app) = app.get_sub_app_mut(RenderApp) { - render_app.init_resource::(); - } - } } #[repr(C)] @@ -110,35 +105,35 @@ pub struct UiTextureSlicePipeline { pub shader: Handle, } -impl FromWorld for UiTextureSlicePipeline { - fn from_world(world: &mut World) -> Self { - let render_device = world.resource::(); +pub fn init_ui_texture_slice_pipeline( + mut commands: Commands, + render_device: Res, + asset_server: Res, +) { + let view_layout = render_device.create_bind_group_layout( + "ui_texture_slice_view_layout", + &BindGroupLayoutEntries::single( + ShaderStages::VERTEX_FRAGMENT, + uniform_buffer::(true), + ), + ); - let view_layout = render_device.create_bind_group_layout( - "ui_texture_slice_view_layout", - &BindGroupLayoutEntries::single( - ShaderStages::VERTEX_FRAGMENT, - uniform_buffer::(true), + let image_layout = render_device.create_bind_group_layout( + "ui_texture_slice_image_layout", + &BindGroupLayoutEntries::sequential( + ShaderStages::FRAGMENT, + ( + texture_2d(TextureSampleType::Float { filterable: true }), + sampler(SamplerBindingType::Filtering), ), - ); + ), + ); - let image_layout = render_device.create_bind_group_layout( - "ui_texture_slice_image_layout", - &BindGroupLayoutEntries::sequential( - ShaderStages::FRAGMENT, - ( - texture_2d(TextureSampleType::Float { filterable: true }), - sampler(SamplerBindingType::Filtering), - ), - ), - ); - - UiTextureSlicePipeline { - view_layout, - image_layout, - shader: load_embedded_asset!(world, "ui_texture_slice.wgsl"), - } - } + commands.insert_resource(UiTextureSlicePipeline { + view_layout, + image_layout, + shader: load_embedded_asset!(asset_server.as_ref(), "ui_texture_slice.wgsl"), + }); } #[derive(Clone, Copy, Hash, PartialEq, Eq)]