From 5315abb9f146bdf6b06e50f345acac759ca57222 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Thu, 13 Jan 2022 00:33:02 +0100 Subject: [PATCH] debug: First iteration - Flesh out FPS counters and graph - Add opacity slider - Add specialized views - Populate workspace state inspector (very basic) --- Cargo.lock | 5 +- Cargo.toml | 6 +- src/backend/x11.rs | 26 ++---- src/debug.rs | 174 ++++++++++++++++++++++++++++++++++++++++ src/main.rs | 4 +- src/rendering/debug.rs | 66 --------------- src/rendering/mod.rs | 6 -- src/shell/workspaces.rs | 12 ++- src/state.rs | 50 ++++++++---- 9 files changed, 234 insertions(+), 115 deletions(-) create mode 100644 src/debug.rs delete mode 100644 src/rendering/debug.rs delete mode 100644 src/rendering/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 2c57008c..2495aacb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -752,7 +752,7 @@ checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" [[package]] name = "smithay" version = "0.3.0" -source = "git+https://github.com/Smithay/smithay.git?branch=feature/egl_userdata#1edff977226474e25e0fe90cbfb91860978b80cc" +source = "git+https://github.com/Smithay/smithay.git?branch=feature/egl_userdata#5cbd4352b9043cf634fb0b950ef244d9aaa9e151" dependencies = [ "appendlist", "bitflags", @@ -773,6 +773,7 @@ dependencies = [ "rand", "scan_fmt", "slog", + "slog-stdlog", "tempfile", "thiserror", "wayland-commons", @@ -786,7 +787,7 @@ dependencies = [ [[package]] name = "smithay-egui" version = "0.1.0" -source = "git+https://github.com/Smithay/smithay-egui.git?rev=ad146d59#ad146d59cc35199ef68024cc04c0a6e0c40b0d91" +source = "git+https://github.com/Smithay/smithay-egui.git#baa131d77b2fc2e3c2e87e881bba3ee18b315d14" dependencies = [ "egui", "lazy_static", diff --git a/Cargo.toml b/Cargo.toml index 0a084a24..11c49d8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ authors = ["Victoria Brekenfeld"] anyhow = { version = "1.0.51", features = ["backtrace"] } bitflags = "1.3.2" slog = "2.7" +#slog = { version = "2.7", features = ["max_level_trace"] } slog-term = "2.8" slog-async = "2.7" slog-scope = "4.4" @@ -20,13 +21,12 @@ version = "0.3" git = "https://github.com/Smithay/smithay.git" branch = "feature/egl_userdata" default-features = false -features = ["backend_x11", "backend_egl", "desktop", "use_system_lib", "renderer_gl", "wayland_frontend"] +features = ["backend_x11", "backend_egl", "desktop", "use_system_lib", "renderer_gl", "wayland_frontend", "slog-stdlog"] [dependencies.smithay-egui] git = "https://github.com/Smithay/smithay-egui.git" -rev = "ad146d59" optional = true [features] -default = ["debug"] +default = [] debug = ["egui", "smithay-egui"] \ No newline at end of file diff --git a/src/backend/x11.rs b/src/backend/x11.rs index 4057ca63..740d7bf7 100644 --- a/src/backend/x11.rs +++ b/src/backend/x11.rs @@ -13,7 +13,7 @@ use smithay::{ drm::DrmNode, egl::{EGLContext, EGLDisplay}, input::{Event, InputEvent}, - renderer::{gles2::Gles2Renderer, Bind, ImportDma, ImportEgl, Unbind}, + renderer::{gles2::Gles2Renderer, Bind, ImportDma, ImportEgl}, x11::{Window, WindowBuilder, X11Backend, X11Event, X11Handle, X11Input, X11Surface}, }, desktop::layer_map_for_output, @@ -37,7 +37,7 @@ use std::{ }; #[cfg(feature = "debug")] -use crate::{rendering::debug_ui, state::Fps}; +use crate::{debug::debug_ui, state::Fps}; pub struct X11State { allocator: Arc>>, @@ -145,26 +145,19 @@ impl Surface { ) -> Result<()> { #[allow(unused_mut)] let mut custom_elements = Vec::new(); - let space = state.spaces.active_space_mut(output); #[cfg(feature = "debug")] - if state.egui.active { + { + let space = state.spaces.active_space(output); let size = space.output_geometry(&self.output).unwrap(); let scale = space.output_scale(&self.output).unwrap(); - let frame = state.egui.state.run( - |ctx| debug_ui(ctx, &self.fps, true), - size, - size.to_f64().to_physical(scale).to_i32_round().size, - scale, - state.egui.alpha, - &state.start_time, - state.egui.modifiers.clone(), - ); + let frame = debug_ui(state, &self.fps, size, scale, true); custom_elements.push( Box::new(frame) as smithay::desktop::space::DynamicRenderElements ); } + let space = state.spaces.active_space_mut(output); let (buffer, age) = self .surface .buffer() @@ -179,8 +172,7 @@ impl Surface { [0.153, 0.161, 0.165, 1.0], &*custom_elements, ) { - Ok(Some(_)) => { - slog_scope::trace!("Finished rendering"); + Ok(_) => { space.send_frames(false, state.start_time.elapsed().as_millis() as u32); self.surface .submit() @@ -190,10 +182,6 @@ impl Surface { self.fps.tick(); } } - Ok(None) => { - let _ = renderer.unbind(); - self.render.ping(); - } Err(err) => { self.surface.reset_buffers(); anyhow::bail!("Rendering failed: {}", err); diff --git a/src/debug.rs b/src/debug.rs new file mode 100644 index 00000000..d137bcb2 --- /dev/null +++ b/src/debug.rs @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use crate::state::{Common, Fps}; +use smithay_egui::EguiFrame; +use smithay::utils::{Rectangle, Logical}; + +pub fn debug_ui(state: &mut Common, fps: &Fps, area: Rectangle, scale: f64, primary: bool) -> EguiFrame { + let size = area.size; + let alpha = state.egui.alpha; + + state.egui.state.run( + |ctx| { + egui::Area::new("main") + .anchor(egui::Align2::LEFT_TOP, (10.0, 10.0)) + .show(ctx, |ui| { + use egui::widgets::plot::{Bar, BarChart, HLine, Legend, Plot}; + + // Basics + + let label_res = ui.label(format!( + "cosmic-comp version {}", + std::env!("CARGO_PKG_VERSION") + )); + if let Some(hash) = std::option_env!("GIT_HASH").and_then(|x| x.get(0..8)) { + ui.label(hash); + } + + if !state.egui.active { + ui.label("Press Mod+Escape for debug menu"); + } else { + ui.set_max_width(label_res.rect.width()); + ui.separator(); + + // FPS + + let (max, min, avg, avg_fps) = ( + fps.max_frametime().as_secs_f64(), + fps.min_frametime().as_secs_f64(), + fps.avg_frametime().as_secs_f64(), + fps.avg_fps(), + ); + + ui.label(egui::RichText::new(format!("FPS: {:>7.3}", avg_fps)).heading()); + ui.label("Frame Times:"); + ui.label(egui::RichText::new(format!("avg: {:>7.6}", avg)).code()); + ui.label(egui::RichText::new(format!("min: {:>7.6}", min)).code()); + ui.label(egui::RichText::new(format!("max: {:>7.6}", max)).code()); + let fps_chart = BarChart::new( + fps.frames + .iter() + .rev().take(30).rev() + .enumerate() + .map(|(i, d)| { + let value = d.as_secs_f64(); + let transformed = ((value - min) / (max - min) * 255.0).round() as u8; + Bar::new(i as f64, value).fill(egui::Color32::from_rgb( + transformed, + 255 - transformed, + 0, + )) + }) + .collect(), + ) + .vertical(); + + Plot::new("FPS") + .legend(Legend::default()) + .view_aspect(33.0) + .include_x(0.0) + .include_x(30.0) + .include_y(0.0) + .include_y(max * 2.0) + .show_x(false) + .show(ui, |plot_ui| { + plot_ui.bar_chart(fps_chart); + plot_ui.hline( + HLine::new(avg) + .highlight() + .color(egui::Color32::LIGHT_BLUE), + ); + }); + + ui.separator(); + + // Toggles and stuff + ui.add(egui::Slider::new(&mut state.egui.alpha, 0.1..=1.0).clamp_to_range(true).text("Opacity")); + ui.checkbox(&mut state.egui.spaces, "Workspace UI"); + //TODO: ui.checkbox(&mut state.egui.outputs, "Outputs UI"); + } + }); + + // don't show these one others then the primary monitor + if primary { + egui::Window::new("Workspaces") + .open(&mut state.egui.spaces) + .vscroll(true) + .show(ctx, |ui| { + use crate::shell::workspaces::{ActiveWorkspace, Mode, MAX_WORKSPACES}; + + ui.set_min_width(250.0); + + // Mode + + ui.label(egui::RichText::new("Mode").heading()); + let mut mode = *state.spaces.mode(); + let active = if let Mode::Global { active } = mode { active } else { 0 }; + ui.radio_value(&mut mode, Mode::OutputBound, "Output bound"); + ui.radio_value(&mut mode, Mode::Global { active }, "Global"); + state.spaces.set_mode(mode); + + match *state.spaces.mode() { + Mode::OutputBound => { + ui.label("Workspaces:"); + for output in state.spaces.outputs().cloned().collect::>() { + ui.horizontal(|ui| { + let active = output + .user_data() + .get::() + .unwrap() + .get() + .unwrap(); + let mut active_val = active as f64; + ui.label(output.name()); + ui.add(egui::DragValue::new(&mut active_val).clamp_range(0..=(MAX_WORKSPACES-1)).speed(1.0)); + if active != active_val as usize { + state.spaces.activate(&output, active_val as usize); + } + }); + } + }, + Mode::Global { active } => { + ui.horizontal(|ui| { + let mut active_val = active as f64; + ui.label("Workspace:"); + ui.add(egui::DragValue::new(&mut active_val).clamp_range(0..=(MAX_WORKSPACES-1)).speed(1.0)); + if active != active_val as usize { + let output = state.spaces.outputs().next().cloned().unwrap(); + state.spaces.activate(&output, active_val as usize); + } + }); + } + } + + // Spaces + for (i, space) in state.spaces.spaces.iter().enumerate() { + ui.collapsing(format!("Space: {}", i), |ui| { + ui.collapsing(format!("Windows"), |ui| { + for window in space.windows() { + ui.collapsing(format!("{:?}", window.toplevel()), |ui| { + ui.label(format!("Rect: {:?}", space.window_geometry(window))); + ui.label(format!("Bounding box: {:?}", space.window_bbox(window))); + }); + } + }) + }); + } + }); + + egui::Window::new("Outputs") + .open(&mut state.egui.outputs) + .hscroll(true) + .show(ctx, |ui| { + + }); + } + }, + area, + size.to_f64().to_physical(scale).to_i32_round(), + scale, + alpha, + &state.start_time, + state.egui.modifiers.clone(), + ) +} diff --git a/src/main.rs b/src/main.rs index fa57538e..0f10571a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,11 +10,13 @@ use slog::Drain; pub mod backend; pub mod input; -pub mod rendering; pub mod shell; pub mod state; pub mod utils; +#[cfg(feature = "debug")] +pub mod debug; + fn main() -> Result<()> { // setup logger let _guard = init_logger(); diff --git a/src/rendering/debug.rs b/src/rendering/debug.rs deleted file mode 100644 index da5b3e9c..00000000 --- a/src/rendering/debug.rs +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only - -use crate::state::{Common, Fps}; -use egui::CtxRef; - -pub fn debug_ui(ctx: &CtxRef, fps: &Fps, primary: bool) { - egui::Area::new("main") - .anchor(egui::Align2::LEFT_TOP, (10.0, 10.0)) - .show(ctx, |ui| { - use egui::widgets::plot::{Bar, BarChart, HLine, Legend, Plot}; - - // Basics - - let label_res = ui.label(format!( - "cosmic-comp version {}", - std::env!("CARGO_PKG_VERSION") - )); - ui.set_max_width(label_res.rect.width()); - if let Some(hash) = std::option_env!("GIT_HASH").and_then(|x| x.get(0..8)) { - ui.label(hash); - } - ui.separator(); - - // FPS - - let (max, min, avg) = ( - fps.max().as_secs_f64(), - fps.min().as_secs_f64(), - fps.avg_fps(), - ); - let fps_chart = BarChart::new( - fps.frames - .iter() - .enumerate() - .map(|(i, duration)| { - let value = duration.as_secs_f64(); - let transformed = ((value - min) / (max - min) * 255.0).round() as u8; - Bar::new(i as f64, value + 1.0).fill(egui::Color32::from_rgb( - transformed, - 255 - transformed, - 0, - )) - }) - .collect(), - ) - .vertical(); - - Plot::new("FPS") - .legend(Legend::default()) - .view_aspect(33.0) - .include_x(0.0) - .include_x(100.0) - .include_y(0.0) - .include_y(max + 1.0) - .show_x(false) - .show(ui, |plot_ui| { - plot_ui.bar_chart(fps_chart); - plot_ui.hline( - HLine::new(avg) - .highlight() - .name(format!("AVG {}", avg.round() as i32)) - .color(egui::Color32::DARK_BLUE), - ); - }); - }); -} diff --git a/src/rendering/mod.rs b/src/rendering/mod.rs deleted file mode 100644 index d92fc703..00000000 --- a/src/rendering/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only - -#[cfg(feature = "debug")] -mod debug; -#[cfg(feature = "debug")] -pub use debug::*; diff --git a/src/shell/workspaces.rs b/src/shell/workspaces.rs index 15adf74a..9edda7ea 100644 --- a/src/shell/workspaces.rs +++ b/src/shell/workspaces.rs @@ -8,14 +8,14 @@ pub use smithay::{ }; use std::{cell::Cell, mem::MaybeUninit}; -const MAX_WORKSPACES: usize = 10; // TODO? +pub const MAX_WORKSPACES: usize = 10; // TODO? pub struct ActiveWorkspace(Cell>); impl ActiveWorkspace { fn new() -> Self { ActiveWorkspace(Cell::new(None)) } - fn get(&self) -> Option { + pub fn get(&self) -> Option { self.0.get() } fn set(&self, active: usize) -> Option { @@ -26,6 +26,7 @@ impl ActiveWorkspace { } } +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Mode { OutputBound, Global { active: usize }, @@ -44,7 +45,7 @@ impl Mode { pub struct Workspaces { mode: Mode, outputs: Vec, - spaces: [Space; MAX_WORKSPACES], + pub spaces: [Space; MAX_WORKSPACES], } const UNINIT_SPACE: MaybeUninit = MaybeUninit::uninit(); @@ -166,6 +167,11 @@ impl Workspaces { }; } + #[cfg(feature = "debug")] + pub fn mode(&self) -> &Mode { + &self.mode + } + pub fn set_mode(&mut self, mode: Mode) { match (&mut self.mode, mode) { (Mode::OutputBound, Mode::Global { .. }) => { diff --git a/src/state.rs b/src/state.rs index d5a54088..35b04e92 100644 --- a/src/state.rs +++ b/src/state.rs @@ -37,6 +37,7 @@ pub struct Common { pub start_time: Instant, pub should_stop: bool, + #[cfg(feature = "debug")] pub egui: Egui, } @@ -46,6 +47,8 @@ pub struct Egui { pub modifiers: smithay::wayland::seat::ModifiersState, pub active: bool, pub alpha: f32, + pub spaces: bool, + pub outputs: bool, } #[cfg(feature = "debug")] @@ -54,15 +57,6 @@ pub struct Fps { pub last: Instant, } -impl Default for Fps { - fn default() -> Fps { - Fps { - frames: VecDeque::with_capacity(101), - last: Instant::now(), - } - } -} - pub enum BackendData { X11(X11State), // TODO @@ -113,6 +107,8 @@ impl State { modifiers: Default::default(), active: false, alpha: 1.0, + outputs: false, + spaces: false, }, }, backend: BackendData::Unset, @@ -122,31 +118,55 @@ impl State { #[cfg(feature = "debug")] impl Fps { + const WINDOW_SIZE: usize = 100; + pub fn tick(&mut self) { let next = Instant::now(); - let frame = next.duration_since(self.last); + let frame_time = next.duration_since(self.last); - self.frames.push_back(frame); - if self.frames.len() > 100 { + self.frames.push_back(frame_time); + if self.frames.len() > Fps::WINDOW_SIZE { self.frames.pop_front(); } self.last = next; } - pub fn max(&self) -> &Duration { + pub fn max_frametime(&self) -> &Duration { self.frames.iter().max().unwrap_or(&Duration::ZERO) } - pub fn min(&self) -> &Duration { + pub fn min_frametime(&self) -> &Duration { self.frames.iter().min().unwrap_or(&Duration::ZERO) } - pub fn avg(&self) -> Duration { + pub fn avg_frametime(&self) -> Duration { + if self.frames.is_empty() { + return Duration::ZERO; + } self.frames.iter().cloned().sum::() / (self.frames.len() as u32) } pub fn avg_fps(&self) -> f64 { + if self.frames.is_empty() { + return 0.0; + } let sum_secs = self.frames.iter().map(|d| d.as_secs_f64()).sum::(); 1.0 / (sum_secs / self.frames.len() as f64) } } + +#[cfg(feature = "debug")] +impl Default for Fps { + fn default() -> Fps { + Fps { + frames: VecDeque::with_capacity(Fps::WINDOW_SIZE + 1), + last: Instant::now(), + } + } +} + +#[cfg(feature = "debug")] +pub fn avg_fps<'a>(iter: impl Iterator) -> f64 { + let sum_secs = iter.map(|d| d.as_secs_f64()).sum::(); + 1.0 / (sum_secs / Fps::WINDOW_SIZE as f64) +} \ No newline at end of file