#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct RenderStatsConfig {
pub history_capacity: usize,
pub enable_history: bool,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct FrameStats {
pub begin_time: f64,
pub end_time: f64,
pub draw_calls: u32,
pub triangle_count: u64,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct RenderStats {
pub config: RenderStatsConfig,
pub current_frame: FrameStats,
pub last_frame: FrameStats,
pub total_draw_calls: u32,
pub total_triangles: u64,
pub frame_count: u64,
}
#[allow(dead_code)]
pub fn default_render_stats_config() -> RenderStatsConfig {
RenderStatsConfig {
history_capacity: 60,
enable_history: true,
}
}
#[allow(dead_code)]
pub fn new_render_stats(cfg: &RenderStatsConfig) -> RenderStats {
RenderStats {
config: cfg.clone(),
current_frame: FrameStats::default(),
last_frame: FrameStats::default(),
total_draw_calls: 0,
total_triangles: 0,
frame_count: 0,
}
}
#[allow(dead_code)]
pub fn render_stats_begin_frame(stats: &mut RenderStats, timestamp: f64) {
stats.current_frame = FrameStats {
begin_time: timestamp,
end_time: timestamp,
draw_calls: 0,
triangle_count: 0,
};
}
#[allow(dead_code)]
pub fn render_stats_end_frame(stats: &mut RenderStats, timestamp: f64) {
stats.current_frame.end_time = timestamp;
stats.total_draw_calls = stats
.total_draw_calls
.saturating_add(stats.current_frame.draw_calls);
stats.total_triangles = stats
.total_triangles
.saturating_add(stats.current_frame.triangle_count);
stats.frame_count += 1;
stats.last_frame = stats.current_frame.clone();
}
#[allow(dead_code)]
pub fn render_stats_add_draw_call(stats: &mut RenderStats, triangle_count: u32) {
stats.current_frame.draw_calls += 1;
stats.current_frame.triangle_count += u64::from(triangle_count);
}
#[allow(dead_code)]
pub fn render_stats_frame_time_ms(stats: &RenderStats) -> f64 {
(stats.last_frame.end_time - stats.last_frame.begin_time) * 1000.0
}
#[allow(dead_code)]
pub fn render_stats_fps(stats: &RenderStats) -> f64 {
let dt = stats.last_frame.end_time - stats.last_frame.begin_time;
if dt <= 0.0 {
0.0
} else {
1.0 / dt
}
}
#[allow(dead_code)]
pub fn render_stats_draw_calls(stats: &RenderStats) -> u32 {
stats.last_frame.draw_calls
}
#[allow(dead_code)]
pub fn render_stats_triangle_count(stats: &RenderStats) -> u64 {
stats.last_frame.triangle_count
}
#[allow(dead_code)]
pub fn render_stats_reset(stats: &mut RenderStats) {
stats.current_frame = FrameStats::default();
stats.last_frame = FrameStats::default();
stats.total_draw_calls = 0;
stats.total_triangles = 0;
stats.frame_count = 0;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let cfg = default_render_stats_config();
assert_eq!(cfg.history_capacity, 60);
assert!(cfg.enable_history);
}
#[test]
fn test_new_stats_zeroed() {
let cfg = default_render_stats_config();
let stats = new_render_stats(&cfg);
assert_eq!(stats.frame_count, 0);
assert_eq!(stats.total_draw_calls, 0);
assert_eq!(stats.total_triangles, 0);
}
#[test]
fn test_begin_end_frame_increments_count() {
let cfg = default_render_stats_config();
let mut stats = new_render_stats(&cfg);
render_stats_begin_frame(&mut stats, 0.0);
render_stats_end_frame(&mut stats, 0.016);
assert_eq!(stats.frame_count, 1);
}
#[test]
fn test_frame_time_ms() {
let cfg = default_render_stats_config();
let mut stats = new_render_stats(&cfg);
render_stats_begin_frame(&mut stats, 1.0);
render_stats_end_frame(&mut stats, 1.016);
let ft = render_stats_frame_time_ms(&stats);
assert!((ft - 16.0).abs() < 0.01);
}
#[test]
fn test_fps_calculation() {
let cfg = default_render_stats_config();
let mut stats = new_render_stats(&cfg);
render_stats_begin_frame(&mut stats, 0.0);
render_stats_end_frame(&mut stats, 0.016_666_67);
let fps = render_stats_fps(&stats);
assert!((fps - 60.0).abs() < 0.1);
}
#[test]
fn test_add_draw_call() {
let cfg = default_render_stats_config();
let mut stats = new_render_stats(&cfg);
render_stats_begin_frame(&mut stats, 0.0);
render_stats_add_draw_call(&mut stats, 1000);
render_stats_add_draw_call(&mut stats, 500);
render_stats_end_frame(&mut stats, 0.01);
assert_eq!(render_stats_draw_calls(&stats), 2);
assert_eq!(render_stats_triangle_count(&stats), 1500);
}
#[test]
fn test_total_accumulation_over_frames() {
let cfg = default_render_stats_config();
let mut stats = new_render_stats(&cfg);
for i in 0..3u64 {
render_stats_begin_frame(&mut stats, i as f64 * 0.016);
render_stats_add_draw_call(&mut stats, 100);
render_stats_end_frame(&mut stats, i as f64 * 0.016 + 0.016);
}
assert_eq!(stats.total_draw_calls, 3);
assert_eq!(stats.total_triangles, 300);
assert_eq!(stats.frame_count, 3);
}
#[test]
fn test_fps_zero_when_dt_zero() {
let cfg = default_render_stats_config();
let stats = new_render_stats(&cfg);
assert_eq!(render_stats_fps(&stats), 0.0);
}
#[test]
fn test_reset_clears_all() {
let cfg = default_render_stats_config();
let mut stats = new_render_stats(&cfg);
render_stats_begin_frame(&mut stats, 0.0);
render_stats_add_draw_call(&mut stats, 999);
render_stats_end_frame(&mut stats, 0.1);
render_stats_reset(&mut stats);
assert_eq!(stats.frame_count, 0);
assert_eq!(stats.total_draw_calls, 0);
assert_eq!(render_stats_frame_time_ms(&stats), 0.0);
}
#[test]
fn test_draw_calls_reset_each_frame() {
let cfg = default_render_stats_config();
let mut stats = new_render_stats(&cfg);
render_stats_begin_frame(&mut stats, 0.0);
render_stats_add_draw_call(&mut stats, 50);
render_stats_end_frame(&mut stats, 0.01);
render_stats_begin_frame(&mut stats, 0.01);
render_stats_end_frame(&mut stats, 0.02);
assert_eq!(render_stats_draw_calls(&stats), 0);
}
}