use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
use ftui_core::geometry::Rect;
use ftui_render::buffer::{Buffer, DoubleBuffer};
use ftui_render::cell::{Cell, PackedRgba};
use std::hint::black_box;
fn bench_buffer_new(c: &mut Criterion) {
let mut group = c.benchmark_group("buffer/new");
for (w, h) in [(80, 24), (120, 40), (200, 60)] {
let cells = w as u64 * h as u64;
group.throughput(Throughput::Elements(cells));
group.bench_with_input(
BenchmarkId::new("alloc", format!("{w}x{h}")),
&(),
|b, _| b.iter(|| black_box(Buffer::new(w, h))),
);
}
group.finish();
}
fn bench_buffer_clone(c: &mut Criterion) {
let mut group = c.benchmark_group("buffer/clone");
for (w, h) in [(80, 24), (120, 40), (200, 60)] {
let cells = w as u64 * h as u64;
group.throughput(Throughput::Elements(cells));
let buf = Buffer::new(w, h);
group.bench_with_input(
BenchmarkId::new("clone", format!("{w}x{h}")),
&buf,
|b, buf| b.iter(|| black_box(buf.clone())),
);
}
group.finish();
}
fn bench_buffer_set(c: &mut Criterion) {
let mut group = c.benchmark_group("buffer/set");
let cell = Cell::from_char('X').with_fg(PackedRgba::rgb(255, 0, 0));
group.bench_function("set_raw_single", |b| {
let mut buf = Buffer::new(80, 24);
b.iter(|| {
buf.set_raw(black_box(40), black_box(12), cell);
black_box(&buf);
})
});
group.bench_function("set_single", |b| {
let mut buf = Buffer::new(80, 24);
b.iter(|| {
buf.set(black_box(40), black_box(12), cell);
black_box(&buf);
})
});
group.bench_function("set_fast_single_transparent_bg", |b| {
let mut buf = Buffer::new(80, 24);
b.iter(|| {
buf.set_fast(black_box(40), black_box(12), cell);
black_box(&buf);
})
});
let opaque_cell = Cell::from_char('X')
.with_fg(PackedRgba::rgb(255, 0, 0))
.with_bg(PackedRgba::rgb(0, 0, 0));
group.bench_function("set_fast_single", |b| {
let mut buf = Buffer::new(80, 24);
b.iter(|| {
buf.set_fast(black_box(40), black_box(12), opaque_cell);
black_box(&buf);
})
});
group.bench_function("set_raw_row_80", |b| {
let mut buf = Buffer::new(80, 24);
b.iter(|| {
for x in 0..80u16 {
buf.set_raw(x, 12, cell);
}
black_box(&buf);
})
});
group.bench_function("set_row_80", |b| {
let mut buf = Buffer::new(80, 24);
b.iter(|| {
for x in 0..80u16 {
buf.set(x, 12, cell);
}
black_box(&buf);
})
});
group.bench_function("set_fast_row_80_transparent_bg", |b| {
let mut buf = Buffer::new(80, 24);
b.iter(|| {
for x in 0..80u16 {
buf.set_fast(x, 12, cell);
}
black_box(&buf);
})
});
group.bench_function("set_fast_row_80_opaque_bg", |b| {
let mut buf = Buffer::new(80, 24);
b.iter(|| {
for x in 0..80u16 {
buf.set_fast(x, 12, opaque_cell);
}
black_box(&buf);
})
});
group.finish();
}
fn bench_buffer_fill(c: &mut Criterion) {
let mut group = c.benchmark_group("buffer/fill");
let cell = Cell::from_char('.').with_bg(PackedRgba::rgb(0, 0, 64));
for (w, h) in [(80, 24), (120, 40), (200, 60)] {
let cells = w as u64 * h as u64;
group.throughput(Throughput::Elements(cells));
group.bench_with_input(
BenchmarkId::new("fill_all", format!("{w}x{h}")),
&(),
|b, _| {
let mut buf = Buffer::new(w, h);
let rect = Rect::from_size(w, h);
b.iter(|| {
buf.fill(rect, cell);
black_box(&buf);
})
},
);
}
group.bench_function("fill_quarter_80x24", |b| {
let mut buf = Buffer::new(80, 24);
let rect = Rect::new(0, 0, 40, 12);
b.iter(|| {
buf.fill(rect, cell);
black_box(&buf);
})
});
group.bench_function("fill_half_width_200x60", |b| {
let mut buf = Buffer::new(200, 60);
let rect = Rect::new(0, 0, 100, 60);
let fill_cell = Cell::from_char('.').with_bg(PackedRgba::rgb(0, 0, 64));
b.iter(|| {
buf.fill(rect, fill_cell);
black_box(&buf);
})
});
group.finish();
}
fn bench_buffer_row_access(c: &mut Criterion) {
let mut group = c.benchmark_group("buffer/row_access");
for (w, h) in [(80, 24), (200, 60)] {
let buf = Buffer::new(w, h);
group.bench_with_input(
BenchmarkId::new("row_cells_all", format!("{w}x{h}")),
&buf,
|b, buf| {
b.iter(|| {
for y in 0..h {
black_box(buf.row_cells(y));
}
})
},
);
}
let buf = Buffer::new(80, 24);
group.bench_function("row_cells_single_80", |b| {
b.iter(|| black_box(buf.row_cells(black_box(12))))
});
group.finish();
}
fn bench_double_buffer_swap(c: &mut Criterion) {
let mut group = c.benchmark_group("buffer/double_buffer");
for (w, h) in [(80, 24), (120, 40), (200, 60)] {
let cells = w as u64 * h as u64;
group.throughput(Throughput::Elements(cells));
group.bench_with_input(
BenchmarkId::new("swap", format!("{w}x{h}")),
&(w, h),
|b, &(w, h)| {
let mut db = DoubleBuffer::new(w, h);
b.iter(|| {
db.swap();
black_box(&db);
})
},
);
group.bench_with_input(
BenchmarkId::new("clear", format!("{w}x{h}")),
&(w, h),
|b, &(w, h)| {
let mut db = DoubleBuffer::new(w, h);
b.iter(|| {
db.current_mut().clear();
black_box(&db);
})
},
);
group.bench_with_input(
BenchmarkId::new("swap_and_clear", format!("{w}x{h}")),
&(w, h),
|b, &(w, h)| {
let mut db = DoubleBuffer::new(w, h);
b.iter(|| {
db.swap();
db.current_mut().clear();
black_box(&db);
})
},
);
}
group.finish();
}
fn bench_buffer_scissor(c: &mut Criterion) {
let mut group = c.benchmark_group("buffer/scissor");
group.bench_function("push_pop_single", |b| {
let mut buf = Buffer::new(80, 24);
let scissor = Rect::new(10, 5, 60, 14);
b.iter(|| {
buf.push_scissor(scissor);
black_box(&buf);
buf.pop_scissor();
})
});
group.bench_function("push_pop_nested_3", |b| {
let mut buf = Buffer::new(80, 24);
let s1 = Rect::new(5, 2, 70, 20);
let s2 = Rect::new(10, 5, 60, 14);
let s3 = Rect::new(20, 8, 40, 8);
b.iter(|| {
buf.push_scissor(s1);
buf.push_scissor(s2);
buf.push_scissor(s3);
black_box(&buf);
buf.pop_scissor();
buf.pop_scissor();
buf.pop_scissor();
})
});
group.finish();
}
criterion_group!(
benches,
bench_buffer_new,
bench_buffer_clone,
bench_double_buffer_swap,
bench_buffer_set,
bench_buffer_fill,
bench_buffer_row_access,
bench_buffer_scissor,
);
criterion_main!(benches);