[demo] Show detailed memory usage statistics of paint lists
This commit is contained in:
parent
570860ec5f
commit
4fc34cca45
6 changed files with 244 additions and 42 deletions
|
@ -8,17 +8,12 @@ use ahash::AHashMap;
|
||||||
use crate::{
|
use crate::{
|
||||||
animation_manager::AnimationManager,
|
animation_manager::AnimationManager,
|
||||||
mutex::{Mutex, MutexGuard},
|
mutex::{Mutex, MutexGuard},
|
||||||
paint::*,
|
paint::{stats::*, *},
|
||||||
*,
|
*,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
struct PaintStats {
|
struct SliceStats<T>(usize, std::marker::PhantomData<T>);
|
||||||
num_jobs: usize,
|
|
||||||
num_primitives: usize,
|
|
||||||
num_vertices: usize,
|
|
||||||
num_triangles: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct Options {
|
struct Options {
|
||||||
|
@ -225,20 +220,10 @@ impl Context {
|
||||||
let mut paint_options = self.options.lock().paint_options;
|
let mut paint_options = self.options.lock().paint_options;
|
||||||
paint_options.aa_size = 1.0 / self.pixels_per_point();
|
paint_options.aa_size = 1.0 / self.pixels_per_point();
|
||||||
let paint_commands = self.drain_paint_lists();
|
let paint_commands = self.drain_paint_lists();
|
||||||
let num_primitives = paint_commands.len();
|
let paint_stats = PaintStats::from_paint_commands(&paint_commands); // TODO: internal allocations
|
||||||
let paint_jobs =
|
let paint_jobs =
|
||||||
tessellator::tessellate_paint_commands(paint_commands, paint_options, self.fonts());
|
tessellator::tessellate_paint_commands(paint_commands, paint_options, self.fonts());
|
||||||
|
*self.paint_stats.lock() = paint_stats.with_paint_jobs(&paint_jobs);
|
||||||
{
|
|
||||||
let mut stats = PaintStats::default();
|
|
||||||
stats.num_jobs = paint_jobs.len();
|
|
||||||
stats.num_primitives = num_primitives;
|
|
||||||
for (_, triangles) in &paint_jobs {
|
|
||||||
stats.num_vertices += triangles.vertices.len();
|
|
||||||
stats.num_triangles += triangles.indices.len() / 3;
|
|
||||||
}
|
|
||||||
*self.paint_stats.lock() = stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
paint_jobs
|
paint_jobs
|
||||||
}
|
}
|
||||||
|
@ -542,21 +527,12 @@ impl Context {
|
||||||
|
|
||||||
pub fn inspection_ui(&self, ui: &mut Ui) {
|
pub fn inspection_ui(&self, ui: &mut Ui) {
|
||||||
use crate::containers::*;
|
use crate::containers::*;
|
||||||
ui.style_mut().body_text_style = TextStyle::Monospace;
|
|
||||||
|
|
||||||
CollapsingHeader::new("Input")
|
CollapsingHeader::new("Input")
|
||||||
.default_open(true)
|
.default_open(true)
|
||||||
.show(ui, |ui| ui.input().clone().ui(ui));
|
.show(ui, |ui| ui.input().clone().ui(ui));
|
||||||
|
|
||||||
ui.collapsing("Stats", |ui| {
|
ui.collapsing("Paint stats", |ui| {
|
||||||
ui.label(format!(
|
|
||||||
"Screen size: {} x {} points, pixels_per_point: {:?}",
|
|
||||||
ui.input().screen_size.x,
|
|
||||||
ui.input().screen_size.y,
|
|
||||||
ui.input().pixels_per_point,
|
|
||||||
));
|
|
||||||
|
|
||||||
ui.heading("Painting:");
|
|
||||||
self.paint_stats.lock().ui(ui);
|
self.paint_stats.lock().ui(ui);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -642,14 +618,3 @@ impl paint::PaintOptions {
|
||||||
ui.checkbox(debug_ignore_clip_rects, "Ignore clip rectangles (debug)");
|
ui.checkbox(debug_ignore_clip_rects, "Ignore clip rectangles (debug)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaintStats {
|
|
||||||
pub fn ui(&self, ui: &mut Ui) {
|
|
||||||
ui.label(format!("Jobs: {}", self.num_jobs))
|
|
||||||
.on_hover_text("Number of separate clip rectangles");
|
|
||||||
ui.label(format!("Primitives: {}", self.num_primitives))
|
|
||||||
.on_hover_text("Boxes, circles, text areas etc");
|
|
||||||
ui.label(format!("Vertices: {}", self.num_vertices));
|
|
||||||
ui.label(format!("Triangles: {}", self.num_triangles));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ impl DemoWindow {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
CollapsingHeader::new("Painting")
|
CollapsingHeader::new("Paint with your mouse")
|
||||||
.default_open(false)
|
.default_open(false)
|
||||||
.show(ui, |ui| self.painting.ui(ui));
|
.show(ui, |ui| self.painting.ui(ui));
|
||||||
|
|
||||||
|
|
|
@ -208,6 +208,10 @@ impl InputState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn screen_rect(&self) -> Rect {
|
||||||
|
Rect::from_min_size(pos2(0.0, 0.0), self.screen_size)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn wants_repaint(&self) -> bool {
|
pub fn wants_repaint(&self) -> bool {
|
||||||
self.mouse.pressed
|
self.mouse.pressed
|
||||||
|| self.mouse.released
|
|| self.mouse.released
|
||||||
|
@ -372,6 +376,7 @@ impl InputState {
|
||||||
events,
|
events,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
|
ui.style_mut().body_text_style = crate::paint::TextStyle::Monospace;
|
||||||
ui.collapsing("Raw Input", |ui| raw.ui(ui));
|
ui.collapsing("Raw Input", |ui| raw.ui(ui));
|
||||||
|
|
||||||
crate::containers::CollapsingHeader::new("mouse")
|
crate::containers::CollapsingHeader::new("mouse")
|
||||||
|
|
|
@ -6,6 +6,7 @@ pub mod color;
|
||||||
pub mod command;
|
pub mod command;
|
||||||
pub mod font;
|
pub mod font;
|
||||||
pub mod fonts;
|
pub mod fonts;
|
||||||
|
pub mod stats;
|
||||||
pub mod tessellator;
|
pub mod tessellator;
|
||||||
mod texture_atlas;
|
mod texture_atlas;
|
||||||
|
|
||||||
|
@ -13,6 +14,7 @@ pub use {
|
||||||
color::{Rgba, Srgba},
|
color::{Rgba, Srgba},
|
||||||
command::{PaintCmd, Stroke},
|
command::{PaintCmd, Stroke},
|
||||||
fonts::{FontDefinitions, Fonts, TextStyle},
|
fonts::{FontDefinitions, Fonts, TextStyle},
|
||||||
tessellator::{PaintJobs, PaintOptions, TextureId, Triangles, Vertex, WHITE_UV},
|
stats::PaintStats,
|
||||||
|
tessellator::{PaintJob, PaintJobs, PaintOptions, TextureId, Triangles, Vertex, WHITE_UV},
|
||||||
texture_atlas::Texture,
|
texture_atlas::Texture,
|
||||||
};
|
};
|
||||||
|
|
224
egui/src/paint/stats.rs
Normal file
224
egui/src/paint/stats.rs
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
use crate::{paint::*, Rect};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
enum ElementSize {
|
||||||
|
Unknown,
|
||||||
|
Homogeneous(usize),
|
||||||
|
Heterogenous,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ElementSize {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Default, PartialEq)]
|
||||||
|
pub struct AllocInfo {
|
||||||
|
element_size: ElementSize,
|
||||||
|
num_allocs: usize,
|
||||||
|
num_elements: usize,
|
||||||
|
num_bytes: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<&[T]> for AllocInfo {
|
||||||
|
fn from(slice: &[T]) -> Self {
|
||||||
|
Self::from_slice(slice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Add for AllocInfo {
|
||||||
|
type Output = AllocInfo;
|
||||||
|
fn add(self, rhs: AllocInfo) -> AllocInfo {
|
||||||
|
use ElementSize::{Heterogenous, Homogeneous, Unknown};
|
||||||
|
let element_size = match (self.element_size, rhs.element_size) {
|
||||||
|
(Heterogenous, _) | (_, Heterogenous) => Heterogenous,
|
||||||
|
(Unknown, other) | (other, Unknown) => other,
|
||||||
|
(Homogeneous(lhs), Homogeneous(rhs)) if lhs == rhs => Homogeneous(lhs),
|
||||||
|
_ => Heterogenous,
|
||||||
|
};
|
||||||
|
|
||||||
|
AllocInfo {
|
||||||
|
element_size,
|
||||||
|
num_allocs: self.num_allocs + rhs.num_allocs,
|
||||||
|
num_elements: self.num_elements + rhs.num_elements,
|
||||||
|
num_bytes: self.num_bytes + rhs.num_bytes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::AddAssign for AllocInfo {
|
||||||
|
fn add_assign(&mut self, rhs: AllocInfo) {
|
||||||
|
*self = *self + rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AllocInfo {
|
||||||
|
pub fn from_paint_cmd(cmd: &PaintCmd) -> Self {
|
||||||
|
match cmd {
|
||||||
|
PaintCmd::Noop
|
||||||
|
| PaintCmd::Circle { .. }
|
||||||
|
| PaintCmd::LineSegment { .. }
|
||||||
|
| PaintCmd::Rect { .. } => Self::default(),
|
||||||
|
PaintCmd::Path { points, .. } => Self::from_slice(points),
|
||||||
|
PaintCmd::Text { galley, .. } => Self::from_galley(galley),
|
||||||
|
PaintCmd::Triangles(triangles) => Self::from_triangles(triangles),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_galley(galley: &font::Galley) -> Self {
|
||||||
|
Self::from_slice(galley.text.as_bytes()) + Self::from_slice(&galley.lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_triangles(triangles: &Triangles) -> Self {
|
||||||
|
Self::from_slice(&triangles.indices) + Self::from_slice(&triangles.vertices)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_slice<T>(slice: &[T]) -> Self {
|
||||||
|
use std::mem::size_of;
|
||||||
|
let element_size = size_of::<T>();
|
||||||
|
Self {
|
||||||
|
element_size: ElementSize::Homogeneous(element_size),
|
||||||
|
num_allocs: 1,
|
||||||
|
num_elements: slice.len(),
|
||||||
|
num_bytes: slice.len() * element_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn num_elements(&self) -> usize {
|
||||||
|
assert!(self.element_size != ElementSize::Heterogenous);
|
||||||
|
self.num_elements
|
||||||
|
}
|
||||||
|
pub fn num_allocs(&self) -> usize {
|
||||||
|
self.num_allocs
|
||||||
|
}
|
||||||
|
pub fn num_bytes(&self) -> usize {
|
||||||
|
self.num_bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn megabytes(&self) -> String {
|
||||||
|
megabytes(self.num_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format(&self, what: &str) -> String {
|
||||||
|
if self.num_allocs() == 0 {
|
||||||
|
format!("{:6} {:12}", 0, what)
|
||||||
|
} else if self.num_allocs() == 1 {
|
||||||
|
format!(
|
||||||
|
"{:6} {:12} {} 1 allocation",
|
||||||
|
self.num_elements,
|
||||||
|
what,
|
||||||
|
self.megabytes()
|
||||||
|
)
|
||||||
|
} else if self.element_size != ElementSize::Heterogenous {
|
||||||
|
format!(
|
||||||
|
"{:6} {:12} {} {:3} allocations",
|
||||||
|
self.num_elements(),
|
||||||
|
what,
|
||||||
|
self.megabytes(),
|
||||||
|
self.num_allocs()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"{:6} {:12} {} {:3} allocations",
|
||||||
|
"",
|
||||||
|
what,
|
||||||
|
self.megabytes(),
|
||||||
|
self.num_allocs()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
|
pub struct PaintStats {
|
||||||
|
primitives: AllocInfo,
|
||||||
|
cmd_text: AllocInfo,
|
||||||
|
cmd_path: AllocInfo,
|
||||||
|
cmd_mesh: AllocInfo,
|
||||||
|
|
||||||
|
/// Number of separate clip rectangles
|
||||||
|
jobs: AllocInfo,
|
||||||
|
vertices: AllocInfo,
|
||||||
|
indices: AllocInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaintStats {
|
||||||
|
pub fn from_paint_commands(paint_commands: &[(Rect, PaintCmd)]) -> Self {
|
||||||
|
let mut stats = Self::default();
|
||||||
|
stats.cmd_path.element_size = ElementSize::Heterogenous; // nicer display later
|
||||||
|
|
||||||
|
stats.primitives = AllocInfo::from_slice(paint_commands);
|
||||||
|
for (_, cmd) in paint_commands {
|
||||||
|
match cmd {
|
||||||
|
PaintCmd::Noop
|
||||||
|
| PaintCmd::Circle { .. }
|
||||||
|
| PaintCmd::LineSegment { .. }
|
||||||
|
| PaintCmd::Rect { .. } => Default::default(),
|
||||||
|
PaintCmd::Path { points, .. } => {
|
||||||
|
stats.cmd_path += AllocInfo::from_slice(points);
|
||||||
|
}
|
||||||
|
PaintCmd::Text { galley, .. } => {
|
||||||
|
stats.cmd_text += AllocInfo::from_galley(galley);
|
||||||
|
}
|
||||||
|
PaintCmd::Triangles(triangles) => {
|
||||||
|
stats.cmd_mesh += AllocInfo::from_triangles(triangles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stats
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_paint_jobs(mut self, paint_jobs: &[crate::paint::PaintJob]) -> Self {
|
||||||
|
self.jobs += AllocInfo::from_slice(paint_jobs);
|
||||||
|
for (_, indices) in paint_jobs {
|
||||||
|
self.vertices += AllocInfo::from_slice(&indices.vertices);
|
||||||
|
self.indices += AllocInfo::from_slice(&indices.indices);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn total(&self) -> AllocInfo {
|
||||||
|
// self.primitives
|
||||||
|
// + self.cmd_text
|
||||||
|
// + self.cmd_path
|
||||||
|
// + self.cmd_mesh
|
||||||
|
// + self.jobs
|
||||||
|
// + self.vertices
|
||||||
|
// + self.indices
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaintStats {
|
||||||
|
pub fn ui(&self, ui: &mut crate::Ui) {
|
||||||
|
ui.label(
|
||||||
|
"Egui generates intermediate level primitives like circles and text. \
|
||||||
|
These are later tessellated into triangles.",
|
||||||
|
);
|
||||||
|
ui.advance_cursor(10.0);
|
||||||
|
|
||||||
|
ui.style_mut().body_text_style = TextStyle::Monospace;
|
||||||
|
ui.label("Intermediate:");
|
||||||
|
ui.label(self.primitives.format("primitives"))
|
||||||
|
.on_hover_text("Boxes, circles, etc");
|
||||||
|
ui.label(self.cmd_text.format("text"));
|
||||||
|
ui.label(self.cmd_path.format("paths"));
|
||||||
|
ui.label(self.cmd_mesh.format("meshes"));
|
||||||
|
ui.advance_cursor(10.0);
|
||||||
|
|
||||||
|
ui.label("Tessellated:");
|
||||||
|
ui.label(self.jobs.format("jobs"))
|
||||||
|
.on_hover_text("Number of separate clip rectangles");
|
||||||
|
ui.label(self.vertices.format("vertices"));
|
||||||
|
ui.label(self.indices.format("indices"))
|
||||||
|
.on_hover_text("Three 32-bit indices per triangles");
|
||||||
|
ui.advance_cursor(10.0);
|
||||||
|
|
||||||
|
// ui.label("Total:");
|
||||||
|
// ui.label(self.total().format(""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn megabytes(size: usize) -> String {
|
||||||
|
format!("{:.2} MB", size as f64 / 1e6)
|
||||||
|
}
|
|
@ -87,6 +87,12 @@ impl Triangles {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn bytes_used(&self) -> usize {
|
||||||
|
std::mem::size_of::<Self>()
|
||||||
|
+ self.vertices.len() * std::mem::size_of::<Vertex>()
|
||||||
|
+ self.indices.len() * std::mem::size_of::<u32>()
|
||||||
|
}
|
||||||
|
|
||||||
/// Are all indices within the bounds of the contained vertices?
|
/// Are all indices within the bounds of the contained vertices?
|
||||||
pub fn is_valid(&self) -> bool {
|
pub fn is_valid(&self) -> bool {
|
||||||
let n = self.vertices.len() as u32;
|
let n = self.vertices.len() as u32;
|
||||||
|
|
Loading…
Reference in a new issue