From 3497271b74c4803c245586faa1e37e34caef59b3 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Fri, 6 Dec 2024 19:36:13 +0000 Subject: [PATCH 01/13] added `debug` module to bevy_ui/render --- crates/bevy_ui/src/render/debug.rs | 1 + crates/bevy_ui/src/render/mod.rs | 1 + 2 files changed, 2 insertions(+) create mode 100644 crates/bevy_ui/src/render/debug.rs diff --git a/crates/bevy_ui/src/render/debug.rs b/crates/bevy_ui/src/render/debug.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/crates/bevy_ui/src/render/debug.rs @@ -0,0 +1 @@ + diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index a77d5cf144f2f..f2ae5068bcc3f 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -1,4 +1,5 @@ pub mod box_shadow; +pub mod debug; mod pipeline; mod render_pass; mod ui_material_pipeline; From f403613f5801c7072154599d17717c14705b7176 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Fri, 6 Dec 2024 20:11:59 +0000 Subject: [PATCH 02/13] * Renamed `debug` module to `debug_overlay`. * Added `UiDebugOptions` resource that can be used to enable or disable the debug overlay and set the line width. * New `extract_debug_overlay` system that extracts the node geometry for rendering. --- crates/bevy_ui/src/lib.rs | 3 + crates/bevy_ui/src/render/debug.rs | 1 - crates/bevy_ui/src/render/debug_overlay.rs | 98 ++++++++++++++++++++++ crates/bevy_ui/src/render/mod.rs | 6 +- 4 files changed, 106 insertions(+), 2 deletions(-) delete mode 100644 crates/bevy_ui/src/render/debug.rs create mode 100644 crates/bevy_ui/src/render/debug_overlay.rs diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 20e7c55b776ad..35bd8677c4a15 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -37,6 +37,7 @@ pub use focus::*; pub use geometry::*; pub use layout::*; pub use measurement::*; +use render::debug_overlay::UiDebugOptions; pub use render::*; pub use ui_material::*; pub use ui_node::*; @@ -230,6 +231,8 @@ impl Plugin for UiPlugin { return; } + app.init_resource::(); + build_ui_render(app); } diff --git a/crates/bevy_ui/src/render/debug.rs b/crates/bevy_ui/src/render/debug.rs deleted file mode 100644 index 8b137891791fe..0000000000000 --- a/crates/bevy_ui/src/render/debug.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/bevy_ui/src/render/debug_overlay.rs b/crates/bevy_ui/src/render/debug_overlay.rs new file mode 100644 index 0000000000000..efecff111e372 --- /dev/null +++ b/crates/bevy_ui/src/render/debug_overlay.rs @@ -0,0 +1,98 @@ +use bevy_asset::AssetId; +use bevy_color::Hsla; +use bevy_ecs::entity::Entity; +use bevy_ecs::system::Commands; +use bevy_ecs::system::Query; +use bevy_ecs::system::Res; +use bevy_ecs::system::ResMut; +use bevy_ecs::system::Resource; +use bevy_math::Rect; +use bevy_math::Vec2; +use bevy_render::sync_world::RenderEntity; +use bevy_render::sync_world::TemporaryRenderEntity; +use bevy_render::Extract; +use bevy_sprite::BorderRect; +use bevy_transform::components::GlobalTransform; + +use crate::ComputedNode; +use crate::DefaultUiCamera; +use crate::TargetCamera; + +use super::ExtractedUiItem; +use super::ExtractedUiNode; +use super::ExtractedUiNodes; +use super::NodeType; + +#[derive(Resource)] +pub struct UiDebugOptions { + pub enabled: bool, + pub line_width: f32, +} + +impl Default for UiDebugOptions { + fn default() -> Self { + Self { + enabled: true, + line_width: 3., + } + } +} + +#[allow(clippy::too_many_arguments)] +pub fn extract_debug_overlay( + mut commands: Commands, + debug_options: Extract>, + mut extracted_uinodes: ResMut, + default_ui_camera: Extract, + uinode_query: Extract< + Query<( + Entity, + &ComputedNode, + &GlobalTransform, + Option<&TargetCamera>, + )>, + >, + mapping: Extract>, +) { + if !debug_options.enabled { + return; + } + + for (entity, uinode, transform, camera) in &uinode_query { + let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get()) + else { + continue; + }; + + let Ok(render_camera_entity) = mapping.get(camera_entity) else { + continue; + }; + + extracted_uinodes.uinodes.insert( + commands.spawn(TemporaryRenderEntity).id(), + ExtractedUiNode { + stack_index: uinode.stack_index + 2_147_483_647, + color: Hsla::sequential_dispersed(entity.index()).into(), + rect: Rect { + min: Vec2::ZERO, + max: uinode.size, + }, + clip: None, + image: AssetId::default(), + camera_entity: render_camera_entity, + item: ExtractedUiItem::Node { + atlas_scaling: None, + transform: transform.compute_matrix(), + flip_x: false, + flip_y: false, + border: BorderRect::square( + debug_options.line_width / uinode.inverse_scale_factor(), + ), + border_radius: uinode.border_radius(), + node_type: NodeType::Border, + }, + main_entity: entity.into(), + }, + ); + } +} diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index f2ae5068bcc3f..540cc618322e4 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -1,5 +1,5 @@ pub mod box_shadow; -pub mod debug; +pub mod debug_overlay; mod pipeline; mod render_pass; mod ui_material_pipeline; @@ -42,6 +42,7 @@ use bevy_render::{ }; use bevy_sprite::TextureAtlasLayout; use bevy_sprite::{BorderRect, SpriteAssetEvents}; +use debug_overlay::extract_debug_overlay; use crate::{Display, Node}; use bevy_text::{ComputedTextBlock, PositionedGlyph, TextColor, TextLayoutInfo}; @@ -99,6 +100,7 @@ pub enum RenderUiSystem { ExtractTextureSlice, ExtractBorders, ExtractText, + ExtractDebugOverlay, } pub fn build_ui_render(app: &mut App) { @@ -126,6 +128,7 @@ pub fn build_ui_render(app: &mut App) { RenderUiSystem::ExtractTextureSlice, RenderUiSystem::ExtractBorders, RenderUiSystem::ExtractText, + RenderUiSystem::ExtractDebugOverlay, ) .chain(), ) @@ -137,6 +140,7 @@ pub fn build_ui_render(app: &mut App) { extract_uinode_images.in_set(RenderUiSystem::ExtractImages), extract_uinode_borders.in_set(RenderUiSystem::ExtractBorders), extract_text_sections.in_set(RenderUiSystem::ExtractText), + extract_debug_overlay.in_set(RenderUiSystem::ExtractDebugOverlay), ), ) .add_systems( From 398e86297c608681929752fc0808724468f66684 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Sat, 7 Dec 2024 12:04:54 +0000 Subject: [PATCH 03/13] Added comments to the extracted overlay node's insertion. --- crates/bevy_ui/src/render/debug_overlay.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ui/src/render/debug_overlay.rs b/crates/bevy_ui/src/render/debug_overlay.rs index efecff111e372..689b9b4434a49 100644 --- a/crates/bevy_ui/src/render/debug_overlay.rs +++ b/crates/bevy_ui/src/render/debug_overlay.rs @@ -32,8 +32,8 @@ pub struct UiDebugOptions { impl Default for UiDebugOptions { fn default() -> Self { Self { - enabled: true, - line_width: 3., + enabled: false, + line_width: 1., } } } @@ -68,10 +68,12 @@ pub fn extract_debug_overlay( continue; }; + // Extract a border box to display an outline for every UI Node in the layout extracted_uinodes.uinodes.insert( commands.spawn(TemporaryRenderEntity).id(), ExtractedUiNode { - stack_index: uinode.stack_index + 2_147_483_647, + // Add a large number to the UI node's stack index so that the overlay is always drawn on top + stack_index: uinode.stack_index + u32::MAX / 2, color: Hsla::sequential_dispersed(entity.index()).into(), rect: Rect { min: Vec2::ZERO, From 541832bfc8076bd7086aef8321bb8f28a86dbb14 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Sat, 7 Dec 2024 12:49:57 +0000 Subject: [PATCH 04/13] * Removed the `ui_debug_ovelay` module from the `bevy_dev_tools`. * Added `toggle` method to the `UiDebugOptions` resource struct. * Updated `testbed_ui` example to use the new overlay. --- crates/bevy_dev_tools/Cargo.toml | 2 - crates/bevy_dev_tools/src/lib.rs | 3 - .../src/ui_debug_overlay/inset.rs | 192 ------------ .../src/ui_debug_overlay/mod.rs | 278 ------------------ crates/bevy_ui/src/render/debug_overlay.rs | 6 + examples/testbed/ui.rs | 26 +- 6 files changed, 10 insertions(+), 497 deletions(-) delete mode 100644 crates/bevy_dev_tools/src/ui_debug_overlay/inset.rs delete mode 100644 crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs diff --git a/crates/bevy_dev_tools/Cargo.toml b/crates/bevy_dev_tools/Cargo.toml index 322c924f81f34..315d7d9923d18 100644 --- a/crates/bevy_dev_tools/Cargo.toml +++ b/crates/bevy_dev_tools/Cargo.toml @@ -9,9 +9,7 @@ license = "MIT OR Apache-2.0" keywords = ["bevy"] [features] -default = ["bevy_ui_debug"] bevy_ci_testing = ["serde", "ron"] -bevy_ui_debug = [] [dependencies] # bevy diff --git a/crates/bevy_dev_tools/src/lib.rs b/crates/bevy_dev_tools/src/lib.rs index d5563a0bd946a..b49604e6c885d 100644 --- a/crates/bevy_dev_tools/src/lib.rs +++ b/crates/bevy_dev_tools/src/lib.rs @@ -15,9 +15,6 @@ pub mod ci_testing; pub mod fps_overlay; -#[cfg(feature = "bevy_ui_debug")] -pub mod ui_debug_overlay; - pub mod states; /// Enables developer tools in an [`App`]. This plugin is added automatically with `bevy_dev_tools` diff --git a/crates/bevy_dev_tools/src/ui_debug_overlay/inset.rs b/crates/bevy_dev_tools/src/ui_debug_overlay/inset.rs deleted file mode 100644 index 1e52723d20d22..0000000000000 --- a/crates/bevy_dev_tools/src/ui_debug_overlay/inset.rs +++ /dev/null @@ -1,192 +0,0 @@ -use bevy_color::Color; -use bevy_gizmos::{config::GizmoConfigGroup, prelude::Gizmos}; -use bevy_math::{Vec2, Vec2Swizzles}; -use bevy_reflect::Reflect; -use bevy_transform::prelude::GlobalTransform; -use bevy_utils::HashMap; - -use super::{CameraQuery, LayoutRect}; - -// Function used here so we don't need to redraw lines that are fairly close to each other. -fn approx_eq(compared: f32, other: f32) -> bool { - (compared - other).abs() < 0.001 -} - -fn rect_border_axis(rect: LayoutRect) -> (f32, f32, f32, f32) { - let pos = rect.pos; - let size = rect.size; - let offset = pos + size; - (pos.x, offset.x, pos.y, offset.y) -} - -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)] -enum Dir { - Start, - End, -} -impl Dir { - const fn increments(self) -> i64 { - match self { - Dir::Start => 1, - Dir::End => -1, - } - } -} -impl From for Dir { - fn from(value: i64) -> Self { - if value.is_positive() { - Dir::Start - } else { - Dir::End - } - } -} -/// Collection of axis aligned "lines" (actually just their coordinate on -/// a given axis). -#[derive(Debug, Clone)] -struct DrawnLines { - lines: HashMap, - width: f32, -} -#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)] -impl DrawnLines { - fn new(width: f32) -> Self { - DrawnLines { - lines: HashMap::new(), - width, - } - } - /// Return `value` offset by as many `increment`s as necessary to make it - /// not overlap with already drawn lines. - fn inset(&self, value: f32) -> f32 { - let scaled = value / self.width; - let fract = scaled.fract(); - let mut on_grid = scaled.floor() as i64; - for _ in 0..10 { - let Some(dir) = self.lines.get(&on_grid) else { - break; - }; - // TODO(clean): This fixes a panic, but I'm not sure how valid this is - let Some(added) = on_grid.checked_add(dir.increments()) else { - break; - }; - on_grid = added; - } - ((on_grid as f32) + fract) * self.width - } - /// Remove a line from the collection of drawn lines. - /// - /// Typically, we only care for pre-existing lines when drawing the children - /// of a container, nothing more. So we remove it after we are done with - /// the children. - fn remove(&mut self, value: f32, increment: i64) { - let mut on_grid = (value / self.width).floor() as i64; - loop { - // TODO(clean): This fixes a panic, but I'm not sure how valid this is - let Some(next_cell) = on_grid.checked_add(increment) else { - return; - }; - if !self.lines.contains_key(&next_cell) { - self.lines.remove(&on_grid); - return; - } - on_grid = next_cell; - } - } - /// Add a line from the collection of drawn lines. - fn add(&mut self, value: f32, increment: i64) { - let mut on_grid = (value / self.width).floor() as i64; - loop { - let old_value = self.lines.insert(on_grid, increment.into()); - if old_value.is_none() { - return; - } - // TODO(clean): This fixes a panic, but I'm not sure how valid this is - let Some(added) = on_grid.checked_add(increment) else { - return; - }; - on_grid = added; - } - } -} - -#[derive(GizmoConfigGroup, Reflect, Default)] -pub struct UiGizmosDebug; - -pub(super) struct InsetGizmo<'w, 's> { - draw: Gizmos<'w, 's, UiGizmosDebug>, - cam: CameraQuery<'w, 's>, - known_y: DrawnLines, - known_x: DrawnLines, -} -impl<'w, 's> InsetGizmo<'w, 's> { - pub(super) fn new( - draw: Gizmos<'w, 's, UiGizmosDebug>, - cam: CameraQuery<'w, 's>, - line_width: f32, - ) -> Self { - InsetGizmo { - draw, - cam, - known_y: DrawnLines::new(line_width), - known_x: DrawnLines::new(line_width), - } - } - fn relative(&self, mut position: Vec2) -> Vec2 { - let zero = GlobalTransform::IDENTITY; - let Ok(cam) = self.cam.get_single() else { - return Vec2::ZERO; - }; - if let Ok(new_position) = cam.world_to_viewport(&zero, position.extend(0.)) { - position = new_position; - }; - position.xy() - } - fn line_2d(&mut self, mut start: Vec2, mut end: Vec2, color: Color) { - if approx_eq(start.x, end.x) { - start.x = self.known_x.inset(start.x); - end.x = start.x; - } else if approx_eq(start.y, end.y) { - start.y = self.known_y.inset(start.y); - end.y = start.y; - } - let (start, end) = (self.relative(start), self.relative(end)); - self.draw.line_2d(start, end, color); - } - pub(super) fn set_scope(&mut self, rect: LayoutRect) { - let (left, right, top, bottom) = rect_border_axis(rect); - self.known_x.add(left, 1); - self.known_x.add(right, -1); - self.known_y.add(top, 1); - self.known_y.add(bottom, -1); - } - pub(super) fn clear_scope(&mut self, rect: LayoutRect) { - let (left, right, top, bottom) = rect_border_axis(rect); - self.known_x.remove(left, 1); - self.known_x.remove(right, -1); - self.known_y.remove(top, 1); - self.known_y.remove(bottom, -1); - } - pub(super) fn rect_2d(&mut self, rect: LayoutRect, color: Color) { - let (left, right, top, bottom) = rect_border_axis(rect); - if approx_eq(left, right) { - self.line_2d(Vec2::new(left, top), Vec2::new(left, bottom), color); - } else if approx_eq(top, bottom) { - self.line_2d(Vec2::new(left, top), Vec2::new(right, top), color); - } else { - let inset_x = |v| self.known_x.inset(v); - let inset_y = |v| self.known_y.inset(v); - let (left, right) = (inset_x(left), inset_x(right)); - let (top, bottom) = (inset_y(top), inset_y(bottom)); - let strip = [ - Vec2::new(left, top), - Vec2::new(left, bottom), - Vec2::new(right, bottom), - Vec2::new(right, top), - Vec2::new(left, top), - ] - .map(|v| self.relative(v)); - self.draw.linestrip_2d(strip, color); - } - } -} diff --git a/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs b/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs deleted file mode 100644 index 97726ce28972d..0000000000000 --- a/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs +++ /dev/null @@ -1,278 +0,0 @@ -//! A visual representation of UI node sizes. -use core::any::{Any, TypeId}; - -use bevy_app::{App, Plugin, PostUpdate}; -use bevy_color::Hsla; -use bevy_core::Name; -use bevy_core_pipeline::core_2d::Camera2d; -use bevy_ecs::{prelude::*, system::SystemParam}; -use bevy_gizmos::{config::GizmoConfigStore, prelude::Gizmos, AppGizmoBuilder}; -use bevy_hierarchy::{Children, Parent}; -use bevy_math::{Vec2, Vec3Swizzles}; -use bevy_render::{ - camera::RenderTarget, - prelude::*, - view::{RenderLayers, VisibilitySystems}, -}; -use bevy_transform::{prelude::GlobalTransform, TransformSystem}; -use bevy_ui::{ComputedNode, DefaultUiCamera, Display, Node, TargetCamera, UiScale}; -use bevy_utils::{default, warn_once}; -use bevy_window::{PrimaryWindow, Window, WindowRef}; - -use inset::InsetGizmo; - -use self::inset::UiGizmosDebug; - -mod inset; - -/// The [`Camera::order`] index used by the layout debug camera. -pub const LAYOUT_DEBUG_CAMERA_ORDER: isize = 255; -/// The [`RenderLayers`] used by the debug gizmos and the debug camera. -pub const LAYOUT_DEBUG_LAYERS: RenderLayers = RenderLayers::layer(16); - -#[derive(Clone, Copy)] -struct LayoutRect { - pos: Vec2, - size: Vec2, -} - -impl LayoutRect { - fn new(trans: &GlobalTransform, node: &ComputedNode, scale: f32) -> Self { - let mut this = Self { - pos: trans.translation().xy() * scale, - size: node.size() * scale, - }; - this.pos -= this.size / 2.; - this - } -} - -#[derive(Component, Debug, Clone, Default)] -struct DebugOverlayCamera; - -/// The debug overlay options. -#[derive(Resource, Clone, Default)] -pub struct UiDebugOptions { - /// Whether the overlay is enabled. - pub enabled: bool, - layout_gizmos_camera: Option, -} -impl UiDebugOptions { - /// This will toggle the enabled field, setting it to false if true and true if false. - pub fn toggle(&mut self) { - self.enabled = !self.enabled; - } -} - -/// The system responsible to change the [`Camera`] config based on changes in [`UiDebugOptions`] and [`GizmoConfig`](bevy_gizmos::prelude::GizmoConfig). -fn update_debug_camera( - mut gizmo_config: ResMut, - mut options: ResMut, - mut cmds: Commands, - mut debug_cams: Query<&mut Camera, With>, -) { - if !options.is_changed() && !gizmo_config.is_changed() { - return; - } - if !options.enabled { - let Some(cam) = options.layout_gizmos_camera else { - return; - }; - let Ok(mut cam) = debug_cams.get_mut(cam) else { - return; - }; - cam.is_active = false; - if let Some((config, _)) = gizmo_config.get_config_mut_dyn(&TypeId::of::()) { - config.enabled = false; - } - } else { - let spawn_cam = || { - cmds.spawn(( - Camera2d, - OrthographicProjection { - far: 1000.0, - viewport_origin: Vec2::new(0.0, 0.0), - ..OrthographicProjection::default_3d() - }, - Camera { - order: LAYOUT_DEBUG_CAMERA_ORDER, - clear_color: ClearColorConfig::None, - ..default() - }, - LAYOUT_DEBUG_LAYERS.clone(), - DebugOverlayCamera, - Name::new("Layout Debug Camera"), - )) - .id() - }; - if let Some((config, _)) = gizmo_config.get_config_mut_dyn(&TypeId::of::()) { - config.enabled = true; - config.render_layers = LAYOUT_DEBUG_LAYERS.clone(); - } - let cam = *options.layout_gizmos_camera.get_or_insert_with(spawn_cam); - let Ok(mut cam) = debug_cams.get_mut(cam) else { - return; - }; - cam.is_active = true; - } -} - -/// The function that goes over every children of given [`Entity`], skipping the not visible ones and drawing the gizmos outlines. -fn outline_nodes(outline: &OutlineParam, draw: &mut InsetGizmo, this_entity: Entity, scale: f32) { - let Ok(to_iter) = outline.children.get(this_entity) else { - return; - }; - - for (entity, trans, node, computed_node, children) in outline.nodes.iter_many(to_iter) { - if matches!(node.display, Display::None) { - continue; - } - - if let Ok(view_visibility) = outline.view_visibility.get(entity) { - if !view_visibility.get() { - continue; - } - } - let rect = LayoutRect::new(trans, computed_node, scale); - outline_node(entity, rect, draw); - if children.is_some() { - outline_nodes(outline, draw, entity, scale); - } - draw.clear_scope(rect); - } -} - -type NodesQuery = ( - Entity, - &'static GlobalTransform, - &'static Node, - &'static ComputedNode, - Option<&'static Children>, -); - -#[derive(SystemParam)] -struct OutlineParam<'w, 's> { - gizmo_config: Res<'w, GizmoConfigStore>, - children: Query<'w, 's, &'static Children>, - nodes: Query<'w, 's, NodesQuery>, - view_visibility: Query<'w, 's, &'static ViewVisibility>, - ui_scale: Res<'w, UiScale>, -} - -type CameraQuery<'w, 's> = Query<'w, 's, &'static Camera, With>; - -#[derive(SystemParam)] -struct CameraParam<'w, 's> { - debug_camera: Query<'w, 's, &'static Camera, With>, - cameras: Query<'w, 's, &'static Camera, Without>, - primary_window: Query<'w, 's, &'static Window, With>, - default_ui_camera: DefaultUiCamera<'w, 's>, -} - -/// system responsible for drawing the gizmos lines around all the node roots, iterating recursively through all visible children. -fn outline_roots( - outline: OutlineParam, - draw: Gizmos, - cam: CameraParam, - roots: Query< - ( - Entity, - &GlobalTransform, - &ComputedNode, - Option<&ViewVisibility>, - Option<&TargetCamera>, - ), - Without, - >, - window: Query<&Window, With>, - nonprimary_windows: Query<&Window, Without>, - options: Res, -) { - if !options.enabled { - return; - } - if !nonprimary_windows.is_empty() { - warn_once!( - "The layout debug view only uses the primary window scale, \ - you might notice gaps between container lines" - ); - } - let window_scale = window.get_single().map_or(1., Window::scale_factor); - let scale_factor = outline.ui_scale.0; - - // We let the line be defined by the window scale alone - let line_width = outline - .gizmo_config - .get_config_dyn(&UiGizmosDebug.type_id()) - .map_or(2., |(config, _)| config.line.width) - / window_scale; - let mut draw = InsetGizmo::new(draw, cam.debug_camera, line_width); - for (entity, trans, node, view_visibility, maybe_target_camera) in &roots { - if let Some(view_visibility) = view_visibility { - // If the entity isn't visible, we will not draw any lines. - if !view_visibility.get() { - continue; - } - } - // We skip ui in other windows that are not the primary one - if let Some(camera_entity) = maybe_target_camera - .map(|target| target.0) - .or(cam.default_ui_camera.get()) - { - let Ok(camera) = cam.cameras.get(camera_entity) else { - // The camera wasn't found. Either the Camera don't exist or the Camera is the debug Camera, that we want to skip and warn - warn_once!("Camera {:?} wasn't found for debug overlay", camera_entity); - continue; - }; - match camera.target { - RenderTarget::Window(window_ref) => { - if let WindowRef::Entity(window_entity) = window_ref { - if cam.primary_window.get(window_entity).is_err() { - // This window isn't the primary, so we skip this root. - continue; - } - } - } - // Hard to know the results of this, better skip this target. - _ => continue, - } - } - - let rect = LayoutRect::new(trans, node, scale_factor); - outline_node(entity, rect, &mut draw); - outline_nodes(&outline, &mut draw, entity, scale_factor); - } -} - -/// Function responsible for drawing the gizmos lines around the given Entity -fn outline_node(entity: Entity, rect: LayoutRect, draw: &mut InsetGizmo) { - let color = Hsla::sequential_dispersed(entity.index()); - - draw.rect_2d(rect, color.into()); - draw.set_scope(rect); -} - -/// The debug overlay plugin. -/// -/// This spawns a new camera with a low order, and draws gizmo. -/// -/// Note that due to limitation with [`bevy_gizmos`], multiple windows with this feature -/// enabled isn't supported and the lines are only drawn in the [`PrimaryWindow`] -pub struct DebugUiPlugin; -impl Plugin for DebugUiPlugin { - fn build(&self, app: &mut App) { - app.init_resource::() - .init_gizmo_group::() - .add_systems( - PostUpdate, - ( - update_debug_camera, - outline_roots - .after(TransformSystem::TransformPropagate) - // This needs to run before VisibilityPropagate so it can relies on ViewVisibility - .before(VisibilitySystems::VisibilityPropagate), - ) - .chain(), - ); - } -} diff --git a/crates/bevy_ui/src/render/debug_overlay.rs b/crates/bevy_ui/src/render/debug_overlay.rs index 689b9b4434a49..9f3cc584fd3bb 100644 --- a/crates/bevy_ui/src/render/debug_overlay.rs +++ b/crates/bevy_ui/src/render/debug_overlay.rs @@ -29,6 +29,12 @@ pub struct UiDebugOptions { pub line_width: f32, } +impl UiDebugOptions { + pub fn toggle(&mut self) { + self.enabled = !self.enabled; + } +} + impl Default for UiDebugOptions { fn default() -> Self { Self { diff --git a/examples/testbed/ui.rs b/examples/testbed/ui.rs index d33013d19a88e..b777b9ecda7b5 100644 --- a/examples/testbed/ui.rs +++ b/examples/testbed/ui.rs @@ -16,13 +16,7 @@ fn main() { let mut app = App::new(); app.add_plugins(DefaultPlugins) .add_systems(Startup, setup) - .add_systems(Update, update_scroll_position); - - #[cfg(feature = "bevy_dev_tools")] - { - app.add_plugins(bevy::dev_tools::ui_debug_overlay::DebugUiPlugin) - .add_systems(Update, toggle_overlay); - } + .add_systems(Update, (update_scroll_position, toggle_debug_overlay)); app.run(); } @@ -79,20 +73,9 @@ fn setup(mut commands: Commands, asset_server: Res) { Label, )); - #[cfg(feature = "bevy_dev_tools")] // Debug overlay text parent.spawn(( - Text::new("Press Space to enable debug outlines."), - TextFont { - font: asset_server.load("fonts/FiraSans-Bold.ttf"), - ..default() - }, - Label, - )); - - #[cfg(not(feature = "bevy_dev_tools"))] - parent.spawn(( - Text::new("Try enabling feature \"bevy_dev_tools\"."), + Text::new("Press Space to toggle debug outlines."), TextFont { font: asset_server.load("fonts/FiraSans-Bold.ttf"), ..default() @@ -347,11 +330,10 @@ fn setup(mut commands: Commands, asset_server: Res) { }); } -#[cfg(feature = "bevy_dev_tools")] // The system that will enable/disable the debug outlines around the nodes -fn toggle_overlay( +fn toggle_debug_overlay( input: Res>, - mut options: ResMut, + mut options: ResMut, ) { info_once!("The debug outlines are enabled, press Space to turn them on/off"); if input.just_pressed(KeyCode::Space) { From 3059680ed9a0d102329ebe5ef87051271c3a2946 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Sat, 7 Dec 2024 13:01:51 +0000 Subject: [PATCH 05/13] Removed unneeded dependencies --- crates/bevy_dev_tools/Cargo.toml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/crates/bevy_dev_tools/Cargo.toml b/crates/bevy_dev_tools/Cargo.toml index 315d7d9923d18..4e635ddab6a10 100644 --- a/crates/bevy_dev_tools/Cargo.toml +++ b/crates/bevy_dev_tools/Cargo.toml @@ -20,20 +20,14 @@ bevy_core = { path = "../bevy_core", version = "0.15.0-dev" } bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.15.0-dev" } bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.15.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev" } -bevy_gizmos = { path = "../bevy_gizmos", version = "0.15.0-dev", features = [ - "bevy_render", -] } bevy_hierarchy = { path = "../bevy_hierarchy", version = "0.15.0-dev" } bevy_input = { path = "../bevy_input", version = "0.15.0-dev" } -bevy_math = { path = "../bevy_math", version = "0.15.0-dev" } bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev" } bevy_render = { path = "../bevy_render", version = "0.15.0-dev" } bevy_time = { path = "../bevy_time", version = "0.15.0-dev" } -bevy_transform = { path = "../bevy_transform", version = "0.15.0-dev" } bevy_text = { path = "../bevy_text", version = "0.15.0-dev" } bevy_ui = { path = "../bevy_ui", version = "0.15.0-dev" } bevy_utils = { path = "../bevy_utils", version = "0.15.0-dev" } -bevy_window = { path = "../bevy_window", version = "0.15.0-dev" } bevy_state = { path = "../bevy_state", version = "0.15.0-dev" } # other From 1b40ed00f9242e0667d5d8779ce9ece4bdb2d77e Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Sat, 7 Dec 2024 13:03:53 +0000 Subject: [PATCH 06/13] Removed more unneeded deps --- crates/bevy_dev_tools/Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/bevy_dev_tools/Cargo.toml b/crates/bevy_dev_tools/Cargo.toml index 4e635ddab6a10..8a66ac3538756 100644 --- a/crates/bevy_dev_tools/Cargo.toml +++ b/crates/bevy_dev_tools/Cargo.toml @@ -16,13 +16,10 @@ bevy_ci_testing = ["serde", "ron"] bevy_app = { path = "../bevy_app", version = "0.15.0-dev" } bevy_asset = { path = "../bevy_asset", version = "0.15.0-dev" } bevy_color = { path = "../bevy_color", version = "0.15.0-dev" } -bevy_core = { path = "../bevy_core", version = "0.15.0-dev" } -bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.15.0-dev" } bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.15.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev" } bevy_hierarchy = { path = "../bevy_hierarchy", version = "0.15.0-dev" } bevy_input = { path = "../bevy_input", version = "0.15.0-dev" } -bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev" } bevy_render = { path = "../bevy_render", version = "0.15.0-dev" } bevy_time = { path = "../bevy_time", version = "0.15.0-dev" } bevy_text = { path = "../bevy_text", version = "0.15.0-dev" } From 2d8aa9c6a9318ef06bb0936a1aa205c09ab2e576 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Sat, 7 Dec 2024 13:29:01 +0000 Subject: [PATCH 07/13] Added `bevy_ui_debug_overlay` feature gate --- Cargo.toml | 3 +++ crates/bevy_ui/Cargo.toml | 2 ++ examples/testbed/ui.rs | 17 ++++++++++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4a4a005e2fc15..ccca73fc4e577 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -156,6 +156,9 @@ bevy_ui_picking_backend = [ "bevy_internal/bevy_ui_picking_backend", ] +# Provides a debug overlay for bevy UI +bevy_ui_debug_overlay = [] + # Force dynamic linking, which improves iterative compile times dynamic_linking = ["dep:bevy_dylib", "bevy_internal/dynamic_linking"] diff --git a/crates/bevy_ui/Cargo.toml b/crates/bevy_ui/Cargo.toml index 70478d8fe7fb2..ed0445dceb6fd 100644 --- a/crates/bevy_ui/Cargo.toml +++ b/crates/bevy_ui/Cargo.toml @@ -43,8 +43,10 @@ smallvec = "1.11" accesskit = "0.17" [features] +default = [] serialize = ["serde", "smallvec/serde", "bevy_math/serialize"] bevy_ui_picking_backend = ["bevy_picking"] +bevy_ui_debug_overlay = [] # Experimental features ghost_nodes = [] diff --git a/examples/testbed/ui.rs b/examples/testbed/ui.rs index b777b9ecda7b5..2f34535995799 100644 --- a/examples/testbed/ui.rs +++ b/examples/testbed/ui.rs @@ -16,7 +16,10 @@ fn main() { let mut app = App::new(); app.add_plugins(DefaultPlugins) .add_systems(Startup, setup) - .add_systems(Update, (update_scroll_position, toggle_debug_overlay)); + .add_systems(Update, update_scroll_position); + + #[cfg(feature = "bevy_ui_debug_overlay")] + app.add_systems(Update, toggle_debug_overlay); app.run(); } @@ -73,6 +76,7 @@ fn setup(mut commands: Commands, asset_server: Res) { Label, )); + #[cfg(feature = "bevy_ui_debug_overlay")] // Debug overlay text parent.spawn(( Text::new("Press Space to toggle debug outlines."), @@ -82,6 +86,16 @@ fn setup(mut commands: Commands, asset_server: Res) { }, Label, )); + + #[cfg(not(feature = "bevy_ui_debug_overlay"))] + parent.spawn(( + Text::new("Try enabling feature \"bevy_ui_debug_overlay\"."), + TextFont { + font: asset_server.load("fonts/FiraSans-Bold.ttf"), + ..default() + }, + Label, + )); }); }); // right vertical fill @@ -330,6 +344,7 @@ fn setup(mut commands: Commands, asset_server: Res) { }); } +#[cfg(feature = "bevy_ui_debug_overlay")] // The system that will enable/disable the debug outlines around the nodes fn toggle_debug_overlay( input: Res>, From a20201a7fbabe5f06dc2db94412ab475362f2fee Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Sat, 7 Dec 2024 13:44:28 +0000 Subject: [PATCH 08/13] Use a smaller font for the enable feature prompt to avoid overflow. --- examples/testbed/ui.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/testbed/ui.rs b/examples/testbed/ui.rs index 2f34535995799..a3a53b2ab9968 100644 --- a/examples/testbed/ui.rs +++ b/examples/testbed/ui.rs @@ -92,6 +92,7 @@ fn setup(mut commands: Commands, asset_server: Res) { Text::new("Try enabling feature \"bevy_ui_debug_overlay\"."), TextFont { font: asset_server.load("fonts/FiraSans-Bold.ttf"), + font_size: 16.0, ..default() }, Label, From 3dbbf80e2a15cc0f4dffe3419f7434eca49b9d08 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Sat, 7 Dec 2024 13:51:13 +0000 Subject: [PATCH 09/13] Added back removed `bevy_window` dependency --- crates/bevy_dev_tools/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bevy_dev_tools/Cargo.toml b/crates/bevy_dev_tools/Cargo.toml index 8a66ac3538756..1d426853cee80 100644 --- a/crates/bevy_dev_tools/Cargo.toml +++ b/crates/bevy_dev_tools/Cargo.toml @@ -25,6 +25,7 @@ bevy_time = { path = "../bevy_time", version = "0.15.0-dev" } bevy_text = { path = "../bevy_text", version = "0.15.0-dev" } bevy_ui = { path = "../bevy_ui", version = "0.15.0-dev" } bevy_utils = { path = "../bevy_utils", version = "0.15.0-dev" } +bevy_window = { path = "../bevy_window", version = "0.15.0-dev" } bevy_state = { path = "../bevy_state", version = "0.15.0-dev" } # other From 0c5d63357f6187ec685e51b9fbec3060f759dd60 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Sat, 7 Dec 2024 13:56:20 +0000 Subject: [PATCH 10/13] Renamed the `bevy_ui_debug_overlay` feature to `bevy_ui_debug` --- Cargo.toml | 2 +- crates/bevy_ui/Cargo.toml | 2 +- examples/testbed/ui.rs | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ccca73fc4e577..79d7b483e550b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -157,7 +157,7 @@ bevy_ui_picking_backend = [ ] # Provides a debug overlay for bevy UI -bevy_ui_debug_overlay = [] +bevy_ui_debug = [] # Force dynamic linking, which improves iterative compile times dynamic_linking = ["dep:bevy_dylib", "bevy_internal/dynamic_linking"] diff --git a/crates/bevy_ui/Cargo.toml b/crates/bevy_ui/Cargo.toml index ed0445dceb6fd..2ea7216f29b95 100644 --- a/crates/bevy_ui/Cargo.toml +++ b/crates/bevy_ui/Cargo.toml @@ -46,7 +46,7 @@ accesskit = "0.17" default = [] serialize = ["serde", "smallvec/serde", "bevy_math/serialize"] bevy_ui_picking_backend = ["bevy_picking"] -bevy_ui_debug_overlay = [] +bevy_ui_debug = [] # Experimental features ghost_nodes = [] diff --git a/examples/testbed/ui.rs b/examples/testbed/ui.rs index a3a53b2ab9968..b30dab0e5c703 100644 --- a/examples/testbed/ui.rs +++ b/examples/testbed/ui.rs @@ -76,7 +76,7 @@ fn setup(mut commands: Commands, asset_server: Res) { Label, )); - #[cfg(feature = "bevy_ui_debug_overlay")] + #[cfg(feature = "bevy_ui_debug")] // Debug overlay text parent.spawn(( Text::new("Press Space to toggle debug outlines."), @@ -87,12 +87,11 @@ fn setup(mut commands: Commands, asset_server: Res) { Label, )); - #[cfg(not(feature = "bevy_ui_debug_overlay"))] + #[cfg(not(feature = "bevy_ui_debug"))] parent.spawn(( - Text::new("Try enabling feature \"bevy_ui_debug_overlay\"."), + Text::new("Try enabling feature \"bevy_ui_debug\"."), TextFont { font: asset_server.load("fonts/FiraSans-Bold.ttf"), - font_size: 16.0, ..default() }, Label, From cc68bd299cbb94dc29aaca63994bb0b49f975f36 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Sat, 7 Dec 2024 14:38:50 +0000 Subject: [PATCH 11/13] Renamed feature to "bevy_ui_debug" --- Cargo.toml | 2 +- crates/bevy_internal/Cargo.toml | 3 +++ crates/bevy_ui/src/lib.rs | 6 +++++- crates/bevy_ui/src/render/mod.rs | 14 +++++++++----- examples/testbed/ui.rs | 9 +++------ 5 files changed, 21 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 79d7b483e550b..e17c737f90f93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -157,7 +157,7 @@ bevy_ui_picking_backend = [ ] # Provides a debug overlay for bevy UI -bevy_ui_debug = [] +bevy_ui_debug = ["bevy_internal/bevy_ui_debug"] # Force dynamic linking, which improves iterative compile times dynamic_linking = ["dep:bevy_dylib", "bevy_internal/dynamic_linking"] diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index c6f3fed2d9eed..3acc57d16cbf0 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -235,6 +235,9 @@ bevy_sprite_picking_backend = [ # Provides a UI picking backend bevy_ui_picking_backend = ["bevy_picking", "bevy_ui/bevy_ui_picking_backend"] +# Provides a UI debug overlay +bevy_ui_debug = ["bevy_ui?/bevy_ui_debug"] + # Enable support for the ios_simulator by downgrading some rendering capabilities ios_simulator = ["bevy_pbr?/ios_simulator", "bevy_render?/ios_simulator"] diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 35bd8677c4a15..4039930b7654c 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -37,16 +37,19 @@ pub use focus::*; pub use geometry::*; pub use layout::*; pub use measurement::*; -use render::debug_overlay::UiDebugOptions; pub use render::*; pub use ui_material::*; pub use ui_node::*; + use widget::{ImageNode, ImageNodeSize}; /// The UI prelude. /// /// This includes the most common types in this crate, re-exported for your convenience. pub mod prelude { + #[doc(hidden)] + #[cfg(feature = "bevy_ui_debug")] + pub use crate::render::UiDebugOptions; #[allow(deprecated)] #[doc(hidden)] pub use crate::widget::TextBundle; @@ -231,6 +234,7 @@ impl Plugin for UiPlugin { return; } + #[cfg(feature = "bevy_ui_debug")] app.init_resource::(); build_ui_render(app); diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 540cc618322e4..b7801584feb05 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -1,10 +1,12 @@ pub mod box_shadow; -pub mod debug_overlay; mod pipeline; mod render_pass; mod ui_material_pipeline; pub mod ui_texture_slice_pipeline; +#[cfg(feature = "bevy_ui_debug")] +mod debug_overlay; + use crate::widget::ImageNode; use crate::{ experimental::UiChildren, BackgroundColor, BorderColor, BoxShadowSamples, CalculatedClip, @@ -42,7 +44,8 @@ use bevy_render::{ }; use bevy_sprite::TextureAtlasLayout; use bevy_sprite::{BorderRect, SpriteAssetEvents}; -use debug_overlay::extract_debug_overlay; +#[cfg(feature = "bevy_ui_debug")] +pub use debug_overlay::UiDebugOptions; use crate::{Display, Node}; use bevy_text::{ComputedTextBlock, PositionedGlyph, TextColor, TextLayoutInfo}; @@ -100,7 +103,7 @@ pub enum RenderUiSystem { ExtractTextureSlice, ExtractBorders, ExtractText, - ExtractDebugOverlay, + ExtractDebug, } pub fn build_ui_render(app: &mut App) { @@ -128,7 +131,7 @@ pub fn build_ui_render(app: &mut App) { RenderUiSystem::ExtractTextureSlice, RenderUiSystem::ExtractBorders, RenderUiSystem::ExtractText, - RenderUiSystem::ExtractDebugOverlay, + RenderUiSystem::ExtractDebug, ) .chain(), ) @@ -140,7 +143,8 @@ pub fn build_ui_render(app: &mut App) { extract_uinode_images.in_set(RenderUiSystem::ExtractImages), extract_uinode_borders.in_set(RenderUiSystem::ExtractBorders), extract_text_sections.in_set(RenderUiSystem::ExtractText), - extract_debug_overlay.in_set(RenderUiSystem::ExtractDebugOverlay), + #[cfg(feature = "bevy_ui_debug")] + debug_overlay::extract_debug_overlay.in_set(RenderUiSystem::ExtractDebug), ), ) .add_systems( diff --git a/examples/testbed/ui.rs b/examples/testbed/ui.rs index b30dab0e5c703..677576cf12bf2 100644 --- a/examples/testbed/ui.rs +++ b/examples/testbed/ui.rs @@ -18,7 +18,7 @@ fn main() { .add_systems(Startup, setup) .add_systems(Update, update_scroll_position); - #[cfg(feature = "bevy_ui_debug_overlay")] + #[cfg(feature = "bevy_ui_debug")] app.add_systems(Update, toggle_debug_overlay); app.run(); @@ -344,12 +344,9 @@ fn setup(mut commands: Commands, asset_server: Res) { }); } -#[cfg(feature = "bevy_ui_debug_overlay")] +#[cfg(feature = "bevy_ui_debug")] // The system that will enable/disable the debug outlines around the nodes -fn toggle_debug_overlay( - input: Res>, - mut options: ResMut, -) { +fn toggle_debug_overlay(input: Res>, mut options: ResMut) { info_once!("The debug outlines are enabled, press Space to turn them on/off"); if input.just_pressed(KeyCode::Space) { // The toggle method will enable the debug_overlay if disabled and disable if enabled From 713146cc6f595cfd5555ed19e28a0c97924b8a90 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Sat, 7 Dec 2024 15:36:27 +0000 Subject: [PATCH 12/13] build-templated-pages --- docs/cargo_features.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/cargo_features.md b/docs/cargo_features.md index 1cc83b9e1102f..c07158ef2e1d4 100644 --- a/docs/cargo_features.md +++ b/docs/cargo_features.md @@ -62,6 +62,7 @@ The default feature set enables most of the expected features of a game engine, |bevy_debug_stepping|Enable stepping-based debugging of Bevy systems| |bevy_dev_tools|Provides a collection of developer tools| |bevy_remote|Enable the Bevy Remote Protocol| +|bevy_ui_debug|Provides a debug overlay for bevy UI| |bmp|BMP image format support| |dds|DDS compressed texture support| |debug_glam_assert|Enable assertions in debug builds to check the validity of parameters passed to glam| From df2fa77ec742f7034731593fe7b286ba49580777 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Tue, 10 Dec 2024 23:09:51 +0000 Subject: [PATCH 13/13] Added doc comments for `UiDebugOptions` --- crates/bevy_ui/src/render/debug_overlay.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/bevy_ui/src/render/debug_overlay.rs b/crates/bevy_ui/src/render/debug_overlay.rs index 9f3cc584fd3bb..4d716bde8815e 100644 --- a/crates/bevy_ui/src/render/debug_overlay.rs +++ b/crates/bevy_ui/src/render/debug_overlay.rs @@ -23,9 +23,12 @@ use super::ExtractedUiNode; use super::ExtractedUiNodes; use super::NodeType; +/// Configuration for the UI debug overlay #[derive(Resource)] pub struct UiDebugOptions { + /// Set to true to enable the UI debug overlay pub enabled: bool, + /// Width of the overlay's lines in logical pixels pub line_width: f32, }