From bfada7d177b97162b6c3b7ceeddadf32d9eb08c2 Mon Sep 17 00:00:00 2001 From: Michael Mauderer Date: Mon, 4 Apr 2022 13:44:11 +0100 Subject: [PATCH 1/8] Add ability to render marks in visualisation. --- .../ensogl/component/flame-graph/src/lib.rs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/rust/ensogl/component/flame-graph/src/lib.rs b/lib/rust/ensogl/component/flame-graph/src/lib.rs index e5b30c94e389..94cdaa7168ce 100644 --- a/lib/rust/ensogl/component/flame-graph/src/lib.rs +++ b/lib/rust/ensogl/component/flame-graph/src/lib.rs @@ -30,7 +30,7 @@ use ensogl_core::display::shape::StyleWatchFrp; use mark::Mark; pub use block::Block; - +use mark::Mark; // ======================== @@ -97,7 +97,22 @@ fn shape_from_mark(mark: profiler_flame_graph::Mark, app: &Application) -> Mark component } -const MIN_INTERVAL_TIME_MS: f64 = 0.0; +/// Instantiate a `Block` shape for the given block data from the profiler. +fn shape_from_mark(mark: profiler_flame_graph::Mark, app: &Application) -> Mark { + let component = app.new_view::(); + let x = mark.position as f32; + let y = 0.0; + let pos = Vector2::new(x, y as f32); + + let label = format!("{} ({:.1}ms)", mark.label, mark.position); + + component.set_content.emit(label); + component.set_position_xy(pos); + + component +} + +const MIN_INTERVALL_TIME: f64 = 0.0; const X_SCALE: f64 = 1.0; impl FlameGraph { @@ -124,7 +139,6 @@ impl FlameGraph { let blocks = blocks_zero_aligned.map(|block| shape_from_block(block, app)).collect_vec(); blocks.iter().for_each(|item| display_object.add_child(item)); - let blocks_marks_aligned = marks.into_iter().map(|mut mark| { mark.position -= origin_x as f64; mark.position *= X_SCALE; From 09171ca846fd6106a68944b90d7f820b9a7216fa Mon Sep 17 00:00:00 2001 From: Michael Mauderer Date: Wed, 6 Apr 2022 14:08:07 +0100 Subject: [PATCH 2/8] Expand profiling. Update visualisation to show rpc events. --- Cargo.lock | 1 + .../ensogl/component/flame-graph/src/lib.rs | 14 +++++++++- lib/rust/ensogl/core/src/lib.rs | 1 + lib/rust/ensogl/core/src/profiler.rs | 28 +++++++++++++++++++ .../example/profiling-run-graph/Cargo.toml | 1 + .../example/profiling-run-graph/src/lib.rs | 4 +++ lib/rust/json-rpc/Cargo.toml | 1 + lib/rust/json-rpc/src/lib.rs | 1 + 8 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 lib/rust/ensogl/core/src/profiler.rs diff --git a/Cargo.lock b/Cargo.lock index 2438fcb9bb70..5c962b3aaf62 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2478,6 +2478,7 @@ version = "0.1.0" dependencies = [ "enso-prelude", "enso-profiler", + "enso-profiler-data", "enso-shapely", "enso-web", "failure", diff --git a/lib/rust/ensogl/component/flame-graph/src/lib.rs b/lib/rust/ensogl/component/flame-graph/src/lib.rs index 94cdaa7168ce..275f56ab9bc9 100644 --- a/lib/rust/ensogl/component/flame-graph/src/lib.rs +++ b/lib/rust/ensogl/component/flame-graph/src/lib.rs @@ -32,6 +32,9 @@ use mark::Mark; pub use block::Block; use mark::Mark; +pub use block::Block; + + // ======================== // === Layout Constants === @@ -94,6 +97,15 @@ fn shape_from_mark(mark: profiler_flame_graph::Mark, app: &Application) -> Mark component.set_content.emit(label); component.set_position_xy(pos); + let style = StyleWatchFrp::new(&app.display.default_scene.style_sheet); + + let color: color::Rgba = match block.kind { + Kind::Active => style.get_color("flame_graph_block_color_active").value(), + Kind::Paused => style.get_color("flame_graph_block_color_paused").value(), + }; + let color: color::Lcha = color.into(); + component.set_color(color); + component } @@ -102,7 +114,7 @@ fn shape_from_mark(mark: profiler_flame_graph::Mark, app: &Application) -> Mark let component = app.new_view::(); let x = mark.position as f32; let y = 0.0; - let pos = Vector2::new(x, y as f32); + let pos = Vector2::new(x, y); let label = format!("{} ({:.1}ms)", mark.label, mark.position); diff --git a/lib/rust/ensogl/core/src/lib.rs b/lib/rust/ensogl/core/src/lib.rs index be8d35b103ef..b4e53a1ae8fb 100644 --- a/lib/rust/ensogl/core/src/lib.rs +++ b/lib/rust/ensogl/core/src/lib.rs @@ -48,6 +48,7 @@ pub mod data; pub mod debug; pub mod display; pub mod gui; +pub mod profiler; pub mod system; pub use animation::Animation; diff --git a/lib/rust/ensogl/core/src/profiler.rs b/lib/rust/ensogl/core/src/profiler.rs new file mode 100644 index 000000000000..0c336d7d011f --- /dev/null +++ b/lib/rust/ensogl/core/src/profiler.rs @@ -0,0 +1,28 @@ +//! Enso specific metadata logging utilities for use with the profiling framework. +use enso_profiler; +use serde; +use serde::Serializer; +use std::fmt::Display; +use std::fmt::Formatter; + + +/// Metadata that is logged within the Enso core libraries. +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Eq, PartialEq)] +pub enum Metadata { + /// An RPC event that was received from the backend. + RpcEvent(String), +} + +impl Display for Metadata { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Metadata::RpcEvent(name) => f.collect_str(name), + } + } +} + +/// Log an RPC Event to the profiling framework. +pub fn log_rpc_event(event_name: &'static str) { + let event_logger = enso_profiler::MetadataLogger::new("RpcEvent"); + event_logger.log(event_name); +} diff --git a/lib/rust/ensogl/example/profiling-run-graph/Cargo.toml b/lib/rust/ensogl/example/profiling-run-graph/Cargo.toml index fbc280f63295..0cf88c2fa120 100644 --- a/lib/rust/ensogl/example/profiling-run-graph/Cargo.toml +++ b/lib/rust/ensogl/example/profiling-run-graph/Cargo.toml @@ -12,6 +12,7 @@ enso-frp = { path = "../../../frp" } enso-profiler = { path = "../../../profiler" } enso-profiler-data = { path = "../../../profiler/data" } enso-profiler-metadata = { path = "../../../../../app/gui/enso-profiler-metadata" } +enso-profiler-data = { path = "../../../profiler/data" } enso-profiler-flame-graph = { path = "../../../profiler/flame-graph" } enso-web = { path = "../../../web" } ensogl-core = { path = "../../core" } diff --git a/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs b/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs index 6594aeddff9e..c1e6edbd5d98 100644 --- a/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs +++ b/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs @@ -28,6 +28,8 @@ use ensogl_core::display::navigation::navigator::Navigator; use ensogl_core::display::object::ObjectOps; use ensogl_core::display::style::theme; use ensogl_core::display::Scene; +use ensogl_core::profiler::log_rpc_event; +use ensogl_core::profiler::Metadata; use ensogl_core::system::web; use ensogl_flame_graph as flame_graph; @@ -172,7 +174,9 @@ fn work(n: u32) { #[profile(Objective)] async fn start_project() { wake_dragon().await; + log_rpc_event("Dragon Awake"); feed_troll(); + log_rpc_event("Troll Fed"); ride_rainbow(); } #[profile(Objective)] diff --git a/lib/rust/json-rpc/Cargo.toml b/lib/rust/json-rpc/Cargo.toml index 286845e8512a..5a2e81029e1b 100644 --- a/lib/rust/json-rpc/Cargo.toml +++ b/lib/rust/json-rpc/Cargo.toml @@ -11,6 +11,7 @@ crate-type = ["cdylib", "rlib"] enso-prelude = { path = "../prelude", features = ["futures"]} enso-shapely = { path = "../shapely"} enso-web = { path = "../web" } +enso-profiler-data = {path = "../profiler/data"} enso-profiler = {path = "../profiler"} futures = { version = "0.3.1" } failure = { version = "0.1.6" } diff --git a/lib/rust/json-rpc/src/lib.rs b/lib/rust/json-rpc/src/lib.rs index 5e0876fe6eef..4b88defd9a80 100644 --- a/lib/rust/json-rpc/src/lib.rs +++ b/lib/rust/json-rpc/src/lib.rs @@ -32,6 +32,7 @@ pub use api::RemoteMethodCall; pub use api::Result; pub use enso_prelude as prelude; pub use enso_profiler; +pub use enso_profiler_data; pub use enso_web as ensogl; pub use error::RpcError; pub use handler::Event; From acee3c3817d126670cb2c88571d541a789da82f4 Mon Sep 17 00:00:00 2001 From: Michael Mauderer Date: Fri, 8 Apr 2022 14:09:24 +0100 Subject: [PATCH 3/8] Integrate EnsoGL stats with profiling framework and visualisation. --- .../ensogl/component/flame-graph/src/lib.rs | 83 +++++++++++------ lib/rust/ensogl/core/src/debug/stats.rs | 2 +- lib/rust/ensogl/core/src/display/world.rs | 3 +- lib/rust/ensogl/core/src/profiler.rs | 12 ++- .../example/profiling-run-graph/src/lib.rs | 92 +++++++++++++++---- lib/rust/profiler/flame-graph/src/lib.rs | 46 +++++----- 6 files changed, 167 insertions(+), 71 deletions(-) diff --git a/lib/rust/ensogl/component/flame-graph/src/lib.rs b/lib/rust/ensogl/component/flame-graph/src/lib.rs index 275f56ab9bc9..d7d806d23a77 100644 --- a/lib/rust/ensogl/component/flame-graph/src/lib.rs +++ b/lib/rust/ensogl/component/flame-graph/src/lib.rs @@ -24,15 +24,11 @@ use ensogl_core::display; mod block; mod mark; -use enso_profiler_flame_graph::State; use ensogl_core::data::color; use ensogl_core::display::shape::StyleWatchFrp; -use mark::Mark; - -pub use block::Block; -use mark::Mark; pub use block::Block; +pub use mark::Mark; @@ -58,10 +54,12 @@ pub struct FlameGraph { display_object: display::object::Instance, blocks: Vec, marks: Vec, + origin_x: f64, + app: Application, } /// Instantiate a `Block` shape for the given block data from the profiler. -fn shape_from_block(block: profiler_flame_graph::Block, app: &Application) -> Block { +pub fn shape_from_block(block: profiler_flame_graph::Block, app: &Application) -> Block { let component = app.new_view::(); let size = Vector2::new(block.width() as f32, ROW_HEIGHT as f32); @@ -99,10 +97,7 @@ fn shape_from_mark(mark: profiler_flame_graph::Mark, app: &Application) -> Mark let style = StyleWatchFrp::new(&app.display.default_scene.style_sheet); - let color: color::Rgba = match block.kind { - Kind::Active => style.get_color("flame_graph_block_color_active").value(), - Kind::Paused => style.get_color("flame_graph_block_color_paused").value(), - }; + let color: color::Rgba = style.get_color(block.theme_color).value(); let color: color::Lcha = color.into(); component.set_color(color); @@ -110,7 +105,7 @@ fn shape_from_mark(mark: profiler_flame_graph::Mark, app: &Application) -> Mark } /// Instantiate a `Block` shape for the given block data from the profiler. -fn shape_from_mark(mark: profiler_flame_graph::Mark, app: &Application) -> Mark { +pub fn shape_from_mark(mark: profiler_flame_graph::Mark, app: &Application) -> Mark { let component = app.new_view::(); let x = mark.position as f32; let y = 0.0; @@ -124,9 +119,28 @@ fn shape_from_mark(mark: profiler_flame_graph::Mark, app: &Application) -> Mark component } -const MIN_INTERVALL_TIME: f64 = 0.0; +const MIN_INTERVAL_TIME: f64 = 0.0; const X_SCALE: f64 = 1.0; +fn align_block( + mut block: profiler_flame_graph::Block, + origin_x: f64, +) -> profiler_flame_graph::Block { + // Shift + block.start -= origin_x; + block.end -= origin_x; + // Scale + block.start *= X_SCALE; + block.end *= X_SCALE; + block +} + +fn align_mark(mut mark: profiler_flame_graph::Mark, origin_x: f64) -> profiler_flame_graph::Mark { + mark.position -= origin_x; + mark.position += X_SCALE; + mark +} + impl FlameGraph { /// Create a `FlameGraph` EnsoGL component from the given graph data from the profiler. pub fn from_data(data: profiler_flame_graph::Graph, app: &Application) -> Self { @@ -137,30 +151,18 @@ impl FlameGraph { let marks = data.marks; let origin_x = - blocks.clone().map(|block| block.start.floor() as u32).min().unwrap_or_default(); - - let blocks_zero_aligned = blocks.clone().map(|mut block| { - // Shift - block.start -= origin_x as f64; - block.end -= origin_x as f64; - // Scale - block.start *= X_SCALE; - block.end *= X_SCALE; - block - }); + blocks.clone().map(|block| block.start.floor() as u32).min().unwrap_or_default() as f64; + + let blocks_zero_aligned = blocks.clone().map(|block| align_block(block, origin_x)); let blocks = blocks_zero_aligned.map(|block| shape_from_block(block, app)).collect_vec(); blocks.iter().for_each(|item| display_object.add_child(item)); - let blocks_marks_aligned = marks.into_iter().map(|mut mark| { - mark.position -= origin_x as f64; - mark.position *= X_SCALE; - mark - }); let marks: Vec<_> = blocks_marks_aligned.into_iter().map(|mark| shape_from_mark(mark, app)).collect(); marks.iter().for_each(|item| display_object.add_child(item)); - Self { display_object, blocks, marks } + let app = app.clone_ref(); + Self { display_object, blocks, marks, origin_x, app } } /// Return a reference to the blocks that make up the flame graph. @@ -172,6 +174,29 @@ impl FlameGraph { pub fn marks(&self) -> &[Mark] { &self.marks } + + /// Add additional blocks to the visualisation. + pub fn add_blocks>( + &mut self, + blocks: Blocks, + ) { + for block in blocks { + let block = align_block(block, self.origin_x); + let shape = shape_from_block(block, &self.app); + self.display_object.add_child(&shape); + self.blocks.push(shape); + } + } + + /// Add additional marks to the visualisation. + pub fn add_marks>(&mut self, marks: Marks) { + for mark in marks { + let mark = align_mark(mark, self.origin_x); + let shape = shape_from_mark(mark, &self.app); + self.display_object.add_child(&shape); + self.marks.push(shape); + } + } } impl display::Object for FlameGraph { diff --git a/lib/rust/ensogl/core/src/debug/stats.rs b/lib/rust/ensogl/core/src/debug/stats.rs index 1e95aa8809d2..9489426a6473 100644 --- a/lib/rust/ensogl/core/src/debug/stats.rs +++ b/lib/rust/ensogl/core/src/debug/stats.rs @@ -158,7 +158,7 @@ macro_rules! gen_stats { // === StatsData === /// Raw data of all the gathered stats. - #[derive(Debug,Default,Clone,Copy)] + #[derive(Debug,Default,Clone,Copy,serde::Serialize,serde::Deserialize)] #[allow(missing_docs)] pub struct StatsData { $(pub $field : $field_type),* diff --git a/lib/rust/ensogl/core/src/display/world.rs b/lib/rust/ensogl/core/src/display/world.rs index 1a3cd7211896..7c91a62078e6 100644 --- a/lib/rust/ensogl/core/src/display/world.rs +++ b/lib/rust/ensogl/core/src/display/world.rs @@ -19,6 +19,7 @@ use crate::display::render; use crate::display::render::passes::SymbolsRenderPass; use crate::display::scene::DomPath; use crate::display::scene::Scene; +use crate::profiler::log_stats_data; use crate::system::web; use web::prelude::Closure; @@ -204,9 +205,9 @@ impl WorldData { let uniforms = Uniforms::new(&default_scene.variables); let debug_hotkeys_handle = default(); let garbage_collector = default(); - let stats_draw_handle = on.prev_frame_stats.add(f!([stats_monitor] (stats: &StatsData) { stats_monitor.sample_and_draw(stats); + log_stats_data(*stats) })); Self { diff --git a/lib/rust/ensogl/core/src/profiler.rs b/lib/rust/ensogl/core/src/profiler.rs index 0c336d7d011f..27ea9e6c138f 100644 --- a/lib/rust/ensogl/core/src/profiler.rs +++ b/lib/rust/ensogl/core/src/profiler.rs @@ -1,4 +1,5 @@ //! Enso specific metadata logging utilities for use with the profiling framework. +use crate::debug::StatsData; use enso_profiler; use serde; use serde::Serializer; @@ -7,16 +8,19 @@ use std::fmt::Formatter; /// Metadata that is logged within the Enso core libraries. -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Eq, PartialEq)] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub enum Metadata { /// An RPC event that was received from the backend. RpcEvent(String), + /// Performance stats gathered from the EnsoGL rendering engine. + RenderStats(StatsData), } impl Display for Metadata { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Metadata::RpcEvent(name) => f.collect_str(name), + Metadata::RenderStats(stats) => f.collect_str(&format!("{:#?}", stats)), } } } @@ -26,3 +30,9 @@ pub fn log_rpc_event(event_name: &'static str) { let event_logger = enso_profiler::MetadataLogger::new("RpcEvent"); event_logger.log(event_name); } + +/// Log an RPC Event to the profiling framework. +pub fn log_stats_data(data: StatsData) { + let event_logger = enso_profiler::MetadataLogger::new("RenderStats"); + event_logger.log(data); +} diff --git a/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs b/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs index c1e6edbd5d98..fcb3bde79c39 100644 --- a/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs +++ b/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs @@ -22,6 +22,9 @@ use enso_profiler::profile; use enso_profiler_data::Profile; use enso_profiler_flame_graph as profiler_flame_graph; use enso_profiler_metadata::Metadata; +use enso_profiler_flame_graph::COLOR_BLOCK_ACTIVE; +use enso_profiler_flame_graph::COLOR_BLOCK_PAUSED; +use enso_profiler_flame_graph::COLOR_MARK_DEFAULT; use ensogl_core::application::Application; use ensogl_core::data::color; use ensogl_core::display::navigation::navigator::Navigator; @@ -33,12 +36,22 @@ use ensogl_core::profiler::Metadata; use ensogl_core::system::web; use ensogl_flame_graph as flame_graph; + + +// ================= +// === Constants === +// ================= + /// Content of a profiler log, that will be rendered. If this is `None` some dummy data will be /// generated and rendered. The file must be located in the assets subdirectory that is /// served by the webserver, i.e. `enso/dist/content` or `app/ide-desktop/lib/content/assets`. /// For example use `Some("profile.json"))`. const PROFILER_LOG_NAME: Option<&str> = None; +const COLOR_FPS_GOOD: &str = "flame_graph_block_color_fps_good"; +const COLOR_FPS_MEDIUM: &str = "flame_graph_block_color_fps_medium"; +const COLOR_FPS_BAD: &str = "flame_graph_block_color_fps_bad"; + // =================== @@ -63,19 +76,15 @@ pub async fn main() { let profile = if let Some(profile) = get_log_data().await { profile } else { create_dummy_data().await }; - let mut measurements = profiler_flame_graph::Graph::new_hybrid_graph(&profile); + let measurements = profiler_flame_graph::Graph::new_hybrid_graph(&profile); - let marks = profile - .iter_metadata() - .map(|metadata: &enso_profiler_data::Metadata| { - let position = metadata.mark.into_ms(); - let label = metadata.data.to_string(); - profiler_flame_graph::Mark { position, label } - }) - .collect(); - measurements.marks = marks; + let mut flame_graph = flame_graph::FlameGraph::from_data(measurements, app); + + let marks = make_marks_from_profile(&profile); + flame_graph.add_marks(marks.into_iter()); - let flame_graph = flame_graph::FlameGraph::from_data(measurements, app); + let performance_blocks = make_rendering_performance_blocks(&profile); + flame_graph.add_blocks(performance_blocks.into_iter()); world.add_child(&flame_graph); scene.add_child(&flame_graph); @@ -99,18 +108,67 @@ fn init_theme(scene: &Scene) { let theme_manager = theme::Manager::from(&scene.style_sheet); let theme = theme::Theme::new(); - theme.set("flame_graph_block_color_active", color::Lcha::blue_green(0.5, 0.8)); - theme.set("flame_graph_block_color_paused", color::Lcha::blue_green(0.8, 0.0)); - theme.set("flame_graph_mark_color", color::Lcha::blue_green(0.9, 0.1)); + theme.set(COLOR_BLOCK_ACTIVE, color::Lcha::blue_green(0.5, 0.8)); + theme.set(COLOR_BLOCK_PAUSED, color::Lcha::blue_green(0.8, 0.0)); + theme.set(COLOR_FPS_BAD, color::Lcha::red(0.4, 0.5)); + theme.set(COLOR_FPS_GOOD, color::Lcha::green(0.8, 0.5)); + theme.set(COLOR_FPS_MEDIUM, color::Lcha::yellow(0.6, 0.5)); + theme.set(COLOR_MARK_DEFAULT, color::Lcha::blue_green(0.9, 0.1)); theme_manager.register("theme", theme); - theme_manager.set_enabled(&["theme".to_string()]); +} + + - let style_watch = ensogl_core::display::shape::StyleWatch::new(&scene.style_sheet); - style_watch.get("flame_graph_color"); +// =========================== +// === Metadata Processing === +// =========================== + + +// Create marks for metadata. This will skip `RenderStats` as they are added separately as blocks. +fn make_marks_from_profile(profile: &Profile) -> Vec { + profile + .iter_metadata() + .filter_map(|metadata: &enso_profiler_data::Metadata| { + let position = metadata.mark.into_ms(); + match metadata.data { + Metadata::RenderStats(_) => None, + _ => { + let label = metadata.data.to_string(); + Some(profiler_flame_graph::Mark { position, label }) + } + } + }) + .collect() } +fn make_rendering_performance_blocks( + profile: &Profile, +) -> Vec { + let mut blocks = Vec::default(); + let render_stats = profile + .iter_metadata() + .filter(|metadata| matches!(metadata.data, Metadata::RenderStats(_))); + for (prev, current) in render_stats.tuple_windows() { + if let Metadata::RenderStats(data) = current.data { + let start = prev.mark.into_ms(); + let end = current.mark.into_ms(); + let row = -1; + let label = format!("{:#?}", data); + let theme_color = match data.fps { + fps if fps > 55.0 => COLOR_FPS_GOOD, + fps if fps > 25.0 => COLOR_FPS_MEDIUM, + _ => COLOR_FPS_BAD, + }; + let block = profiler_flame_graph::Block { start, end, row, label, theme_color }; + blocks.push(block); + } + } + blocks +} + + // ============================ diff --git a/lib/rust/profiler/flame-graph/src/lib.rs b/lib/rust/profiler/flame-graph/src/lib.rs index 69b56ddf4b57..323422e17fc3 100644 --- a/lib/rust/profiler/flame-graph/src/lib.rs +++ b/lib/rust/profiler/flame-graph/src/lib.rs @@ -6,38 +6,40 @@ #![deny(non_ascii_idents)] #![warn(unsafe_code)] -use crate::State::Active; -use crate::State::Paused; - use enso_profiler as profiler; use enso_profiler_data as data; + +// ================= +// === Constants === +// ================= + +pub const COLOR_BLOCK_ACTIVE: &str = "flame_graph_block_color_active"; +pub const COLOR_BLOCK_PAUSED: &str = "flame_graph_block_color_paused"; +pub const COLOR_MARK_DEFAULT: &str = "flame_graph_mark_color"; + + +type RowNumber = i32; + + // ================== // === Block Data === // ================== -/// Indicates whether a block indicates an active interval or a paused interval. Paused intervals -/// are used to represent async tasks that are started and awaited, but that have made no progress. -#[derive(Copy, Clone, Debug)] -pub enum State { - Active, - Paused, -} - /// A `Block` contains the data required to render a single block of a frame graph. #[derive(Clone, Debug)] pub struct Block { /// Start x coordinate of the block. - pub start: f64, + pub start: f64, /// End x coordinate of the block. - pub end: f64, + pub end: f64, /// Row that the block should be placed in. - pub row: u32, + pub row: RowNumber, /// The label to be displayed with the block. - pub label: String, - /// Indicates what state this block represents (active/paused). - pub state: State, + pub label: String, + /// Indicates the name of the color for lookup in the theme. + pub theme_color: &'static str, } impl Block { @@ -135,7 +137,7 @@ impl<'p, Metadata> CallgraphBuilder<'p, Metadata> { impl<'p, Metadata> CallgraphBuilder<'p, Metadata> { /// Create a block for an interval; recurse into children. - fn visit_interval(&mut self, active: data::IntervalId, row: u32) { + fn visit_interval(&mut self, active: data::IntervalId, row: RowNumber) { let active = &self.profile[active]; let start = active.interval.start.into_ms(); let end = active.interval.end.map(|mark| mark.into_ms()).unwrap_or(f64::MAX); @@ -144,7 +146,7 @@ impl<'p, Metadata> CallgraphBuilder<'p, Metadata> { return; } let label = self.profile[active.measurement].label.to_string(); - self.blocks.push(Block { start, end, label, row, state: Active }); + self.blocks.push(Block { start, end, label, row, theme_color: COLOR_BLOCK_ACTIVE }); for child in &active.children { self.visit_interval(*child, row + 1); } @@ -161,7 +163,7 @@ impl<'p, Metadata> CallgraphBuilder<'p, Metadata> { struct RungraphBuilder<'p, Metadata> { profile: &'p data::Profile, blocks: Vec, - next_row: u32, + next_row: RowNumber, } impl<'p, Metadata> RungraphBuilder<'p, Metadata> { @@ -211,7 +213,7 @@ impl<'p, Metadata> RungraphBuilder<'p, Metadata> { end: active_interval[1], label: label_active, row, - state: Active, + theme_color: COLOR_BLOCK_ACTIVE, }); self.blocks.push(Block { @@ -219,7 +221,7 @@ impl<'p, Metadata> RungraphBuilder<'p, Metadata> { end: sleep_interval[1], label: label_sleep, row, - state: Paused, + theme_color: COLOR_BLOCK_PAUSED, }); } } From a78eb907ca037c417f609edd4ba5da50f56e43b6 Mon Sep 17 00:00:00 2001 From: Michael Mauderer Date: Tue, 19 Apr 2022 14:37:09 +0200 Subject: [PATCH 4/8] Rebase and update. --- Cargo.lock | 1 + app/gui/enso-profiler-metadata/Cargo.toml | 1 + app/gui/enso-profiler-metadata/src/lib.rs | 5 ++- .../ensogl/component/flame-graph/src/lib.rs | 31 ++------------- lib/rust/ensogl/core/src/display/world.rs | 19 +++++++++- lib/rust/ensogl/core/src/lib.rs | 1 - lib/rust/ensogl/core/src/profiler.rs | 38 ------------------- .../example/profiling-run-graph/Cargo.toml | 3 +- .../example/profiling-run-graph/src/lib.rs | 9 +---- lib/rust/profiler/flame-graph/src/lib.rs | 8 ++-- 10 files changed, 33 insertions(+), 83 deletions(-) delete mode 100644 lib/rust/ensogl/core/src/profiler.rs diff --git a/Cargo.lock b/Cargo.lock index 5c962b3aaf62..63b7972de5ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1198,6 +1198,7 @@ name = "enso-profiler-metadata" version = "0.1.0" dependencies = [ "enso-profiler", + "ensogl-core", "serde", ] diff --git a/app/gui/enso-profiler-metadata/Cargo.toml b/app/gui/enso-profiler-metadata/Cargo.toml index cc4c56ecb283..e1b559f4e92f 100644 --- a/app/gui/enso-profiler-metadata/Cargo.toml +++ b/app/gui/enso-profiler-metadata/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] +ensogl-core = { path = "../../../lib/rust/ensogl/core"} enso-profiler = { path = "../../../lib/rust/profiler"} serde = { version = "1" } diff --git a/app/gui/enso-profiler-metadata/src/lib.rs b/app/gui/enso-profiler-metadata/src/lib.rs index b66a716c76a9..246232b663da 100644 --- a/app/gui/enso-profiler-metadata/src/lib.rs +++ b/app/gui/enso-profiler-metadata/src/lib.rs @@ -10,16 +10,19 @@ use std::fmt::Formatter; // ================ /// Metadata that is logged within the Enso core libraries. -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Eq, PartialEq)] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub enum Metadata { /// An RPC event that was received from the backend. RpcEvent(String), + /// Performance stats gathered from the EnsoGL rendering engine. + RenderStats(ensogl_core::debug::StatsData), } impl Display for Metadata { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Metadata::RpcEvent(name) => f.collect_str(name), + Metadata::RenderStats(stats) => f.collect_str(&format!("{:#?}", stats)), } } } diff --git a/lib/rust/ensogl/component/flame-graph/src/lib.rs b/lib/rust/ensogl/component/flame-graph/src/lib.rs index d7d806d23a77..21c45b80e1c6 100644 --- a/lib/rust/ensogl/component/flame-graph/src/lib.rs +++ b/lib/rust/ensogl/component/flame-graph/src/lib.rs @@ -73,10 +73,7 @@ pub fn shape_from_block(block: profiler_flame_graph::Block, app: &Application) - let style = StyleWatchFrp::new(&app.display.default_scene.style_sheet); - let color: color::Rgba = match block.state { - State::Active => style.get_color("flame_graph_block_color_active").value(), - State::Paused => style.get_color("flame_graph_block_color_paused").value(), - }; + let color: color::Rgba = style.get_color(block.theme_color).value(); let color: color::Lcha = color.into(); component.set_color(color); @@ -95,31 +92,10 @@ fn shape_from_mark(mark: profiler_flame_graph::Mark, app: &Application) -> Mark component.set_content.emit(label); component.set_position_xy(pos); - let style = StyleWatchFrp::new(&app.display.default_scene.style_sheet); - - let color: color::Rgba = style.get_color(block.theme_color).value(); - let color: color::Lcha = color.into(); - component.set_color(color); - - component -} - -/// Instantiate a `Block` shape for the given block data from the profiler. -pub fn shape_from_mark(mark: profiler_flame_graph::Mark, app: &Application) -> Mark { - let component = app.new_view::(); - let x = mark.position as f32; - let y = 0.0; - let pos = Vector2::new(x, y); - - let label = format!("{} ({:.1}ms)", mark.label, mark.position); - - component.set_content.emit(label); - component.set_position_xy(pos); - component } -const MIN_INTERVAL_TIME: f64 = 0.0; +const MIN_INTERVAL_TIME_MS: f64 = 0.0; const X_SCALE: f64 = 1.0; fn align_block( @@ -157,8 +133,7 @@ impl FlameGraph { let blocks = blocks_zero_aligned.map(|block| shape_from_block(block, app)).collect_vec(); blocks.iter().for_each(|item| display_object.add_child(item)); - let marks: Vec<_> = - blocks_marks_aligned.into_iter().map(|mark| shape_from_mark(mark, app)).collect(); + let marks: Vec<_> = marks.into_iter().map(|mark| shape_from_mark(mark, app)).collect(); marks.iter().for_each(|item| display_object.add_child(item)); let app = app.clone_ref(); diff --git a/lib/rust/ensogl/core/src/display/world.rs b/lib/rust/ensogl/core/src/display/world.rs index 7c91a62078e6..8a8425c4b2fc 100644 --- a/lib/rust/ensogl/core/src/display/world.rs +++ b/lib/rust/ensogl/core/src/display/world.rs @@ -19,7 +19,6 @@ use crate::display::render; use crate::display::render::passes::SymbolsRenderPass; use crate::display::scene::DomPath; use crate::display::scene::Scene; -use crate::profiler::log_stats_data; use crate::system::web; use web::prelude::Closure; @@ -56,6 +55,22 @@ impl Uniforms { } +// ========================= +// === Metadata Profiler === +// ========================= + + +thread_local! { + /// A common preamble used to start every shader program. + static RENDER_STATS_LOGGER: enso_profiler::MetadataLogger = enso_profiler::MetadataLogger::new("RenderStats"); +} + +/// Log rendering stats to the profiling framework. +pub fn log_render_stats(stats: StatsData) { + RENDER_STATS_LOGGER.with(|logger| logger.log(stats)); +} + + // ============= // === World === @@ -207,7 +222,7 @@ impl WorldData { let garbage_collector = default(); let stats_draw_handle = on.prev_frame_stats.add(f!([stats_monitor] (stats: &StatsData) { stats_monitor.sample_and_draw(stats); - log_stats_data(*stats) + log_render_stats(*stats) })); Self { diff --git a/lib/rust/ensogl/core/src/lib.rs b/lib/rust/ensogl/core/src/lib.rs index b4e53a1ae8fb..be8d35b103ef 100644 --- a/lib/rust/ensogl/core/src/lib.rs +++ b/lib/rust/ensogl/core/src/lib.rs @@ -48,7 +48,6 @@ pub mod data; pub mod debug; pub mod display; pub mod gui; -pub mod profiler; pub mod system; pub use animation::Animation; diff --git a/lib/rust/ensogl/core/src/profiler.rs b/lib/rust/ensogl/core/src/profiler.rs deleted file mode 100644 index 27ea9e6c138f..000000000000 --- a/lib/rust/ensogl/core/src/profiler.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! Enso specific metadata logging utilities for use with the profiling framework. -use crate::debug::StatsData; -use enso_profiler; -use serde; -use serde::Serializer; -use std::fmt::Display; -use std::fmt::Formatter; - - -/// Metadata that is logged within the Enso core libraries. -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub enum Metadata { - /// An RPC event that was received from the backend. - RpcEvent(String), - /// Performance stats gathered from the EnsoGL rendering engine. - RenderStats(StatsData), -} - -impl Display for Metadata { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - Metadata::RpcEvent(name) => f.collect_str(name), - Metadata::RenderStats(stats) => f.collect_str(&format!("{:#?}", stats)), - } - } -} - -/// Log an RPC Event to the profiling framework. -pub fn log_rpc_event(event_name: &'static str) { - let event_logger = enso_profiler::MetadataLogger::new("RpcEvent"); - event_logger.log(event_name); -} - -/// Log an RPC Event to the profiling framework. -pub fn log_stats_data(data: StatsData) { - let event_logger = enso_profiler::MetadataLogger::new("RenderStats"); - event_logger.log(data); -} diff --git a/lib/rust/ensogl/example/profiling-run-graph/Cargo.toml b/lib/rust/ensogl/example/profiling-run-graph/Cargo.toml index 0cf88c2fa120..cebd649c409d 100644 --- a/lib/rust/ensogl/example/profiling-run-graph/Cargo.toml +++ b/lib/rust/ensogl/example/profiling-run-graph/Cargo.toml @@ -11,9 +11,8 @@ crate-type = ["cdylib", "rlib"] enso-frp = { path = "../../../frp" } enso-profiler = { path = "../../../profiler" } enso-profiler-data = { path = "../../../profiler/data" } -enso-profiler-metadata = { path = "../../../../../app/gui/enso-profiler-metadata" } -enso-profiler-data = { path = "../../../profiler/data" } enso-profiler-flame-graph = { path = "../../../profiler/flame-graph" } +enso-profiler-metadata = { path = "../../../../../app/gui/enso-profiler-metadata" } enso-web = { path = "../../../web" } ensogl-core = { path = "../../core" } ensogl-flame-graph = { path = "../../component/flame-graph" } diff --git a/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs b/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs index fcb3bde79c39..aa7473f9343e 100644 --- a/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs +++ b/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs @@ -21,18 +21,16 @@ use enso_profiler as profiler; use enso_profiler::profile; use enso_profiler_data::Profile; use enso_profiler_flame_graph as profiler_flame_graph; -use enso_profiler_metadata::Metadata; use enso_profiler_flame_graph::COLOR_BLOCK_ACTIVE; use enso_profiler_flame_graph::COLOR_BLOCK_PAUSED; use enso_profiler_flame_graph::COLOR_MARK_DEFAULT; +use enso_profiler_metadata::Metadata; use ensogl_core::application::Application; use ensogl_core::data::color; use ensogl_core::display::navigation::navigator::Navigator; use ensogl_core::display::object::ObjectOps; use ensogl_core::display::style::theme; use ensogl_core::display::Scene; -use ensogl_core::profiler::log_rpc_event; -use ensogl_core::profiler::Metadata; use ensogl_core::system::web; use ensogl_flame_graph as flame_graph; @@ -130,7 +128,7 @@ fn init_theme(scene: &Scene) { fn make_marks_from_profile(profile: &Profile) -> Vec { profile .iter_metadata() - .filter_map(|metadata: &enso_profiler_data::Metadata| { + .filter_map(|metadata: &enso_profiler_data::Metadata| { let position = metadata.mark.into_ms(); match metadata.data { Metadata::RenderStats(_) => None, @@ -170,7 +168,6 @@ fn make_rendering_performance_blocks( - // ============================ // === Profiler Log Reading === // ============================ @@ -232,9 +229,7 @@ fn work(n: u32) { #[profile(Objective)] async fn start_project() { wake_dragon().await; - log_rpc_event("Dragon Awake"); feed_troll(); - log_rpc_event("Troll Fed"); ride_rainbow(); } #[profile(Objective)] diff --git a/lib/rust/profiler/flame-graph/src/lib.rs b/lib/rust/profiler/flame-graph/src/lib.rs index 323422e17fc3..28e451d646e2 100644 --- a/lib/rust/profiler/flame-graph/src/lib.rs +++ b/lib/rust/profiler/flame-graph/src/lib.rs @@ -234,7 +234,7 @@ impl<'p, Metadata> RungraphBuilder<'p, Metadata> { end: first.interval.start.into_ms(), label: self.profile[first.measurement].label.to_string(), row, - state: Paused, + theme_color: COLOR_BLOCK_PAUSED, }); // Add last active interval. @@ -245,7 +245,7 @@ impl<'p, Metadata> RungraphBuilder<'p, Metadata> { end: last.interval.end.map(|end| end.into_ms()).unwrap_or(f64::INFINITY), label: self.profile[last.measurement].label.to_string(), row, - state: Active, + theme_color: COLOR_BLOCK_ACTIVE, }); } @@ -317,10 +317,10 @@ struct FlamegraphGrapher { } impl FlamegraphGrapher { - fn visit_frame(&mut self, frame: &data::aggregate::Frame, label: String, row: u32) { + fn visit_frame(&mut self, frame: &data::aggregate::Frame, label: String, row: RowNumber) { let start = self.time; let end = self.time + frame.total_duration(); - self.blocks.push(Block { start, end, label, row, state: State::Active }); + self.blocks.push(Block { start, end, label, row, theme_color: COLOR_BLOCK_ACTIVE }); for (label, frame) in &frame.children { self.visit_frame(frame, label.to_string(), row + 1); } From ff9782fa6a15118bc05fb45be16661fa686be0d7 Mon Sep 17 00:00:00 2001 From: Michael Mauderer Date: Tue, 19 Apr 2022 16:08:53 +0200 Subject: [PATCH 5/8] Increase wasm size limit. --- build/run.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/run.js b/build/run.js index 0fffea70d7e5..60ef860ef52d 100755 --- a/build/run.js +++ b/build/run.js @@ -198,7 +198,7 @@ commands.build.rust = async function (argv) { console.log('Minimizing the WASM binary.') await gzip(paths.wasm.main, paths.wasm.mainGz) - const limitMb = 4.05 + const limitMb = 4.06 await checkWasmSize(paths.wasm.mainGz, limitMb) } // Copy WASM files from temporary directory to Webpack's `dist` directory. From a3de180ea67a3d501e3596cb9ec78217eef31e12 Mon Sep 17 00:00:00 2001 From: Michael Mauderer Date: Wed, 20 Apr 2022 12:21:25 +0200 Subject: [PATCH 6/8] Review feedback. --- app/gui/src/model/project/synchronized.rs | 2 +- .../ensogl/component/flame-graph/src/lib.rs | 131 +++++++++++++----- lib/rust/ensogl/core/src/display/world.rs | 2 +- .../example/profiling-run-graph/src/lib.rs | 39 +++--- lib/rust/profiler/flame-graph/src/lib.rs | 86 +++++++----- 5 files changed, 173 insertions(+), 87 deletions(-) diff --git a/app/gui/src/model/project/synchronized.rs b/app/gui/src/model/project/synchronized.rs index 5ac4cb8f4854..d04fd3ba4e03 100644 --- a/app/gui/src/model/project/synchronized.rs +++ b/app/gui/src/model/project/synchronized.rs @@ -33,7 +33,7 @@ use parser::Parser; // ================= thread_local! { - /// A common preamble used to start every shader program. + /// Profiling metadata logger for RPC event names.. static RPC_EVENT_LOGGER: enso_profiler::MetadataLogger<& 'static str> = enso_profiler::MetadataLogger::new("RpcEvent"); } diff --git a/lib/rust/ensogl/component/flame-graph/src/lib.rs b/lib/rust/ensogl/component/flame-graph/src/lib.rs index 21c45b80e1c6..fe38178a1997 100644 --- a/lib/rust/ensogl/component/flame-graph/src/lib.rs +++ b/lib/rust/ensogl/component/flame-graph/src/lib.rs @@ -24,8 +24,11 @@ use ensogl_core::display; mod block; mod mark; +use enso_profiler_flame_graph::Activity; +use enso_profiler_flame_graph::Performance; use ensogl_core::data::color; use ensogl_core::display::shape::StyleWatchFrp; +use ensogl_core::display::style; pub use block::Block; pub use mark::Mark; @@ -42,6 +45,61 @@ pub(crate) const BASE_TEXT_SIZE: f32 = 18.0; +// ============== +// === Colors === +// ============== + +/// Theme path for the color of an activity block that is active. +pub const COLOR_BLOCK_ACTIVE: &str = "flame_graph_block_color_active"; +/// Theme path for the color of an activity block that is paused. +pub const COLOR_BLOCK_PAUSED: &str = "flame_graph_block_color_paused"; + +/// Theme path for the color of a performance block that indicates good performance. +pub const COLOR_PERFORMANCE_GOOD: &str = "flame_graph_block_color_performance_good"; +/// Theme path for the color of a performance block that indicates medium performance. +pub const COLOR_PERFORMANCE_MEDIUM: &str = "flame_graph_block_color_performance_medium"; +/// Theme path for the color of a performance block that indicates bad performance.. +pub const COLOR_PERFORMANCE_BAD: &str = "flame_graph_block_color_performance_bad"; + +/// Theme path for the color that is sued to color a mark. +pub const COLOR_MARK_DEFAULT: &str = "flame_graph_mark_color"; + + +/// Trait that allows retrieval of a style::Path. +pub trait IntoThemePath { + /// Return the `style::Path` associated with this object. + fn theme_path(&self) -> style::Path; +} + +impl IntoThemePath for Activity { + fn theme_path(&self) -> style::Path { + match self { + Activity::Active => COLOR_BLOCK_ACTIVE, + Activity::Paused => COLOR_BLOCK_PAUSED, + } + .into() + } +} + +impl IntoThemePath for Performance { + fn theme_path(&self) -> style::Path { + match self { + Performance::Good => COLOR_PERFORMANCE_GOOD, + Performance::Medium => COLOR_PERFORMANCE_MEDIUM, + Performance::Bad => COLOR_PERFORMANCE_BAD, + } + .into() + } +} + +impl IntoThemePath for profiler_flame_graph::Block { + fn theme_path(&self) -> style::Path { + self.block_type.theme_path() + } +} + + + // =================== // === Flame Graph === // =================== @@ -59,9 +117,17 @@ pub struct FlameGraph { } /// Instantiate a `Block` shape for the given block data from the profiler. -pub fn shape_from_block(block: profiler_flame_graph::Block, app: &Application) -> Block { +pub fn shape_from_block( + block: profiler_flame_graph::Block, + app: &Application, +) -> Block { let component = app.new_view::(); + let style = StyleWatchFrp::new(&app.display.default_scene.style_sheet); + let color: color::Rgba = style.get_color(block.theme_path()).value(); + let color: color::Lcha = color.into(); + component.set_color(color); + let size = Vector2::new(block.width() as f32, ROW_HEIGHT as f32); let x = block.start + block.width() / 2.0; let y = block.row as f64 * (ROW_HEIGHT + ROW_PADDING); @@ -71,12 +137,6 @@ pub fn shape_from_block(block: profiler_flame_graph::Block, app: &Application) - component.set_size.emit(size); component.set_position_xy(pos); - let style = StyleWatchFrp::new(&app.display.default_scene.style_sheet); - - let color: color::Rgba = style.get_color(block.theme_color).value(); - let color: color::Lcha = color.into(); - component.set_color(color); - component } @@ -98,10 +158,10 @@ fn shape_from_mark(mark: profiler_flame_graph::Mark, app: &Application) -> Mark const MIN_INTERVAL_TIME_MS: f64 = 0.0; const X_SCALE: f64 = 1.0; -fn align_block( - mut block: profiler_flame_graph::Block, +fn align_block( + mut block: profiler_flame_graph::Block, origin_x: f64, -) -> profiler_flame_graph::Block { +) -> profiler_flame_graph::Block { // Shift block.start -= origin_x; block.end -= origin_x; @@ -113,7 +173,7 @@ fn align_block( fn align_mark(mut mark: profiler_flame_graph::Mark, origin_x: f64) -> profiler_flame_graph::Mark { mark.position -= origin_x; - mark.position += X_SCALE; + mark.position *= X_SCALE; mark } @@ -123,14 +183,23 @@ impl FlameGraph { let logger = Logger::new("FlameGraph"); let display_object = display::object::Instance::new(&logger); - let blocks = data.blocks.into_iter().filter(|block| block.width() > MIN_INTERVAL_TIME_MS); + let activity_blocks = + data.activity_blocks.into_iter().filter(|block| block.width() > MIN_INTERVAL_TIME_MS); + let performance_blocks = data.performance_blocks.into_iter(); let marks = data.marks; - let origin_x = - blocks.clone().map(|block| block.start.floor() as u32).min().unwrap_or_default() as f64; + let origin_x = activity_blocks + .clone() + .map(|block| block.start.floor() as u32) + .min() + .unwrap_or_default() as f64; + + let activity_block_shapes = + activity_blocks.map(|block| shape_from_block(align_block(block, origin_x), app)); + let performance_block_shapes = + performance_blocks.map(|block| shape_from_block(align_block(block, origin_x), app)); - let blocks_zero_aligned = blocks.clone().map(|block| align_block(block, origin_x)); - let blocks = blocks_zero_aligned.map(|block| shape_from_block(block, app)).collect_vec(); + let blocks = activity_block_shapes.chain(performance_block_shapes).collect_vec(); blocks.iter().for_each(|item| display_object.add_child(item)); let marks: Vec<_> = marks.into_iter().map(|mark| shape_from_mark(mark, app)).collect(); @@ -150,27 +219,23 @@ impl FlameGraph { &self.marks } - /// Add additional blocks to the visualisation. - pub fn add_blocks>( + /// Add an additional activity block to the visualisation. + pub fn add_block( &mut self, - blocks: Blocks, + block: profiler_flame_graph::Block, ) { - for block in blocks { - let block = align_block(block, self.origin_x); - let shape = shape_from_block(block, &self.app); - self.display_object.add_child(&shape); - self.blocks.push(shape); - } + let block = align_block(block, self.origin_x); + let shape = shape_from_block(block, &self.app); + self.display_object.add_child(&shape); + self.blocks.push(shape); } - /// Add additional marks to the visualisation. - pub fn add_marks>(&mut self, marks: Marks) { - for mark in marks { - let mark = align_mark(mark, self.origin_x); - let shape = shape_from_mark(mark, &self.app); - self.display_object.add_child(&shape); - self.marks.push(shape); - } + /// Add additional mark to the visualisation. + pub fn add_mark(&mut self, mark: profiler_flame_graph::Mark) { + let mark = align_mark(mark, self.origin_x); + let shape = shape_from_mark(mark, &self.app); + self.display_object.add_child(&shape); + self.marks.push(shape); } } diff --git a/lib/rust/ensogl/core/src/display/world.rs b/lib/rust/ensogl/core/src/display/world.rs index 8a8425c4b2fc..93f28c0d238c 100644 --- a/lib/rust/ensogl/core/src/display/world.rs +++ b/lib/rust/ensogl/core/src/display/world.rs @@ -61,7 +61,7 @@ impl Uniforms { thread_local! { - /// A common preamble used to start every shader program. + /// Profiling metadata logger for `StatsData`. static RENDER_STATS_LOGGER: enso_profiler::MetadataLogger = enso_profiler::MetadataLogger::new("RenderStats"); } diff --git a/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs b/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs index aa7473f9343e..0877ee7744de 100644 --- a/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs +++ b/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs @@ -21,9 +21,7 @@ use enso_profiler as profiler; use enso_profiler::profile; use enso_profiler_data::Profile; use enso_profiler_flame_graph as profiler_flame_graph; -use enso_profiler_flame_graph::COLOR_BLOCK_ACTIVE; -use enso_profiler_flame_graph::COLOR_BLOCK_PAUSED; -use enso_profiler_flame_graph::COLOR_MARK_DEFAULT; +use enso_profiler_flame_graph::Performance; use enso_profiler_metadata::Metadata; use ensogl_core::application::Application; use ensogl_core::data::color; @@ -33,7 +31,12 @@ use ensogl_core::display::style::theme; use ensogl_core::display::Scene; use ensogl_core::system::web; use ensogl_flame_graph as flame_graph; - +use ensogl_flame_graph::COLOR_BLOCK_ACTIVE; +use ensogl_flame_graph::COLOR_BLOCK_PAUSED; +use ensogl_flame_graph::COLOR_MARK_DEFAULT; +use ensogl_flame_graph::COLOR_PERFORMANCE_BAD; +use ensogl_flame_graph::COLOR_PERFORMANCE_GOOD; +use ensogl_flame_graph::COLOR_PERFORMANCE_MEDIUM; // ================= @@ -44,11 +47,7 @@ use ensogl_flame_graph as flame_graph; /// generated and rendered. The file must be located in the assets subdirectory that is /// served by the webserver, i.e. `enso/dist/content` or `app/ide-desktop/lib/content/assets`. /// For example use `Some("profile.json"))`. -const PROFILER_LOG_NAME: Option<&str> = None; - -const COLOR_FPS_GOOD: &str = "flame_graph_block_color_fps_good"; -const COLOR_FPS_MEDIUM: &str = "flame_graph_block_color_fps_medium"; -const COLOR_FPS_BAD: &str = "flame_graph_block_color_fps_bad"; +const PROFILER_LOG_NAME: Option<&str> = Some("profile.json"); @@ -79,10 +78,10 @@ pub async fn main() { let mut flame_graph = flame_graph::FlameGraph::from_data(measurements, app); let marks = make_marks_from_profile(&profile); - flame_graph.add_marks(marks.into_iter()); + marks.into_iter().for_each(|mark| flame_graph.add_mark(mark)); let performance_blocks = make_rendering_performance_blocks(&profile); - flame_graph.add_blocks(performance_blocks.into_iter()); + performance_blocks.into_iter().for_each(|block| flame_graph.add_block(block)); world.add_child(&flame_graph); scene.add_child(&flame_graph); @@ -108,9 +107,9 @@ fn init_theme(scene: &Scene) { let theme = theme::Theme::new(); theme.set(COLOR_BLOCK_ACTIVE, color::Lcha::blue_green(0.5, 0.8)); theme.set(COLOR_BLOCK_PAUSED, color::Lcha::blue_green(0.8, 0.0)); - theme.set(COLOR_FPS_BAD, color::Lcha::red(0.4, 0.5)); - theme.set(COLOR_FPS_GOOD, color::Lcha::green(0.8, 0.5)); - theme.set(COLOR_FPS_MEDIUM, color::Lcha::yellow(0.6, 0.5)); + theme.set(COLOR_PERFORMANCE_BAD, color::Lcha::red(0.4, 0.5)); + theme.set(COLOR_PERFORMANCE_GOOD, color::Lcha::green(0.8, 0.5)); + theme.set(COLOR_PERFORMANCE_MEDIUM, color::Lcha::yellow(0.6, 0.5)); theme.set(COLOR_MARK_DEFAULT, color::Lcha::blue_green(0.9, 0.1)); theme_manager.register("theme", theme); @@ -143,7 +142,7 @@ fn make_marks_from_profile(profile: &Profile) -> Vec, -) -> Vec { +) -> Vec> { let mut blocks = Vec::default(); let render_stats = profile .iter_metadata() @@ -154,12 +153,12 @@ fn make_rendering_performance_blocks( let end = current.mark.into_ms(); let row = -1; let label = format!("{:#?}", data); - let theme_color = match data.fps { - fps if fps > 55.0 => COLOR_FPS_GOOD, - fps if fps > 25.0 => COLOR_FPS_MEDIUM, - _ => COLOR_FPS_BAD, + let block_type = match data.fps { + fps if fps > 55.0 => Performance::Good, + fps if fps > 25.0 => Performance::Medium, + _ => Performance::Bad, }; - let block = profiler_flame_graph::Block { start, end, row, label, theme_color }; + let block = profiler_flame_graph::Block { start, end, row, label, block_type }; blocks.push(block); } } diff --git a/lib/rust/profiler/flame-graph/src/lib.rs b/lib/rust/profiler/flame-graph/src/lib.rs index 28e451d646e2..49ac56c4dfa4 100644 --- a/lib/rust/profiler/flame-graph/src/lib.rs +++ b/lib/rust/profiler/flame-graph/src/lib.rs @@ -15,11 +15,6 @@ use enso_profiler_data as data; // === Constants === // ================= -pub const COLOR_BLOCK_ACTIVE: &str = "flame_graph_block_color_active"; -pub const COLOR_BLOCK_PAUSED: &str = "flame_graph_block_color_paused"; -pub const COLOR_MARK_DEFAULT: &str = "flame_graph_mark_color"; - - type RowNumber = i32; @@ -27,22 +22,35 @@ type RowNumber = i32; // === Block Data === // ================== +#[derive(Copy, Clone, Debug)] +pub enum Activity { + Active, + Paused, +} + +#[derive(Copy, Clone, Debug)] +pub enum Performance { + Good, + Medium, + Bad, +} + /// A `Block` contains the data required to render a single block of a frame graph. #[derive(Clone, Debug)] -pub struct Block { +pub struct Block { /// Start x coordinate of the block. - pub start: f64, + pub start: f64, /// End x coordinate of the block. - pub end: f64, + pub end: f64, /// Row that the block should be placed in. - pub row: RowNumber, + pub row: RowNumber, /// The label to be displayed with the block. - pub label: String, - /// Indicates the name of the color for lookup in the theme. - pub theme_color: &'static str, + pub label: String, + /// Indicates the type of the block. + pub block_type: T, } -impl Block { +impl Block { /// Width of the block. pub fn width(&self) -> f64 { self.end - self.start @@ -74,9 +82,11 @@ pub struct Mark { #[derive(Debug, Default)] pub struct Graph { /// Collection of all blocks making up the flame graph. - pub blocks: Vec, + pub activity_blocks: Vec>, + /// Collection of all blocks indicating performance characteristics. + pub performance_blocks: Vec>, /// Collection of marks that can be shown in the flame graph. - pub marks: Vec, + pub marks: Vec, } impl Graph { @@ -117,7 +127,7 @@ impl Graph { /// Build a graph that illustrates the call stack over time. struct CallgraphBuilder<'p, Metadata> { profile: &'p data::Profile, - blocks: Vec, + blocks: Vec>, } impl<'p, Metadata> CallgraphBuilder<'p, Metadata> { @@ -130,8 +140,11 @@ impl<'p, Metadata> CallgraphBuilder<'p, Metadata> { builder.visit_interval(*child, 0); } let Self { blocks, .. } = builder; - let marks = Vec::default(); - Graph { blocks, marks } + Graph { + activity_blocks: blocks, + marks: Vec::default(), + performance_blocks: Vec::default(), + } } } @@ -146,7 +159,7 @@ impl<'p, Metadata> CallgraphBuilder<'p, Metadata> { return; } let label = self.profile[active.measurement].label.to_string(); - self.blocks.push(Block { start, end, label, row, theme_color: COLOR_BLOCK_ACTIVE }); + self.blocks.push(Block { start, end, label, row, block_type: Activity::Active }); for child in &active.children { self.visit_interval(*child, row + 1); } @@ -162,7 +175,7 @@ impl<'p, Metadata> CallgraphBuilder<'p, Metadata> { /// Build a graph that illustrates async tasks over time. struct RungraphBuilder<'p, Metadata> { profile: &'p data::Profile, - blocks: Vec, + blocks: Vec>, next_row: RowNumber, } @@ -177,8 +190,11 @@ impl<'p, Metadata> RungraphBuilder<'p, Metadata> { builder.visit_measurement(*child); } let Self { blocks, .. } = builder; - let marks = Vec::default(); - Graph { blocks, marks } + Graph { + activity_blocks: blocks, + marks: Vec::default(), + performance_blocks: Vec::default(), + } } } @@ -213,7 +229,7 @@ impl<'p, Metadata> RungraphBuilder<'p, Metadata> { end: active_interval[1], label: label_active, row, - theme_color: COLOR_BLOCK_ACTIVE, + block_type: Activity::Active, }); self.blocks.push(Block { @@ -221,7 +237,7 @@ impl<'p, Metadata> RungraphBuilder<'p, Metadata> { end: sleep_interval[1], label: label_sleep, row, - theme_color: COLOR_BLOCK_PAUSED, + block_type: Activity::Paused, }); } } @@ -234,7 +250,7 @@ impl<'p, Metadata> RungraphBuilder<'p, Metadata> { end: first.interval.start.into_ms(), label: self.profile[first.measurement].label.to_string(), row, - theme_color: COLOR_BLOCK_PAUSED, + block_type: Activity::Paused, }); // Add last active interval. @@ -245,7 +261,7 @@ impl<'p, Metadata> RungraphBuilder<'p, Metadata> { end: last.interval.end.map(|end| end.into_ms()).unwrap_or(f64::INFINITY), label: self.profile[last.measurement].label.to_string(), row, - theme_color: COLOR_BLOCK_ACTIVE, + block_type: Activity::Active, }); } @@ -273,8 +289,11 @@ fn new_hybrid_graph(profile: &data::Profile) -> Graph { callgraph.visit_interval(*child, next_row); } let CallgraphBuilder { blocks, .. } = callgraph; - let marks = Vec::default(); - Graph { blocks, marks } + Graph { + activity_blocks: blocks, + marks: Vec::default(), + performance_blocks: Vec::default(), + } } @@ -304,15 +323,18 @@ impl From for Graph { grapher.visit_frame(frame, label.to_string(), 0); } let FlamegraphGrapher { blocks, .. } = grapher; - let marks = Vec::default(); - Self { blocks, marks } + Graph { + activity_blocks: blocks, + marks: Vec::default(), + performance_blocks: Vec::default(), + } } } /// Builds a flamegraph [`Graph`] from [`data::aggregate::Frame`]s. #[derive(Default)] struct FlamegraphGrapher { - blocks: Vec, + blocks: Vec>, time: f64, } @@ -320,7 +342,7 @@ impl FlamegraphGrapher { fn visit_frame(&mut self, frame: &data::aggregate::Frame, label: String, row: RowNumber) { let start = self.time; let end = self.time + frame.total_duration(); - self.blocks.push(Block { start, end, label, row, theme_color: COLOR_BLOCK_ACTIVE }); + self.blocks.push(Block { start, end, label, row, block_type: Activity::Active }); for (label, frame) in &frame.children { self.visit_frame(frame, label.to_string(), row + 1); } From 2940f6e6c859c16e874afabe3fc24a63167b9b5e Mon Sep 17 00:00:00 2001 From: Michael Mauderer Date: Wed, 20 Apr 2022 12:27:16 +0200 Subject: [PATCH 7/8] Fix tests. --- lib/rust/profiler/flame-graph/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/rust/profiler/flame-graph/src/lib.rs b/lib/rust/profiler/flame-graph/src/lib.rs index 49ac56c4dfa4..57de79ff6252 100644 --- a/lib/rust/profiler/flame-graph/src/lib.rs +++ b/lib/rust/profiler/flame-graph/src/lib.rs @@ -375,11 +375,11 @@ mod tests { let profile: data::Profile = profiler::internal::take_log().parse().unwrap(); let flame_graph = Graph::new_callgraph(&profile); - assert_eq!(flame_graph.blocks.len(), 2); + assert_eq!(flame_graph.activity_blocks.len(), 2); - assert_eq!(flame_graph.blocks[1].row, 1); - assert!(flame_graph.blocks[1].label.contains("profiled_b")); - assert_eq!(flame_graph.blocks[0].row, 0); - assert!(flame_graph.blocks[0].label.contains("profiled_a")); + assert_eq!(flame_graph.activity_blocks[1].row, 1); + assert!(flame_graph.activity_blocks[1].label.contains("profiled_b")); + assert_eq!(flame_graph.activity_blocks[0].row, 0); + assert!(flame_graph.activity_blocks[0].label.contains("profiled_a")); } } From 08835e955b3cc6b2f2133874441b1d88aeeb09c2 Mon Sep 17 00:00:00 2001 From: Michael Mauderer Date: Thu, 21 Apr 2022 10:20:22 +0200 Subject: [PATCH 8/8] Refactor unwrapping/iteration. --- .../example/profiling-run-graph/src/lib.rs | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs b/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs index 0877ee7744de..eed84b48f025 100644 --- a/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs +++ b/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs @@ -144,23 +144,25 @@ fn make_rendering_performance_blocks( profile: &Profile, ) -> Vec> { let mut blocks = Vec::default(); - let render_stats = profile - .iter_metadata() - .filter(|metadata| matches!(metadata.data, Metadata::RenderStats(_))); - for (prev, current) in render_stats.tuple_windows() { - if let Metadata::RenderStats(data) = current.data { - let start = prev.mark.into_ms(); - let end = current.mark.into_ms(); - let row = -1; - let label = format!("{:#?}", data); - let block_type = match data.fps { - fps if fps > 55.0 => Performance::Good, - fps if fps > 25.0 => Performance::Medium, - _ => Performance::Bad, - }; - let block = profiler_flame_graph::Block { start, end, row, label, block_type }; - blocks.push(block); + let render_stats = profile.iter_metadata().filter_map(|metadata| match metadata.data { + Metadata::RenderStats(data) => { + let mark = metadata.mark; + Some(enso_profiler_data::Metadata { mark, data }) } + _ => None, + }); + for (prev, current) in render_stats.tuple_windows() { + let start = prev.mark.into_ms(); + let end = current.mark.into_ms(); + let row = -1; + let label = format!("{:#?}", current.data); + let block_type = match current.data.fps { + fps if fps > 55.0 => Performance::Good, + fps if fps > 25.0 => Performance::Medium, + _ => Performance::Bad, + }; + let block = profiler_flame_graph::Block { start, end, row, label, block_type }; + blocks.push(block); } blocks }