diff options
Diffstat (limited to '')
-rw-r--r-- | winit/Cargo.toml | 20 | ||||
-rw-r--r-- | winit/src/application.rs | 66 | ||||
-rw-r--r-- | winit/src/application/profiler.rs | 101 | ||||
-rw-r--r-- | winit/src/lib.rs | 2 |
4 files changed, 177 insertions, 12 deletions
diff --git a/winit/Cargo.toml b/winit/Cargo.toml index ebbadb12..94aaa2ca 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -11,6 +11,8 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"] categories = ["gui"] [features] +trace = ["tracing", "tracing-core", "tracing-subscriber"] +chrome-trace = ["trace", "tracing-chrome"] debug = ["iced_native/debug"] system = ["sysinfo"] application = [] @@ -37,6 +39,24 @@ path = "../graphics" version = "0.5" path = "../futures" +[dependencies.tracing] +version = "0.1.37" +optional = true +features = ["std"] + +[dependencies.tracing-core] +version = "0.1.30" +optional = true + +[dependencies.tracing-subscriber] +version = "0.3.16" +optional = true +features = ["registry"] + +[dependencies.tracing-chrome] +version = "0.7.0" +optional = true + [target.'cfg(target_os = "windows")'.dependencies.winapi] version = "0.3.6" diff --git a/winit/src/application.rs b/winit/src/application.rs index 1973fdce..74c73815 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -1,4 +1,6 @@ //! Create interactive, native cross-platform applications. +#[cfg(feature = "trace")] +mod profiler; mod state; pub use state::State; @@ -24,6 +26,11 @@ pub use iced_native::application::{Appearance, StyleSheet}; use std::mem::ManuallyDrop; +#[cfg(feature = "trace")] +pub use profiler::Profiler; +#[cfg(feature = "trace")] +use tracing::{info_span, instrument::Instrument}; + /// An interactive, native cross-platform application. /// /// This trait is the main entrypoint of Iced. Once implemented, you can run @@ -111,9 +118,15 @@ where use futures::Future; use winit::event_loop::EventLoopBuilder; + #[cfg(feature = "trace")] + let _guard = Profiler::init(); + let mut debug = Debug::new(); debug.startup_started(); + #[cfg(feature = "trace")] + let _ = info_span!("Application", "RUN").entered(); + let event_loop = EventLoopBuilder::with_user_event().build(); let proxy = event_loop.create_proxy(); @@ -175,18 +188,26 @@ where let (mut sender, receiver) = mpsc::unbounded(); - let mut instance = Box::pin(run_instance::<A, E, C>( - application, - compositor, - renderer, - runtime, - proxy, - debug, - receiver, - init_command, - window, - settings.exit_on_close_request, - )); + let mut instance = Box::pin({ + let run_instance = run_instance::<A, E, C>( + application, + compositor, + renderer, + runtime, + proxy, + debug, + receiver, + init_command, + window, + settings.exit_on_close_request, + ); + + #[cfg(feature = "trace")] + let run_instance = + run_instance.instrument(info_span!("Application", "LOOP")); + + run_instance + }); let mut context = task::Context::from_waker(task::noop_waker_ref()); @@ -391,6 +412,9 @@ async fn run_instance<A, E, C>( messages.push(message); } event::Event::RedrawRequested(_) => { + #[cfg(feature = "trace")] + let _ = info_span!("Application", "FRAME").entered(); + let physical_size = state.physical_size(); if physical_size.width == 0 || physical_size.height == 0 { @@ -529,12 +553,24 @@ pub fn build_user_interface<'a, A: Application>( where <A::Renderer as crate::Renderer>::Theme: StyleSheet, { + #[cfg(feature = "trace")] + let view_span = info_span!("Application", "VIEW").entered(); + debug.view_started(); let view = application.view(); + + #[cfg(feature = "trace")] + let _ = view_span.exit(); debug.view_finished(); + #[cfg(feature = "trace")] + let layout_span = info_span!("Application", "LAYOUT").entered(); + debug.layout_started(); let user_interface = UserInterface::build(view, size, cache, renderer); + + #[cfg(feature = "trace")] + let _ = layout_span.exit(); debug.layout_finished(); user_interface @@ -559,10 +595,16 @@ pub fn update<A: Application, E: Executor>( <A::Renderer as crate::Renderer>::Theme: StyleSheet, { for message in messages.drain(..) { + #[cfg(feature = "trace")] + let update_span = info_span!("Application", "UPDATE").entered(); + debug.log_message(&message); debug.update_started(); let command = runtime.enter(|| application.update(message)); + + #[cfg(feature = "trace")] + let _ = update_span.exit(); debug.update_finished(); run_command( diff --git a/winit/src/application/profiler.rs b/winit/src/application/profiler.rs new file mode 100644 index 00000000..23eaa390 --- /dev/null +++ b/winit/src/application/profiler.rs @@ -0,0 +1,101 @@ +//! A simple profiler for Iced. +use std::ffi::OsStr; +use std::path::Path; +use std::time::Duration; +use tracing_subscriber::prelude::*; +use tracing_subscriber::Registry; +#[cfg(feature = "chrome-trace")] +use { + tracing_chrome::FlushGuard, + tracing_subscriber::fmt::{format::DefaultFields, FormattedFields}, +}; + +/// Profiler state. This will likely need to be updated or reworked when adding new tracing backends. +#[allow(missing_debug_implementations)] +pub struct Profiler { + #[cfg(feature = "chrome-trace")] + /// [`FlushGuard`] must not be dropped until the application scope is dropped for accurate tracing. + _guard: FlushGuard, +} + +impl Profiler { + /// Initializes the [`Profiler`]. + pub fn init() -> Self { + // Registry stores the spans & generates unique span IDs + let subscriber = Registry::default(); + + let default_path = Path::new(env!("CARGO_MANIFEST_DIR")); + let curr_exe = std::env::current_exe() + .unwrap_or_else(|_| default_path.to_path_buf()); + let out_dir = curr_exe.parent().unwrap_or(default_path).join("traces"); + + #[cfg(feature = "chrome-trace")] + let (chrome_layer, guard) = { + let mut layer = tracing_chrome::ChromeLayerBuilder::new(); + + // Optional configurable env var: CHROME_TRACE_FILE=/path/to/trace_file/file.json, + // for uploading to chrome://tracing (old) or ui.perfetto.dev (new). + if let Ok(path) = std::env::var("CHROME_TRACE_FILE") { + layer = layer.file(path); + } else if std::fs::create_dir_all(&out_dir).is_ok() { + let time = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or(Duration::from_millis(0)) + .as_millis(); + + let curr_exe_name = curr_exe + .file_name() + .unwrap_or_else(|| OsStr::new("trace")) + .to_str() + .unwrap_or("trace"); + + let path = out_dir + .join(format!("{}_trace_{}.json", curr_exe_name, time)); + + layer = layer.file(path); + } else { + layer = layer.file(env!("CARGO_MANIFEST_DIR")) + } + + let (chrome_layer, guard) = layer + .name_fn(Box::new(|event_or_span| match event_or_span { + tracing_chrome::EventOrSpan::Event(event) => { + event.metadata().name().into() + } + tracing_chrome::EventOrSpan::Span(span) => { + if let Some(fields) = span + .extensions() + .get::<FormattedFields<DefaultFields>>() + { + format!( + "{}: {}", + span.metadata().name(), + fields.fields.as_str() + ) + } else { + span.metadata().name().into() + } + } + })) + .build(); + + (chrome_layer, guard) + }; + + let fmt_layer = tracing_subscriber::fmt::Layer::default(); + let subscriber = subscriber.with(fmt_layer); + + #[cfg(feature = "chrome-trace")] + let subscriber = subscriber.with(chrome_layer); + + // create dispatcher which will forward span events to the subscriber + // this can only be set once or will panic + tracing::subscriber::set_global_default(subscriber) + .expect("Tracer could not set the global default subscriber."); + + Profiler { + #[cfg(feature = "chrome-trace")] + _guard: guard, + } + } +} diff --git a/winit/src/lib.rs b/winit/src/lib.rs index b8ed492d..06674109 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -51,6 +51,8 @@ mod proxy; #[cfg(feature = "application")] pub use application::Application; +#[cfg(feature = "trace")] +pub use application::Profiler; pub use clipboard::Clipboard; pub use error::Error; pub use position::Position; |