#![allow(clippy::cast_precision_loss)]
use criterion::{criterion_group, criterion_main, Criterion};
use dotmax::animation::{DifferentialRenderer, FrameBuffer, FrameTimer};
use dotmax::BrailleGrid;
use std::hint::black_box;
fn bench_frame_buffer_creation_80x24(c: &mut Criterion) {
c.bench_function("frame_buffer_creation_80x24", |b| {
b.iter(|| black_box(FrameBuffer::new(80, 24)));
});
}
fn bench_frame_buffer_creation_200x50(c: &mut Criterion) {
c.bench_function("frame_buffer_creation_200x50", |b| {
b.iter(|| black_box(FrameBuffer::new(200, 50)));
});
}
fn bench_swap_buffers_80x24(c: &mut Criterion) {
c.bench_function("swap_buffers_80x24", |b| {
let mut buffer = FrameBuffer::new(80, 24);
for y in 0..24 {
for x in 0..80 {
let _ = buffer.get_back_buffer().set_dot(x * 2, y * 4);
}
}
b.iter(|| {
buffer.swap_buffers();
black_box(&buffer);
});
});
}
fn bench_swap_buffers_200x50(c: &mut Criterion) {
c.bench_function("swap_buffers_200x50", |b| {
let mut buffer = FrameBuffer::new(200, 50);
for y in 0..50 {
for x in 0..200 {
let _ = buffer.get_back_buffer().set_dot(x * 2, y * 4);
}
}
b.iter(|| {
buffer.swap_buffers();
black_box(&buffer);
});
});
}
fn bench_get_back_buffer(c: &mut Criterion) {
c.bench_function("get_back_buffer", |b| {
let mut buffer = FrameBuffer::new(80, 24);
b.iter(|| {
black_box(buffer.get_back_buffer());
});
});
}
fn bench_full_frame_cycle(c: &mut Criterion) {
c.bench_function("full_frame_cycle_80x24", |b| {
let mut buffer = FrameBuffer::new(80, 24);
b.iter(|| {
buffer.get_back_buffer().clear();
let _ = buffer.get_back_buffer().set_dot(40, 48);
let _ = buffer.get_back_buffer().set_dot(41, 48);
let _ = buffer.get_back_buffer().set_dot(40, 49);
let _ = buffer.get_back_buffer().set_dot(41, 49);
buffer.swap_buffers();
black_box(&buffer);
});
});
}
fn bench_frame_timer_creation(c: &mut Criterion) {
c.bench_function("frame_timer_creation", |b| {
b.iter(|| black_box(FrameTimer::new(60)));
});
}
fn bench_frame_timer_actual_fps(c: &mut Criterion) {
c.bench_function("frame_timer_actual_fps", |b| {
let mut timer = FrameTimer::new(60);
for _ in 0..60 {
timer.wait_for_next_frame();
}
b.iter(|| {
black_box(timer.actual_fps());
});
});
}
fn bench_frame_timer_frame_time(c: &mut Criterion) {
c.bench_function("frame_timer_frame_time", |b| {
let mut timer = FrameTimer::new(60);
timer.wait_for_next_frame();
b.iter(|| {
black_box(timer.frame_time());
});
});
}
fn bench_frame_timer_reset(c: &mut Criterion) {
c.bench_function("frame_timer_reset", |b| {
let mut timer = FrameTimer::new(60);
for _ in 0..60 {
timer.wait_for_next_frame();
}
b.iter(|| {
timer.reset();
black_box(&timer);
});
});
}
fn bench_frame_timer_overhead(c: &mut Criterion) {
c.bench_function("frame_timer_wait_overhead", |b| {
let mut timer = FrameTimer::new(240);
b.iter(|| {
timer.wait_for_next_frame();
black_box(&timer);
});
});
}
fn bench_differential_renderer_creation(c: &mut Criterion) {
c.bench_function("differential_renderer_creation", |b| {
b.iter(|| black_box(DifferentialRenderer::new()));
});
}
fn bench_differential_count_changes(c: &mut Criterion) {
c.bench_function("differential_count_changes_80x24", |b| {
let renderer = DifferentialRenderer::new();
let frame1 = BrailleGrid::new(80, 24).unwrap();
let mut frame2 = BrailleGrid::new(80, 24).unwrap();
for i in 0..96 {
let x = (i * 7) % 160; let y = (i * 13) % 96;
let _ = frame2.set_dot(x, y);
}
b.iter(|| black_box(renderer.count_changed_cells(&frame2, &frame1)));
});
}
fn bench_differential_no_changes(c: &mut Criterion) {
c.bench_function("differential_no_changes_80x24", |b| {
let renderer = DifferentialRenderer::new();
let frame1 = BrailleGrid::new(80, 24).unwrap();
let frame2 = BrailleGrid::new(80, 24).unwrap();
b.iter(|| black_box(renderer.count_changed_cells(&frame2, &frame1)));
});
}
fn bench_differential_all_changes(c: &mut Criterion) {
c.bench_function("differential_all_changes_80x24", |b| {
let renderer = DifferentialRenderer::new();
let frame1 = BrailleGrid::new(80, 24).unwrap();
let mut frame2 = BrailleGrid::new(80, 24).unwrap();
for y in 0..96 {
for x in 0..160 {
let _ = frame2.set_dot(x, y);
}
}
b.iter(|| black_box(renderer.count_changed_cells(&frame2, &frame1)));
});
}
fn bench_differential_moving_object(c: &mut Criterion) {
c.bench_function("differential_moving_object_80x24", |b| {
let renderer = DifferentialRenderer::new();
let mut frame1 = BrailleGrid::new(80, 24).unwrap();
for x in 0..160 {
let _ = frame1.set_dot(x, 0);
let _ = frame1.set_dot(x, 95);
}
for y in 0..96 {
let _ = frame1.set_dot(0, y);
let _ = frame1.set_dot(159, y);
}
let mut frame2 = frame1.clone();
for dy in 0..8 {
for dx in 0..4 {
let _ = frame2.set_dot(44 + dx, 44 + dy);
}
}
b.iter(|| {
let changed = renderer.count_changed_cells(&frame2, &frame1);
black_box(changed);
});
});
}
fn bench_differential_io_reduction_verification(c: &mut Criterion) {
c.bench_function("differential_io_reduction_verify", |b| {
let renderer = DifferentialRenderer::new();
let frame1 = BrailleGrid::new(80, 24).unwrap();
let mut frame2 = BrailleGrid::new(80, 24).unwrap();
for i in 0..96 {
let x = (i * 17) % 160;
let y = (i * 23) % 96;
let _ = frame2.set_dot(x, y);
}
b.iter(|| {
let total_cells = 80 * 24; let changed = renderer.count_changed_cells(&frame2, &frame1);
let reduction = ((total_cells - changed) as f64 / total_cells as f64) * 100.0;
black_box(reduction);
assert!(
reduction > 60.0,
"I/O reduction should be >60%, got {reduction:.1}%"
);
});
});
}
fn bench_60fps_sustained_100_frames(c: &mut Criterion) {
use std::time::Duration;
let mut group = c.benchmark_group("60fps_sustained");
group.measurement_time(Duration::from_secs(15));
group.bench_function("100_frames_80x24", |b| {
let mut buffer = FrameBuffer::new(80, 24);
b.iter(|| {
for frame in 0..100_u32 {
buffer.get_back_buffer().clear();
let ball_x = ((frame * 2) % 156) as usize + 2; let ball_y = ((frame * 3) % 92) as usize + 2;
for dy in 0..4 {
for dx in 0..4 {
let _ = buffer.get_back_buffer().set_dot(ball_x + dx, ball_y + dy);
}
}
let chars = buffer.get_back_buffer().to_unicode_grid();
black_box(&chars);
buffer.swap_buffers();
}
black_box(&buffer);
});
});
group.bench_function("100_frames_200x50", |b| {
let mut buffer = FrameBuffer::new(200, 50);
b.iter(|| {
for frame in 0..100_u32 {
buffer.get_back_buffer().clear();
let ball_x = ((frame * 2) % 396) as usize + 2;
let ball_y = ((frame * 3) % 196) as usize + 2;
for dy in 0..8 {
for dx in 0..8 {
let _ = buffer.get_back_buffer().set_dot(ball_x + dx, ball_y + dy);
}
}
let chars = buffer.get_back_buffer().to_unicode_grid();
black_box(&chars);
buffer.swap_buffers();
}
black_box(&buffer);
});
});
group.finish();
}
fn bench_frame_preparation_time(c: &mut Criterion) {
let mut group = c.benchmark_group("frame_preparation");
group.bench_function("single_frame_80x24", |b| {
let mut buffer = FrameBuffer::new(80, 24);
b.iter(|| {
buffer.get_back_buffer().clear();
for i in 0..10 {
let x = (i * 15) % 156;
let y = (i * 9) % 92;
for dy in 0..4 {
for dx in 0..4 {
let _ = buffer.get_back_buffer().set_dot(x + dx, y + dy);
}
}
}
let chars = buffer.get_back_buffer().to_unicode_grid();
black_box(&chars);
buffer.swap_buffers();
black_box(&buffer);
});
});
group.bench_function("single_frame_200x50", |b| {
let mut buffer = FrameBuffer::new(200, 50);
b.iter(|| {
buffer.get_back_buffer().clear();
for i in 0..20 {
let x = (i * 19) % 396;
let y = (i * 9) % 196;
for dy in 0..8 {
for dx in 0..8 {
let _ = buffer.get_back_buffer().set_dot(x + dx, y + dy);
}
}
}
let chars = buffer.get_back_buffer().to_unicode_grid();
black_box(&chars);
buffer.swap_buffers();
black_box(&buffer);
});
});
group.finish();
}
fn bench_60fps_computational_budget(c: &mut Criterion) {
use std::time::Duration;
let mut group = c.benchmark_group("60fps_budget");
group.measurement_time(Duration::from_secs(10));
group.bench_function("100_frames_compute_only_80x24", |b| {
let mut buffer = FrameBuffer::new(80, 24);
let renderer = DifferentialRenderer::new();
let mut prev_grid = BrailleGrid::new(80, 24).unwrap();
b.iter(|| {
for frame in 0..100_u32 {
buffer.get_back_buffer().clear();
let ball_x = ((frame * 2) % 156) as usize + 2;
let ball_y = ((frame * 3) % 92) as usize + 2;
for dy in 0..4 {
for dx in 0..4 {
let _ = buffer.get_back_buffer().set_dot(ball_x + dx, ball_y + dy);
}
}
let changed = renderer.count_changed_cells(buffer.get_back_buffer(), &prev_grid);
black_box(changed);
let chars = buffer.get_back_buffer().to_unicode_grid();
black_box(&chars);
prev_grid = buffer.get_back_buffer().clone();
buffer.swap_buffers();
}
});
});
group.finish();
}
criterion_group!(
benches,
bench_frame_buffer_creation_80x24,
bench_frame_buffer_creation_200x50,
bench_swap_buffers_80x24,
bench_swap_buffers_200x50,
bench_get_back_buffer,
bench_full_frame_cycle,
bench_frame_timer_creation,
bench_frame_timer_actual_fps,
bench_frame_timer_frame_time,
bench_frame_timer_reset,
bench_frame_timer_overhead,
bench_differential_renderer_creation,
bench_differential_count_changes,
bench_differential_no_changes,
bench_differential_all_changes,
bench_differential_moving_object,
bench_differential_io_reduction_verification,
bench_60fps_sustained_100_frames,
bench_frame_preparation_time,
bench_60fps_computational_budget
);
criterion_main!(benches);