kitgit

tirbofish/dropbear · diff

44a392b · tk

fps counter + performance statistics

Unverified

diff --git a/Cargo.toml b/Cargo.toml
index ad5298d..a925a0e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -62,6 +62,8 @@ sha2 = "0.10"
 wesl = "0.2"
 dashmap = "6.1"
 open = "5.3"
+egui_plot = "0.34"
+memory-stats = "1.2"
 
 [workspace.dependencies.image]
 version = "0.25"
diff --git a/README.md b/README.md
index c3526be..54c2d45 100644
--- a/README.md
+++ b/README.md
@@ -25,7 +25,7 @@ If you might have not realised, all the crates/projects names are after Australi
 
 ## Build
 
-To build, ensure build requirements, clone the repository, then build it. It will build in debug mode, and use a lot of packages, so if your CPU is not fast enough for building you should brew a cup of coffee during the build time.
+To build, ensure build requirements, clone the repository, then build it. It will build in debug mode, and use a lot of packages, so if your CPU is not fast enough for building you should brew a cup of coffee during the build time or scroll through Instagram Reels (you could get a nice doomscroll in).
 
 With Unix systems (macOS not tested), you will have to download a couple of dependencies if building locally:
 
diff --git a/eucalyptus-editor/Cargo.toml b/eucalyptus-editor/Cargo.toml
index b18523f..c9c8958 100644
--- a/eucalyptus-editor/Cargo.toml
+++ b/eucalyptus-editor/Cargo.toml
@@ -39,6 +39,8 @@ libloading.workspace = true
 indexmap.workspace = true
 open.workspace = true
 rustc_version_runtime.workspace = true
+egui_plot.workspace = true
+memory-stats.workspace = true
 
 [target.'cfg(not(target_os = "android"))'.dependencies]
 rfd.workspace = true
diff --git a/eucalyptus-editor/src/editor/mod.rs b/eucalyptus-editor/src/editor/mod.rs
index fa0d1f3..d402c17 100644
--- a/eucalyptus-editor/src/editor/mod.rs
+++ b/eucalyptus-editor/src/editor/mod.rs
@@ -10,6 +10,7 @@ use crate::camera::UndoableCameraAction;
 use crate::debug;
 use crate::graphics::OutlineShader;
 use crate::plugin::PluginRegistry;
+use crate::stats::NerdStats;
 use crossbeam_channel::Receiver;
 use dropbear_engine::shader::Shader;
 use dropbear_engine::{
@@ -135,6 +136,7 @@ pub struct Editor {
 
     // about
     show_about: bool,
+    nerd_stats: NerdStats,
 }
 
 impl Editor {
@@ -225,6 +227,7 @@ impl Editor {
             pending_scene_load: None,
             pending_scene_creation: None,
             show_about: false,
+            nerd_stats: NerdStats::default(),
         })
     }
 
@@ -897,6 +900,10 @@ impl Editor {
                         };
                     }
 
+                    if ui.button("Nerdy Stuff").clicked() {
+                        self.show_nerdy_stuff = true
+                    }
+
                     if ui.button("About").clicked() {
                         self.show_about = true
                     }
diff --git a/eucalyptus-editor/src/editor/scene.rs b/eucalyptus-editor/src/editor/scene.rs
index 51f3abe..32b4105 100644
--- a/eucalyptus-editor/src/editor/scene.rs
+++ b/eucalyptus-editor/src/editor/scene.rs
@@ -269,6 +269,8 @@ impl Scene for Editor {
                 .update(graphics.shared.clone(), &self.world);
         }
 
+        self.nerd_stats.update(dt);
+
         self.input_state.window = self.window.clone();
         self.previously_selected_entity = self.selected_entity;
     }
