diff --git a/Cargo.toml b/Cargo.toml index 7f55ce0e..aef0d9f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ qr_code = ["iced_widget/qr_code"] # Enables lazy widgets lazy = ["iced_widget/lazy"] # Enables a debug view in native platforms (press F12) -debug = ["iced_winit/debug"] +debug = ["iced_debug/enable"] # Enables `tokio` as the `executor::Default` on native platforms tokio = ["iced_futures/tokio"] # Enables `async-std` as the `executor::Default` on native platforms @@ -58,6 +58,7 @@ fira-sans = ["iced_renderer/fira-sans"] [dependencies] iced_core.workspace = true +iced_debug.workspace = true iced_futures.workspace = true iced_renderer.workspace = true iced_widget.workspace = true @@ -85,11 +86,13 @@ strip = "debuginfo" [workspace] members = [ "core", + "debug", "futures", "graphics", "highlighter", "renderer", "runtime", + "sentinel", "style", "tiny_skia", "wgpu", @@ -111,11 +114,13 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"] [workspace.dependencies] iced = { version = "0.13.0-dev", path = "." } iced_core = { version = "0.13.0-dev", path = "core" } +iced_debug = { version = "0.13.0-dev", path = "debug" } iced_futures = { version = "0.13.0-dev", path = "futures" } iced_graphics = { version = "0.13.0-dev", path = "graphics" } iced_highlighter = { version = "0.13.0-dev", path = "highlighter" } iced_renderer = { version = "0.13.0-dev", path = "renderer" } iced_runtime = { version = "0.13.0-dev", path = "runtime" } +iced_sentinel = { version = "0.13.0-dev", path = "sentinel" } iced_style = { version = "0.13.0-dev", path = "style" } iced_tiny_skia = { version = "0.13.0-dev", path = "tiny_skia" } iced_wgpu = { version = "0.13.0-dev", path = "wgpu" } @@ -145,6 +150,9 @@ qrcode = { version = "0.13", default-features = false } raw-window-handle = "0.6" resvg = "0.36" rustc-hash = "1.0" +serde = "1.0" +serde_json = "1.0" +semver = "1.0" smol = "1.0" smol_str = "0.2" softbuffer = "0.4" diff --git a/debug/Cargo.toml b/debug/Cargo.toml new file mode 100644 index 00000000..77cabf6a --- /dev/null +++ b/debug/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "iced_debug" +description = "A pluggable API for debugging iced applications" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true +categories.workspace = true +keywords.workspace = true + +[features] +enable = ["iced_sentinel", "once_cell"] + +[dependencies] +iced_core.workspace = true + +iced_sentinel.workspace = true +iced_sentinel.optional = true + +once_cell.workspace = true +once_cell.optional = true diff --git a/debug/src/lib.rs b/debug/src/lib.rs new file mode 100644 index 00000000..9edf0073 --- /dev/null +++ b/debug/src/lib.rs @@ -0,0 +1,162 @@ +pub use iced_core as core; + +pub use internal::Timer; + +pub fn open_axe() {} + +pub fn log_message(_message: &impl std::fmt::Debug) {} + +pub fn boot_time() -> Timer { + internal::boot_time() +} + +pub fn update_time() -> Timer { + internal::update_time() +} + +pub fn view_time() -> Timer { + internal::view_time() +} + +pub fn layout_time() -> Timer { + internal::layout_time() +} + +pub fn interact_time() -> Timer { + internal::interact_time() +} + +pub fn draw_time() -> Timer { + internal::draw_time() +} + +pub fn render_time() -> Timer { + internal::render_time() +} + +pub fn time(name: impl AsRef) -> Timer { + internal::time(name) +} + +#[cfg(feature = "enable")] +mod internal { + use crate::core::time::Instant; + + use iced_sentinel::client::{self, Client}; + use iced_sentinel::timing::{self, Timing}; + use iced_sentinel::Report; + + use once_cell::sync::Lazy; + use std::sync::{Mutex, MutexGuard}; + + pub fn boot_time() -> Timer { + timer(timing::Stage::Boot) + } + + pub fn update_time() -> Timer { + timer(timing::Stage::Update) + } + + pub fn view_time() -> Timer { + timer(timing::Stage::View) + } + + pub fn layout_time() -> Timer { + timer(timing::Stage::Layout) + } + + pub fn interact_time() -> Timer { + timer(timing::Stage::Interact) + } + + pub fn draw_time() -> Timer { + timer(timing::Stage::Draw) + } + + pub fn render_time() -> Timer { + timer(timing::Stage::Render) + } + + pub fn time(name: impl AsRef) -> Timer { + timer(timing::Stage::Custom(name.as_ref().to_owned())) + } + + fn timer(stage: timing::Stage) -> Timer { + Timer { + stage, + start: Instant::now(), + } + } + + #[derive(Debug)] + pub struct Timer { + stage: timing::Stage, + start: Instant, + } + + impl Timer { + pub fn finish(self) { + lock().sentinel.report(Report::Timing(Timing { + stage: self.stage, + duration: self.start.elapsed(), + })); + } + } + + #[derive(Debug)] + struct Debug { + sentinel: Client, + } + + fn lock() -> MutexGuard<'static, Debug> { + static DEBUG: Lazy> = Lazy::new(|| { + Mutex::new(Debug { + sentinel: client::connect(), + }) + }); + + DEBUG.lock().expect("Acquire debug lock") + } +} + +#[cfg(not(feature = "enable"))] +mod internal { + pub fn boot_time() -> Timer { + Timer + } + + pub fn update_time() -> Timer { + Timer + } + + pub fn view_time() -> Timer { + Timer + } + + pub fn layout_time() -> Timer { + Timer + } + + pub fn interact_time() -> Timer { + Timer + } + + pub fn draw_time() -> Timer { + Timer + } + + pub fn render_time() -> Timer { + Timer + } + + pub fn time(_name: impl AsRef) -> Timer { + Timer + } + + #[derive(Debug)] + pub struct Timer; + + impl Timer { + pub fn finish(self) {} + } +} diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs index 0e2e53ac..63efcbea 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration/src/main.rs @@ -13,7 +13,6 @@ use iced_winit::core::window; use iced_winit::core::{Color, Font, Pixels, Size}; use iced_winit::futures; use iced_winit::runtime::program; -use iced_winit::runtime::Debug; use iced_winit::style::Theme; use iced_winit::winit; use iced_winit::Clipboard; @@ -155,19 +154,14 @@ pub fn main() -> Result<(), Box> { let controls = Controls::new(); // Initialize iced - let mut debug = Debug::new(); let mut renderer = Renderer::new( Backend::new(&adapter, &device, &queue, Settings::default(), format), Font::default(), Pixels(16.0), ); - let mut state = program::State::new( - controls, - viewport.logical_size(), - &mut renderer, - &mut debug, - ); + let mut state = + program::State::new(controls, viewport.logical_size(), &mut renderer); // Run event loop event_loop.run(move |event, window_target| { @@ -239,7 +233,6 @@ pub fn main() -> Result<(), Box> { &view, primitive, &viewport, - &debug.overlay(), ); }); @@ -315,7 +308,6 @@ pub fn main() -> Result<(), Box> { text_color: Color::WHITE, }, &mut clipboard, - &mut debug, ); // and request a redraw diff --git a/graphics/src/compositor.rs b/graphics/src/compositor.rs index 0188f4d8..786fd8a4 100644 --- a/graphics/src/compositor.rs +++ b/graphics/src/compositor.rs @@ -55,26 +55,24 @@ pub trait Compositor: Sized { /// /// [`Renderer`]: Self::Renderer /// [`Surface`]: Self::Surface - fn present>( + fn present( &mut self, renderer: &mut Self::Renderer, surface: &mut Self::Surface, viewport: &Viewport, background_color: Color, - overlay: &[T], ) -> Result<(), SurfaceError>; /// Screenshots the current [`Renderer`] primitives to an offscreen texture, and returns the bytes of /// the texture ordered as `RGBA` in the `sRGB` color space. /// /// [`Renderer`]: Self::Renderer - fn screenshot>( + fn screenshot( &mut self, renderer: &mut Self::Renderer, surface: &mut Self::Surface, viewport: &Viewport, background_color: Color, - overlay: &[T], ) -> Vec; } diff --git a/renderer/src/compositor.rs b/renderer/src/compositor.rs index dc2c50ff..0777f6e4 100644 --- a/renderer/src/compositor.rs +++ b/renderer/src/compositor.rs @@ -101,13 +101,12 @@ impl crate::graphics::Compositor for Compositor { } } - fn present>( + fn present( &mut self, renderer: &mut Self::Renderer, surface: &mut Self::Surface, viewport: &Viewport, background_color: Color, - overlay: &[T], ) -> Result<(), SurfaceError> { match (self, renderer, surface) { ( @@ -121,7 +120,6 @@ impl crate::graphics::Compositor for Compositor { primitives, viewport, background_color, - overlay, ) }), #[cfg(feature = "wgpu")] @@ -137,7 +135,6 @@ impl crate::graphics::Compositor for Compositor { primitives, viewport, background_color, - overlay, ) }), #[allow(unreachable_patterns)] @@ -148,13 +145,12 @@ impl crate::graphics::Compositor for Compositor { } } - fn screenshot>( + fn screenshot( &mut self, renderer: &mut Self::Renderer, surface: &mut Self::Surface, viewport: &Viewport, background_color: Color, - overlay: &[T], ) -> Vec { match (self, renderer, surface) { ( @@ -168,7 +164,6 @@ impl crate::graphics::Compositor for Compositor { primitives, viewport, background_color, - overlay, ) }), #[cfg(feature = "wgpu")] @@ -183,7 +178,6 @@ impl crate::graphics::Compositor for Compositor { primitives, viewport, background_color, - overlay, ) }), #[allow(unreachable_patterns)] diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 3a47a971..6d49ef93 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -11,11 +11,12 @@ categories.workspace = true keywords.workspace = true [features] -debug = [] multi-window = [] [dependencies] iced_core.workspace = true +iced_debug.workspace = true + iced_futures.workspace = true iced_futures.features = ["thread-pool"] diff --git a/runtime/src/debug/basic.rs b/runtime/src/debug/basic.rs deleted file mode 100644 index 4c994a2f..00000000 --- a/runtime/src/debug/basic.rs +++ /dev/null @@ -1,220 +0,0 @@ -#![allow(missing_docs)] -use crate::core::time; - -use std::collections::VecDeque; - -/// A bunch of time measurements for debugging purposes. -#[derive(Debug)] -pub struct Debug { - is_enabled: bool, - - startup_start: time::Instant, - startup_duration: time::Duration, - - update_start: time::Instant, - update_durations: TimeBuffer, - - view_start: time::Instant, - view_durations: TimeBuffer, - - layout_start: time::Instant, - layout_durations: TimeBuffer, - - event_start: time::Instant, - event_durations: TimeBuffer, - - draw_start: time::Instant, - draw_durations: TimeBuffer, - - render_start: time::Instant, - render_durations: TimeBuffer, - - message_count: usize, - last_messages: VecDeque, -} - -impl Debug { - /// Creates a new [`struct@Debug`]. - pub fn new() -> Self { - let now = time::Instant::now(); - - Self { - is_enabled: false, - startup_start: now, - startup_duration: time::Duration::from_secs(0), - - update_start: now, - update_durations: TimeBuffer::new(200), - - view_start: now, - view_durations: TimeBuffer::new(200), - - layout_start: now, - layout_durations: TimeBuffer::new(200), - - event_start: now, - event_durations: TimeBuffer::new(200), - - draw_start: now, - draw_durations: TimeBuffer::new(200), - - render_start: now, - render_durations: TimeBuffer::new(50), - - message_count: 0, - last_messages: VecDeque::new(), - } - } - - pub fn toggle(&mut self) { - self.is_enabled = !self.is_enabled; - } - - pub fn startup_started(&mut self) { - self.startup_start = time::Instant::now(); - } - - pub fn startup_finished(&mut self) { - self.startup_duration = self.startup_start.elapsed(); - } - - pub fn update_started(&mut self) { - self.update_start = time::Instant::now(); - } - - pub fn update_finished(&mut self) { - self.update_durations.push(self.update_start.elapsed()); - } - - pub fn view_started(&mut self) { - self.view_start = time::Instant::now(); - } - - pub fn view_finished(&mut self) { - self.view_durations.push(self.view_start.elapsed()); - } - - pub fn layout_started(&mut self) { - self.layout_start = time::Instant::now(); - } - - pub fn layout_finished(&mut self) { - self.layout_durations.push(self.layout_start.elapsed()); - } - - pub fn event_processing_started(&mut self) { - self.event_start = time::Instant::now(); - } - - pub fn event_processing_finished(&mut self) { - self.event_durations.push(self.event_start.elapsed()); - } - - pub fn draw_started(&mut self) { - self.draw_start = time::Instant::now(); - } - - pub fn draw_finished(&mut self) { - self.draw_durations.push(self.draw_start.elapsed()); - } - - pub fn render_started(&mut self) { - self.render_start = time::Instant::now(); - } - - pub fn render_finished(&mut self) { - self.render_durations.push(self.render_start.elapsed()); - } - - pub fn log_message(&mut self, message: &Message) { - self.last_messages.push_back(format!("{message:?}")); - - if self.last_messages.len() > 10 { - let _ = self.last_messages.pop_front(); - } - - self.message_count += 1; - } - - pub fn overlay(&self) -> Vec { - if !self.is_enabled { - return Vec::new(); - } - - let mut lines = Vec::new(); - - fn key_value(key: &str, value: T) -> String { - format!("{key} {value:?}") - } - - lines.push(format!( - "{} {} - {}", - env!("CARGO_PKG_NAME"), - env!("CARGO_PKG_VERSION"), - env!("CARGO_PKG_REPOSITORY"), - )); - lines.push(key_value("Startup:", self.startup_duration)); - lines.push(key_value("Update:", self.update_durations.average())); - lines.push(key_value("View:", self.view_durations.average())); - lines.push(key_value("Layout:", self.layout_durations.average())); - lines.push(key_value( - "Event processing:", - self.event_durations.average(), - )); - lines.push(key_value( - "Primitive generation:", - self.draw_durations.average(), - )); - lines.push(key_value("Render:", self.render_durations.average())); - lines.push(key_value("Message count:", self.message_count)); - lines.push(String::from("Last messages:")); - lines.extend(self.last_messages.iter().map(|msg| { - if msg.len() <= 100 { - format!(" {msg}") - } else { - format!(" {msg:.100}...") - } - })); - - lines - } -} - -impl Default for Debug { - fn default() -> Self { - Self::new() - } -} - -#[derive(Debug)] -struct TimeBuffer { - head: usize, - size: usize, - contents: Vec, -} - -impl TimeBuffer { - fn new(capacity: usize) -> TimeBuffer { - TimeBuffer { - head: 0, - size: 0, - contents: vec![time::Duration::from_secs(0); capacity], - } - } - - fn push(&mut self, duration: time::Duration) { - self.head = (self.head + 1) % self.contents.len(); - self.contents[self.head] = duration; - self.size = (self.size + 1).min(self.contents.len()); - } - - fn average(&self) -> time::Duration { - let sum: time::Duration = if self.size == self.contents.len() { - self.contents[..].iter().sum() - } else { - self.contents[..self.size].iter().sum() - }; - - sum / self.size.max(1) as u32 - } -} diff --git a/runtime/src/debug/null.rs b/runtime/src/debug/null.rs deleted file mode 100644 index 2db0eebb..00000000 --- a/runtime/src/debug/null.rs +++ /dev/null @@ -1,47 +0,0 @@ -#![allow(missing_docs)] -#[derive(Debug, Default)] -pub struct Debug; - -impl Debug { - pub fn new() -> Self { - Self - } - - pub fn startup_started(&mut self) {} - - pub fn startup_finished(&mut self) {} - - pub fn update_started(&mut self) {} - - pub fn update_finished(&mut self) {} - - pub fn view_started(&mut self) {} - - pub fn view_finished(&mut self) {} - - pub fn layout_started(&mut self) {} - - pub fn layout_finished(&mut self) {} - - pub fn event_processing_started(&mut self) {} - - pub fn event_processing_finished(&mut self) {} - - pub fn draw_started(&mut self) {} - - pub fn draw_finished(&mut self) {} - - pub fn render_started(&mut self) {} - - pub fn render_finished(&mut self) {} - - pub fn log_message( - &mut self, - _message: &Message, - ) { - } - - pub fn overlay(&self) -> Vec { - Vec::new() - } -} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 5c2836a5..18059ef5 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -29,20 +29,11 @@ pub mod window; #[cfg(feature = "multi-window")] pub mod multi_window; -// We disable debug capabilities on release builds unless the `debug` feature -// is explicitly enabled. -#[cfg(feature = "debug")] -#[path = "debug/basic.rs"] -mod debug; -#[cfg(not(feature = "debug"))] -#[path = "debug/null.rs"] -mod debug; - pub use iced_core as core; +pub use iced_debug as debug; pub use iced_futures as futures; pub use command::Command; -pub use debug::Debug; pub use font::Font; pub use program::Program; pub use user_interface::UserInterface; diff --git a/runtime/src/multi_window/state.rs b/runtime/src/multi_window/state.rs index afd04519..215c87e1 100644 --- a/runtime/src/multi_window/state.rs +++ b/runtime/src/multi_window/state.rs @@ -4,8 +4,9 @@ use crate::core::mouse; use crate::core::renderer; use crate::core::widget::operation::{self, Operation}; use crate::core::{Clipboard, Size}; +use crate::debug; use crate::user_interface::{self, UserInterface}; -use crate::{Command, Debug, Program}; +use crate::{Command, Program}; /// The execution state of a multi-window [`Program`]. It leverages caching, event /// processing, and rendering primitive storage. @@ -27,18 +28,12 @@ where { /// Creates a new [`State`] with the provided [`Program`], initializing its /// primitive with the given logical bounds and renderer. - pub fn new( - program: P, - bounds: Size, - renderer: &mut P::Renderer, - debug: &mut Debug, - ) -> Self { + pub fn new(program: P, bounds: Size, renderer: &mut P::Renderer) -> Self { let user_interface = build_user_interface( &program, user_interface::Cache::default(), renderer, bounds, - debug, ); let caches = Some(vec![user_interface.into_cache()]); @@ -95,17 +90,15 @@ where theme: &P::Theme, style: &renderer::Style, clipboard: &mut dyn Clipboard, - debug: &mut Debug, ) -> (Vec, Option>) { let mut user_interfaces = build_user_interfaces( &self.program, self.caches.take().unwrap(), renderer, bounds, - debug, ); - debug.event_processing_started(); + let interact_timer = debug::interact_time(); let mut messages = Vec::new(); let uncaptured_events = user_interfaces.iter_mut().fold( @@ -135,17 +128,15 @@ where self.queued_events.clear(); messages.append(&mut self.queued_messages); - debug.event_processing_finished(); + drop(interact_timer); let commands = if messages.is_empty() { - debug.draw_started(); - + let draw_timer = debug::draw_time(); for ui in &mut user_interfaces { self.mouse_interaction = ui.draw(renderer, theme, style, cursor); } - - debug.draw_finished(); + drop(draw_timer); self.caches = Some( user_interfaces @@ -164,11 +155,11 @@ where drop(user_interfaces); let commands = Command::batch(messages.into_iter().map(|msg| { - debug.log_message(&msg); + debug::log_message(&msg); - debug.update_started(); + let update_timer = debug::update_time(); let command = self.program.update(msg); - debug.update_finished(); + drop(update_timer); command })); @@ -178,15 +169,14 @@ where temp_caches, renderer, bounds, - debug, ); - debug.draw_started(); + let draw_timer = debug::draw_time(); for ui in &mut user_interfaces { self.mouse_interaction = ui.draw(renderer, theme, style, cursor); } - debug.draw_finished(); + drop(draw_timer); self.caches = Some( user_interfaces @@ -207,14 +197,12 @@ where renderer: &mut P::Renderer, operations: impl Iterator>>, bounds: Size, - debug: &mut Debug, ) { let mut user_interfaces = build_user_interfaces( &self.program, self.caches.take().unwrap(), renderer, bounds, - debug, ); for operation in operations { @@ -251,13 +239,10 @@ fn build_user_interfaces<'a, P: Program>( mut caches: Vec, renderer: &mut P::Renderer, size: Size, - debug: &mut Debug, ) -> Vec> { caches .drain(..) - .map(|cache| { - build_user_interface(program, cache, renderer, size, debug) - }) + .map(|cache| build_user_interface(program, cache, renderer, size)) .collect() } @@ -266,15 +251,14 @@ fn build_user_interface<'a, P: Program>( cache: user_interface::Cache, renderer: &mut P::Renderer, size: Size, - debug: &mut Debug, ) -> UserInterface<'a, P::Message, P::Theme, P::Renderer> { - debug.view_started(); + let view_timer = debug::view_time(); let view = program.view(); - debug.view_finished(); + drop(view_timer); - debug.layout_started(); + let layout_timer = debug::layout_time(); let user_interface = UserInterface::build(view, size, cache, renderer); - debug.layout_finished(); + drop(layout_timer); user_interface } diff --git a/runtime/src/program/state.rs b/runtime/src/program/state.rs index d685b07c..0c9051d2 100644 --- a/runtime/src/program/state.rs +++ b/runtime/src/program/state.rs @@ -3,8 +3,9 @@ use crate::core::mouse; use crate::core::renderer; use crate::core::widget::operation::{self, Operation}; use crate::core::{Clipboard, Size}; +use crate::debug; use crate::user_interface::{self, UserInterface}; -use crate::{Command, Debug, Program}; +use crate::{Command, Program}; /// The execution state of a [`Program`]. It leverages caching, event /// processing, and rendering primitive storage. @@ -30,14 +31,12 @@ where mut program: P, bounds: Size, renderer: &mut P::Renderer, - debug: &mut Debug, ) -> Self { let user_interface = build_user_interface( &mut program, user_interface::Cache::default(), renderer, bounds, - debug, ); let cache = Some(user_interface.into_cache()); @@ -94,17 +93,15 @@ where theme: &P::Theme, style: &renderer::Style, clipboard: &mut dyn Clipboard, - debug: &mut Debug, ) -> (Vec, Option>) { let mut user_interface = build_user_interface( &mut self.program, self.cache.take().unwrap(), renderer, bounds, - debug, ); - debug.event_processing_started(); + let interact_timer = debug::interact_time(); let mut messages = Vec::new(); let (_, event_statuses) = user_interface.update( @@ -127,13 +124,13 @@ where self.queued_events.clear(); messages.append(&mut self.queued_messages); - debug.event_processing_finished(); + drop(interact_timer); let command = if messages.is_empty() { - debug.draw_started(); + let draw_timer = debug::draw_time(); self.mouse_interaction = user_interface.draw(renderer, theme, style, cursor); - debug.draw_finished(); + drop(draw_timer); self.cache = Some(user_interface.into_cache()); @@ -145,11 +142,11 @@ where let commands = Command::batch(messages.into_iter().map(|message| { - debug.log_message(&message); + debug::log_message(&message); - debug.update_started(); + let update_timer = debug::update_time(); let command = self.program.update(message); - debug.update_finished(); + drop(update_timer); command })); @@ -159,13 +156,12 @@ where temp_cache, renderer, bounds, - debug, ); - debug.draw_started(); + let draw_timer = debug::draw_time(); self.mouse_interaction = user_interface.draw(renderer, theme, style, cursor); - debug.draw_finished(); + drop(draw_timer); self.cache = Some(user_interface.into_cache()); @@ -181,14 +177,12 @@ where renderer: &mut P::Renderer, operations: impl Iterator>>, bounds: Size, - debug: &mut Debug, ) { let mut user_interface = build_user_interface( &mut self.program, self.cache.take().unwrap(), renderer, bounds, - debug, ); for operation in operations { @@ -218,15 +212,14 @@ fn build_user_interface<'a, P: Program>( cache: user_interface::Cache, renderer: &mut P::Renderer, size: Size, - debug: &mut Debug, ) -> UserInterface<'a, P::Message, P::Theme, P::Renderer> { - debug.view_started(); + let view_timer = debug::view_time(); let view = program.view(); - debug.view_finished(); + drop(view_timer); - debug.layout_started(); + let layout_timer = debug::layout_time(); let user_interface = UserInterface::build(view, size, cache, renderer); - debug.layout_finished(); + drop(layout_timer); user_interface } diff --git a/sentinel/Cargo.toml b/sentinel/Cargo.toml new file mode 100644 index 00000000..efb36659 --- /dev/null +++ b/sentinel/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "iced_sentinel" +description = "A client/server protocol to monitor and supervise iced applications" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true +categories.workspace = true +keywords.workspace = true + +[dependencies] +iced_core.workspace = true +serde_json.workspace = true +futures.workspace = true +log.workspace = true + +tokio.workspace = true +tokio.features = ["rt", "rt-multi-thread", "net", "sync", "time", "io-util", "macros"] + +serde.workspace = true +serde.features = ["derive"] + +semver.workspace = true +semver.features = ["serde"] diff --git a/sentinel/src/client.rs b/sentinel/src/client.rs new file mode 100644 index 00000000..3e173ecf --- /dev/null +++ b/sentinel/src/client.rs @@ -0,0 +1,80 @@ +use crate::{Input, Report, SOCKET_ADDRESS}; + +use tokio::io::{self, AsyncWriteExt}; +use tokio::net; +use tokio::sync::mpsc; +use tokio::time; + +#[derive(Debug, Clone)] +pub struct Client { + sender: mpsc::Sender, +} + +impl Client { + pub fn report(&mut self, report: Report) { + let _ = self.sender.try_send(Input::Reported(report)); + } +} + +#[must_use] +pub fn connect() -> Client { + let (sender, receiver) = mpsc::channel(1_000); + + std::thread::spawn(move || run(receiver)); + + Client { sender } +} + +#[tokio::main] +async fn run(mut receiver: mpsc::Receiver) { + let version = semver::Version::parse(env!("CARGO_PKG_VERSION")) + .expect("Parse package version"); + + loop { + match _connect().await { + Ok(mut stream) => { + let _ = send(&mut stream, Input::Connected(version)).await; + + while let Some(input) = receiver.recv().await { + if send(&mut stream, input).await.is_err() { + break; + } + } + + break; + } + Err(_) => { + time::sleep(time::Duration::from_secs(2)).await; + } + } + } +} + +async fn _connect() -> Result, io::Error> { + log::debug!("Attempting to connect sentinel to server..."); + let stream = net::TcpStream::connect(SOCKET_ADDRESS).await?; + + stream.set_nodelay(true)?; + stream.writable().await?; + + Ok(io::BufStream::new(stream)) +} + +async fn send( + stream: &mut io::BufStream, + input: Input, +) -> Result<(), io::Error> { + stream + .write_all( + format!( + "{}\n", + serde_json::to_string(&input).expect("Serialize input message") + ) + .as_bytes(), + ) + .await?; + + stream.flush().await?; + + Ok(()) +} diff --git a/sentinel/src/lib.rs b/sentinel/src/lib.rs new file mode 100644 index 00000000..f18b9267 --- /dev/null +++ b/sentinel/src/lib.rs @@ -0,0 +1,105 @@ +pub use iced_core as core; + +pub mod client; +pub mod timing; + +use crate::timing::Timing; + +use futures::future; +use futures::stream::{self, Stream, StreamExt}; +use semver::Version; +use serde::{Deserialize, Serialize}; +use tokio::io::{self, AsyncBufReadExt, BufStream}; +use tokio::net; + +pub const SOCKET_ADDRESS: &str = "127.0.0.1:9167"; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum Input { + Connected(Version), + Reported(Report), +} + +#[derive(Debug, Clone)] +pub enum Event { + Connected(Version), + Disconnected, + Reported(Report), +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum Report { + Timing(Timing), +} + +pub fn run() -> impl Stream { + enum State { + Disconnected, + Connected(BufStream), + } + + stream::unfold(State::Disconnected, |state| async { + match state { + State::Disconnected => match connect().await { + Ok(stream) => { + let stream = BufStream::new(stream); + + Some((None, State::Connected(stream))) + } + Err(_error) => Some((None, State::Disconnected)), + }, + State::Connected(stream) => match receive(stream).await { + Ok((_, Event::Disconnected)) | Err(_) => { + Some((Some(Event::Disconnected), State::Disconnected)) + } + Ok((stream, message)) => { + Some((Some(message), State::Connected(stream))) + } + }, + } + }) + .filter_map(future::ready) +} + +async fn connect() -> Result { + let listener = net::TcpListener::bind(SOCKET_ADDRESS).await?; + + let (stream, _) = listener.accept().await?; + + stream.set_nodelay(true)?; + stream.readable().await?; + + Ok(stream) +} + +async fn receive( + mut stream: BufStream, +) -> Result<(BufStream, Event), io::Error> { + let mut input = String::new(); + + loop { + match stream.read_line(&mut input).await? { + 0 => return Ok((stream, Event::Disconnected)), + n => { + match serde_json::from_str(&input[..n]) { + Ok(input) => { + return Ok(( + stream, + match dbg!(input) { + Input::Connected(version) => { + Event::Connected(version) + } + Input::Reported(report) => { + Event::Reported(report) + } + }, + )) + } + Err(_) => { + // TODO: Log decoding error + } + } + } + } + } +} diff --git a/sentinel/src/timing.rs b/sentinel/src/timing.rs new file mode 100644 index 00000000..b4a588f2 --- /dev/null +++ b/sentinel/src/timing.rs @@ -0,0 +1,25 @@ +use crate::core::time::Duration; + +use serde::{Deserialize, Serialize}; + +#[derive( + Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, +)] +pub struct Timing { + pub stage: Stage, + pub duration: Duration, +} + +#[derive( + Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, +)] +pub enum Stage { + Boot, + Update, + View, + Layout, + Interact, + Draw, + Render, + Custom(String), +} diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index b6487b38..d2811fe2 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -31,7 +31,7 @@ impl Backend { } } - pub fn draw>( + pub fn draw( &mut self, pixels: &mut tiny_skia::PixmapMut<'_>, clip_mask: &mut tiny_skia::Mask, @@ -39,38 +39,9 @@ impl Backend { viewport: &Viewport, damage: &[Rectangle], background_color: Color, - overlay: &[T], ) { - let physical_size = viewport.physical_size(); let scale_factor = viewport.scale_factor() as f32; - if !overlay.is_empty() { - let path = tiny_skia::PathBuilder::from_rect( - tiny_skia::Rect::from_xywh( - 0.0, - 0.0, - physical_size.width as f32, - physical_size.height as f32, - ) - .expect("Create damage rectangle"), - ); - - pixels.fill_path( - &path, - &tiny_skia::Paint { - shader: tiny_skia::Shader::SolidColor(into_color(Color { - a: 0.1, - ..background_color - })), - anti_alias: false, - ..Default::default() - }, - tiny_skia::FillRule::default(), - tiny_skia::Transform::identity(), - None, - ); - } - for ®ion in damage { let path = tiny_skia::PathBuilder::from_rect( tiny_skia::Rect::from_xywh( @@ -109,25 +80,6 @@ impl Backend { Transformation::IDENTITY, ); } - - if !overlay.is_empty() { - pixels.stroke_path( - &path, - &tiny_skia::Paint { - shader: tiny_skia::Shader::SolidColor(into_color( - Color::from_rgb(1.0, 0.0, 0.0), - )), - anti_alias: false, - ..tiny_skia::Paint::default() - }, - &tiny_skia::Stroke { - width: 1.0, - ..tiny_skia::Stroke::default() - }, - tiny_skia::Transform::identity(), - None, - ); - } } self.text_pipeline.trim_cache(); diff --git a/tiny_skia/src/window/compositor.rs b/tiny_skia/src/window/compositor.rs index 21ccf620..09531a5d 100644 --- a/tiny_skia/src/window/compositor.rs +++ b/tiny_skia/src/window/compositor.rs @@ -95,43 +95,27 @@ impl crate::graphics::Compositor for Compositor { } } - fn present>( + fn present( &mut self, renderer: &mut Self::Renderer, surface: &mut Self::Surface, viewport: &Viewport, background_color: Color, - overlay: &[T], ) -> Result<(), compositor::SurfaceError> { renderer.with_primitives(|backend, primitives| { - present( - backend, - surface, - primitives, - viewport, - background_color, - overlay, - ) + present(backend, surface, primitives, viewport, background_color) }) } - fn screenshot>( + fn screenshot( &mut self, renderer: &mut Self::Renderer, surface: &mut Self::Surface, viewport: &Viewport, background_color: Color, - overlay: &[T], ) -> Vec { renderer.with_primitives(|backend, primitives| { - screenshot( - surface, - backend, - primitives, - viewport, - background_color, - overlay, - ) + screenshot(surface, backend, primitives, viewport, background_color) }) } } @@ -147,13 +131,12 @@ pub fn new( Compositor { context, settings } } -pub fn present>( +pub fn present( backend: &mut Backend, surface: &mut Surface, primitives: &[Primitive], viewport: &Viewport, background_color: Color, - overlay: &[T], ) -> Result<(), compositor::SurfaceError> { let physical_size = viewport.physical_size(); let scale_factor = viewport.scale_factor() as f32; @@ -206,19 +189,17 @@ pub fn present>( viewport, &damage, background_color, - overlay, ); buffer.present().map_err(|_| compositor::SurfaceError::Lost) } -pub fn screenshot>( +pub fn screenshot( surface: &mut Surface, backend: &mut Backend, primitives: &[Primitive], viewport: &Viewport, background_color: Color, - overlay: &[T], ) -> Vec { let size = viewport.physical_size(); @@ -240,7 +221,6 @@ pub fn screenshot>( size.height as f32, ))], background_color, - overlay, ); offscreen_buffer.iter().fold( diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs index 09ddbe4d..8ce0b26d 100644 --- a/wgpu/src/backend.rs +++ b/wgpu/src/backend.rs @@ -67,7 +67,7 @@ impl Backend { /// /// The text provided as overlay will be rendered on top of the primitives. /// This is useful for rendering debug information. - pub fn present>( + pub fn present( &mut self, device: &wgpu::Device, queue: &wgpu::Queue, @@ -77,7 +77,6 @@ impl Backend { frame: &wgpu::TextureView, primitives: &[Primitive], viewport: &Viewport, - overlay_text: &[T], ) { log::debug!("Drawing"); #[cfg(feature = "tracing")] @@ -87,11 +86,7 @@ impl Backend { let scale_factor = viewport.scale_factor() as f32; let transformation = viewport.projection(); - let mut layers = Layer::generate(primitives, viewport); - - if !overlay_text.is_empty() { - layers.push(Layer::overlay(overlay_text, viewport)); - } + let layers = Layer::generate(primitives, viewport); self.prepare( device, diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs index 0a5d2c8f..fdec1152 100644 --- a/wgpu/src/window/compositor.rs +++ b/wgpu/src/window/compositor.rs @@ -172,14 +172,13 @@ pub fn new( } /// Presents the given primitives with the given [`Compositor`] and [`Backend`]. -pub fn present>( +pub fn present( compositor: &mut Compositor, backend: &mut Backend, surface: &mut wgpu::Surface<'static>, primitives: &[Primitive], viewport: &Viewport, background_color: Color, - overlay: &[T], ) -> Result<(), compositor::SurfaceError> { match surface.get_current_texture() { Ok(frame) => { @@ -202,7 +201,6 @@ pub fn present>( view, primitives, viewport, - overlay, ); // Submit work @@ -294,13 +292,12 @@ impl graphics::Compositor for Compositor { } } - fn present>( + fn present( &mut self, renderer: &mut Self::Renderer, surface: &mut Self::Surface, viewport: &Viewport, background_color: Color, - overlay: &[T], ) -> Result<(), compositor::SurfaceError> { renderer.with_primitives(|backend, primitives| { present( @@ -310,28 +307,19 @@ impl graphics::Compositor for Compositor { primitives, viewport, background_color, - overlay, ) }) } - fn screenshot>( + fn screenshot( &mut self, renderer: &mut Self::Renderer, _surface: &mut Self::Surface, viewport: &Viewport, background_color: Color, - overlay: &[T], ) -> Vec { renderer.with_primitives(|backend, primitives| { - screenshot( - self, - backend, - primitives, - viewport, - background_color, - overlay, - ) + screenshot(self, backend, primitives, viewport, background_color) }) } } @@ -339,13 +327,12 @@ impl graphics::Compositor for Compositor { /// Renders the current surface to an offscreen buffer. /// /// Returns RGBA bytes of the texture data. -pub fn screenshot>( +pub fn screenshot( compositor: &Compositor, backend: &mut Backend, primitives: &[Primitive], viewport: &Viewport, background_color: Color, - overlay: &[T], ) -> Vec { let mut encoder = compositor.device.create_command_encoder( &wgpu::CommandEncoderDescriptor { @@ -385,7 +372,6 @@ pub fn screenshot>( &view, primitives, viewport, - overlay, ); let texture = crate::color::convert( diff --git a/winit/Cargo.toml b/winit/Cargo.toml index 87e600ae..fca2919a 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -12,7 +12,6 @@ keywords.workspace = true [features] default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"] -debug = ["iced_runtime/debug"] system = ["sysinfo"] application = [] x11 = ["winit/x11"] diff --git a/winit/src/application.rs b/winit/src/application.rs index 05a4f070..6a056d88 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -11,13 +11,14 @@ use crate::core::time::Instant; use crate::core::widget::operation; use crate::core::window; use crate::core::{Event, Point, Size}; +use crate::debug; use crate::futures::futures; use crate::futures::{Executor, Runtime, Subscription}; use crate::graphics::compositor::{self, Compositor}; use crate::runtime::clipboard; use crate::runtime::program::Program; use crate::runtime::user_interface::{self, UserInterface}; -use crate::runtime::{Command, Debug}; +use crate::runtime::Command; use crate::style::application::{Appearance, StyleSheet}; use crate::{Clipboard, Error, Proxy, Settings}; @@ -111,8 +112,7 @@ where use futures::Future; use winit::event_loop::EventLoopBuilder; - let mut debug = Debug::new(); - debug.startup_started(); + let boot_timer = debug::boot_time(); let event_loop = EventLoopBuilder::with_user_event() .build() @@ -206,13 +206,13 @@ where renderer, runtime, proxy, - debug, event_receiver, control_sender, init_command, window, should_be_visible, exit_on_close_request, + boot_timer, )); let mut context = task::Context::from_waker(task::noop_waker_ref()); @@ -276,7 +276,6 @@ async fn run_instance( mut renderer: A::Renderer, mut runtime: Runtime, A::Message>, mut proxy: winit::event_loop::EventLoopProxy, - mut debug: Debug, mut event_receiver: mpsc::UnboundedReceiver< winit::event::Event, >, @@ -285,6 +284,7 @@ async fn run_instance( window: Arc, should_be_visible: bool, exit_on_close_request: bool, + boot_timer: debug::Timer, ) where A: Application + 'static, E: Executor + 'static, @@ -324,17 +324,16 @@ async fn run_instance( &mut clipboard, &mut should_exit, &mut proxy, - &mut debug, &window, ); runtime.track(application.subscription().into_recipes()); + boot_timer.finish(); let mut user_interface = ManuallyDrop::new(build_user_interface( &application, cache, &mut renderer, state.logical_size(), - &mut debug, )); let mut mouse_interaction = mouse::Interaction::default(); @@ -342,8 +341,6 @@ async fn run_instance( let mut messages = Vec::new(); let mut redraw_pending = false; - debug.startup_finished(); - while let Some(event) = event_receiver.next().await { match event { event::Event::NewEvents( @@ -382,12 +379,12 @@ async fn run_instance( if viewport_version != current_viewport_version { let logical_size = state.logical_size(); - debug.layout_started(); + let layout_timer = debug::layout_time(); user_interface = ManuallyDrop::new( ManuallyDrop::into_inner(user_interface) .relayout(logical_size, &mut renderer), ); - debug.layout_finished(); + layout_timer.finish(); compositor.configure_surface( &mut surface, @@ -434,7 +431,7 @@ async fn run_instance( runtime.broadcast(redraw_event, core::event::Status::Ignored); - debug.draw_started(); + let draw_timer = debug::draw_time(); let new_mouse_interaction = user_interface.draw( &mut renderer, state.theme(), @@ -444,7 +441,7 @@ async fn run_instance( state.cursor(), ); redraw_pending = false; - debug.draw_finished(); + draw_timer.finish(); if new_mouse_interaction != mouse_interaction { window.set_cursor_icon(conversion::mouse_interaction( @@ -454,19 +451,15 @@ async fn run_instance( mouse_interaction = new_mouse_interaction; } - debug.render_started(); + let render_timer = debug::render_time(); match compositor.present( &mut renderer, &mut surface, state.viewport(), state.background_color(), - &debug.overlay(), ) { Ok(()) => { - debug.render_finished(); - - // TODO: Handle animations! - // Maybe we can use `ControlFlow::WaitUntil` for this. + render_timer.finish(); } Err(error) => match error { // This is an unrecoverable error. @@ -474,8 +467,6 @@ async fn run_instance( panic!("{error:?}"); } _ => { - debug.render_finished(); - // Try rendering again next frame. window.request_redraw(); } @@ -492,7 +483,7 @@ async fn run_instance( break; } - state.update(&window, &window_event, &mut debug); + state.update(&window, &window_event); if let Some(event) = conversion::window_event( window::Id::MAIN, @@ -508,8 +499,7 @@ async fn run_instance( continue; } - debug.event_processing_started(); - + let interact_timer = debug::interact_time(); let (interface_state, statuses) = user_interface.update( &events, state.cursor(), @@ -517,8 +507,7 @@ async fn run_instance( &mut clipboard, &mut messages, ); - - debug.event_processing_finished(); + interact_timer.finish(); for (event, status) in events.drain(..).zip(statuses.into_iter()) @@ -547,7 +536,6 @@ async fn run_instance( &mut clipboard, &mut should_exit, &mut proxy, - &mut debug, &mut messages, &window, ); @@ -557,7 +545,6 @@ async fn run_instance( cache, &mut renderer, state.logical_size(), - &mut debug, )); if should_exit { @@ -609,18 +596,17 @@ pub fn build_user_interface<'a, A: Application>( cache: user_interface::Cache, renderer: &mut A::Renderer, size: Size, - debug: &mut Debug, ) -> UserInterface<'a, A::Message, A::Theme, A::Renderer> where A::Theme: StyleSheet, { - debug.view_started(); + let view_timer = debug::view_time(); let view = application.view(); - debug.view_finished(); + view_timer.finish(); - debug.layout_started(); + let layout_timer = debug::layout_time(); let user_interface = UserInterface::build(view, size, cache, renderer); - debug.layout_finished(); + layout_timer.finish(); user_interface } @@ -638,7 +624,6 @@ pub fn update( clipboard: &mut Clipboard, should_exit: &mut bool, proxy: &mut winit::event_loop::EventLoopProxy, - debug: &mut Debug, messages: &mut Vec, window: &winit::window::Window, ) where @@ -646,11 +631,11 @@ pub fn update( A::Theme: StyleSheet, { for message in messages.drain(..) { - debug.log_message(&message); + debug::log_message(&message); - debug.update_started(); + let update_timer = debug::update_time(); let command = runtime.enter(|| application.update(message)); - debug.update_finished(); + update_timer.finish(); run_command( application, @@ -664,7 +649,6 @@ pub fn update( clipboard, should_exit, proxy, - debug, window, ); } @@ -688,7 +672,6 @@ pub fn run_command( clipboard: &mut Clipboard, should_exit: &mut bool, proxy: &mut winit::event_loop::EventLoopProxy, - debug: &mut Debug, window: &winit::window::Window, ) where A: Application, @@ -855,7 +838,6 @@ pub fn run_command( surface, state.viewport(), state.background_color(), - &debug.overlay(), ); proxy @@ -895,7 +877,6 @@ pub fn run_command( current_cache, renderer, state.logical_size(), - debug, ); while let Some(mut operation) = current_operation.take() { diff --git a/winit/src/application/state.rs b/winit/src/application/state.rs index c17a3bcc..14c4507f 100644 --- a/winit/src/application/state.rs +++ b/winit/src/application/state.rs @@ -3,7 +3,6 @@ use crate::conversion; use crate::core::mouse; use crate::core::{Color, Size}; use crate::graphics::Viewport; -use crate::runtime::Debug; use crate::Application; use std::marker::PhantomData; @@ -122,12 +121,7 @@ where /// Processes the provided window event and updates the [`State`] /// accordingly. - pub fn update( - &mut self, - window: &Window, - event: &WindowEvent, - _debug: &mut Debug, - ) { + pub fn update(&mut self, window: &Window, event: &WindowEvent) { match event { WindowEvent::Resized(new_size) => { let size = Size::new(new_size.width, new_size.height); @@ -176,7 +170,7 @@ where .. }, .. - } => _debug.toggle(), + } => crate::debug::open_axe(), _ => {} } } diff --git a/winit/src/lib.rs b/winit/src/lib.rs index 3b1b0d3a..5936ded3 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -29,6 +29,7 @@ pub use iced_graphics as graphics; pub use iced_runtime as runtime; pub use iced_runtime::core; +pub use iced_runtime::debug; pub use iced_runtime::futures; pub use iced_style as style; pub use winit; diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 03066d6c..10278c77 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -11,6 +11,7 @@ use crate::core::renderer; use crate::core::widget::operation; use crate::core::window; use crate::core::{Point, Size}; +use crate::debug; use crate::futures::futures::channel::mpsc; use crate::futures::futures::{task, Future, StreamExt}; use crate::futures::{Executor, Runtime, Subscription}; @@ -19,7 +20,6 @@ use crate::multi_window::window_manager::WindowManager; use crate::runtime::command::{self, Command}; use crate::runtime::multi_window::Program; use crate::runtime::user_interface::{self, UserInterface}; -use crate::runtime::Debug; use crate::style::application::StyleSheet; use crate::{Clipboard, Error, Proxy, Settings}; @@ -112,8 +112,7 @@ where { use winit::event_loop::EventLoopBuilder; - let mut debug = Debug::new(); - debug.startup_started(); + let boot_timer = debug::boot_time(); let event_loop = EventLoopBuilder::with_user_event() .build() @@ -202,12 +201,12 @@ where compositor, runtime, proxy, - debug, event_receiver, control_sender, init_command, window_manager, should_main_be_visible, + boot_timer, )); let mut context = task::Context::from_waker(task::noop_waker_ref()); @@ -339,12 +338,12 @@ async fn run_instance( mut compositor: C, mut runtime: Runtime, A::Message>, mut proxy: winit::event_loop::EventLoopProxy, - mut debug: Debug, mut event_receiver: mpsc::UnboundedReceiver>, mut control_sender: mpsc::UnboundedSender, init_command: Command, mut window_manager: WindowManager, should_main_window_be_visible: bool, + boot_timer: debug::Timer, ) where A: Application + 'static, E: Executor + 'static, @@ -377,15 +376,6 @@ async fn run_instance( }; let mut ui_caches = HashMap::new(); - let mut user_interfaces = ManuallyDrop::new(build_user_interfaces( - &application, - &mut debug, - &mut window_manager, - HashMap::from_iter([( - window::Id::MAIN, - user_interface::Cache::default(), - )]), - )); run_command( &application, @@ -395,17 +385,24 @@ async fn run_instance( &mut clipboard, &mut control_sender, &mut proxy, - &mut debug, &mut window_manager, &mut ui_caches, ); runtime.track(application.subscription().into_recipes()); + boot_timer.finish(); + + let mut user_interfaces = ManuallyDrop::new(build_user_interfaces( + &application, + &mut window_manager, + HashMap::from_iter([( + window::Id::MAIN, + user_interface::Cache::default(), + )]), + )); let mut messages = Vec::new(); - debug.startup_finished(); - 'main: while let Some(event) = event_receiver.next().await { match event { Event::WindowCreated { @@ -430,7 +427,6 @@ async fn run_instance( user_interface::Cache::default(), &mut window.renderer, logical_size, - &mut debug, id, ), ); @@ -513,7 +509,7 @@ async fn run_instance( &mut messages, ); - debug.draw_started(); + let draw_timer = debug::draw_time(); let new_mouse_interaction = ui.draw( &mut window.renderer, window.state.theme(), @@ -522,7 +518,7 @@ async fn run_instance( }, cursor, ); - debug.draw_finished(); + draw_timer.finish(); if new_mouse_interaction != window.mouse_interaction { window.raw.set_cursor_icon( @@ -569,7 +565,7 @@ async fn run_instance( { let logical_size = window.state.logical_size(); - debug.layout_started(); + let layout_time = debug::layout_time(); let ui = user_interfaces .remove(&id) .expect("Remove user interface"); @@ -578,9 +574,9 @@ async fn run_instance( id, ui.relayout(logical_size, &mut window.renderer), ); - debug.layout_finished(); + layout_time.finish(); - debug.draw_started(); + let draw_time = debug::draw_time(); let new_mouse_interaction = user_interfaces .get_mut(&id) .expect("Get user interface") @@ -592,7 +588,7 @@ async fn run_instance( }, window.state.cursor(), ); - debug.draw_finished(); + draw_time.finish(); if new_mouse_interaction != window.mouse_interaction { @@ -616,16 +612,15 @@ async fn run_instance( window.state.viewport_version(); } - debug.render_started(); + let render_time = debug::render_time(); match compositor.present( &mut window.renderer, &mut window.surface, window.state.viewport(), window.state.background_color(), - &debug.overlay(), ) { Ok(()) => { - debug.render_finished(); + render_time.finish(); // TODO: Handle animations! // Maybe we can use `ControlFlow::WaitUntil` for this. @@ -636,8 +631,6 @@ async fn run_instance( panic!("{:?}", error); } _ => { - debug.render_finished(); - log::error!( "Error {error:?} when \ presenting surface." @@ -681,11 +674,7 @@ async fn run_instance( break 'main; } } else { - window.state.update( - &window.raw, - &window_event, - &mut debug, - ); + window.state.update(&window.raw, &window_event); if let Some(event) = conversion::window_event( id, @@ -702,7 +691,7 @@ async fn run_instance( continue; } - debug.event_processing_started(); + let interact_time = debug::interact_time(); let mut uis_stale = false; for (id, window) in window_manager.iter_mut() { @@ -749,8 +738,7 @@ async fn run_instance( runtime.broadcast(event, status); } } - - debug.event_processing_finished(); + interact_time.finish(); // TODO mw application update returns which window IDs to update if !messages.is_empty() || uis_stale { @@ -770,7 +758,6 @@ async fn run_instance( &mut clipboard, &mut control_sender, &mut proxy, - &mut debug, &mut messages, &mut window_manager, &mut cached_interfaces, @@ -794,7 +781,6 @@ async fn run_instance( user_interfaces = ManuallyDrop::new(build_user_interfaces( &application, - &mut debug, &mut window_manager, cached_interfaces, )); @@ -815,19 +801,18 @@ fn build_user_interface<'a, A: Application>( cache: user_interface::Cache, renderer: &mut A::Renderer, size: Size, - debug: &mut Debug, id: window::Id, ) -> UserInterface<'a, A::Message, A::Theme, A::Renderer> where A::Theme: StyleSheet, { - debug.view_started(); + let view_timer = debug::view_time(); let view = application.view(id); - debug.view_finished(); + view_timer.finish(); - debug.layout_started(); + let layout_timer = debug::layout_time(); let user_interface = UserInterface::build(view, size, cache, renderer); - debug.layout_finished(); + layout_timer.finish(); user_interface } @@ -841,7 +826,6 @@ fn update( clipboard: &mut Clipboard, control_sender: &mut mpsc::UnboundedSender, proxy: &mut winit::event_loop::EventLoopProxy, - debug: &mut Debug, messages: &mut Vec, window_manager: &mut WindowManager, ui_caches: &mut HashMap, @@ -850,11 +834,11 @@ fn update( A::Theme: StyleSheet, { for message in messages.drain(..) { - debug.log_message(&message); - debug.update_started(); + debug::log_message(&message); + let update_timer = debug::update_time(); let command = runtime.enter(|| application.update(message)); - debug.update_finished(); + update_timer.finish(); run_command( application, @@ -864,7 +848,6 @@ fn update( clipboard, control_sender, proxy, - debug, window_manager, ui_caches, ); @@ -883,7 +866,6 @@ fn run_command( clipboard: &mut Clipboard, control_sender: &mut mpsc::UnboundedSender, proxy: &mut winit::event_loop::EventLoopProxy, - debug: &mut Debug, window_manager: &mut WindowManager, ui_caches: &mut HashMap, ) where @@ -1118,7 +1100,6 @@ fn run_command( &mut window.surface, window.state.viewport(), window.state.background_color(), - &debug.overlay(), ); proxy @@ -1155,7 +1136,6 @@ fn run_command( let mut uis = build_user_interfaces( application, - debug, window_manager, std::mem::take(ui_caches), ); @@ -1211,7 +1191,6 @@ fn run_command( /// Build the user interface for every window. pub fn build_user_interfaces<'a, A: Application, C: Compositor>( application: &'a A, - debug: &mut Debug, window_manager: &mut WindowManager, mut cached_user_interfaces: HashMap, ) -> HashMap> @@ -1231,7 +1210,6 @@ where cache, &mut window.renderer, window.state.logical_size(), - debug, id, ), )) diff --git a/winit/src/multi_window/state.rs b/winit/src/multi_window/state.rs index 2e97a13d..aeada137 100644 --- a/winit/src/multi_window/state.rs +++ b/winit/src/multi_window/state.rs @@ -138,12 +138,7 @@ where } /// Processes the provided window event and updates the [`State`] accordingly. - pub fn update( - &mut self, - window: &Window, - event: &WindowEvent, - _debug: &mut crate::runtime::Debug, - ) { + pub fn update(&mut self, window: &Window, event: &WindowEvent) { match event { WindowEvent::Resized(new_size) => { let size = Size::new(new_size.width, new_size.height);