| use piet::kurbo::Point; |
| use piet::{RenderContext, Text, TextAttribute, TextLayoutBuilder}; |
| use piet_gpu_hal::{CmdBuf, Error, ImageLayout, Instance, Session, SubmittedCmdBuf}; |
| |
| use piet_gpu::{test_scenes, PietGpuRenderContext, Renderer}; |
| |
| use clap::{App, Arg}; |
| |
| use winit::{ |
| event::{Event, WindowEvent}, |
| event_loop::{ControlFlow, EventLoop}, |
| window::WindowBuilder, |
| }; |
| |
| const NUM_FRAMES: usize = 2; |
| |
| 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)) |
| .arg(Arg::with_name("flip").short("f").long("flip")) |
| .arg( |
| Arg::with_name("scale") |
| .short("s") |
| .long("scale") |
| .takes_value(true), |
| ) |
| .get_matches(); |
| |
| let event_loop = EventLoop::new(); |
| let window = WindowBuilder::new() |
| .with_inner_size(winit::dpi::LogicalSize { |
| width: (WIDTH / 2) as f64, |
| height: (HEIGHT / 2) as f64, |
| }) |
| .with_resizable(false) // currently not supported |
| .build(&event_loop)?; |
| |
| let (instance, surface) = Instance::new(Some(&window))?; |
| let mut info_string = "info".to_string(); |
| unsafe { |
| let device = instance.device(surface.as_ref())?; |
| let mut swapchain = |
| instance.swapchain(WIDTH / 2, HEIGHT / 2, &device, surface.as_ref().unwrap())?; |
| let session = Session::new(device); |
| |
| let mut current_frame = 0; |
| let present_semaphores = (0..NUM_FRAMES) |
| .map(|_| session.create_semaphore()) |
| .collect::<Result<Vec<_>, Error>>()?; |
| let query_pools = (0..NUM_FRAMES) |
| .map(|_| session.create_query_pool(8)) |
| .collect::<Result<Vec<_>, Error>>()?; |
| let mut cmd_bufs: [Option<CmdBuf>; NUM_FRAMES] = Default::default(); |
| 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 |
| |
| match event { |
| Event::WindowEvent { event, window_id } if window_id == window.id() => { |
| match event { |
| WindowEvent::CloseRequested => { |
| *control_flow = ControlFlow::Exit; |
| } |
| _ => (), |
| } |
| } |
| Event::MainEventsCleared => { |
| window.request_redraw(); |
| } |
| 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", |
| frame_s, |
| ts[6] * 1e3, |
| ts[0] * 1e3, |
| (ts[1] - ts[0]) * 1e3, |
| (ts[2] - ts[1]) * 1e3, |
| (ts[3] - ts[2]) * 1e3, |
| (ts[4] - ts[3]) * 1e3, |
| (ts[5] - ts[4]) * 1e3, |
| (ts[6] - ts[5]) * 1e3, |
| ); |
| } |
| |
| let mut ctx = PietGpuRenderContext::new(); |
| if let Some(input) = matches.value_of("INPUT") { |
| let mut scale = matches |
| .value_of("scale") |
| .map(|scale| scale.parse().unwrap()) |
| .unwrap_or(8.0); |
| if matches.is_present("flip") { |
| scale = -scale; |
| } |
| test_scenes::render_svg(&mut ctx, input, scale); |
| } else { |
| mmark.draw(&mut ctx); |
| } |
| render_info_string(&mut ctx, &info_string); |
| if let Err(e) = renderer.upload_render_ctx(&mut ctx, frame_idx) { |
| println!("error in uploading: {}", e); |
| } |
| |
| let (image_idx, acquisition_semaphore) = swapchain.next().unwrap(); |
| let swap_image = swapchain.image(image_idx); |
| let query_pool = &query_pools[frame_idx]; |
| let mut cmd_buf = cmd_bufs[frame_idx].take().unwrap_or_else(|| session.cmd_buf().unwrap()); |
| cmd_buf.begin(); |
| renderer.record(&mut cmd_buf, &query_pool, frame_idx); |
| |
| // Image -> Swapchain |
| cmd_buf.image_barrier( |
| &swap_image, |
| ImageLayout::Undefined, |
| ImageLayout::BlitDst, |
| ); |
| cmd_buf.blit_image(&renderer.image_dev, &swap_image); |
| cmd_buf.image_barrier(&swap_image, ImageLayout::BlitDst, ImageLayout::Present); |
| cmd_buf.finish(); |
| |
| submitted[frame_idx] = Some(session |
| .run_cmd_buf( |
| cmd_buf, |
| &[&acquisition_semaphore], |
| &[&present_semaphores[frame_idx]], |
| ) |
| .unwrap()); |
| |
| swapchain |
| .present(image_idx, &[&present_semaphores[frame_idx]]) |
| .unwrap(); |
| |
| current_frame += 1; |
| } |
| Event::LoopDestroyed => { |
| for cmd_buf in &mut submitted { |
| // Wait for command list submission, otherwise dropping of renderer may |
| // cause validation errors (and possibly crashes). |
| if let Some(cmd_buf) = cmd_buf.take() { |
| cmd_buf.wait().unwrap(); |
| } |
| } |
| } |
| _ => (), |
| } |
| }) |
| } |
| } |
| |
| fn render_info_string(rc: &mut impl RenderContext, info: &str) { |
| let layout = rc |
| .text() |
| .new_text_layout(info.to_string()) |
| .default_attribute(TextAttribute::FontSize(40.0)) |
| .build() |
| .unwrap(); |
| rc.draw_text(&layout, Point::new(110.0, 50.0)); |
| } |