debug: split fps overlay and debug ui
This commit is contained in:
parent
2d76672188
commit
78cc1713ad
2 changed files with 178 additions and 145 deletions
260
src/debug.rs
260
src/debug.rs
|
|
@ -4,25 +4,43 @@ use crate::state::{Common, Fps};
|
||||||
use smithay::utils::{Logical, Rectangle};
|
use smithay::utils::{Logical, Rectangle};
|
||||||
use smithay_egui::EguiFrame;
|
use smithay_egui::EguiFrame;
|
||||||
|
|
||||||
pub fn debug_ui(
|
pub fn fps_ui(
|
||||||
state: &mut Common,
|
state: &Common,
|
||||||
fps: &Fps,
|
fps: &mut Fps,
|
||||||
area: Rectangle<i32, Logical>,
|
area: Rectangle<i32, Logical>,
|
||||||
scale: f64,
|
scale: f64,
|
||||||
primary: bool,
|
|
||||||
) -> EguiFrame {
|
) -> EguiFrame {
|
||||||
let size = area.size;
|
use egui::widgets::plot::{Bar, BarChart, HLine, Legend, Plot};
|
||||||
let alpha = state.egui.alpha;
|
|
||||||
|
|
||||||
state.egui.state.run(
|
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(),
|
||||||
|
);
|
||||||
|
let bars = 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, transformed as f64).fill(egui::Color32::from_rgb(
|
||||||
|
transformed,
|
||||||
|
255 - transformed,
|
||||||
|
0,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
fps.state.run(
|
||||||
|ctx| {
|
|ctx| {
|
||||||
egui::Area::new("main")
|
egui::Area::new("main")
|
||||||
.anchor(egui::Align2::LEFT_TOP, (10.0, 10.0))
|
.anchor(egui::Align2::LEFT_TOP, (10.0, 10.0))
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
use egui::widgets::plot::{Bar, BarChart, HLine, Legend, Plot};
|
|
||||||
|
|
||||||
// Basics
|
|
||||||
|
|
||||||
let label_res = ui.label(format!(
|
let label_res = ui.label(format!(
|
||||||
"cosmic-comp version {}",
|
"cosmic-comp version {}",
|
||||||
std::env!("CARGO_PKG_VERSION")
|
std::env!("CARGO_PKG_VERSION")
|
||||||
|
|
@ -34,43 +52,15 @@ pub fn debug_ui(
|
||||||
if !state.egui.active {
|
if !state.egui.active {
|
||||||
ui.label("Press Mod+Escape for debug menu");
|
ui.label("Press Mod+Escape for debug menu");
|
||||||
} else {
|
} else {
|
||||||
ui.set_max_width(label_res.rect.width());
|
ui.set_max_width(label_res.rect.min.x + label_res.rect.width());
|
||||||
ui.separator();
|
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(egui::RichText::new(format!("FPS: {:>7.3}", avg_fps)).heading());
|
||||||
ui.label("Frame Times:");
|
ui.label("Frame Times:");
|
||||||
ui.label(egui::RichText::new(format!("avg: {:>7.6}", avg)).code());
|
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!("min: {:>7.6}", min)).code());
|
||||||
ui.label(egui::RichText::new(format!("max: {:>7.6}", max)).code());
|
ui.label(egui::RichText::new(format!("max: {:>7.6}", max)).code());
|
||||||
let fps_chart = BarChart::new(
|
let fps_chart = BarChart::new(bars).vertical();
|
||||||
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")
|
Plot::new("FPS")
|
||||||
.legend(Legend::default())
|
.legend(Legend::default())
|
||||||
|
|
@ -78,7 +68,7 @@ pub fn debug_ui(
|
||||||
.include_x(0.0)
|
.include_x(0.0)
|
||||||
.include_x(30.0)
|
.include_x(30.0)
|
||||||
.include_y(0.0)
|
.include_y(0.0)
|
||||||
.include_y(max * 2.0)
|
.include_y(300.0)
|
||||||
.show_x(false)
|
.show_x(false)
|
||||||
.show(ui, |plot_ui| {
|
.show(ui, |plot_ui| {
|
||||||
plot_ui.bar_chart(fps_chart);
|
plot_ui.bar_chart(fps_chart);
|
||||||
|
|
@ -86,117 +76,133 @@ pub fn debug_ui(
|
||||||
HLine::new(avg).highlight().color(egui::Color32::LIGHT_BLUE),
|
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");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
area,
|
||||||
|
scale,
|
||||||
|
1.0,
|
||||||
|
&state.start_time,
|
||||||
|
fps.modifiers.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// don't show these one others then the primary monitor
|
pub fn debug_ui(
|
||||||
if primary {
|
state: &mut Common,
|
||||||
egui::Window::new("Workspaces")
|
area: Rectangle<i32, Logical>,
|
||||||
.open(&mut state.egui.spaces)
|
scale: f64,
|
||||||
.vscroll(true)
|
) -> Option<EguiFrame> {
|
||||||
.show(ctx, |ui| {
|
if !state.egui.active {
|
||||||
use crate::shell::workspaces::{ActiveWorkspace, Mode, MAX_WORKSPACES};
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
ui.set_min_width(250.0);
|
Some(state.egui.state.run(
|
||||||
|
|ctx| {
|
||||||
|
egui::Window::new("Workspaces")
|
||||||
|
.default_pos([0.0, 300.0])
|
||||||
|
.vscroll(true)
|
||||||
|
.collapsible(true)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
use crate::shell::workspaces::{ActiveWorkspace, Mode, MAX_WORKSPACES};
|
||||||
|
|
||||||
// Mode
|
ui.set_min_width(250.0);
|
||||||
|
|
||||||
ui.label(egui::RichText::new("Mode").heading());
|
// Mode
|
||||||
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() {
|
ui.label(egui::RichText::new("Mode").heading());
|
||||||
Mode::OutputBound => {
|
let mut mode = *state.spaces.mode();
|
||||||
ui.label("Workspaces:");
|
let active = if let Mode::Global { active } = mode {
|
||||||
for output in state.spaces.outputs().cloned().collect::<Vec<_>>() {
|
active
|
||||||
ui.horizontal(|ui| {
|
} else {
|
||||||
let active = output
|
0
|
||||||
.user_data()
|
};
|
||||||
.get::<ActiveWorkspace>()
|
ui.radio_value(&mut mode, Mode::OutputBound, "Output bound");
|
||||||
.unwrap()
|
ui.radio_value(&mut mode, Mode::Global { active }, "Global");
|
||||||
.get()
|
state.spaces.set_mode(mode);
|
||||||
.unwrap();
|
|
||||||
let mut active_val = active as f64;
|
match *state.spaces.mode() {
|
||||||
ui.label(output.name());
|
Mode::OutputBound => {
|
||||||
ui.add(
|
ui.label("Workspaces:");
|
||||||
egui::DragValue::new(&mut active_val)
|
for output in state.spaces.outputs().cloned().collect::<Vec<_>>() {
|
||||||
.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| {
|
ui.horizontal(|ui| {
|
||||||
|
let active = output
|
||||||
|
.user_data()
|
||||||
|
.get::<ActiveWorkspace>()
|
||||||
|
.unwrap()
|
||||||
|
.get()
|
||||||
|
.unwrap();
|
||||||
let mut active_val = active as f64;
|
let mut active_val = active as f64;
|
||||||
ui.label("Workspace:");
|
ui.label(output.name());
|
||||||
ui.add(
|
ui.add(
|
||||||
egui::DragValue::new(&mut active_val)
|
egui::DragValue::new(&mut active_val)
|
||||||
.clamp_range(0..=(MAX_WORKSPACES - 1))
|
.clamp_range(0..=(MAX_WORKSPACES - 1))
|
||||||
.speed(1.0),
|
.speed(1.0),
|
||||||
);
|
);
|
||||||
if active != active_val as usize {
|
if active != active_val as usize {
|
||||||
let output =
|
|
||||||
state.spaces.outputs().next().cloned().unwrap();
|
|
||||||
state.spaces.activate(&output, active_val as usize);
|
state.spaces.activate(&output, active_val as usize);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Mode::Global { active } => {
|
||||||
// Spaces
|
ui.horizontal(|ui| {
|
||||||
for (i, space) in state.spaces.spaces.iter().enumerate() {
|
let mut active_val = active as f64;
|
||||||
ui.collapsing(format!("Space: {}", i), |ui| {
|
ui.label("Workspace:");
|
||||||
ui.collapsing(format!("Windows"), |ui| {
|
ui.add(
|
||||||
for window in space.windows() {
|
egui::DragValue::new(&mut active_val)
|
||||||
ui.collapsing(format!("{:?}", window.toplevel()), |ui| {
|
.clamp_range(0..=(MAX_WORKSPACES - 1))
|
||||||
ui.label(format!(
|
.speed(1.0),
|
||||||
"Rect: {:?}",
|
);
|
||||||
space.window_geometry(window)
|
if active != active_val as usize {
|
||||||
));
|
let output = state.spaces.outputs().next().cloned().unwrap();
|
||||||
ui.label(format!(
|
state.spaces.activate(&output, active_val as usize);
|
||||||
"Bounding box: {:?}",
|
}
|
||||||
space.window_bbox(window)
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
egui::Window::new("Outputs")
|
// Spaces
|
||||||
.open(&mut state.egui.outputs)
|
for (i, space) in state.spaces.spaces.iter().enumerate() {
|
||||||
.hscroll(true)
|
ui.collapsing(format!("Space: {}", i), |ui| {
|
||||||
.show(ctx, |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")
|
||||||
|
.collapsible(true)
|
||||||
|
.hscroll(true)
|
||||||
|
.default_pos([300.0, 300.0])
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ui.label(format!("Global Space: {:?}", state.spaces.global_space()));
|
||||||
|
for output in state.spaces.outputs().cloned().collect::<Vec<_>>().into_iter() {
|
||||||
|
ui.separator();
|
||||||
|
ui.collapsing(output.name(), |ui| {
|
||||||
|
ui.label(format!("Output: {:#?}", output));
|
||||||
|
ui.label(format!("Geometry: {:?}", state.spaces.output_geometry(&output)));
|
||||||
|
ui.label(format!("Local Geometry: {:?}", state.spaces.active_space(&output).output_geometry(&output)));
|
||||||
|
ui.label(format!("Relative Geometry: {:?}", state.spaces.space_relative_output_geometry((0, 0), &output)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
area,
|
area,
|
||||||
size.to_f64().to_physical(scale).to_i32_round(),
|
|
||||||
scale,
|
scale,
|
||||||
alpha,
|
state.egui.alpha,
|
||||||
&state.start_time,
|
&state.start_time,
|
||||||
state.egui.modifiers.clone(),
|
state.egui.modifiers.clone(),
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
63
src/state.rs
63
src/state.rs
|
|
@ -60,14 +60,14 @@ pub struct Egui {
|
||||||
pub modifiers: smithay::wayland::seat::ModifiersState,
|
pub modifiers: smithay::wayland::seat::ModifiersState,
|
||||||
pub active: bool,
|
pub active: bool,
|
||||||
pub alpha: f32,
|
pub alpha: f32,
|
||||||
pub spaces: bool,
|
|
||||||
pub outputs: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
pub struct Fps {
|
pub struct Fps {
|
||||||
pub frames: VecDeque<Duration>,
|
pub state: smithay_egui::EguiState,
|
||||||
pub last: Instant,
|
pub modifiers: smithay::wayland::seat::ModifiersState,
|
||||||
|
pub frames: VecDeque<(Instant, Duration)>,
|
||||||
|
pub start: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum BackendData {
|
pub enum BackendData {
|
||||||
|
|
@ -163,12 +163,10 @@ impl State {
|
||||||
|
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
egui: Egui {
|
egui: Egui {
|
||||||
state: smithay_egui::EguiState::new(),
|
state: smithay_egui::EguiState::new(smithay_egui::EguiMode::Continuous),
|
||||||
modifiers: Default::default(),
|
modifiers: Default::default(),
|
||||||
active: false,
|
active: false,
|
||||||
alpha: 1.0,
|
alpha: 1.0,
|
||||||
outputs: false,
|
|
||||||
spaces: false,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
backend: BackendData::Unset,
|
backend: BackendData::Unset,
|
||||||
|
|
@ -180,38 +178,59 @@ impl State {
|
||||||
impl Fps {
|
impl Fps {
|
||||||
const WINDOW_SIZE: usize = 100;
|
const WINDOW_SIZE: usize = 100;
|
||||||
|
|
||||||
pub fn tick(&mut self) {
|
pub fn start(&mut self) {
|
||||||
let next = Instant::now();
|
self.start = Instant::now();
|
||||||
let frame_time = next.duration_since(self.last);
|
}
|
||||||
|
|
||||||
self.frames.push_back(frame_time);
|
pub fn end(&mut self) {
|
||||||
|
let frame_time = Instant::now().duration_since(self.start);
|
||||||
|
|
||||||
|
self.frames.push_back((self.start, frame_time));
|
||||||
if self.frames.len() > Fps::WINDOW_SIZE {
|
if self.frames.len() > Fps::WINDOW_SIZE {
|
||||||
self.frames.pop_front();
|
self.frames.pop_front();
|
||||||
}
|
}
|
||||||
self.last = next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn max_frametime(&self) -> &Duration {
|
pub fn max_frametime(&self) -> &Duration {
|
||||||
self.frames.iter().max().unwrap_or(&Duration::ZERO)
|
self.frames
|
||||||
|
.iter()
|
||||||
|
.map(|(_, f)| f)
|
||||||
|
.max()
|
||||||
|
.unwrap_or(&Duration::ZERO)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn min_frametime(&self) -> &Duration {
|
pub fn min_frametime(&self) -> &Duration {
|
||||||
self.frames.iter().min().unwrap_or(&Duration::ZERO)
|
self.frames
|
||||||
|
.iter()
|
||||||
|
.map(|(_, f)| f)
|
||||||
|
.min()
|
||||||
|
.unwrap_or(&Duration::ZERO)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn avg_frametime(&self) -> Duration {
|
pub fn avg_frametime(&self) -> Duration {
|
||||||
if self.frames.is_empty() {
|
if self.frames.is_empty() {
|
||||||
return Duration::ZERO;
|
return Duration::ZERO;
|
||||||
}
|
}
|
||||||
self.frames.iter().cloned().sum::<Duration>() / (self.frames.len() as u32)
|
self.frames
|
||||||
|
.iter()
|
||||||
|
.map(|(_, f)| f)
|
||||||
|
.cloned()
|
||||||
|
.sum::<Duration>()
|
||||||
|
/ (self.frames.len() as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn avg_fps(&self) -> f64 {
|
pub fn avg_fps(&self) -> f64 {
|
||||||
if self.frames.is_empty() {
|
if self.frames.is_empty() {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
let sum_secs = self.frames.iter().map(|d| d.as_secs_f64()).sum::<f64>();
|
let secs = match (self.frames.front(), self.frames.back()) {
|
||||||
1.0 / (sum_secs / self.frames.len() as f64)
|
(Some((start, _)), Some((end, dur))) => {
|
||||||
|
end.duration_since(*start) + *dur
|
||||||
|
}
|
||||||
|
_ => Duration::ZERO,
|
||||||
|
}
|
||||||
|
.as_secs_f64();
|
||||||
|
1.0 / (secs / self.frames.len() as f64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,8 +238,16 @@ impl Fps {
|
||||||
impl Default for Fps {
|
impl Default for Fps {
|
||||||
fn default() -> Fps {
|
fn default() -> Fps {
|
||||||
Fps {
|
Fps {
|
||||||
|
state: {
|
||||||
|
let state = smithay_egui::EguiState::new(smithay_egui::EguiMode::Continuous);
|
||||||
|
let mut visuals: egui::style::Visuals = Default::default();
|
||||||
|
visuals.window_shadow.extrusion = 0.0;
|
||||||
|
state.context().set_visuals(visuals);
|
||||||
|
state
|
||||||
|
},
|
||||||
|
modifiers: Default::default(),
|
||||||
frames: VecDeque::with_capacity(Fps::WINDOW_SIZE + 1),
|
frames: VecDeque::with_capacity(Fps::WINDOW_SIZE + 1),
|
||||||
last: Instant::now(),
|
start: Instant::now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue