docs: compare cold grid and translated pan rendering alternatives
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
//! Renderer-level phase translation experiment, not application code.
|
||||
use iced::advanced::{Renderer as _, graphics::geometry::Renderer as _, renderer::Headless};
|
||||
use iced::{
|
||||
Color, Point, Rectangle, Renderer, Size, Vector,
|
||||
widget::canvas::{Cache, Frame, Geometry, Path},
|
||||
};
|
||||
use std::{cell::Cell, hint::black_box, time::Instant};
|
||||
const BG: Color = Color::from_rgb8(246, 248, 251);
|
||||
const DOT: Color = Color::from_rgb(0.86, 0.89, 0.91);
|
||||
fn dots(f: &mut Frame, size: Size, step: f32, phase: Vector) {
|
||||
for x in 0..(size.width / step) as i32 + 1 {
|
||||
for y in 0..(size.height / step) as i32 + 1 {
|
||||
f.fill(
|
||||
&Path::circle(
|
||||
Point::new(phase.x + x as f32 * step, phase.y + y as f32 * step),
|
||||
0.65,
|
||||
),
|
||||
DOT,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn old(r: &Renderer, size: Size, step: f32, phase: Vector) -> Geometry {
|
||||
let mut f = Frame::new(r, size);
|
||||
f.fill_rectangle(Point::ORIGIN, size, BG);
|
||||
dots(&mut f, size, step, phase);
|
||||
f.into_geometry()
|
||||
}
|
||||
fn phase(r: &Renderer, cache: &Cache, size: Size, step: f32, builds: &Cell<usize>) -> Geometry {
|
||||
cache.draw_with_bounds(
|
||||
r,
|
||||
Rectangle {
|
||||
x: -step,
|
||||
y: -step,
|
||||
width: size.width + 3. * step,
|
||||
height: size.height + 3. * step,
|
||||
},
|
||||
|f| {
|
||||
builds.set(builds.get() + 1);
|
||||
f.fill_rectangle(
|
||||
Point::new(-step, -step),
|
||||
Size::new(size.width + 3. * step, size.height + 3. * step),
|
||||
BG,
|
||||
);
|
||||
dots(f, size, step, Vector::ZERO);
|
||||
},
|
||||
)
|
||||
}
|
||||
fn submit(r: &mut Renderer, size: Size, g: Geometry, pan: Vector) {
|
||||
r.with_layer(Rectangle::with_size(size), |r| {
|
||||
r.with_translation(pan, |r| r.draw_geometry(g))
|
||||
});
|
||||
}
|
||||
fn shot(r: &mut Renderer, size: Size, scale: f32) -> Vec<u8> {
|
||||
Headless::screenshot(
|
||||
r,
|
||||
Size::new(
|
||||
(size.width * scale).ceil() as u32,
|
||||
(size.height * scale).ceil() as u32,
|
||||
),
|
||||
scale,
|
||||
BG,
|
||||
)
|
||||
}
|
||||
fn main() {
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
let mut r = rt
|
||||
.block_on(<Renderer as Headless>::new(
|
||||
iced::Font::DEFAULT,
|
||||
16.into(),
|
||||
None,
|
||||
))
|
||||
.unwrap();
|
||||
println!(
|
||||
"renderer={}; phase-cache prototype; NOT native FPS",
|
||||
r.name()
|
||||
);
|
||||
for (size, zoom) in [
|
||||
(Size::new(1900., 660.), 1.),
|
||||
(Size::new(1900., 660.), 0.5),
|
||||
(Size::new(1440., 900.), 1.),
|
||||
(Size::new(1440., 900.), 0.5),
|
||||
(Size::new(301.5, 211.25), 0.73),
|
||||
] {
|
||||
if size.width != 301.5 {
|
||||
continue;
|
||||
}
|
||||
let step = (24f32 * zoom).max(14.);
|
||||
let cache = Cache::new();
|
||||
let builds = Cell::new(0);
|
||||
println!(
|
||||
"size={}x{} zoom={zoom} step={step}",
|
||||
size.width, size.height
|
||||
);
|
||||
for scale in [1., 1.25, 2.] {
|
||||
for pan in [
|
||||
Vector::ZERO,
|
||||
Vector::new(-13.25, 9.75),
|
||||
Vector::new(6.125, -8.5),
|
||||
Vector::new(step - 0.125, step - 0.25),
|
||||
Vector::new(-0.125, 0.125),
|
||||
] {
|
||||
let offset = Vector::new(pan.x.rem_euclid(step), pan.y.rem_euclid(step));
|
||||
r.reset(Rectangle::with_size(size));
|
||||
let g = old(&r, size, step, offset);
|
||||
r.draw_geometry(g);
|
||||
let expected = shot(&mut r, size, scale);
|
||||
r.reset(Rectangle::with_size(size));
|
||||
let g = phase(&r, &cache, size, step, &builds);
|
||||
submit(&mut r, size, g, offset);
|
||||
let actual = shot(&mut r, size, scale);
|
||||
assert_eq!(expected.len(), actual.len());
|
||||
for (index, (a, b)) in expected
|
||||
.chunks_exact(4)
|
||||
.zip(actual.chunks_exact(4))
|
||||
.enumerate()
|
||||
.filter(|(_, (a, b))| a != b)
|
||||
.take(20)
|
||||
{
|
||||
println!(
|
||||
"difference_xy={},{} expected={a:?} actual={b:?}",
|
||||
index % (size.width * scale).ceil() as usize,
|
||||
index / (size.width * scale).ceil() as usize
|
||||
);
|
||||
}
|
||||
let changed = expected
|
||||
.chunks_exact(4)
|
||||
.zip(actual.chunks_exact(4))
|
||||
.filter(|(a, b)| a != b)
|
||||
.count();
|
||||
let max = expected
|
||||
.iter()
|
||||
.zip(&actual)
|
||||
.map(|(a, b)| a.abs_diff(*b))
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
println!(
|
||||
"scale={scale} pan={},{} changed_pixels={changed} max_channel_delta={max}",
|
||||
pan.x, pan.y
|
||||
);
|
||||
}
|
||||
}
|
||||
assert_eq!(builds.get(), 1);
|
||||
for cached in [false, true] {
|
||||
let mut ms = Vec::new();
|
||||
for i in 0..23 {
|
||||
r.reset(Rectangle::with_size(size));
|
||||
let offset = Vector::new((i as f32 * 0.25).rem_euclid(step), 9.75);
|
||||
let now = Instant::now();
|
||||
let g = if cached {
|
||||
phase(&r, &cache, size, step, &builds)
|
||||
} else {
|
||||
old(&r, size, step, offset)
|
||||
};
|
||||
if cached {
|
||||
submit(&mut r, size, g, offset)
|
||||
} else {
|
||||
r.draw_geometry(g)
|
||||
};
|
||||
let elapsed = black_box(now.elapsed().as_secs_f64() * 1000.);
|
||||
if i >= 3 {
|
||||
ms.push(elapsed);
|
||||
}
|
||||
}
|
||||
ms.sort_by(f64::total_cmp);
|
||||
println!(
|
||||
"cached={cached} construct_and_submit changing_pan n=20 p50_ms={:.4} p95_ms={:.4} max_ms={:.4}",
|
||||
ms[10], ms[19], ms[19]
|
||||
);
|
||||
}
|
||||
println!("cache_builds={}", builds.get());
|
||||
assert_eq!(builds.get(), 1);
|
||||
// Includes backend prepare/upload and readback; still not native presentation.
|
||||
if size.width == 1900. && zoom == 1. || size.width == 1440. && zoom == 0.5 {
|
||||
for cached in [false, true] {
|
||||
let mut ms = Vec::new();
|
||||
for i in 0..10 {
|
||||
r.reset(Rectangle::with_size(size));
|
||||
let offset = Vector::new((i as f32 * 0.25).rem_euclid(step), 9.75);
|
||||
let now = Instant::now();
|
||||
let g = if cached {
|
||||
phase(&r, &cache, size, step, &builds)
|
||||
} else {
|
||||
old(&r, size, step, offset)
|
||||
};
|
||||
if cached {
|
||||
submit(&mut r, size, g, offset);
|
||||
} else {
|
||||
r.draw_geometry(g);
|
||||
}
|
||||
let pixels = black_box(shot(&mut r, size, 1.));
|
||||
let elapsed = now.elapsed().as_secs_f64() * 1000.;
|
||||
drop(pixels);
|
||||
if i >= 2 {
|
||||
ms.push(elapsed);
|
||||
}
|
||||
}
|
||||
ms.sort_by(f64::total_cmp);
|
||||
println!(
|
||||
"cached={cached} construct_submit_screenshot changing_pan n=8 p50_ms={:.4} min_ms={:.4} max_ms={:.4}",
|
||||
ms[4], ms[0], ms[7]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user