diff --git a/crates/bevy_core_widgets/src/core_scrollbar.rs b/crates/bevy_core_widgets/src/core_scrollbar.rs index cf52d7eb8c80c..01582cd0234fa 100644 --- a/crates/bevy_core_widgets/src/core_scrollbar.rs +++ b/crates/bevy_core_widgets/src/core_scrollbar.rs @@ -12,7 +12,7 @@ use bevy_math::Vec2; use bevy_picking::events::{Cancel, Drag, DragEnd, DragStart, Pointer, Press}; use bevy_reflect::{prelude::ReflectDefault, Reflect}; use bevy_ui::{ - ComputedNode, ComputedUiTargetCamera, Node, ScrollPosition, UiGlobalTransform, UiScale, Val, + ComputedNode, ComputedUiRenderTargetInfo, Node, ScrollPosition, UiGlobalTransform, UiScale, Val, }; /// Used to select the orientation of a scrollbar, slider, or other oriented control. @@ -104,7 +104,7 @@ fn scrollbar_on_pointer_down( mut q_scrollbar: Query<( &CoreScrollbar, &ComputedNode, - &ComputedUiTargetCamera, + &ComputedUiRenderTargetInfo, &UiGlobalTransform, )>, mut q_scroll_pos: Query<(&mut ScrollPosition, &ComputedNode), Without>, diff --git a/crates/bevy_core_widgets/src/core_slider.rs b/crates/bevy_core_widgets/src/core_slider.rs index 6d38cf7732327..58bf44a1a76b3 100644 --- a/crates/bevy_core_widgets/src/core_slider.rs +++ b/crates/bevy_core_widgets/src/core_slider.rs @@ -24,7 +24,7 @@ use bevy_math::ops; use bevy_picking::events::{Drag, DragEnd, DragStart, Pointer, Press}; use bevy_reflect::{prelude::ReflectDefault, Reflect}; use bevy_ui::{ - ComputedNode, ComputedUiTargetCamera, InteractionDisabled, UiGlobalTransform, UiScale, + ComputedNode, ComputedUiRenderTargetInfo, InteractionDisabled, UiGlobalTransform, UiScale, }; use crate::{Callback, Notify, ValueChange}; @@ -236,7 +236,7 @@ pub(crate) fn slider_on_pointer_down( &SliderStep, Option<&SliderPrecision>, &ComputedNode, - &ComputedUiTargetCamera, + &ComputedUiRenderTargetInfo, &UiGlobalTransform, Has, )>, diff --git a/crates/bevy_ui/src/focus.rs b/crates/bevy_ui/src/focus.rs index eddb6f54add14..0d0d5a2eeca76 100644 --- a/crates/bevy_ui/src/focus.rs +++ b/crates/bevy_ui/src/focus.rs @@ -235,7 +235,7 @@ pub fn ui_focus_system( } return None; } - let camera_entity = node.target_camera.camera()?; + let camera_entity = node.target_camera.get()?; let cursor_position = camera_cursor_positions.get(&camera_entity); diff --git a/crates/bevy_ui/src/layout/mod.rs b/crates/bevy_ui/src/layout/mod.rs index d85161e07572b..5420039abf16e 100644 --- a/crates/bevy_ui/src/layout/mod.rs +++ b/crates/bevy_ui/src/layout/mod.rs @@ -1,8 +1,8 @@ use crate::{ experimental::{UiChildren, UiRootNodes}, ui_transform::{UiGlobalTransform, UiTransform}, - BorderRadius, ComputedNode, ComputedUiTargetCamera, ContentSize, Display, LayoutConfig, Node, - Outline, OverflowAxis, ScrollPosition, + BorderRadius, ComputedNode, ComputedUiRenderTargetInfo, ContentSize, Display, LayoutConfig, + Node, Outline, OverflowAxis, ScrollPosition, }; use bevy_ecs::{ change_detection::{DetectChanges, DetectChangesMut}, @@ -77,7 +77,7 @@ pub fn ui_layout_system( Entity, Ref, Option<&mut ContentSize>, - Ref, + Ref, )>, added_node_query: Query<(), Added>, mut node_update_query: Query<( @@ -378,6 +378,9 @@ mod tests { app.add_plugins(HierarchyPropagatePlugin::::new( PostUpdate, )); + app.add_plugins(HierarchyPropagatePlugin::::new( + PostUpdate, + )); app.init_resource::(); app.init_resource::(); app.init_resource::(); @@ -405,6 +408,13 @@ mod tests { .before(ui_layout_system), ); + app.configure_sets( + PostUpdate, + PropagateSet::::default() + .after(propagate_ui_target_cameras) + .before(ui_layout_system), + ); + let world = app.world_mut(); // spawn a dummy primary window and camera world.spawn(( diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index a4966947047ed..9e2dcb5bc0245 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -159,6 +159,13 @@ impl Plugin for UiPlugin { .add_plugins(HierarchyPropagatePlugin::::new( PostUpdate, )) + .configure_sets( + PostUpdate, + PropagateSet::::default().in_set(UiSystems::Propagate), + ) + .add_plugins(HierarchyPropagatePlugin::::new( + PostUpdate, + )) .add_systems( PreUpdate, ui_focus_system.in_set(UiSystems::Focus).after(InputSystems), diff --git a/crates/bevy_ui/src/picking_backend.rs b/crates/bevy_ui/src/picking_backend.rs index e944dd3dd3229..30edd56a4ee79 100644 --- a/crates/bevy_ui/src/picking_backend.rs +++ b/crates/bevy_ui/src/picking_backend.rs @@ -180,7 +180,7 @@ pub fn ui_picking( { continue; } - let Some(camera_entity) = node.target_camera.camera() else { + let Some(camera_entity) = node.target_camera.get() else { continue; }; @@ -224,7 +224,7 @@ pub fn ui_picking( for (hovered_node, position) in hovered { let node = node_query.get(*hovered_node).unwrap(); - let Some(camera_entity) = node.target_camera.camera() else { + let Some(camera_entity) = node.target_camera.get() else { continue; }; diff --git a/crates/bevy_ui/src/ui_node.rs b/crates/bevy_ui/src/ui_node.rs index 8c5b2212dadda..9a6563644b861 100644 --- a/crates/bevy_ui/src/ui_node.rs +++ b/crates/bevy_ui/src/ui_node.rs @@ -381,6 +381,7 @@ impl From for ScrollPosition { #[require( ComputedNode, ComputedUiTargetCamera, + ComputedUiRenderTargetInfo, UiTransform, BackgroundColor, BorderColor, @@ -2806,29 +2807,43 @@ impl<'w, 's> DefaultUiCamera<'w, 's> { #[reflect(Component, Default, PartialEq, Clone)] pub struct ComputedUiTargetCamera { pub(crate) camera: Entity, - /// The scale factor of the target camera's render target. - pub(crate) scale_factor: f32, - /// The size of the target camera's viewport in physical pixels. - pub(crate) physical_size: UVec2, } impl Default for ComputedUiTargetCamera { fn default() -> Self { Self { camera: Entity::PLACEHOLDER, - scale_factor: 1., - physical_size: UVec2::ZERO, } } } impl ComputedUiTargetCamera { /// Returns the id of the target camera for this UI node. - pub fn camera(&self) -> Option { + pub fn get(&self) -> Option { Some(self.camera).filter(|&entity| entity != Entity::PLACEHOLDER) } +} + +/// Derived information about the render target for this UI node. +#[derive(Component, Clone, Copy, Debug, Reflect, PartialEq)] +#[reflect(Component, Default, PartialEq, Clone)] +pub struct ComputedUiRenderTargetInfo { + /// The scale factor of the target camera's render target. + pub(crate) scale_factor: f32, + /// The size of the target camera's viewport in physical pixels. + pub(crate) physical_size: UVec2, +} + +impl Default for ComputedUiRenderTargetInfo { + fn default() -> Self { + Self { + scale_factor: 1., + physical_size: UVec2::ZERO, + } + } +} - /// Returns the scale factor of the target camera's render target. +impl ComputedUiRenderTargetInfo { pub const fn scale_factor(&self) -> f32 { self.scale_factor } diff --git a/crates/bevy_ui/src/update.rs b/crates/bevy_ui/src/update.rs index 7280540206d06..6acbadfa4f874 100644 --- a/crates/bevy_ui/src/update.rs +++ b/crates/bevy_ui/src/update.rs @@ -3,8 +3,8 @@ use crate::{ experimental::{UiChildren, UiRootNodes}, ui_transform::UiGlobalTransform, - CalculatedClip, ComputedUiTargetCamera, DefaultUiCamera, Display, Node, OverflowAxis, - OverrideClip, UiScale, UiTargetCamera, + CalculatedClip, ComputedUiRenderTargetInfo, ComputedUiTargetCamera, DefaultUiCamera, Display, + Node, OverflowAxis, OverrideClip, UiScale, UiTargetCamera, }; use super::ComputedNode; @@ -151,6 +151,10 @@ pub fn propagate_ui_target_cameras( .or(default_camera_entity) .unwrap_or(Entity::PLACEHOLDER); + commands + .entity(root_entity) + .insert(Propagate(ComputedUiTargetCamera { camera })); + let (scale_factor, physical_size) = camera_query .get(camera) .ok() @@ -164,8 +168,7 @@ pub fn propagate_ui_target_cameras( commands .entity(root_entity) - .insert(Propagate(ComputedUiTargetCamera { - camera, + .insert(Propagate(ComputedUiRenderTargetInfo { scale_factor, physical_size, })); @@ -204,6 +207,7 @@ pub(crate) fn update_cameras_test_system( #[cfg(test)] mod tests { use crate::update::propagate_ui_target_cameras; + use crate::ComputedUiRenderTargetInfo; use crate::ComputedUiTargetCamera; use crate::IsDefaultUiCamera; use crate::Node; @@ -233,12 +237,19 @@ mod tests { app.add_plugins(HierarchyPropagatePlugin::::new( PostUpdate, )); - app.configure_sets( PostUpdate, PropagateSet::::default(), ); + app.add_plugins(HierarchyPropagatePlugin::::new( + PostUpdate, + )); + app.configure_sets( + PostUpdate, + PropagateSet::::default(), + ); + app.add_systems( bevy_app::Update, ( @@ -277,8 +288,12 @@ mod tests { assert_eq!( *world.get::(uinode).unwrap(), - ComputedUiTargetCamera { - camera, + ComputedUiTargetCamera { camera } + ); + + assert_eq!( + *world.get::(uinode).unwrap(), + ComputedUiRenderTargetInfo { physical_size, scale_factor, } @@ -341,10 +356,14 @@ mod tests { ] { assert_eq!( *world.get::(uinode).unwrap(), - ComputedUiTargetCamera { - camera, - scale_factor, + ComputedUiTargetCamera { camera } + ); + + assert_eq!( + *world.get::(uinode).unwrap(), + ComputedUiRenderTargetInfo { physical_size, + scale_factor, } ); } @@ -395,7 +414,7 @@ mod tests { assert_eq!( world - .get::(uinode) + .get::(uinode) .unwrap() .scale_factor, scale1 @@ -403,7 +422,7 @@ mod tests { assert_eq!( world - .get::(uinode) + .get::(uinode) .unwrap() .physical_size, size1 @@ -413,7 +432,7 @@ mod tests { world .get::(uinode) .unwrap() - .camera() + .get() .unwrap(), camera1 ); @@ -425,7 +444,7 @@ mod tests { assert_eq!( world - .get::(uinode) + .get::(uinode) .unwrap() .scale_factor, scale2 @@ -433,7 +452,7 @@ mod tests { assert_eq!( world - .get::(uinode) + .get::(uinode) .unwrap() .physical_size, size2 @@ -443,7 +462,7 @@ mod tests { world .get::(uinode) .unwrap() - .camera() + .get() .unwrap(), camera2 ); @@ -496,7 +515,7 @@ mod tests { assert_eq!( world - .get::(uinode1) + .get::(uinode1) .unwrap() .scale_factor(), scale1 @@ -504,7 +523,7 @@ mod tests { assert_eq!( world - .get::(uinode1) + .get::(uinode1) .unwrap() .physical_size(), size1 @@ -514,7 +533,7 @@ mod tests { world .get::(uinode1) .unwrap() - .camera() + .get() .unwrap(), camera1 ); @@ -523,7 +542,7 @@ mod tests { world .get::(uinode2) .unwrap() - .camera() + .get() .unwrap(), camera1 ); @@ -536,7 +555,7 @@ mod tests { assert_eq!( world - .get::(uinode1) + .get::(uinode1) .unwrap() .scale_factor(), scale2 @@ -544,7 +563,7 @@ mod tests { assert_eq!( world - .get::(uinode1) + .get::(uinode1) .unwrap() .physical_size(), size2 @@ -554,7 +573,7 @@ mod tests { world .get::(uinode1) .unwrap() - .camera() + .get() .unwrap(), camera2 ); @@ -563,7 +582,7 @@ mod tests { world .get::(uinode2) .unwrap() - .camera() + .get() .unwrap(), camera1 ); @@ -600,7 +619,7 @@ mod tests { assert_eq!( world - .get::(uinode) + .get::(uinode) .unwrap() .scale_factor, scale @@ -608,7 +627,7 @@ mod tests { assert_eq!( world - .get::(uinode) + .get::(uinode) .unwrap() .physical_size, size @@ -618,7 +637,7 @@ mod tests { world .get::(uinode) .unwrap() - .camera() + .get() .unwrap(), camera ); @@ -630,7 +649,7 @@ mod tests { assert_eq!( world - .get::(uinode) + .get::(uinode) .unwrap() .scale_factor(), 2. diff --git a/crates/bevy_ui/src/widget/image.rs b/crates/bevy_ui/src/widget/image.rs index 581cd5bb7033c..10f61405df93b 100644 --- a/crates/bevy_ui/src/widget/image.rs +++ b/crates/bevy_ui/src/widget/image.rs @@ -1,4 +1,4 @@ -use crate::{ComputedUiTargetCamera, ContentSize, Measure, MeasureArgs, Node, NodeMeasure}; +use crate::{ComputedUiRenderTargetInfo, ContentSize, Measure, MeasureArgs, Node, NodeMeasure}; use bevy_asset::{Assets, Handle}; use bevy_color::Color; use bevy_ecs::prelude::*; @@ -260,7 +260,7 @@ pub fn update_image_content_size_system( &mut ContentSize, Ref, &mut ImageNodeSize, - Ref, + Ref, ), UpdateImageFilter, >, diff --git a/crates/bevy_ui/src/widget/text.rs b/crates/bevy_ui/src/widget/text.rs index 36e1c2aecefc6..bcde6973b0112 100644 --- a/crates/bevy_ui/src/widget/text.rs +++ b/crates/bevy_ui/src/widget/text.rs @@ -1,6 +1,6 @@ use crate::{ - ComputedNode, ComputedUiTargetCamera, ContentSize, FixedMeasure, Measure, MeasureArgs, Node, - NodeMeasure, + ComputedNode, ComputedUiRenderTargetInfo, ContentSize, FixedMeasure, Measure, MeasureArgs, + Node, NodeMeasure, }; use bevy_asset::Assets; use bevy_color::Color; @@ -262,7 +262,7 @@ fn create_text_measure<'a>( /// A `Measure` is used by the UI's layout algorithm to determine the appropriate amount of space /// to provide for the text given the fonts, the text itself and the constraints of the layout. /// -/// * Measures are regenerated on changes to either [`ComputedTextBlock`] or [`ComputedUiTargetCamera`]. +/// * Measures are regenerated on changes to either [`ComputedTextBlock`] or [`ComputedUiRenderTargetInfo`]. /// * Changes that only modify the colors of a `Text` do not require a new `Measure`. This system /// is only able to detect that a `Text` component has changed and will regenerate the `Measure` on /// color changes. This can be expensive, particularly for large blocks of text, and the [`bypass_change_detection`](bevy_ecs::change_detection::DetectChangesMut::bypass_change_detection) @@ -276,7 +276,7 @@ pub fn measure_text_system( &mut ContentSize, &mut TextNodeFlags, &mut ComputedTextBlock, - &ComputedUiTargetCamera, + Ref, &ComputedNode, ), With, diff --git a/crates/bevy_ui_render/src/box_shadow.rs b/crates/bevy_ui_render/src/box_shadow.rs index 2b7b9e679e135..0c81a29068744 100644 --- a/crates/bevy_ui_render/src/box_shadow.rs +++ b/crates/bevy_ui_render/src/box_shadow.rs @@ -28,8 +28,8 @@ use bevy_render::{ use bevy_render::{RenderApp, RenderStartup}; use bevy_shader::{Shader, ShaderDefVal}; use bevy_ui::{ - BoxShadow, CalculatedClip, ComputedNode, ComputedUiTargetCamera, ResolvedBorderRadius, - UiGlobalTransform, Val, + BoxShadow, CalculatedClip, ComputedNode, ComputedUiRenderTargetInfo, ComputedUiTargetCamera, + ResolvedBorderRadius, UiGlobalTransform, Val, }; use bevy_utils::default; use bytemuck::{Pod, Zeroable}; @@ -224,13 +224,16 @@ pub fn extract_shadows( &BoxShadow, Option<&CalculatedClip>, &ComputedUiTargetCamera, + &ComputedUiRenderTargetInfo, )>, >, camera_map: Extract, ) { let mut mapping = camera_map.get_mapper(); - for (entity, uinode, transform, visibility, box_shadow, clip, camera) in &box_shadow_query { + for (entity, uinode, transform, visibility, box_shadow, clip, camera, target) in + &box_shadow_query + { // Skip if no visible shadows if !visibility.get() || box_shadow.is_empty() || uinode.is_empty() { continue; @@ -240,8 +243,8 @@ pub fn extract_shadows( continue; }; - let ui_physical_viewport_size = camera.physical_size().as_vec2(); - let scale_factor = uinode.inverse_scale_factor.recip(); + let ui_physical_viewport_size = target.physical_size().as_vec2(); + let scale_factor = target.scale_factor(); for drop_shadow in box_shadow.iter() { if drop_shadow.color.is_fully_transparent() { diff --git a/crates/bevy_ui_render/src/gradient.rs b/crates/bevy_ui_render/src/gradient.rs index 93fb500779114..463979a606629 100644 --- a/crates/bevy_ui_render/src/gradient.rs +++ b/crates/bevy_ui_render/src/gradient.rs @@ -34,8 +34,8 @@ use bevy_render::{sync_world::MainEntity, RenderStartup}; use bevy_shader::Shader; use bevy_sprite::BorderRect; use bevy_ui::{ - BackgroundGradient, BorderGradient, ColorStop, ConicGradient, Gradient, - InterpolationColorSpace, LinearGradient, RadialGradient, ResolvedBorderRadius, Val, + BackgroundGradient, BorderGradient, ColorStop, ComputedUiRenderTargetInfo, ConicGradient, + Gradient, InterpolationColorSpace, LinearGradient, RadialGradient, ResolvedBorderRadius, Val, }; use bevy_utils::default; use bytemuck::{Pod, Zeroable}; @@ -353,6 +353,7 @@ pub fn extract_gradients( Entity, &ComputedNode, &ComputedUiTargetCamera, + &ComputedUiRenderTargetInfo, &UiGlobalTransform, &InheritedVisibility, Option<&CalculatedClip>, @@ -367,6 +368,7 @@ pub fn extract_gradients( for ( entity, uinode, + camera, target, transform, inherited_visibility, @@ -379,7 +381,7 @@ pub fn extract_gradients( continue; } - let Some(extracted_camera_entity) = camera_mapper.map(target) else { + let Some(extracted_camera_entity) = camera_mapper.map(camera) else { continue; }; diff --git a/crates/bevy_ui_render/src/lib.rs b/crates/bevy_ui_render/src/lib.rs index f6a4524240ab4..a5fc2506fc0d6 100644 --- a/crates/bevy_ui_render/src/lib.rs +++ b/crates/bevy_ui_render/src/lib.rs @@ -326,7 +326,7 @@ pub struct UiCameraMapper<'w, 's> { impl<'w, 's> UiCameraMapper<'w, 's> { /// Returns the render entity corresponding to the given [`ComputedUiTargetCamera`]'s camera, or none if no corresponding entity was found. pub fn map(&mut self, computed_target: &ComputedUiTargetCamera) -> Option { - let camera_entity = computed_target.camera()?; + let camera_entity = computed_target.get()?; if self.camera_entity != camera_entity { let new_render_camera_entity = self.mapping.get(camera_entity).ok()?; self.render_entity = new_render_camera_entity; diff --git a/examples/3d/split_screen.rs b/examples/3d/split_screen.rs index ed939e97b2a30..f708748a6d157 100644 --- a/examples/3d/split_screen.rs +++ b/examples/3d/split_screen.rs @@ -193,7 +193,7 @@ fn button_system( // Since TargetCamera propagates to the children, we can use it to find // which side of the screen the button is on. if let Some(mut camera_transform) = computed_target - .camera() + .get() .and_then(|camera| camera_query.get_mut(camera).ok()) { let angle = match direction { diff --git a/release-content/migration-guides/split_up_computeduitargetcamera.md b/release-content/migration-guides/split_up_computeduitargetcamera.md new file mode 100644 index 0000000000000..34f3f76d72320 --- /dev/null +++ b/release-content/migration-guides/split_up_computeduitargetcamera.md @@ -0,0 +1,6 @@ +--- +title: The render target info from `ComputedUiTargetCamera` has been removed. +pull_requests: [20535] +--- + +The render target info, scale factor and physical size, has been removed from `ComputedUiTargetCamera` and placed into a new component `ComputedUiRenderTargetInfo`.