| use std::fs::File; |
| use std::io::BufWriter; |
| use std::path::Path; |
| |
| use clap::{App, Arg}; |
| |
| use piet_gpu_hal::{BufferUsage, Error, Instance, InstanceFlags, Session}; |
| |
| use piet_gpu::{test_scenes, PietGpuRenderContext, Renderer}; |
| |
| const WIDTH: usize = 2048; |
| const HEIGHT: usize = 1536; |
| |
| #[allow(unused)] |
| fn dump_scene(buf: &[u8]) { |
| for i in 0..(buf.len() / 4) { |
| let mut buf_u32 = [0u8; 4]; |
| buf_u32.copy_from_slice(&buf[i * 4..i * 4 + 4]); |
| println!("{:4x}: {:8x}", i * 4, u32::from_le_bytes(buf_u32)); |
| } |
| } |
| |
| #[allow(unused)] |
| fn dump_state(buf: &[u8]) { |
| for i in 0..(buf.len() / 48) { |
| let j = i * 48; |
| let floats = (0..11) |
| .map(|k| { |
| let mut buf_f32 = [0u8; 4]; |
| buf_f32.copy_from_slice(&buf[j + k * 4..j + k * 4 + 4]); |
| f32::from_le_bytes(buf_f32) |
| }) |
| .collect::<Vec<_>>(); |
| println!( |
| "{}: [{} {} {} {} {} {}] ({}, {})-({} {}) {} {}", |
| i, |
| floats[0], |
| floats[1], |
| floats[2], |
| floats[3], |
| floats[4], |
| floats[5], |
| floats[6], |
| floats[7], |
| floats[8], |
| floats[9], |
| floats[10], |
| buf[j + 44] |
| ); |
| } |
| } |
| |
| /// Interpret the output of the binning stage, for diagnostic purposes. |
| #[allow(unused)] |
| fn trace_merge(buf: &[u32]) { |
| for bin in 0..256 { |
| println!("bin {}:", bin); |
| let mut starts = (0..16) |
| .map(|i| Some((bin * 16 + i) * 64)) |
| .collect::<Vec<Option<usize>>>(); |
| loop { |
| let min_start = starts |
| .iter() |
| .map(|st| { |
| st.map(|st| { |
| if buf[st / 4] == 0 { |
| !0 |
| } else { |
| buf[st / 4 + 2] |
| } |
| }) |
| .unwrap_or(!0) |
| }) |
| .min() |
| .unwrap(); |
| if min_start == !0 { |
| break; |
| } |
| let mut selected = !0; |
| for i in 0..16 { |
| if let Some(st) = starts[i] { |
| if buf[st / 4] != 0 && buf[st / 4 + 2] == min_start { |
| selected = i; |
| break; |
| } |
| } |
| } |
| let st = starts[selected].unwrap(); |
| println!("selected {}, start {:x}", selected, st); |
| for j in 0..buf[st / 4] { |
| println!("{:x}", buf[st / 4 + 2 + j as usize]) |
| } |
| if buf[st / 4 + 1] == 0 { |
| starts[selected] = None; |
| } else { |
| starts[selected] = Some(buf[st / 4 + 1] as usize); |
| } |
| } |
| } |
| } |
| |
| /// Interpret the output of the coarse raster stage, for diagnostic purposes. |
| #[allow(unused)] |
| fn trace_ptcl(buf: &[u32]) { |
| for y in 0..96 { |
| for x in 0..128 { |
| let tile_ix = y * 128 + x; |
| println!("tile {} @({}, {})", tile_ix, x, y); |
| let mut tile_offset = tile_ix * 1024; |
| loop { |
| let tag = buf[tile_offset / 4]; |
| match tag { |
| 0 => break, |
| 3 => { |
| let backdrop = buf[tile_offset / 4 + 2]; |
| let rgba_color = buf[tile_offset / 4 + 3]; |
| println!(" {:x}: fill {:x} {}", tile_offset, rgba_color, backdrop); |
| let mut seg_chunk = buf[tile_offset / 4 + 1] as usize; |
| let n = buf[seg_chunk / 4] as usize; |
| let segs = buf[seg_chunk / 4 + 2] as usize; |
| println!(" chunk @{:x}: n={}, segs @{:x}", seg_chunk, n, segs); |
| for i in 0..n { |
| let x0 = f32::from_bits(buf[segs / 4 + i * 5]); |
| let y0 = f32::from_bits(buf[segs / 4 + i * 5 + 1]); |
| let x1 = f32::from_bits(buf[segs / 4 + i * 5 + 2]); |
| let y1 = f32::from_bits(buf[segs / 4 + i * 5 + 3]); |
| let y_edge = f32::from_bits(buf[segs / 4 + i * 5 + 4]); |
| println!( |
| " ({:.3}, {:.3}) - ({:.3}, {:.3}) | {:.3}", |
| x0, y0, x1, y1, y_edge |
| ); |
| } |
| loop { |
| seg_chunk = buf[seg_chunk / 4 + 1] as usize; |
| if seg_chunk == 0 { |
| break; |
| } |
| } |
| } |
| 4 => { |
| let line_width = f32::from_bits(buf[tile_offset / 4 + 2]); |
| let rgba_color = buf[tile_offset / 4 + 3]; |
| println!( |
| " {:x}: stroke {:x} {}", |
| tile_offset, rgba_color, line_width |
| ); |
| let mut seg_chunk = buf[tile_offset / 4 + 1] as usize; |
| let n = buf[seg_chunk / 4] as usize; |
| let segs = buf[seg_chunk / 4 + 2] as usize; |
| println!(" chunk @{:x}: n={}, segs @{:x}", seg_chunk, n, segs); |
| for i in 0..n { |
| let x0 = f32::from_bits(buf[segs / 4 + i * 5]); |
| let y0 = f32::from_bits(buf[segs / 4 + i * 5 + 1]); |
| let x1 = f32::from_bits(buf[segs / 4 + i * 5 + 2]); |
| let y1 = f32::from_bits(buf[segs / 4 + i * 5 + 3]); |
| let y_edge = f32::from_bits(buf[segs / 4 + i * 5 + 4]); |
| println!( |
| " ({:.3}, {:.3}) - ({:.3}, {:.3}) | {:.3}", |
| x0, y0, x1, y1, y_edge |
| ); |
| } |
| loop { |
| seg_chunk = buf[seg_chunk / 4 + 1] as usize; |
| if seg_chunk == 0 { |
| break; |
| } |
| } |
| } |
| 6 => { |
| let backdrop = buf[tile_offset / 4 + 2]; |
| println!(" {:x}: begin_clip {}", tile_offset, backdrop); |
| let mut seg_chunk = buf[tile_offset / 4 + 1] as usize; |
| let n = buf[seg_chunk / 4] as usize; |
| let segs = buf[seg_chunk / 4 + 2] as usize; |
| println!(" chunk @{:x}: n={}, segs @{:x}", seg_chunk, n, segs); |
| for i in 0..n { |
| let x0 = f32::from_bits(buf[segs / 4 + i * 5]); |
| let y0 = f32::from_bits(buf[segs / 4 + i * 5 + 1]); |
| let x1 = f32::from_bits(buf[segs / 4 + i * 5 + 2]); |
| let y1 = f32::from_bits(buf[segs / 4 + i * 5 + 3]); |
| let y_edge = f32::from_bits(buf[segs / 4 + i * 5 + 4]); |
| println!( |
| " ({:.3}, {:.3}) - ({:.3}, {:.3}) | {:.3}", |
| x0, y0, x1, y1, y_edge |
| ); |
| } |
| loop { |
| seg_chunk = buf[seg_chunk / 4 + 1] as usize; |
| if seg_chunk == 0 { |
| break; |
| } |
| } |
| } |
| 7 => { |
| let backdrop = buf[tile_offset / 4 + 1]; |
| println!("{:x}: solid_clip {:x}", tile_offset, backdrop); |
| } |
| 8 => { |
| println!("{:x}: end_clip", tile_offset); |
| } |
| _ => { |
| println!("{:x}: {}", tile_offset, tag); |
| } |
| } |
| if tag == 0 { |
| break; |
| } |
| if tag == 8 { |
| tile_offset = buf[tile_offset / 4 + 1] as usize; |
| } else { |
| tile_offset += 20; |
| } |
| } |
| } |
| } |
| } |
| |
| 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 (instance, _) = Instance::new(None, InstanceFlags::default())?; |
| unsafe { |
| let device = instance.device(None)?; |
| let session = Session::new(device); |
| |
| let mut cmd_buf = session.cmd_buf()?; |
| let query_pool = session.create_query_pool(8)?; |
| |
| 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 { |
| test_scenes::render_scene(&mut ctx); |
| } |
| |
| let mut renderer = Renderer::new(&session, WIDTH, HEIGHT, 1)?; |
| renderer.upload_render_ctx(&mut ctx, 0)?; |
| let image_usage = BufferUsage::MAP_READ | BufferUsage::COPY_DST; |
| let image_buf = session.create_buffer((WIDTH * HEIGHT * 4) as u64, image_usage)?; |
| |
| cmd_buf.begin(); |
| renderer.record(&mut cmd_buf, &query_pool, 0); |
| cmd_buf.copy_image_to_buffer(&renderer.image_dev, &image_buf); |
| cmd_buf.finish_timestamps(&query_pool); |
| cmd_buf.host_barrier(); |
| cmd_buf.finish(); |
| let start = std::time::Instant::now(); |
| let submitted = session.run_cmd_buf(cmd_buf, &[], &[])?; |
| submitted.wait()?; |
| println!("elapsed = {:?}", start.elapsed()); |
| let ts = session.fetch_query_pool(&query_pool).unwrap(); |
| if !ts.is_empty() { |
| println!("Element kernel time: {:.3}ms", ts[0] * 1e3); |
| println!( |
| "Tile allocation kernel time: {:.3}ms", |
| (ts[1] - ts[0]) * 1e3 |
| ); |
| println!("Coarse path kernel time: {:.3}ms", (ts[2] - ts[1]) * 1e3); |
| println!("Backdrop kernel time: {:.3}ms", (ts[3] - ts[2]) * 1e3); |
| println!("Binning kernel time: {:.3}ms", (ts[4] - ts[3]) * 1e3); |
| println!("Coarse raster kernel time: {:.3}ms", (ts[5] - ts[4]) * 1e3); |
| println!("Render kernel time: {:.3}ms", (ts[6] - ts[5]) * 1e3); |
| } |
| |
| /* |
| let mut data: Vec<u32> = Default::default(); |
| renderer.memory_buf_dev.read(&mut data).unwrap(); |
| piet_gpu::dump_k1_data(&data[2..]); |
| */ |
| |
| let mut img_data: Vec<u8> = Default::default(); |
| // Note: because png can use a `&[u8]` slice, we could avoid an extra copy |
| // (probably passing a slice into a closure). But for now: keep it simple. |
| image_buf.read(&mut img_data).unwrap(); |
| |
| // Write image as PNG file. |
| let path = Path::new("image.png"); |
| let file = File::create(path).unwrap(); |
| let ref mut w = BufWriter::new(file); |
| |
| let mut encoder = png::Encoder::new(w, WIDTH as u32, HEIGHT as u32); |
| encoder.set_color(png::ColorType::RGBA); |
| encoder.set_depth(png::BitDepth::Eight); |
| let mut writer = encoder.write_header().unwrap(); |
| |
| writer.write_image_data(&img_data).unwrap(); |
| } |
| |
| Ok(()) |
| } |