@@ -288,6 +290,7 @@ impl Scene for Editor {
         {
             self.show_ui(&graphics.shared.get_egui_context());
         }
+        self.nerd_stats.show(&graphics.shared.get_egui_context());
 
         self.window = Some(graphics.shared.window.clone());
         logging::render(&graphics.shared.get_egui_context());
diff --git a/eucalyptus-editor/src/lib.rs b/eucalyptus-editor/src/lib.rs
index fc11149..f1dee0e 100644
--- a/eucalyptus-editor/src/lib.rs
+++ b/eucalyptus-editor/src/lib.rs
@@ -1,3 +1,4 @@
+pub mod stats;
 pub mod build;
 pub mod camera;
 pub mod debug;
diff --git a/eucalyptus-editor/src/stats.rs b/eucalyptus-editor/src/stats.rs
new file mode 100644
index 0000000..fd1809e
--- /dev/null
+++ b/eucalyptus-editor/src/stats.rs
@@ -0,0 +1,285 @@
+use std::{collections::VecDeque, time::Instant};
+
+use egui::{Context, RichText};
+
+/// Statistics for checking performance of the editor. 
+pub struct NerdStats {
+    show_window: bool,
+
+    fps_history: VecDeque<[f64; 2]>,
+    frame_times: VecDeque<f64>,
+    last_fps_update: Instant,
+    current_fps: f32,
+    
+    frame_time_history: VecDeque<[f64; 2]>,
+    
+    memory_history: VecDeque<[f64; 2]>,
+    
+    start_time: Instant,
+    total_frames: u64,
+    
+    min_fps: f32,
+    max_fps: f32,
+    avg_fps: f32,
+}
+
+impl Default for NerdStats {
+    fn default() -> Self {
+        Self {
+            fps_history: VecDeque::with_capacity(300),
+            frame_times: VecDeque::with_capacity(60),
+            last_fps_update: Instant::now(),
+            current_fps: 0.0,
+            frame_time_history: VecDeque::with_capacity(300),
+            memory_history: VecDeque::with_capacity(300),
+            start_time: Instant::now(),
+            total_frames: 0,
+            min_fps: f32::MAX,
+            max_fps: 0.0,
+            avg_fps: 0.0,
+            show_window: false,
+        }
+    }
+}
+
+impl NerdStats {
+    /// Updates all information in [`NerdStats`] with the deltatime provided by the scene
+    pub fn update(&mut self, dt: f32) {
+        self.total_frames += 1;
+        let elapsed = self.start_time.elapsed().as_secs_f64();
+        
+        let frame_time_ms = (dt * 1000.0) as f64;
+        self.frame_times.push_back(dt as f64);
+        if self.frame_times.len() > 60 {
+            self.frame_times.pop_front();
+        }
+        
+        if self.last_fps_update.elapsed().as_secs_f32() >= 0.1 {
+            if dt > 0.0 {
+                self.current_fps = 1.0 / dt;
+                
+                self.min_fps = self.min_fps.min(self.current_fps);
+                self.max_fps = self.max_fps.max(self.current_fps);
+                
+                if !self.frame_times.is_empty() {
+                    let avg_frame_time: f64 = self.frame_times.iter().sum::<f64>() / self.frame_times.len() as f64;
+                    self.avg_fps = (1.0 / avg_frame_time) as f32;
+                }
+                
+                self.fps_history.push_back([elapsed, self.current_fps as f64]);
+                if self.fps_history.len() > 300 {
+                    self.fps_history.pop_front();
+                }
+                
+                self.frame_time_history.push_back([elapsed, frame_time_ms]);
+                if self.frame_time_history.len() > 300 {
+                    self.frame_time_history.pop_front();
+                }
+                
+                self.last_fps_update = Instant::now();
+            }
+        }
+        
+        if self.total_frames % 30 == 0 {
+            let memory_mb = if let Some(usage) = memory_stats::memory_stats() {
+                (usage.physical_mem / 1024 / 1024) as f64
+            } else {
+                0.0
+            };
+            
+            self.memory_history.push_back([elapsed, memory_mb]);
+            if self.memory_history.len() > 300 {
+                self.memory_history.pop_front();
+            }
+        }
+    }
+
+    /// Resets statistics to their defaults
+    pub fn reset_stats(&mut self) {
+        self.min_fps = self.current_fps;
+        self.max_fps = self.current_fps;
+        self.fps_history.clear();
+        self.frame_time_history.clear();
+        self.memory_history.clear();
+        self.start_time = Instant::now();
+        self.total_frames = 0;
+    }
+
+    /// Shows the egui window
+    pub fn show(&mut self, ctx: &Context) {
+        egui::Window::new("Nerdy Stuff 🤓")
+            .resizable(true)
+            .collapsible(false)
+            .default_size([600.0, 500.0])
+            .open(show_nerdy_stuff)
+            .show(ctx, |ui| {
+                ui.vertical(|ui| {
+                    ui.horizontal(|ui| {
+                        ui.heading("Performance Monitor");
+                        ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
+                            if ui.button("Reset Stats").clicked() {
+                                stats.reset_stats();
+                            }
+                        });
+                    });
+                    
+                    ui.separator();
+                    
+                    ui.horizontal(|ui| {
+                        ui.vertical(|ui| {
+                            ui.label(RichText::new("Current FPS").strong());
+                            let fps_color = if stats.current_fps >= 60.0 {
+                                Color32::GREEN
+                            } else if stats.current_fps >= 30.0 {
+                                Color32::YELLOW
+                            } else {
+                                Color32::RED
+                            };
+                            ui.label(
+                                RichText::new(format!("{:.1}", stats.current_fps))
+                                    .size(24.0)
+                                    .color(fps_color)
+                            );
+                        });
+                        
+                        ui.separator();
+                        
+                        ui.vertical(|ui| {
+                            ui.label(RichText::new("Frame Time").strong());
+                            ui.label(
+                                RichText::new(format!("{:.2} ms", 1000.0 / stats.current_fps.max(1.0)))
+                                    .size(24.0)
+                            );
+                        });
+                        
+                        ui.separator();
+                        
+                        ui.vertical(|ui| {
+                            ui.label(RichText::new("Avg FPS").strong());
+                            ui.label(
+                                RichText::new(format!("{:.1}", stats.avg_fps))
+                                    .size(24.0)
+                            );
+                        });
+                    });
+                    
+                    ui.add_space(5.0);
+                    
+                    ui.horizontal(|ui| {
+                        ui.label(format!("Min: {:.1} fps", stats.min_fps));
+                        ui.separator();
+                        ui.label(format!("Max: {:.1} fps", stats.max_fps));
+                        ui.separator();
+                        ui.label(format!("Total Frames: {}", stats.total_frames));
+                        ui.separator();
+                        ui.label(format!("Uptime: {:.1}s", stats.start_time.elapsed().as_secs_f32()));
+                    });
+                    
+                    ui.separator();
+                    
+                    ui.label(RichText::new("FPS Over Time").strong());
+                    Plot::new("fps_plot")
+                        .height(150.0)
+                        .show_axes([false, true])
+                        .show_grid([false, true])
+                        .legend(Legend::default())
+                        .show(ui, |plot_ui| {
+                            if !stats.fps_history.is_empty() {
+                                let points: Vec<[f64; 2]> = stats.fps_history.iter().cloned().collect();
+                                plot_ui.line(
+                                    Line::new(PlotPoints::from(points))
+                                        .color(Color32::from_rgb(100, 200, 100))
+                                        .name("FPS")
+                                );
+                                
+                                if let Some(first) = stats.fps_history.front() {
+                                    if let Some(last) = stats.fps_history.back() {
+                                        plot_ui.line(
+                                            Line::new(PlotPoints::from(vec![
+                                                [first[0], 60.0],
+                                                [last[0], 60.0]
+                                            ]))
+                                            .color(Color32::from_rgba_unmultiplied(255, 255, 0, 100))
+                                            .style(egui_plot::LineStyle::Dashed { length: 5.0 })
+                                            .name("60 FPS Target")
+                                        );
+                                    }
+                                }
+                            }
+                        });
+                    
+                    ui.add_space(5.0);
+                    
+                    ui.label(RichText::new("Frame Time").strong());
+                    Plot::new("frame_time_plot")
+                        .height(150.0)
+                        .show_axes([false, true])
+                        .show_grid([false, true])
+                        .legend(Legend::default())
+                        .show(ui, |plot_ui| {
+                            if !stats.frame_time_history.is_empty() {
+                                let points: Vec<[f64; 2]> = stats.frame_time_history.iter().cloned().collect();
+                                plot_ui.line(
+                                    Line::new(PlotPoints::from(points))
+                                        .color(Color32::from_rgb(100, 150, 255))
+                                        .name("Frame Time (ms)")
+                                );
+                                
+                                if let Some(first) = stats.frame_time_history.front() {
+                                    if let Some(last) = stats.frame_time_history.back() {
+                                        plot_ui.line(
+                                            Line::new(PlotPoints::from(vec![
+                                                [first[0], 16.67],
+                                                [last[0], 16.67]
+                                            ]))
+                                            .color(Color32::from_rgba_unmultiplied(255, 255, 0, 100))
+                                            .style(egui_plot::LineStyle::Dashed { length: 5.0 })
+                                            .name("16.67ms (60 FPS)")
+                                        );
+                                    }
+                                }
+                            }
+                        });
+                    
+                    ui.add_space(5.0);
+                    
+                    ui.label(RichText::new("Memory Usage").strong());
+                    Plot::new("memory_plot")
+                        .height(120.0)
+                        .show_axes([false, true])
+                        .show_grid([false, true])
+                        .legend(Legend::default())
+                        .show(ui, |plot_ui| {
+                            if !stats.memory_history.is_empty() {
+                                let points: Vec<[f64; 2]> = stats.memory_history.iter().cloned().collect();
+                                plot_ui.line(
+                                    Line::new(PlotPoints::from(points))
+                                        .color(Color32::from_rgb(255, 150, 100))
+                                        .name("Memory (MB)")
+                                );
+                            }
+                        });
+                    
+                    ui.separator();
+                    ui.collapsing("System Information", |ui| {
+                        ui.horizontal(|ui| {
+                            ui.label("egui version:");
+                            ui.label(egui::VERSION);
+                        });
+                        ui.horizontal(|ui| {
+                            ui.label("Backend:");
+                            ui.label("wgpu");
+                        });
+                        ui.horizontal(|ui| {
+                            ui.label("OS:");
+                            ui.label(std::env::consts::OS);
+                        });
+                        ui.horizontal(|ui| {
+                            ui.label("Architecture:");
+                            ui.label(std::env::consts::ARCH);
+                        });
+                    });
+                });
+            });
+    }
+}
\ No newline at end of file