use bevy_ecs::prelude::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DebugMode {
None,
Wireframe,
Normals,
DiffuseOnly,
SpecularOnly,
Metallic,
Roughness,
AmbientOcclusion,
UVs,
Depth,
}
impl DebugMode {
pub fn is_normal(&self) -> bool {
matches!(self, DebugMode::None)
}
}
impl Default for DebugMode {
fn default() -> Self {
DebugMode::None
}
}
#[derive(Debug, Clone, Resource)]
pub struct RenderStats {
pub draw_calls: u32,
pub triangles: u32,
pub vertices: u32,
pub active_lights: u32,
pub culled_objects: u32,
pub visible_objects: u32,
pub frame_time_ms: f32,
pub fps: f32,
pub gpu_memory_bytes: u64,
}
impl RenderStats {
pub fn new() -> Self {
Self {
draw_calls: 0,
triangles: 0,
vertices: 0,
active_lights: 0,
culled_objects: 0,
visible_objects: 0,
frame_time_ms: 0.0,
fps: 0.0,
gpu_memory_bytes: 0,
}
}
pub fn record_draw_call(&mut self, triangle_count: u32) {
self.draw_calls += 1;
self.triangles += triangle_count;
}
pub fn update_frame_time(&mut self, dt_seconds: f32) {
self.frame_time_ms = dt_seconds * 1000.0;
self.fps = if dt_seconds > 0.0 { 1.0 / dt_seconds } else { 0.0 };
}
pub fn reset_frame(&mut self) {
self.draw_calls = 0;
self.triangles = 0;
self.vertices = 0;
self.culled_objects = 0;
self.visible_objects = 0;
}
pub fn summary(&self) -> String {
format!(
"FPS: {:.0} | {:.1}ms | Draw: {} | Tri: {} | Vis: {}/{}",
self.fps, self.frame_time_ms,
self.draw_calls, self.triangles,
self.visible_objects, self.visible_objects + self.culled_objects,
)
}
}
impl Default for RenderStats {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Resource)]
pub struct DebugOverlay {
pub show_stats: bool,
pub show_wireframe: bool,
pub show_bounds: bool,
pub show_lights: bool,
pub show_skeleton: bool,
pub debug_mode: DebugMode,
}
impl Default for DebugOverlay {
fn default() -> Self {
Self {
show_stats: false,
show_wireframe: false,
show_bounds: false,
show_lights: false,
show_skeleton: false,
debug_mode: DebugMode::None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_debug_mode() {
assert!(DebugMode::None.is_normal());
assert!(!DebugMode::Wireframe.is_normal());
assert!(!DebugMode::Normals.is_normal());
}
#[test]
fn test_render_stats() {
let mut stats = RenderStats::new();
stats.record_draw_call(100);
stats.record_draw_call(50);
assert_eq!(stats.draw_calls, 2);
assert_eq!(stats.triangles, 150);
stats.update_frame_time(1.0 / 60.0);
assert!((stats.fps - 60.0).abs() < 1.0);
let summary = stats.summary();
assert!(summary.contains("FPS:"));
assert!(summary.contains("Draw: 2"));
}
#[test]
fn test_render_stats_reset() {
let mut stats = RenderStats::new();
stats.record_draw_call(100);
stats.visible_objects = 5;
stats.culled_objects = 3;
stats.reset_frame();
assert_eq!(stats.draw_calls, 0);
assert_eq!(stats.triangles, 0);
assert_eq!(stats.visible_objects, 0);
}
#[test]
fn test_debug_overlay_default() {
let overlay = DebugOverlay::default();
assert!(!overlay.show_stats);
assert!(overlay.debug_mode.is_normal());
}
}