Adaptation of MotionMark 1.2 path test This is a quick and dirty adapation of the MotionMark 1.2 path test into Rust. It is not at all a fair comparison, as the original is using miter joins and butt caps, while this one is round/round (to use the SDF-based stroke renderer). Nonetheless, it might be fun to play with.
diff --git a/piet-gpu-hal/src/vulkan.rs b/piet-gpu-hal/src/vulkan.rs index 26e095f..e3f52d6 100644 --- a/piet-gpu-hal/src/vulkan.rs +++ b/piet-gpu-hal/src/vulkan.rs
@@ -409,7 +409,7 @@ .get_physical_device_surface_present_modes(device.physical_device, surface.surface)?; // Can change to MAILBOX to force high frame rates. - const PREFERRED_MODE: vk::PresentModeKHR = vk::PresentModeKHR::FIFO; + const PREFERRED_MODE: vk::PresentModeKHR = vk::PresentModeKHR::IMMEDIATE; let present_mode = present_modes .into_iter() .find(|mode| *mode == PREFERRED_MODE)
diff --git a/piet-gpu/bin/winit.rs b/piet-gpu/bin/winit.rs index ef41b31..7e3d4c6 100644 --- a/piet-gpu/bin/winit.rs +++ b/piet-gpu/bin/winit.rs
@@ -17,6 +17,8 @@ const WIDTH: usize = 2048; const HEIGHT: usize = 1536; +const MMARK_SIZE: usize = 60_000; + fn main() -> Result<(), Error> { let matches = App::new("piet-gpu test") .arg(Arg::with_name("INPUT").index(1)) @@ -57,6 +59,8 @@ let mut submitted: [Option<SubmittedCmdBuf>; NUM_FRAMES] = Default::default(); let mut renderer = Renderer::new(&session, WIDTH, HEIGHT, NUM_FRAMES)?; + let mut mmark = piet_gpu::mmark::MMark::new(MMARK_SIZE); + let mut last_time = std::time::Instant::now(); event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Poll; // `ControlFlow::Wait` if only re-render on event @@ -75,12 +79,17 @@ } Event::RedrawRequested(window_id) if window_id == window.id() => { let frame_idx = current_frame % NUM_FRAMES; + let now = std::time::Instant::now(); + let elapsed = now - last_time; + last_time = now; + let frame_s = format!("{:.1}ms", elapsed.as_micros() as f64 * 1e-3); if let Some(submitted) = submitted[frame_idx].take() { cmd_bufs[frame_idx] = submitted.wait().unwrap(); let ts = session.fetch_query_pool(&query_pools[frame_idx]).unwrap(); info_string = format!( - "{:.3}ms :: e:{:.3}ms|alloc:{:.3}ms|cp:{:.3}ms|bd:{:.3}ms|bin:{:.3}ms|cr:{:.3}ms|r:{:.3}ms", + "{} {:.3}ms :: e:{:.3}ms|alloc:{:.3}ms|cp:{:.3}ms|bd:{:.3}ms|bin:{:.3}ms|cr:{:.3}ms|r:{:.3}ms", + frame_s, ts[6] * 1e3, ts[0] * 1e3, (ts[1] - ts[0]) * 1e3, @@ -103,7 +112,7 @@ } test_scenes::render_svg(&mut ctx, input, scale); } else { - test_scenes::render_anim_frame(&mut ctx, current_frame); + mmark.draw(&mut ctx); } render_info_string(&mut ctx, &info_string); if let Err(e) = renderer.upload_render_ctx(&mut ctx, frame_idx) {
diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 30fcf8f..b445331 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs
@@ -1,4 +1,5 @@ mod gradient; +pub mod mmark; mod pico_svg; mod render_ctx; pub mod test_scenes;
diff --git a/piet-gpu/src/mmark.rs b/piet-gpu/src/mmark.rs new file mode 100644 index 0000000..8deda1b --- /dev/null +++ b/piet-gpu/src/mmark.rs
@@ -0,0 +1,121 @@ +//! A benchmark based on MotionMark 1.2's path benchmark. + +use rand::{Rng, seq::SliceRandom}; +use piet::{Color, RenderContext, kurbo::{BezPath, CubicBez, Line, ParamCurve, PathSeg, Point, QuadBez}}; + +use crate::PietGpuRenderContext; + +const WIDTH: usize = 1600; +const HEIGHT: usize = 900; + +const GRID_WIDTH: i64 = 80; +const GRID_HEIGHT: i64 = 40; + +pub struct MMark { + elements: Vec<Element>, +} + +struct Element { + seg: PathSeg, + color: Color, + width: f64, + is_split: bool, + grid_point: GridPoint, +} + +#[derive(Clone, Copy)] +struct GridPoint((i64, i64)); + +impl MMark { + pub fn new(n: usize) -> MMark { + let mut last = GridPoint((GRID_WIDTH / 2, GRID_HEIGHT / 2)); + let elements = (0..n).map(|_| { + let element = Element::new_rand(last); + last = element.grid_point; + element + }).collect(); + MMark { elements } + } + + pub fn draw(&mut self, ctx: &mut PietGpuRenderContext) { + let mut rng = rand::thread_rng(); + let mut path = BezPath::new(); + let len = self.elements.len(); + for (i, element) in self.elements.iter_mut().enumerate() { + if path.is_empty() { + path.move_to(element.seg.start()); + } + match element.seg { + PathSeg::Line(l) => path.line_to(l.p1), + PathSeg::Quad(q) => path.quad_to(q.p1, q.p2), + PathSeg::Cubic(c) => path.curve_to(c.p1, c.p2, c.p3), + } + if element.is_split || i == len { + // This gets color and width from the last element, original + // gets it from the first, but this should not matter. + ctx.stroke(&path, &element.color, element.width); + path = BezPath::new(); // Should have clear method, to avoid allocations. + } + if rng.gen::<f32>() > 0.995 { + element.is_split ^= true; + } + } + } +} + +const COLORS: &[Color] = &[ + Color::rgb8(0x10, 0x10, 0x10), + Color::rgb8(0x80, 0x80, 0x80), + Color::rgb8(0xc0, 0xc0, 0xc0), + Color::rgb8(0x10, 0x10, 0x10), + Color::rgb8(0x80, 0x80, 0x80), + Color::rgb8(0xc0, 0xc0, 0xc0), + Color::rgb8(0xe0, 0x10, 0x40), +]; + +impl Element { + fn new_rand(last: GridPoint) -> Element { + let mut rng = rand::thread_rng(); + let seg_type = rng.gen_range(0, 4); + let next = GridPoint::random_point(last); + let (grid_point, seg) = if seg_type < 2 { + (next, PathSeg::Line(Line::new(last.coordinate(), next.coordinate()))) + } else if seg_type < 3 { + let p2 = GridPoint::random_point(next); + (p2, PathSeg::Quad(QuadBez::new(last.coordinate(), next.coordinate(), p2.coordinate()))) + } else { + let p2 = GridPoint::random_point(next); + let p3 = GridPoint::random_point(next); + (p3, PathSeg::Cubic(CubicBez::new(last.coordinate(), next.coordinate(), p2.coordinate(), p3.coordinate()))) + }; + let color = COLORS.choose(&mut rng).unwrap().clone(); + let width = rng.gen::<f64>().powi(5) * 20.0 + 1.0; + let is_split = rng.gen(); + Element { seg, color, width, is_split, grid_point } + } +} + +const OFFSETS: &[(i64, i64)] = &[(-4, 0), (2, 0), (1, -2), (1, 2)]; + +impl GridPoint { + fn random_point(last: GridPoint) -> GridPoint { + let mut rng = rand::thread_rng(); + + let offset = OFFSETS.choose(&mut rng).unwrap(); + let mut x = last.0.0 + offset.0; + if x < 0 || x > GRID_WIDTH { + x -= offset.0 * 2; + } + let mut y = last.0.1 + offset.1; + if y < 0 || y > GRID_HEIGHT { + y -= offset.1 * 2; + } + GridPoint((x, y)) + } + + fn coordinate(&self) -> Point { + let scale_x = WIDTH as f64 / ((GRID_WIDTH + 1) as f64); + let scale_y = HEIGHT as f64 / ((GRID_HEIGHT + 1) as f64); + Point::new((self.0.0 as f64 + 0.5) * scale_x, 100.0 + (self.0.1 as f64 + 0.5) * scale_y) + } +}