From 9a8854ffab17188e6eff6462254b04aa33fcb205 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Mon, 11 May 2020 20:01:06 -0700 Subject: [PATCH 01/32] Experimenting with sort-middle Starting a prototype that explores the sort-middle approach. This commit has a prefix sum pass computing state per element. --- piet-gpu-types/src/lib.rs | 1 + piet-gpu-types/src/main.rs | 1 + piet-gpu-types/src/scene.rs | 43 ++++++ piet-gpu-types/src/state.rs | 14 ++ piet-gpu/bin/cli.rs | 49 +++---- piet-gpu/bin/winit.rs | 11 +- piet-gpu/shader/build.ninja | 3 + piet-gpu/shader/elements.comp | 173 ++++++++++++++++++++++++ piet-gpu/shader/elements.spv | Bin 0 -> 26760 bytes piet-gpu/shader/scene.h | 238 ++++++++++++++++++++++++++++++++++ piet-gpu/shader/state.h | 59 +++++++++ piet-gpu/src/lib.rs | 204 ++++++++++++----------------- piet-gpu/src/pico_svg.rs | 8 +- piet-gpu/src/render_ctx.rs | 226 ++++++++++++++++---------------- 14 files changed, 762 insertions(+), 268 deletions(-) create mode 100644 piet-gpu-types/src/state.rs create mode 100644 piet-gpu/shader/elements.comp create mode 100644 piet-gpu/shader/elements.spv create mode 100644 piet-gpu/shader/state.h diff --git a/piet-gpu-types/src/lib.rs b/piet-gpu-types/src/lib.rs index d85df70..288f71c 100644 --- a/piet-gpu-types/src/lib.rs +++ b/piet-gpu-types/src/lib.rs @@ -3,5 +3,6 @@ pub mod fill_seg; pub mod ptcl; pub mod scene; pub mod segment; +pub mod state; pub mod test; pub mod tilegroup; diff --git a/piet-gpu-types/src/main.rs b/piet-gpu-types/src/main.rs index c0b9d7e..033bec4 100644 --- a/piet-gpu-types/src/main.rs +++ b/piet-gpu-types/src/main.rs @@ -5,6 +5,7 @@ fn main() { .expect("provide a module name"); match mod_name.as_str() { "scene" => print!("{}", piet_gpu_types::scene::gen_gpu_scene()), + "state" => print!("{}", piet_gpu_types::state::gen_gpu_state()), "tilegroup" => print!("{}", piet_gpu_types::tilegroup::gen_gpu_tilegroup()), "segment" => print!("{}", piet_gpu_types::segment::gen_gpu_segment()), "fill_seg" => print!("{}", piet_gpu_types::fill_seg::gen_gpu_fill_seg()), diff --git a/piet-gpu-types/src/scene.rs b/piet-gpu-types/src/scene.rs index 5f95c40..7451c9c 100644 --- a/piet-gpu-types/src/scene.rs +++ b/piet-gpu-types/src/scene.rs @@ -4,6 +4,8 @@ pub use self::scene::{ Bbox, PietCircle, PietFill, PietItem, PietStrokeLine, PietStrokePolyLine, Point, SimpleGroup, }; +pub use self::scene::{CubicSeg, Element, Fill, LineSeg, QuadSeg, SetLineWidth, Stroke, Transform}; + piet_gpu! { #[rust_encode] mod scene { @@ -51,5 +53,46 @@ piet_gpu! { Fill(PietFill), Poly(PietStrokePolyLine), } + + // New approach follows (above to be deleted) + struct LineSeg { + p0: [f32; 2], + p1: [f32; 2], + } + struct QuadSeg { + p0: [f32; 2], + p1: [f32; 2], + p2: [f32; 2], + } + struct CubicSeg { + p0: [f32; 2], + p1: [f32; 2], + p2: [f32; 2], + p3: [f32; 2], + } + struct Fill { + rgba_color: u32, + } + struct Stroke { + rgba_color: u32, + } + struct SetLineWidth { + width: f32, + } + struct Transform { + mat: [f32; 4], + translate: [f32; 2], + } + enum Element { + Nop, + // The segments need a flag to indicate fill/stroke + Line(LineSeg), + Quad(QuadSeg), + Cubic(CubicSeg), + Stroke(Stroke), + Fill(Fill), + SetLineWidth(SetLineWidth), + Transform(Transform), + } } } diff --git a/piet-gpu-types/src/state.rs b/piet-gpu-types/src/state.rs new file mode 100644 index 0000000..35076f0 --- /dev/null +++ b/piet-gpu-types/src/state.rs @@ -0,0 +1,14 @@ +use piet_gpu_derive::piet_gpu; + +piet_gpu! { + #[gpu_write] + mod state { + struct State { + mat: [f32; 4], + translate: [f32; 2], + bbox: [f32; 4], + linewidth: f32, + flags: u32, + } + } +} diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index 839c262..82f3491 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -5,7 +5,7 @@ use std::path::Path; use piet_gpu_hal::vulkan::VkInstance; use piet_gpu_hal::{CmdBuf, Device, Error, MemFlags}; -use piet_gpu::{PietGpuRenderContext, Renderer, render_scene, WIDTH, HEIGHT}; +use piet_gpu::{render_scene, PietGpuRenderContext, Renderer, HEIGHT, WIDTH}; #[allow(unused)] fn dump_scene(buf: &[u8]) { @@ -16,6 +16,24 @@ fn dump_scene(buf: &[u8]) { } } +#[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::>(); + 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]); + } + +} + fn main() -> Result<(), Error> { let (instance, _) = VkInstance::new(None)?; unsafe { @@ -23,7 +41,7 @@ fn main() -> Result<(), Error> { let fence = device.create_fence(false)?; let mut cmd_buf = device.create_cmd_buf()?; - let query_pool = device.create_query_pool(6)?; + let query_pool = device.create_query_pool(2)?; let mut ctx = PietGpuRenderContext::new(); render_scene(&mut ctx); @@ -31,7 +49,8 @@ fn main() -> Result<(), Error> { //dump_scene(&scene); let renderer = Renderer::new(&device, scene)?; - let image_buf = device.create_buffer((WIDTH * HEIGHT * 4) as u64, MemFlags::host_coherent())?; + let image_buf = + device.create_buffer((WIDTH * HEIGHT * 4) as u64, MemFlags::host_coherent())?; cmd_buf.begin(); renderer.record(&mut cmd_buf, &query_pool); @@ -40,28 +59,12 @@ fn main() -> Result<(), Error> { device.run_cmd_buf(&cmd_buf, &[], &[], Some(&fence))?; device.wait_and_reset(&[fence])?; let timestamps = device.reap_query_pool(&query_pool).unwrap(); - println!("Kernel 1 time: {:.3}ms", timestamps[0] * 1e3); - println!( - "Kernel 2s time: {:.3}ms", - (timestamps[1] - timestamps[0]) * 1e3 - ); - println!( - "Kernel 2f time: {:.3}ms", - (timestamps[2] - timestamps[1]) * 1e3 - ); - println!( - "Kernel 3 time: {:.3}ms", - (timestamps[3] - timestamps[2]) * 1e3 - ); - println!( - "Render time: {:.3}ms", - (timestamps[4] - timestamps[3]) * 1e3 - ); + println!("Element kernel time: {:.3}ms", timestamps[0] * 1e3); /* - let mut k1_data: Vec = Default::default(); - device.read_buffer(&segment_buf, &mut k1_data).unwrap(); - dump_k1_data(&k1_data); + let mut data: Vec = Default::default(); + device.read_buffer(&renderer.state_buf, &mut data).unwrap(); + dump_state(&data); */ let mut img_data: Vec = Default::default(); diff --git a/piet-gpu/bin/winit.rs b/piet-gpu/bin/winit.rs index e5f174a..1c263bb 100644 --- a/piet-gpu/bin/winit.rs +++ b/piet-gpu/bin/winit.rs @@ -1,7 +1,7 @@ use piet_gpu_hal::vulkan::VkInstance; use piet_gpu_hal::{CmdBuf, Device, Error, ImageLayout}; -use piet_gpu::{PietGpuRenderContext, Renderer, render_scene, WIDTH, HEIGHT}; +use piet_gpu::{render_scene, PietGpuRenderContext, Renderer, HEIGHT, WIDTH}; use winit::{ event::{Event, WindowEvent}, @@ -69,7 +69,8 @@ fn main() -> Result<(), Error> { device.wait_and_reset(&[frame_fences[frame_idx]]).unwrap(); let timestamps = device.reap_query_pool(query_pool).unwrap(); - window.set_title(&format!("k1: {:.3}ms, k2s: {:.3}ms, k2f: {:.3}ms, k3: {:.3}ms, k4: {:.3}ms", + window.set_title(&format!( + "k1: {:.3}ms, k2s: {:.3}ms, k2f: {:.3}ms, k3: {:.3}ms, k4: {:.3}ms", timestamps[0] * 1e3, (timestamps[1] - timestamps[0]) * 1e3, (timestamps[2] - timestamps[1]) * 1e3, @@ -93,11 +94,7 @@ fn main() -> Result<(), Error> { ImageLayout::BlitDst, ); cmd_buf.blit_image(&renderer.image_dev, &swap_image); - cmd_buf.image_barrier( - &swap_image, - ImageLayout::BlitDst, - ImageLayout::Present, - ); + cmd_buf.image_barrier(&swap_image, ImageLayout::BlitDst, ImageLayout::Present); cmd_buf.finish(); device diff --git a/piet-gpu/shader/build.ninja b/piet-gpu/shader/build.ninja index 0aaecae..b429a71 100644 --- a/piet-gpu/shader/build.ninja +++ b/piet-gpu/shader/build.ninja @@ -18,3 +18,6 @@ build kernel2f.spv: glsl kernel2f.comp | scene.h tilegroup.h fill_seg.h setup.h build kernel3.spv: glsl kernel3.comp | scene.h tilegroup.h segment.h fill_seg.h ptcl.h setup.h build kernel4.spv: glsl kernel4.comp | ptcl.h segment.h fill_seg.h setup.h + + +build elements.spv: glsl elements.comp | scene.h state.h diff --git a/piet-gpu/shader/elements.comp b/piet-gpu/shader/elements.comp new file mode 100644 index 0000000..5cede7c --- /dev/null +++ b/piet-gpu/shader/elements.comp @@ -0,0 +1,173 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +#define N_ROWS 4 +#define WG_SIZE 32 +#define LG_WG_SIZE 5 +#define TILE_SIZE (WG_SIZE * N_ROWS) + +layout(local_size_x = WG_SIZE, local_size_y = 1) in; + +layout(set = 0, binding = 0) readonly buffer SceneBuf { + uint[] scene; +}; + +// This will be used for inter-wprkgroup aggregates +layout(set = 0, binding = 1) buffer StateBuf { + uint[] state; +}; + +#include "scene.h" +#include "state.h" + +#define FLAG_SET_LINEWIDTH 1 +#define FLAG_RESET_BBOX 2 + +// This is almost like a monoid (the interaction between transformation and +// bounding boxes is approximate) +State combine_state(State a, State b) { + State c; + c.bbox.x = min(a.mat.x * b.bbox.x, a.mat.x * b.bbox.z) + min(a.mat.z * b.bbox.y, a.mat.z * b.bbox.w) + a.translate.x; + c.bbox.y = min(a.mat.y * b.bbox.x, a.mat.y * b.bbox.z) + min(a.mat.w * b.bbox.y, a.mat.w * b.bbox.w) + a.translate.y; + c.bbox.z = max(a.mat.x * b.bbox.x, a.mat.x * b.bbox.z) + max(a.mat.z * b.bbox.y, a.mat.z * b.bbox.w) + a.translate.x; + c.bbox.w = max(a.mat.y * b.bbox.x, a.mat.y * b.bbox.z) + max(a.mat.w * b.bbox.y, a.mat.w * b.bbox.w) + a.translate.y; + if ((a.flags & FLAG_RESET_BBOX) == 0 && b.bbox.z <= b.bbox.x && b.bbox.w <= b.bbox.y) { + c.bbox = a.bbox; + } else if ((a.flags & FLAG_RESET_BBOX) == 0 && (a.bbox.z > a.bbox.x || a.bbox.w > a.bbox.y)) { + c.bbox.xy = min(a.bbox.xy, c.bbox.xy); + c.bbox.zw = max(a.bbox.zw, c.bbox.zw); + } + // It would be more concise to cast to matrix types; ah well. + c.mat.x = a.mat.x * b.mat.x + a.mat.z * b.mat.y; + c.mat.y = a.mat.y * b.mat.x + a.mat.w * b.mat.y; + c.mat.z = a.mat.x * b.mat.z + a.mat.z * b.mat.w; + c.mat.w = a.mat.y * b.mat.z + a.mat.w * b.mat.w; + c.translate.x = a.mat.x * b.translate.x + a.mat.z * b.translate.y + a.translate.x; + c.translate.y = a.mat.y * b.translate.x + a.mat.w * b.translate.y + a.translate.y; + c.linewidth = (b.flags & FLAG_SET_LINEWIDTH) == 0 ? a.linewidth : b.linewidth; + c.flags = a.flags | b.flags; + return c; +} + +State map_element(ElementRef ref) { + // TODO: it would *probably* be more efficient to make the memory read patterns less + // divergent, though it would be more wasted memory. + uint tag = Element_tag(ref); + State c; + c.bbox = vec4(0.0, 0.0, 0.0, 0.0); + c.mat = vec4(1.0, 0.0, 0.0, 1.0); + c.translate = vec2(0.0, 0.0); + c.linewidth = 0.0; + c.flags = 0; + switch (tag) { + case Element_Line: + LineSeg line = Element_Line_read(ref); + c.bbox.xy = min(line.p0, line.p1); + c.bbox.zw = max(line.p0, line.p1); + break; + case Element_Quad: + QuadSeg quad = Element_Quad_read(ref); + c.bbox.xy = min(min(quad.p0, quad.p1), quad.p2); + c.bbox.zw = max(max(quad.p0, quad.p1), quad.p2); + break; + case Element_Cubic: + CubicSeg cubic = Element_Cubic_read(ref); + c.bbox.xy = min(min(cubic.p0, cubic.p1), min(cubic.p2, cubic.p3)); + c.bbox.zw = max(max(cubic.p0, cubic.p1), max(cubic.p2, cubic.p3)); + break; + case Element_Fill: + case Element_Stroke: + c.flags = FLAG_RESET_BBOX; + break; + case Element_SetLineWidth: + SetLineWidth lw = Element_SetLineWidth_read(ref); + c.linewidth = lw.width; + c.flags = FLAG_SET_LINEWIDTH; + break; + case Element_Transform: + Transform t = Element_Transform_read(ref); + c.mat = t.mat; + c.translate = t.translate; + break; + } + return c; +} + +// We should be able to use an array of structs but the NV shader compiler +// doesn't seem to like it :/ +//shared State sh_state[WG_SIZE]; +shared vec4 sh_mat[WG_SIZE]; +shared vec2 sh_translate[WG_SIZE]; +shared vec4 sh_bbox[WG_SIZE]; +shared float sh_width[WG_SIZE]; +shared uint sh_flags[WG_SIZE]; + +void main() { + State th_state[N_ROWS]; + // this becomes an atomic counter + uint tile_ix = gl_WorkGroupID.x; + + uint ix = tile_ix * TILE_SIZE + gl_LocalInvocationID.x * N_ROWS; + ElementRef ref = ElementRef(ix * Element_size); + + th_state[0] = map_element(ref); + for (uint i = 1; i < N_ROWS; i++) { + // discussion question: would it be faster to load using more coherent patterns + // into thread memory? This is kinda strided. + th_state[i] = combine_state(th_state[i - 1], map_element(Element_index(ref, i))); + } + State agg = th_state[N_ROWS - 1]; + sh_mat[gl_LocalInvocationID.x] = agg.mat; + sh_translate[gl_LocalInvocationID.x] = agg.translate; + sh_bbox[gl_LocalInvocationID.x] = agg.bbox; + sh_width[gl_LocalInvocationID.x] = agg.linewidth; + sh_flags[gl_LocalInvocationID.x] = agg.flags; + for (uint i = 0; i < LG_WG_SIZE; i++) { + barrier(); + if (gl_LocalInvocationID.x >= (1 << i)) { + State other; + uint ix = gl_LocalInvocationID.x - (1 << i); + other.mat = sh_mat[ix]; + other.translate = sh_translate[ix]; + other.bbox = sh_bbox[ix]; + other.linewidth = sh_width[ix]; + other.flags = sh_flags[ix]; + agg = combine_state(other, agg); + } + barrier(); + sh_mat[gl_LocalInvocationID.x] = agg.mat; + sh_translate[gl_LocalInvocationID.x] = agg.translate; + sh_bbox[gl_LocalInvocationID.x] = agg.bbox; + sh_width[gl_LocalInvocationID.x] = agg.linewidth; + sh_flags[gl_LocalInvocationID.x] = agg.flags; + } + + // TODO: if last invocation in wg, publish agg. + + barrier(); + State exclusive; + exclusive.bbox = vec4(0.0, 0.0, 0.0, 0.0); + exclusive.mat = vec4(1.0, 0.0, 0.0, 1.0); + exclusive.translate = vec2(0.0, 0.0); + exclusive.linewidth = 0.0; + exclusive.flags = 0; + // TODO: do decoupled look-back + + State row = exclusive; + if (gl_LocalInvocationID.x > 0) { + uint ix = gl_LocalInvocationID.x - 1; + State other; + other.mat = sh_mat[ix]; + other.translate = sh_translate[ix]; + other.bbox = sh_bbox[ix]; + other.linewidth = sh_width[ix]; + other.flags = sh_flags[ix]; + row = combine_state(row, other); + } + for (uint i = 0; i < N_ROWS; i++) { + State this_state = combine_state(row, th_state[i]); + // We write the state now for development purposes, but the + // actual goal is to write transformed and annotated elements. + State_write(StateRef((ix + i) * State_size), this_state); + } +} diff --git a/piet-gpu/shader/elements.spv b/piet-gpu/shader/elements.spv new file mode 100644 index 0000000000000000000000000000000000000000..e97226cab6d9f24f9299099f741ac590504b77ac GIT binary patch literal 26760 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 DvZB!T literal 0 HcmV?d00001 diff --git a/piet-gpu/shader/scene.h b/piet-gpu/shader/scene.h index 5e36abc..84ef80d 100644 --- a/piet-gpu/shader/scene.h +++ b/piet-gpu/shader/scene.h @@ -32,6 +32,38 @@ struct PietItemRef { uint offset; }; +struct LineSegRef { + uint offset; +}; + +struct QuadSegRef { + uint offset; +}; + +struct CubicSegRef { + uint offset; +}; + +struct FillRef { + uint offset; +}; + +struct StrokeRef { + uint offset; +}; + +struct SetLineWidthRef { + uint offset; +}; + +struct TransformRef { + uint offset; +}; + +struct ElementRef { + uint offset; +}; + struct Bbox { ivec4 bbox; }; @@ -128,6 +160,97 @@ PietItemRef PietItem_index(PietItemRef ref, uint index) { return PietItemRef(ref.offset + index * PietItem_size); } +struct LineSeg { + vec2 p0; + vec2 p1; +}; + +#define LineSeg_size 16 + +LineSegRef LineSeg_index(LineSegRef ref, uint index) { + return LineSegRef(ref.offset + index * LineSeg_size); +} + +struct QuadSeg { + vec2 p0; + vec2 p1; + vec2 p2; +}; + +#define QuadSeg_size 24 + +QuadSegRef QuadSeg_index(QuadSegRef ref, uint index) { + return QuadSegRef(ref.offset + index * QuadSeg_size); +} + +struct CubicSeg { + vec2 p0; + vec2 p1; + vec2 p2; + vec2 p3; +}; + +#define CubicSeg_size 32 + +CubicSegRef CubicSeg_index(CubicSegRef ref, uint index) { + return CubicSegRef(ref.offset + index * CubicSeg_size); +} + +struct Fill { + uint rgba_color; +}; + +#define Fill_size 4 + +FillRef Fill_index(FillRef ref, uint index) { + return FillRef(ref.offset + index * Fill_size); +} + +struct Stroke { + uint rgba_color; +}; + +#define Stroke_size 4 + +StrokeRef Stroke_index(StrokeRef ref, uint index) { + return StrokeRef(ref.offset + index * Stroke_size); +} + +struct SetLineWidth { + float width; +}; + +#define SetLineWidth_size 4 + +SetLineWidthRef SetLineWidth_index(SetLineWidthRef ref, uint index) { + return SetLineWidthRef(ref.offset + index * SetLineWidth_size); +} + +struct Transform { + vec4 mat; + vec2 translate; +}; + +#define Transform_size 24 + +TransformRef Transform_index(TransformRef ref, uint index) { + return TransformRef(ref.offset + index * Transform_size); +} + +#define Element_Nop 0 +#define Element_Line 1 +#define Element_Quad 2 +#define Element_Cubic 3 +#define Element_Stroke 4 +#define Element_Fill 5 +#define Element_SetLineWidth 6 +#define Element_Transform 7 +#define Element_size 36 + +ElementRef Element_index(ElementRef ref, uint index) { + return ElementRef(ref.offset + index * Element_size); +} + Bbox Bbox_read(BboxRef ref) { uint ix = ref.offset >> 2; uint raw0 = scene[ix + 0]; @@ -236,3 +359,118 @@ PietStrokePolyLine PietItem_Poly_read(PietItemRef ref) { return PietStrokePolyLine_read(PietStrokePolyLineRef(ref.offset + 4)); } +LineSeg LineSeg_read(LineSegRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + uint raw1 = scene[ix + 1]; + uint raw2 = scene[ix + 2]; + uint raw3 = scene[ix + 3]; + LineSeg s; + s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); + s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + return s; +} + +QuadSeg QuadSeg_read(QuadSegRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + uint raw1 = scene[ix + 1]; + uint raw2 = scene[ix + 2]; + uint raw3 = scene[ix + 3]; + uint raw4 = scene[ix + 4]; + uint raw5 = scene[ix + 5]; + QuadSeg s; + s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); + s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.p2 = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); + return s; +} + +CubicSeg CubicSeg_read(CubicSegRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + uint raw1 = scene[ix + 1]; + uint raw2 = scene[ix + 2]; + uint raw3 = scene[ix + 3]; + uint raw4 = scene[ix + 4]; + uint raw5 = scene[ix + 5]; + uint raw6 = scene[ix + 6]; + uint raw7 = scene[ix + 7]; + CubicSeg s; + s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); + s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.p2 = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); + s.p3 = vec2(uintBitsToFloat(raw6), uintBitsToFloat(raw7)); + return s; +} + +Fill Fill_read(FillRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + Fill s; + s.rgba_color = raw0; + return s; +} + +Stroke Stroke_read(StrokeRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + Stroke s; + s.rgba_color = raw0; + return s; +} + +SetLineWidth SetLineWidth_read(SetLineWidthRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + SetLineWidth s; + s.width = uintBitsToFloat(raw0); + return s; +} + +Transform Transform_read(TransformRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + uint raw1 = scene[ix + 1]; + uint raw2 = scene[ix + 2]; + uint raw3 = scene[ix + 3]; + uint raw4 = scene[ix + 4]; + uint raw5 = scene[ix + 5]; + Transform s; + s.mat = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.translate = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); + return s; +} + +uint Element_tag(ElementRef ref) { + return scene[ref.offset >> 2]; +} + +LineSeg Element_Line_read(ElementRef ref) { + return LineSeg_read(LineSegRef(ref.offset + 4)); +} + +QuadSeg Element_Quad_read(ElementRef ref) { + return QuadSeg_read(QuadSegRef(ref.offset + 4)); +} + +CubicSeg Element_Cubic_read(ElementRef ref) { + return CubicSeg_read(CubicSegRef(ref.offset + 4)); +} + +Stroke Element_Stroke_read(ElementRef ref) { + return Stroke_read(StrokeRef(ref.offset + 4)); +} + +Fill Element_Fill_read(ElementRef ref) { + return Fill_read(FillRef(ref.offset + 4)); +} + +SetLineWidth Element_SetLineWidth_read(ElementRef ref) { + return SetLineWidth_read(SetLineWidthRef(ref.offset + 4)); +} + +Transform Element_Transform_read(ElementRef ref) { + return Transform_read(TransformRef(ref.offset + 4)); +} + diff --git a/piet-gpu/shader/state.h b/piet-gpu/shader/state.h new file mode 100644 index 0000000..2547b93 --- /dev/null +++ b/piet-gpu/shader/state.h @@ -0,0 +1,59 @@ +// Code auto-generated by piet-gpu-derive + +struct StateRef { + uint offset; +}; + +struct State { + vec4 mat; + vec2 translate; + vec4 bbox; + float linewidth; + uint flags; +}; + +#define State_size 48 + +StateRef State_index(StateRef ref, uint index) { + return StateRef(ref.offset + index * State_size); +} + +State State_read(StateRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = state[ix + 0]; + uint raw1 = state[ix + 1]; + uint raw2 = state[ix + 2]; + uint raw3 = state[ix + 3]; + uint raw4 = state[ix + 4]; + uint raw5 = state[ix + 5]; + uint raw6 = state[ix + 6]; + uint raw7 = state[ix + 7]; + uint raw8 = state[ix + 8]; + uint raw9 = state[ix + 9]; + uint raw10 = state[ix + 10]; + uint raw11 = state[ix + 11]; + State s; + s.mat = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.translate = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); + s.bbox = vec4(uintBitsToFloat(raw6), uintBitsToFloat(raw7), uintBitsToFloat(raw8), uintBitsToFloat(raw9)); + s.linewidth = uintBitsToFloat(raw10); + s.flags = raw11; + return s; +} + +void State_write(StateRef ref, State s) { + uint ix = ref.offset >> 2; + state[ix + 0] = floatBitsToUint(s.mat.x); + state[ix + 1] = floatBitsToUint(s.mat.y); + state[ix + 2] = floatBitsToUint(s.mat.z); + state[ix + 3] = floatBitsToUint(s.mat.w); + state[ix + 4] = floatBitsToUint(s.translate.x); + state[ix + 5] = floatBitsToUint(s.translate.y); + state[ix + 6] = floatBitsToUint(s.bbox.x); + state[ix + 7] = floatBitsToUint(s.bbox.y); + state[ix + 8] = floatBitsToUint(s.bbox.z); + state[ix + 9] = floatBitsToUint(s.bbox.w); + state[ix + 10] = floatBitsToUint(s.linewidth); + state[ix + 11] = s.flags; +} + diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index a47737a..82b20c8 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -1,5 +1,5 @@ -mod render_ctx; mod pico_svg; +mod render_ctx; pub use render_ctx::PietGpuRenderContext; @@ -8,6 +8,8 @@ use rand::{Rng, RngCore}; use piet::kurbo::{BezPath, Circle, Line, Point, Vec2}; use piet::{Color, RenderContext}; +use piet_gpu_types::encoder::Encode; + use piet_gpu_hal::{CmdBuf, Device, Error, ImageLayout, MemFlags}; use pico_svg::PicoSvg; @@ -110,6 +112,12 @@ pub struct Renderer { scene_buf: D::Buffer, scene_dev: D::Buffer, + pub state_buf: D::Buffer, + + el_pipeline: D::Pipeline, + el_ds: D::DescriptorSet, + + /* k1_alloc_buf_host: D::Buffer, k1_alloc_buf_dev: D::Buffer, k2s_alloc_buf_host: D::Buffer, @@ -131,6 +139,8 @@ pub struct Renderer { k3_ds: D::DescriptorSet, k4_pipeline: D::Pipeline, k4_ds: D::DescriptorSet, + */ + n_elements: usize, } impl Renderer { @@ -146,175 +156,123 @@ impl Renderer { .unwrap(); device.write_buffer(&scene_buf, &scene)?; + let state_buf = device.create_buffer(4 * 1024 * 1024, dev)?; + let image_dev = device.create_image2d(WIDTH as u32, HEIGHT as u32, dev)?; + + let el_code = include_bytes!("../shader/elements.spv"); + let el_pipeline = device.create_simple_compute_pipeline(el_code, 2, 0)?; + let el_ds = device.create_descriptor_set( + &el_pipeline, + &[&scene_dev, &state_buf], + &[], + )?; + + let n_elements = scene.len() / piet_gpu_types::scene::Element::fixed_size(); + println!("scene: {} elements", n_elements); + + /* let tilegroup_buf = device.create_buffer(4 * 1024 * 1024, dev)?; let ptcl_buf = device.create_buffer(48 * 1024 * 1024, dev)?; let segment_buf = device.create_buffer(64 * 1024 * 1024, dev)?; let fill_seg_buf = device.create_buffer(64 * 1024 * 1024, dev)?; - let image_dev = device.create_image2d(WIDTH as u32, HEIGHT as u32, dev)?; let k1_alloc_buf_host = device.create_buffer(4, host)?; let k1_alloc_buf_dev = device.create_buffer(4, dev)?; let k1_alloc_start = WIDTH_IN_TILEGROUPS * HEIGHT_IN_TILEGROUPS * TILEGROUP_STRIDE; device.write_buffer(&k1_alloc_buf_host, &[k1_alloc_start as u32])?; let k1_code = include_bytes!("../shader/kernel1.spv"); - let k1_pipeline = device - .create_simple_compute_pipeline(k1_code, 3, 0)?; - let k1_ds = device - .create_descriptor_set( - &k1_pipeline, - &[&scene_dev, &tilegroup_buf, &k1_alloc_buf_dev], - &[], - )?; + let k1_pipeline = device.create_simple_compute_pipeline(k1_code, 3, 0)?; + let k1_ds = device.create_descriptor_set( + &k1_pipeline, + &[&scene_dev, &tilegroup_buf, &k1_alloc_buf_dev], + &[], + )?; let k2s_alloc_buf_host = device.create_buffer(4, host)?; let k2s_alloc_buf_dev = device.create_buffer(4, dev)?; let k2s_alloc_start = WIDTH_IN_TILES * HEIGHT_IN_TILES * K2_PER_TILE_SIZE; - device - .write_buffer(&k2s_alloc_buf_host, &[k2s_alloc_start as u32]) - ?; + device.write_buffer(&k2s_alloc_buf_host, &[k2s_alloc_start as u32])?; let k2s_code = include_bytes!("../shader/kernel2s.spv"); - let k2s_pipeline = device - .create_simple_compute_pipeline(k2s_code, 4, 0) - ?; - let k2s_ds = device - .create_descriptor_set( - &k2s_pipeline, - &[&scene_dev, &tilegroup_buf, &segment_buf, &k2s_alloc_buf_dev], - &[], - ) - ?; + let k2s_pipeline = device.create_simple_compute_pipeline(k2s_code, 4, 0)?; + let k2s_ds = device.create_descriptor_set( + &k2s_pipeline, + &[&scene_dev, &tilegroup_buf, &segment_buf, &k2s_alloc_buf_dev], + &[], + )?; let k2f_alloc_buf_host = device.create_buffer(4, host)?; let k2f_alloc_buf_dev = device.create_buffer(4, dev)?; let k2f_alloc_start = WIDTH_IN_TILES * HEIGHT_IN_TILES * K2_PER_TILE_SIZE; - device - .write_buffer(&k2f_alloc_buf_host, &[k2f_alloc_start as u32]) - ?; + device.write_buffer(&k2f_alloc_buf_host, &[k2f_alloc_start as u32])?; let k2f_code = include_bytes!("../shader/kernel2f.spv"); let k2f_pipeline = device.create_simple_compute_pipeline(k2f_code, 4, 0)?; - let k2f_ds = device - .create_descriptor_set( - &k2f_pipeline, - &[ - &scene_dev, - &tilegroup_buf, - &fill_seg_buf, - &k2f_alloc_buf_dev, - ], - &[], - ) - ?; + let k2f_ds = device.create_descriptor_set( + &k2f_pipeline, + &[ + &scene_dev, + &tilegroup_buf, + &fill_seg_buf, + &k2f_alloc_buf_dev, + ], + &[], + )?; let k3_alloc_buf_host = device.create_buffer(4, host)?; let k3_alloc_buf_dev = device.create_buffer(4, dev)?; let k3_alloc_start = WIDTH_IN_TILES * HEIGHT_IN_TILES * PTCL_INITIAL_ALLOC; - device - .write_buffer(&k3_alloc_buf_host, &[k3_alloc_start as u32]) - ?; + device.write_buffer(&k3_alloc_buf_host, &[k3_alloc_start as u32])?; let k3_code = include_bytes!("../shader/kernel3.spv"); let k3_pipeline = device.create_simple_compute_pipeline(k3_code, 6, 0)?; - let k3_ds = device - .create_descriptor_set( - &k3_pipeline, - &[ - &scene_dev, - &tilegroup_buf, - &segment_buf, - &fill_seg_buf, - &ptcl_buf, - &k3_alloc_buf_dev, - ], - &[], - ) - ?; + let k3_ds = device.create_descriptor_set( + &k3_pipeline, + &[ + &scene_dev, + &tilegroup_buf, + &segment_buf, + &fill_seg_buf, + &ptcl_buf, + &k3_alloc_buf_dev, + ], + &[], + )?; let k4_code = include_bytes!("../shader/kernel4.spv"); let k4_pipeline = device.create_simple_compute_pipeline(k4_code, 3, 1)?; - let k4_ds = device - .create_descriptor_set(&k4_pipeline, &[&ptcl_buf, &segment_buf, &fill_seg_buf], &[&image_dev]) - ?; + let k4_ds = device.create_descriptor_set( + &k4_pipeline, + &[&ptcl_buf, &segment_buf, &fill_seg_buf], + &[&image_dev], + )?; + */ Ok(Renderer { scene_buf, scene_dev, image_dev, - k1_alloc_buf_host, - k1_alloc_buf_dev, - k2s_alloc_buf_host, - k2s_alloc_buf_dev, - k2f_alloc_buf_host, - k2f_alloc_buf_dev, - k3_alloc_buf_host, - k3_alloc_buf_dev, - tilegroup_buf, - ptcl_buf, - k1_pipeline, - k1_ds, - k2s_pipeline, - k2s_ds, - k2f_pipeline, - k2f_ds, - k3_pipeline, - k3_ds, - k4_pipeline, - k4_ds, + el_pipeline, + el_ds, + state_buf, + n_elements, }) } pub unsafe fn record(&self, cmd_buf: &mut impl CmdBuf, query_pool: &D::QueryPool) { cmd_buf.copy_buffer(&self.scene_buf, &self.scene_dev); - // Note: we could use one alloc buf and reuse it. But we'll stick with - // multiple ones for clarity. - cmd_buf.copy_buffer(&self.k1_alloc_buf_host, &self.k1_alloc_buf_dev); - cmd_buf.copy_buffer(&self.k2s_alloc_buf_host, &self.k2s_alloc_buf_dev); - cmd_buf.copy_buffer(&self.k2f_alloc_buf_host, &self.k2f_alloc_buf_dev); - cmd_buf.copy_buffer(&self.k3_alloc_buf_host, &self.k3_alloc_buf_dev); - // Note: these clears aren't necessary, and are here to make inspection - // of the buffers cleaner. Can likely be removed. - cmd_buf.clear_buffer(&self.tilegroup_buf); - cmd_buf.clear_buffer(&self.ptcl_buf); cmd_buf.memory_barrier(); - cmd_buf.image_barrier(&self.image_dev, ImageLayout::Undefined, ImageLayout::General); + cmd_buf.image_barrier( + &self.image_dev, + ImageLayout::Undefined, + ImageLayout::General, + ); cmd_buf.reset_query_pool(&query_pool); cmd_buf.write_timestamp(&query_pool, 0); cmd_buf.dispatch( - &self.k1_pipeline, - &self.k1_ds, - ((WIDTH / 512) as u32, (HEIGHT / 512) as u32, 1), + &self.el_pipeline, + &self.el_ds, + ((self.n_elements / 128) as u32, 1, 1), ); cmd_buf.write_timestamp(&query_pool, 1); cmd_buf.memory_barrier(); - cmd_buf.dispatch( - &self.k2s_pipeline, - &self.k2s_ds, - ((WIDTH / 512) as u32, (HEIGHT / 16) as u32, 1), - ); - cmd_buf.write_timestamp(&query_pool, 2); - // Note: this barrier is not necessary (k2f does not depend on - // k2s output), but I'm keeping it here to increase transparency - // of performance. - cmd_buf.memory_barrier(); - cmd_buf.dispatch( - &self.k2f_pipeline, - &self.k2f_ds, - ((WIDTH / 512) as u32, (HEIGHT / 16) as u32, 2), - ); - cmd_buf.write_timestamp(&query_pool, 3); - cmd_buf.memory_barrier(); - cmd_buf.dispatch( - &self.k3_pipeline, - &self.k3_ds, - ((WIDTH / 512) as u32, (HEIGHT / 16) as u32, 3), - ); - cmd_buf.write_timestamp(&query_pool, 4); - cmd_buf.memory_barrier(); - cmd_buf.dispatch( - &self.k4_pipeline, - &self.k4_ds, - ((WIDTH / TILE_W) as u32, (HEIGHT / TILE_H) as u32, 1), - ); - cmd_buf.write_timestamp(&query_pool, 5); - cmd_buf.memory_barrier(); cmd_buf.image_barrier(&self.image_dev, ImageLayout::General, ImageLayout::BlitSrc); } } diff --git a/piet-gpu/src/pico_svg.rs b/piet-gpu/src/pico_svg.rs index a4c92d0..4ddf94b 100644 --- a/piet-gpu/src/pico_svg.rs +++ b/piet-gpu/src/pico_svg.rs @@ -41,10 +41,14 @@ impl PicoSvg { let path = Affine::scale(scale) * bp; if let Some(fill_color) = el.attribute("fill") { let color = parse_color(fill_color); - items.push(Item::Fill(FillItem { color, path: path.clone() })); + items.push(Item::Fill(FillItem { + color, + path: path.clone(), + })); } if let Some(stroke_color) = el.attribute("stroke") { - let width = f64::from_str(el.attribute("stroke-width").ok_or("missing width")?)?; + let width = + f64::from_str(el.attribute("stroke-width").ok_or("missing width")?)?; let color = parse_color(stroke_color); items.push(Item::Stroke(StrokeItem { width, color, path })); } diff --git a/piet-gpu/src/render_ctx.rs b/piet-gpu/src/render_ctx.rs index 6367301..ad84a60 100644 --- a/piet-gpu/src/render_ctx.rs +++ b/piet-gpu/src/render_ctx.rs @@ -2,7 +2,11 @@ use std::borrow::Cow; use piet_gpu_types::encoder::{Encode, Encoder, Ref}; use piet_gpu_types::scene; -use piet_gpu_types::scene::{Bbox, PietCircle, PietFill, PietItem, PietStrokePolyLine, SimpleGroup}; +use piet_gpu_types::scene::{ + Bbox, PietCircle, PietFill, PietItem, PietStrokePolyLine, SimpleGroup, +}; + +use piet_gpu_types::scene::{CubicSeg, Element, Fill, LineSeg, QuadSeg, SetLineWidth, Stroke}; use piet::kurbo::{Affine, PathEl, Point, Rect, Shape}; @@ -27,10 +31,10 @@ pub struct PietGpuText; pub struct PietGpuRenderContext { encoder: Encoder, - bboxes: Vec, - items: Vec, + elements: Vec, // Will probably need direct accesss to hal Device to create images etc. inner_text: PietGpuText, + stroke_width: f32, } #[derive(Clone)] @@ -43,47 +47,22 @@ const TOLERANCE: f64 = 0.25; impl PietGpuRenderContext { pub fn new() -> PietGpuRenderContext { - let mut encoder = Encoder::new(); - let _reserve_root = encoder.alloc_chunk(PietItem::fixed_size() as u32); - let bboxes = Vec::new(); - let items = Vec::new(); + let encoder = Encoder::new(); + let elements = Vec::new(); let inner_text = PietGpuText; + let stroke_width = 0.0; PietGpuRenderContext { encoder, - bboxes, - items, + elements, inner_text, + stroke_width, } } pub fn get_scene_buf(&mut self) -> &[u8] { - let n_items = self.bboxes.len() as u32; - let bboxes = self.bboxes.encode(&mut self.encoder).transmute(); - let items = self.items.encode(&mut self.encoder).transmute(); - let offset = scene::Point { xy: [0.0, 0.0] }; - let simple_group = SimpleGroup { - n_items, - bboxes, - items, - offset, - }; - let root_item = PietItem::Group(simple_group); - root_item.encode_to(&mut self.encoder.buf_mut()[0..PietItem::fixed_size()]); + self.elements.encode(&mut self.encoder); self.encoder.buf() } - - fn push_item(&mut self, item: PietItem, bbox: Rect) { - let scene_bbox = Bbox { - bbox: [ - bbox.x0.floor() as i16, - bbox.y0.floor() as i16, - bbox.x1.ceil() as i16, - bbox.y1.ceil() as i16, - ], - }; - self.items.push(item); - self.bboxes.push(scene_bbox); - } } impl RenderContext for PietGpuRenderContext { @@ -107,20 +86,19 @@ impl RenderContext for PietGpuRenderContext { fn clear(&mut self, _color: Color) {} fn stroke(&mut self, shape: impl Shape, brush: &impl IntoBrush, width: f64) { - let bbox = shape.bounding_box(); - let brush = brush.make_brush(self, || bbox).into_owned(); + let width = width as f32; + if self.stroke_width != width { + self.elements + .push(Element::SetLineWidth(SetLineWidth { width })); + self.stroke_width = width; + } + let brush = brush.make_brush(self, || shape.bounding_box()).into_owned(); let path = shape.to_bez_path(TOLERANCE); - let (n_points, points) = flatten_shape(&mut self.encoder, path); + self.encode_path(path); match brush { PietGpuBrush::Solid(rgba_color) => { - let poly_line = PietStrokePolyLine { - rgba_color, - width: width as f32, - n_points, - points, - }; - let bbox = bbox.inset(-0.5 * width); - self.push_item(PietItem::Poly(poly_line), bbox); + let stroke = Stroke { rgba_color }; + self.elements.push(Element::Stroke(stroke)); } _ => (), } @@ -136,35 +114,13 @@ impl RenderContext for PietGpuRenderContext { } fn fill(&mut self, shape: impl Shape, brush: &impl IntoBrush) { - let bbox = shape.bounding_box(); let brush = brush.make_brush(self, || shape.bounding_box()).into_owned(); - - if let Some(circle) = shape.as_circle() { - match brush { - PietGpuBrush::Solid(rgba_color) => { - let piet_circle = PietCircle { - rgba_color, - center: to_scene_point(circle.center), - radius: circle.radius as f32, - }; - let bbox = circle.bounding_box(); - self.push_item(PietItem::Circle(piet_circle), bbox); - } - _ => {} - } - return; - } let path = shape.to_bez_path(TOLERANCE); - let (n_points, points) = flatten_shape(&mut self.encoder, path); + self.encode_path(path); match brush { PietGpuBrush::Solid(rgba_color) => { - let fill = PietFill { - flags: 0, - rgba_color, - n_points, - points, - }; - self.push_item(PietItem::Fill(fill), bbox); + let fill = Fill { rgba_color }; + self.elements.push(Element::Fill(fill)); } _ => (), } @@ -241,45 +197,96 @@ impl RenderContext for PietGpuRenderContext { } } -fn flatten_shape( - encoder: &mut Encoder, - path: impl Iterator, -) -> (u32, Ref) { - let mut points = Vec::new(); - let mut start_pt = None; - let mut last_pt = None; - piet::kurbo::flatten(path, TOLERANCE, |el| { - match el { - PathEl::MoveTo(p) => { - let scene_pt = to_scene_point(p); - start_pt = Some(clone_scene_pt(&scene_pt)); - if !points.is_empty() { - points.push(scene::Point { - xy: [std::f32::NAN, std::f32::NAN], - }); +impl PietGpuRenderContext { + fn encode_path(&mut self, path: impl Iterator) { + let flatten = false; + if flatten { + let mut start_pt = None; + let mut last_pt = None; + piet::kurbo::flatten(path, TOLERANCE, |el| { + match el { + PathEl::MoveTo(p) => { + let scene_pt = to_f32_2(p); + last_pt = Some(scene_pt); + } + PathEl::LineTo(p) => { + let scene_pt = to_f32_2(p); + let seg = LineSeg { + p0: last_pt.unwrap(), + p1: scene_pt, + }; + self.elements.push(Element::Line(seg)); + last_pt = Some(scene_pt); + } + PathEl::ClosePath => { + if let (Some(start), Some(last)) = (start_pt.take(), last_pt.take()) { + let seg = LineSeg { + p0: last, + p1: start, + }; + self.elements.push(Element::Line(seg)); + } + } + _ => (), } - last_pt = Some(clone_scene_pt(&scene_pt)); - points.push(scene_pt); - } - PathEl::LineTo(p) => { - let scene_pt = to_scene_point(p); - last_pt = Some(clone_scene_pt(&scene_pt)); - points.push(scene_pt); - } - PathEl::ClosePath => { - if let (Some(start), Some(last)) = (start_pt.take(), last_pt.take()) { - if start.xy != last.xy { - points.push(start); + //println!("{:?}", el); + }); + } else { + let mut start_pt = None; + let mut last_pt = None; + for el in path { + match el { + PathEl::MoveTo(p) => { + let scene_pt = to_f32_2(p); + last_pt = Some(scene_pt); + } + PathEl::LineTo(p) => { + let scene_pt = to_f32_2(p); + let seg = LineSeg { + p0: last_pt.unwrap(), + p1: scene_pt, + }; + self.elements.push(Element::Line(seg)); + last_pt = Some(scene_pt); + } + PathEl::QuadTo(p1, p2) => { + let scene_p1 = to_f32_2(p1); + let scene_p2 = to_f32_2(p2); + let seg = QuadSeg { + p0: last_pt.unwrap(), + p1: scene_p1, + p2: scene_p2, + }; + self.elements.push(Element::Quad(seg)); + last_pt = Some(scene_p2); + } + PathEl::CurveTo(p1, p2, p3) => { + let scene_p1 = to_f32_2(p1); + let scene_p2 = to_f32_2(p2); + let scene_p3 = to_f32_2(p3); + let seg = CubicSeg { + p0: last_pt.unwrap(), + p1: scene_p1, + p2: scene_p2, + p3: scene_p3, + }; + self.elements.push(Element::Cubic(seg)); + last_pt = Some(scene_p3); + } + PathEl::ClosePath => { + if let (Some(start), Some(last)) = (start_pt.take(), last_pt.take()) { + let seg = LineSeg { + p0: last, + p1: start, + }; + self.elements.push(Element::Line(seg)); + } } } + //println!("{:?}", el); } - _ => (), } - //println!("{:?}", el); - }); - let n_points = points.len() as u32; - let points_ref = points.encode(encoder).transmute(); - (n_points, points_ref) + } } impl Text for PietGpuText { @@ -360,13 +367,6 @@ impl IntoBrush for PietGpuBrush { } } -fn to_scene_point(point: Point) -> scene::Point { - scene::Point { - xy: [point.x as f32, point.y as f32], - } -} - -// TODO: allow #[derive(Clone)] in piet-gpu-derive. -fn clone_scene_pt(p: &scene::Point) -> scene::Point { - scene::Point { xy: p.xy } +fn to_f32_2(point: Point) -> [f32; 2] { + [point.x as f32, point.y as f32] } From 736f883f66dbe1458fba63c9b8fb20ee0d9f3005 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Tue, 12 May 2020 10:53:54 -0700 Subject: [PATCH 02/32] Store annotated elements Apply transform to paths and annotate with computed linewidth and bounding box information, storing the result. --- piet-gpu-types/src/annotated.rs | 46 +++++ piet-gpu-types/src/lib.rs | 1 + piet-gpu-types/src/main.rs | 1 + piet-gpu/shader/annotated.h | 290 ++++++++++++++++++++++++++++++++ piet-gpu/shader/elements.comp | 49 +++++- piet-gpu/shader/elements.spv | Bin 26760 -> 32624 bytes piet-gpu/src/lib.rs | 9 +- piet-gpu/src/render_ctx.rs | 2 +- 8 files changed, 391 insertions(+), 7 deletions(-) create mode 100644 piet-gpu-types/src/annotated.rs create mode 100644 piet-gpu/shader/annotated.h diff --git a/piet-gpu-types/src/annotated.rs b/piet-gpu-types/src/annotated.rs new file mode 100644 index 0000000..247ab12 --- /dev/null +++ b/piet-gpu-types/src/annotated.rs @@ -0,0 +1,46 @@ +use piet_gpu_derive::piet_gpu; + +piet_gpu! { + #[gpu_write] + mod annotated { + struct AnnoLineSeg { + p0: [f32; 2], + p1: [f32; 2], + // halfwidth in both x and y for binning + stroke: [f32; 2], + } + struct AnnoQuadSeg { + p0: [f32; 2], + p1: [f32; 2], + p2: [f32; 2], + stroke: [f32; 2], + } + struct AnnoCubicSeg { + p0: [f32; 2], + p1: [f32; 2], + p2: [f32; 2], + p3: [f32; 2], + stroke: [f32; 2], + } + struct AnnoFill { + rgba_color: u32, + bbox: [f32; 4], + } + struct AnnoStroke { + rgba_color: u32, + bbox: [f32; 4], + // For the nonuniform scale case, this needs to be a 2x2 matrix. + // That's expected to be uncommon, so we could special-case it. + linewidth: f32, + } + enum Annotated { + Nop, + // The segments need a flag to indicate fill/stroke + Line(AnnoLineSeg), + Quad(AnnoQuadSeg), + Cubic(AnnoCubicSeg), + Stroke(AnnoStroke), + Fill(AnnoFill), + } + } +} diff --git a/piet-gpu-types/src/lib.rs b/piet-gpu-types/src/lib.rs index 288f71c..1759c4d 100644 --- a/piet-gpu-types/src/lib.rs +++ b/piet-gpu-types/src/lib.rs @@ -1,3 +1,4 @@ +pub mod annotated; pub mod encoder; pub mod fill_seg; pub mod ptcl; diff --git a/piet-gpu-types/src/main.rs b/piet-gpu-types/src/main.rs index 033bec4..68e6487 100644 --- a/piet-gpu-types/src/main.rs +++ b/piet-gpu-types/src/main.rs @@ -6,6 +6,7 @@ fn main() { match mod_name.as_str() { "scene" => print!("{}", piet_gpu_types::scene::gen_gpu_scene()), "state" => print!("{}", piet_gpu_types::state::gen_gpu_state()), + "annotated" => print!("{}", piet_gpu_types::annotated::gen_gpu_annotated()), "tilegroup" => print!("{}", piet_gpu_types::tilegroup::gen_gpu_tilegroup()), "segment" => print!("{}", piet_gpu_types::segment::gen_gpu_segment()), "fill_seg" => print!("{}", piet_gpu_types::fill_seg::gen_gpu_fill_seg()), diff --git a/piet-gpu/shader/annotated.h b/piet-gpu/shader/annotated.h new file mode 100644 index 0000000..a3fc464 --- /dev/null +++ b/piet-gpu/shader/annotated.h @@ -0,0 +1,290 @@ +// Code auto-generated by piet-gpu-derive + +struct AnnoLineSegRef { + uint offset; +}; + +struct AnnoQuadSegRef { + uint offset; +}; + +struct AnnoCubicSegRef { + uint offset; +}; + +struct AnnoFillRef { + uint offset; +}; + +struct AnnoStrokeRef { + uint offset; +}; + +struct AnnotatedRef { + uint offset; +}; + +struct AnnoLineSeg { + vec2 p0; + vec2 p1; + vec2 stroke; +}; + +#define AnnoLineSeg_size 24 + +AnnoLineSegRef AnnoLineSeg_index(AnnoLineSegRef ref, uint index) { + return AnnoLineSegRef(ref.offset + index * AnnoLineSeg_size); +} + +struct AnnoQuadSeg { + vec2 p0; + vec2 p1; + vec2 p2; + vec2 stroke; +}; + +#define AnnoQuadSeg_size 32 + +AnnoQuadSegRef AnnoQuadSeg_index(AnnoQuadSegRef ref, uint index) { + return AnnoQuadSegRef(ref.offset + index * AnnoQuadSeg_size); +} + +struct AnnoCubicSeg { + vec2 p0; + vec2 p1; + vec2 p2; + vec2 p3; + vec2 stroke; +}; + +#define AnnoCubicSeg_size 40 + +AnnoCubicSegRef AnnoCubicSeg_index(AnnoCubicSegRef ref, uint index) { + return AnnoCubicSegRef(ref.offset + index * AnnoCubicSeg_size); +} + +struct AnnoFill { + uint rgba_color; + vec4 bbox; +}; + +#define AnnoFill_size 20 + +AnnoFillRef AnnoFill_index(AnnoFillRef ref, uint index) { + return AnnoFillRef(ref.offset + index * AnnoFill_size); +} + +struct AnnoStroke { + uint rgba_color; + vec4 bbox; + float linewidth; +}; + +#define AnnoStroke_size 24 + +AnnoStrokeRef AnnoStroke_index(AnnoStrokeRef ref, uint index) { + return AnnoStrokeRef(ref.offset + index * AnnoStroke_size); +} + +#define Annotated_Nop 0 +#define Annotated_Line 1 +#define Annotated_Quad 2 +#define Annotated_Cubic 3 +#define Annotated_Stroke 4 +#define Annotated_Fill 5 +#define Annotated_size 44 + +AnnotatedRef Annotated_index(AnnotatedRef ref, uint index) { + return AnnotatedRef(ref.offset + index * Annotated_size); +} + +AnnoLineSeg AnnoLineSeg_read(AnnoLineSegRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = annotated[ix + 0]; + uint raw1 = annotated[ix + 1]; + uint raw2 = annotated[ix + 2]; + uint raw3 = annotated[ix + 3]; + uint raw4 = annotated[ix + 4]; + uint raw5 = annotated[ix + 5]; + AnnoLineSeg s; + s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); + s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.stroke = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); + return s; +} + +void AnnoLineSeg_write(AnnoLineSegRef ref, AnnoLineSeg s) { + uint ix = ref.offset >> 2; + annotated[ix + 0] = floatBitsToUint(s.p0.x); + annotated[ix + 1] = floatBitsToUint(s.p0.y); + annotated[ix + 2] = floatBitsToUint(s.p1.x); + annotated[ix + 3] = floatBitsToUint(s.p1.y); + annotated[ix + 4] = floatBitsToUint(s.stroke.x); + annotated[ix + 5] = floatBitsToUint(s.stroke.y); +} + +AnnoQuadSeg AnnoQuadSeg_read(AnnoQuadSegRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = annotated[ix + 0]; + uint raw1 = annotated[ix + 1]; + uint raw2 = annotated[ix + 2]; + uint raw3 = annotated[ix + 3]; + uint raw4 = annotated[ix + 4]; + uint raw5 = annotated[ix + 5]; + uint raw6 = annotated[ix + 6]; + uint raw7 = annotated[ix + 7]; + AnnoQuadSeg s; + s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); + s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.p2 = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); + s.stroke = vec2(uintBitsToFloat(raw6), uintBitsToFloat(raw7)); + return s; +} + +void AnnoQuadSeg_write(AnnoQuadSegRef ref, AnnoQuadSeg s) { + uint ix = ref.offset >> 2; + annotated[ix + 0] = floatBitsToUint(s.p0.x); + annotated[ix + 1] = floatBitsToUint(s.p0.y); + annotated[ix + 2] = floatBitsToUint(s.p1.x); + annotated[ix + 3] = floatBitsToUint(s.p1.y); + annotated[ix + 4] = floatBitsToUint(s.p2.x); + annotated[ix + 5] = floatBitsToUint(s.p2.y); + annotated[ix + 6] = floatBitsToUint(s.stroke.x); + annotated[ix + 7] = floatBitsToUint(s.stroke.y); +} + +AnnoCubicSeg AnnoCubicSeg_read(AnnoCubicSegRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = annotated[ix + 0]; + uint raw1 = annotated[ix + 1]; + uint raw2 = annotated[ix + 2]; + uint raw3 = annotated[ix + 3]; + uint raw4 = annotated[ix + 4]; + uint raw5 = annotated[ix + 5]; + uint raw6 = annotated[ix + 6]; + uint raw7 = annotated[ix + 7]; + uint raw8 = annotated[ix + 8]; + uint raw9 = annotated[ix + 9]; + AnnoCubicSeg s; + s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); + s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.p2 = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); + s.p3 = vec2(uintBitsToFloat(raw6), uintBitsToFloat(raw7)); + s.stroke = vec2(uintBitsToFloat(raw8), uintBitsToFloat(raw9)); + return s; +} + +void AnnoCubicSeg_write(AnnoCubicSegRef ref, AnnoCubicSeg s) { + uint ix = ref.offset >> 2; + annotated[ix + 0] = floatBitsToUint(s.p0.x); + annotated[ix + 1] = floatBitsToUint(s.p0.y); + annotated[ix + 2] = floatBitsToUint(s.p1.x); + annotated[ix + 3] = floatBitsToUint(s.p1.y); + annotated[ix + 4] = floatBitsToUint(s.p2.x); + annotated[ix + 5] = floatBitsToUint(s.p2.y); + annotated[ix + 6] = floatBitsToUint(s.p3.x); + annotated[ix + 7] = floatBitsToUint(s.p3.y); + annotated[ix + 8] = floatBitsToUint(s.stroke.x); + annotated[ix + 9] = floatBitsToUint(s.stroke.y); +} + +AnnoFill AnnoFill_read(AnnoFillRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = annotated[ix + 0]; + uint raw1 = annotated[ix + 1]; + uint raw2 = annotated[ix + 2]; + uint raw3 = annotated[ix + 3]; + uint raw4 = annotated[ix + 4]; + AnnoFill s; + s.rgba_color = raw0; + s.bbox = vec4(uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3), uintBitsToFloat(raw4)); + return s; +} + +void AnnoFill_write(AnnoFillRef ref, AnnoFill s) { + uint ix = ref.offset >> 2; + annotated[ix + 0] = s.rgba_color; + annotated[ix + 1] = floatBitsToUint(s.bbox.x); + annotated[ix + 2] = floatBitsToUint(s.bbox.y); + annotated[ix + 3] = floatBitsToUint(s.bbox.z); + annotated[ix + 4] = floatBitsToUint(s.bbox.w); +} + +AnnoStroke AnnoStroke_read(AnnoStrokeRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = annotated[ix + 0]; + uint raw1 = annotated[ix + 1]; + uint raw2 = annotated[ix + 2]; + uint raw3 = annotated[ix + 3]; + uint raw4 = annotated[ix + 4]; + uint raw5 = annotated[ix + 5]; + AnnoStroke s; + s.rgba_color = raw0; + s.bbox = vec4(uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3), uintBitsToFloat(raw4)); + s.linewidth = uintBitsToFloat(raw5); + return s; +} + +void AnnoStroke_write(AnnoStrokeRef ref, AnnoStroke s) { + uint ix = ref.offset >> 2; + annotated[ix + 0] = s.rgba_color; + annotated[ix + 1] = floatBitsToUint(s.bbox.x); + annotated[ix + 2] = floatBitsToUint(s.bbox.y); + annotated[ix + 3] = floatBitsToUint(s.bbox.z); + annotated[ix + 4] = floatBitsToUint(s.bbox.w); + annotated[ix + 5] = floatBitsToUint(s.linewidth); +} + +uint Annotated_tag(AnnotatedRef ref) { + return annotated[ref.offset >> 2]; +} + +AnnoLineSeg Annotated_Line_read(AnnotatedRef ref) { + return AnnoLineSeg_read(AnnoLineSegRef(ref.offset + 4)); +} + +AnnoQuadSeg Annotated_Quad_read(AnnotatedRef ref) { + return AnnoQuadSeg_read(AnnoQuadSegRef(ref.offset + 4)); +} + +AnnoCubicSeg Annotated_Cubic_read(AnnotatedRef ref) { + return AnnoCubicSeg_read(AnnoCubicSegRef(ref.offset + 4)); +} + +AnnoStroke Annotated_Stroke_read(AnnotatedRef ref) { + return AnnoStroke_read(AnnoStrokeRef(ref.offset + 4)); +} + +AnnoFill Annotated_Fill_read(AnnotatedRef ref) { + return AnnoFill_read(AnnoFillRef(ref.offset + 4)); +} + +void Annotated_Nop_write(AnnotatedRef ref) { + annotated[ref.offset >> 2] = Annotated_Nop; +} + +void Annotated_Line_write(AnnotatedRef ref, AnnoLineSeg s) { + annotated[ref.offset >> 2] = Annotated_Line; + AnnoLineSeg_write(AnnoLineSegRef(ref.offset + 4), s); +} + +void Annotated_Quad_write(AnnotatedRef ref, AnnoQuadSeg s) { + annotated[ref.offset >> 2] = Annotated_Quad; + AnnoQuadSeg_write(AnnoQuadSegRef(ref.offset + 4), s); +} + +void Annotated_Cubic_write(AnnotatedRef ref, AnnoCubicSeg s) { + annotated[ref.offset >> 2] = Annotated_Cubic; + AnnoCubicSeg_write(AnnoCubicSegRef(ref.offset + 4), s); +} + +void Annotated_Stroke_write(AnnotatedRef ref, AnnoStroke s) { + annotated[ref.offset >> 2] = Annotated_Stroke; + AnnoStroke_write(AnnoStrokeRef(ref.offset + 4), s); +} + +void Annotated_Fill_write(AnnotatedRef ref, AnnoFill s) { + annotated[ref.offset >> 2] = Annotated_Fill; + AnnoFill_write(AnnoFillRef(ref.offset + 4), s); +} + diff --git a/piet-gpu/shader/elements.comp b/piet-gpu/shader/elements.comp index 5cede7c..1061fab 100644 --- a/piet-gpu/shader/elements.comp +++ b/piet-gpu/shader/elements.comp @@ -12,13 +12,21 @@ layout(set = 0, binding = 0) readonly buffer SceneBuf { uint[] scene; }; -// This will be used for inter-wprkgroup aggregates +// This will be used for inter-workgroup aggregates. In the +// meantime, for development, it has been used to store the +// scan of the state objects. layout(set = 0, binding = 1) buffer StateBuf { uint[] state; }; +// The annotated results are stored here. +layout(set = 0, binding = 2) buffer AnnotatedBuf { + uint[] annotated; +}; + #include "scene.h" #include "state.h" +#include "annotated.h" #define FLAG_SET_LINEWIDTH 1 #define FLAG_RESET_BBOX 2 @@ -93,6 +101,12 @@ State map_element(ElementRef ref) { return c; } +// Get the bounding box of a circle transformed by the matrix into an ellipse. +vec2 get_linewidth(State st) { + // See https://www.iquilezles.org/www/articles/ellipses/ellipses.htm + return 0.5 * st.linewidth * vec2(length(st.mat.xz), length(st.mat.yw)); +} + // We should be able to use an array of structs but the NV shader compiler // doesn't seem to like it :/ //shared State sh_state[WG_SIZE]; @@ -165,9 +179,38 @@ void main() { row = combine_state(row, other); } for (uint i = 0; i < N_ROWS; i++) { - State this_state = combine_state(row, th_state[i]); + State st = combine_state(row, th_state[i]); // We write the state now for development purposes, but the // actual goal is to write transformed and annotated elements. - State_write(StateRef((ix + i) * State_size), this_state); + //State_write(StateRef((ix + i) * State_size), st); + + // Here we read again from the original scene. There may be + // gains to be had from stashing in shared memory or possibly + // registers (though register pressure is an issue). + ElementRef this_ref = Element_index(ref, i); + AnnotatedRef out_ref = AnnotatedRef((ix + i) * Annotated_size); + uint tag = Element_tag(this_ref); + switch (tag) { + case Element_Line: + LineSeg line = Element_Line_read(this_ref); + AnnoLineSeg anno_line; + anno_line.p0 = st.mat.xz * line.p0.x + st.mat.yw * line.p0.y + st.translate; + anno_line.p1 = st.mat.xz * line.p1.x + st.mat.yw * line.p1.y + st.translate; + anno_line.stroke = get_linewidth(st); + Annotated_Line_write(out_ref, anno_line); + break; + case Element_Stroke: + Stroke stroke = Element_Stroke_read(this_ref); + AnnoStroke anno_stroke; + anno_stroke.rgba_color = stroke.rgba_color; + vec2 lw = get_linewidth(st); + anno_stroke.bbox = st.bbox + vec4(-lw, lw); + anno_stroke.linewidth = st.linewidth * sqrt(st.mat.x * st.mat.w - st.mat.y * st.mat.z); + Annotated_Stroke_write(out_ref, anno_stroke); + break; + default: + Annotated_Nop_write(out_ref); + break; + } } } diff --git a/piet-gpu/shader/elements.spv b/piet-gpu/shader/elements.spv index e97226cab6d9f24f9299099f741ac590504b77ac..41d9bc150159c4d8e83ae3898d8255e12b2a95c0 100644 GIT binary patch literal 32624 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 BF?0X` literal 26760 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 DvZB!T diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 82b20c8..0753054 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -113,6 +113,7 @@ pub struct Renderer { scene_dev: D::Buffer, pub state_buf: D::Buffer, + pub anno_buf: D::Buffer, el_pipeline: D::Pipeline, el_ds: D::DescriptorSet, @@ -156,14 +157,15 @@ impl Renderer { .unwrap(); device.write_buffer(&scene_buf, &scene)?; - let state_buf = device.create_buffer(4 * 1024 * 1024, dev)?; + let state_buf = device.create_buffer(64 * 1024 * 1024, dev)?; + let anno_buf = device.create_buffer(64 * 1024 * 1024, dev)?; let image_dev = device.create_image2d(WIDTH as u32, HEIGHT as u32, dev)?; let el_code = include_bytes!("../shader/elements.spv"); - let el_pipeline = device.create_simple_compute_pipeline(el_code, 2, 0)?; + let el_pipeline = device.create_simple_compute_pipeline(el_code, 3, 0)?; let el_ds = device.create_descriptor_set( &el_pipeline, - &[&scene_dev, &state_buf], + &[&scene_dev, &state_buf, &anno_buf], &[], )?; @@ -252,6 +254,7 @@ impl Renderer { el_pipeline, el_ds, state_buf, + anno_buf, n_elements, }) } diff --git a/piet-gpu/src/render_ctx.rs b/piet-gpu/src/render_ctx.rs index ad84a60..e01a6ae 100644 --- a/piet-gpu/src/render_ctx.rs +++ b/piet-gpu/src/render_ctx.rs @@ -199,7 +199,7 @@ impl RenderContext for PietGpuRenderContext { impl PietGpuRenderContext { fn encode_path(&mut self, path: impl Iterator) { - let flatten = false; + let flatten = true; if flatten { let mut start_pt = None; let mut last_pt = None; From 343e4c3075a1df6c4bef9b39160a35aae2e6a878 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Tue, 12 May 2020 13:38:26 -0700 Subject: [PATCH 03/32] Binning stage Adds a binning stage. This is a first draft, and a number of loose ends exist. --- piet-gpu-types/src/bins.rs | 19 ++++ piet-gpu-types/src/lib.rs | 3 + piet-gpu-types/src/main.rs | 1 + piet-gpu/bin/cli.rs | 13 +-- piet-gpu/shader/binning.comp | 169 ++++++++++++++++++++++++++++++++++ piet-gpu/shader/binning.spv | Bin 0 -> 14804 bytes piet-gpu/shader/bins.h | 60 ++++++++++++ piet-gpu/shader/build.ninja | 4 +- piet-gpu/shader/elements.comp | 13 +++ piet-gpu/shader/elements.spv | Bin 32624 -> 35448 bytes piet-gpu/src/lib.rs | 51 +++++++++- 11 files changed, 322 insertions(+), 11 deletions(-) create mode 100644 piet-gpu-types/src/bins.rs create mode 100644 piet-gpu/shader/binning.comp create mode 100644 piet-gpu/shader/binning.spv create mode 100644 piet-gpu/shader/bins.h diff --git a/piet-gpu-types/src/bins.rs b/piet-gpu-types/src/bins.rs new file mode 100644 index 0000000..88f16f1 --- /dev/null +++ b/piet-gpu-types/src/bins.rs @@ -0,0 +1,19 @@ +use piet_gpu_derive::piet_gpu; + +// The output of the binning stage, organized as a linked list of chunks. + +piet_gpu! { + #[gpu_write] + mod bins { + struct BinInstance { + element_ix: u32, + } + + struct BinChunk { + // First chunk can have n = 0, subsequent ones not. + n: u32, + next: Ref, + // Instances follow + } + } +} diff --git a/piet-gpu-types/src/lib.rs b/piet-gpu-types/src/lib.rs index 1759c4d..29ed806 100644 --- a/piet-gpu-types/src/lib.rs +++ b/piet-gpu-types/src/lib.rs @@ -1,4 +1,7 @@ +// Structures used only internally probably don't need to be pub. + pub mod annotated; +pub mod bins; pub mod encoder; pub mod fill_seg; pub mod ptcl; diff --git a/piet-gpu-types/src/main.rs b/piet-gpu-types/src/main.rs index 68e6487..41ae021 100644 --- a/piet-gpu-types/src/main.rs +++ b/piet-gpu-types/src/main.rs @@ -7,6 +7,7 @@ fn main() { "scene" => print!("{}", piet_gpu_types::scene::gen_gpu_scene()), "state" => print!("{}", piet_gpu_types::state::gen_gpu_state()), "annotated" => print!("{}", piet_gpu_types::annotated::gen_gpu_annotated()), + "bins" => print!("{}", piet_gpu_types::bins::gen_gpu_bins()), "tilegroup" => print!("{}", piet_gpu_types::tilegroup::gen_gpu_tilegroup()), "segment" => print!("{}", piet_gpu_types::segment::gen_gpu_segment()), "fill_seg" => print!("{}", piet_gpu_types::fill_seg::gen_gpu_fill_seg()), diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index 82f3491..4a4fed3 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -41,7 +41,7 @@ fn main() -> Result<(), Error> { let fence = device.create_fence(false)?; let mut cmd_buf = device.create_cmd_buf()?; - let query_pool = device.create_query_pool(2)?; + let query_pool = device.create_query_pool(3)?; let mut ctx = PietGpuRenderContext::new(); render_scene(&mut ctx); @@ -58,13 +58,14 @@ fn main() -> Result<(), Error> { cmd_buf.finish(); device.run_cmd_buf(&cmd_buf, &[], &[], Some(&fence))?; device.wait_and_reset(&[fence])?; - let timestamps = device.reap_query_pool(&query_pool).unwrap(); - println!("Element kernel time: {:.3}ms", timestamps[0] * 1e3); + let ts = device.reap_query_pool(&query_pool).unwrap(); + println!("Element kernel time: {:.3}ms", ts[0] * 1e3); + println!("Binning kernel time: {:.3}ms", (ts[1] - ts[0]) * 1e3); /* - let mut data: Vec = Default::default(); - device.read_buffer(&renderer.state_buf, &mut data).unwrap(); - dump_state(&data); + let mut data: Vec = Default::default(); + device.read_buffer(&renderer.bin_buf, &mut data).unwrap(); + piet_gpu::dump_k1_data(&data); */ let mut img_data: Vec = Default::default(); diff --git a/piet-gpu/shader/binning.comp b/piet-gpu/shader/binning.comp new file mode 100644 index 0000000..bf7bbae --- /dev/null +++ b/piet-gpu/shader/binning.comp @@ -0,0 +1,169 @@ +// The binning stage of the pipeline. + +#version 450 +#extension GL_GOOGLE_include_directive : enable + +#define N_ROWS 4 +#define WG_SIZE 32 +#define LG_WG_SIZE 5 +#define TILE_SIZE (WG_SIZE * N_ROWS) + +// TODO: move these to setup file +#define N_TILE_X 16 +#define N_TILE_Y 16 +#define N_TILE (N_TILE_X * N_TILE_Y) +#define N_SLICE (N_TILE / 32) +#define N_WG 16 // Number of workgroups, should be 1 per SM + +#define BIN_INITIAL_ALLOC 64 +#define BIN_ALLOC 256 + +layout(local_size_x = N_TILE, local_size_y = 1) in; + +layout(set = 0, binding = 0) buffer AnnotatedBuf { + uint[] annotated; +}; + +layout(set = 0, binding = 1) buffer AllocBuf { + uint n_elements; + // Will be incremented atomically to claim tiles + uint tile_ix; + uint alloc; +}; + +layout(set = 0, binding = 2) buffer BinsBuf { + uint[] bins; +}; + +#include "annotated.h" +#include "bins.h" +#include "setup.h" + +// scale factors useful for converting coordinates to bins +#define SX (1.0 / float(N_TILE_X * TILE_WIDTH_PX)) +#define SY (1.0 / float(N_TILE_Y * TILE_HEIGHT_PX)) + +// Note: cudaraster has N_TILE + 1 to cut down on bank conflicts. +shared uint bitmaps[N_SLICE][N_TILE]; +shared uint sh_my_tile; + +void main() { + BinChunkRef chunk_ref = BinChunkRef((gl_LocalInvocationID.x * N_WG + gl_WorkGroupID.x) * BIN_INITIAL_ALLOC); + uint chunk_limit = chunk_ref.offset + BIN_INITIAL_ALLOC - BinInstance_size; + uint chunk_n = 0; + BinInstanceRef instance_ref = BinInstanceRef(chunk_ref.offset + BinChunk_size); + while (true) { + if (gl_LocalInvocationID.x == 0) { + sh_my_tile = atomicAdd(tile_ix, 1); + } + barrier(); + uint my_tile = sh_my_tile; + if (my_tile * N_TILE >= n_elements) { + break; + } + + for (uint i = 0; i < N_SLICE; i++) { + bitmaps[i][gl_LocalInvocationID.x] = 0; + } + barrier(); + + // Read inputs and determine coverage of bins + uint element_ix = my_tile * N_TILE + gl_LocalInvocationID.x; + AnnotatedRef ref = AnnotatedRef(element_ix * Annotated_size); + uint tag = Annotated_tag(ref); + int x0 = 0, y0 = 0, x1 = 0, y1 = 0; + switch (tag) { + case Annotated_Line: + AnnoLineSeg line = Annotated_Line_read(ref); + x0 = int(floor((min(line.p0.x, line.p1.x) - line.stroke.x) * SX)); + y0 = int(floor((min(line.p0.y, line.p1.y) - line.stroke.y) * SY)); + x1 = int(ceil((max(line.p0.x, line.p1.x) + line.stroke.x) * SX)); + y1 = int(ceil((max(line.p0.y, line.p1.y) + line.stroke.y) * SY)); + break; + case Annotated_Fill: + case Annotated_Stroke: + // Note: we take advantage of the fact that fills and strokes + // have compatible layout. + AnnoFill fill = Annotated_Fill_read(ref); + x0 = int(floor(fill.bbox.x * SX)); + y0 = int(floor(fill.bbox.y * SY)); + x1 = int(ceil(fill.bbox.z * SX)); + y1 = int(ceil(fill.bbox.w * SY)); + break; + } + // At this point, we run an iterator over the coverage area, + // trying to keep divergence low. + // Right now, it's just a bbox, but we'll get finer with + // segments. + x0 = clamp(x0, 0, N_TILE_X); + x1 = clamp(x1, x0, N_TILE_X); + y0 = clamp(y0, 0, N_TILE_Y); + y1 = clamp(y1, y0, N_TILE_Y); + if (x0 == x1) y1 = y0; + int x = x0, y = y0; + uint my_slice = gl_LocalInvocationID.x / 32; + uint my_mask = 1 << (gl_LocalInvocationID.x & 31); + while (y < y1) { + atomicOr(bitmaps[my_slice][y * N_TILE_X + x], my_mask); + x++; + if (x == x1) { + x = x0; + y++; + } + } + + barrier(); + // Allocate output segments. + uint element_count = 0; + for (uint i = 0; i < N_SLICE; i++) { + element_count += bitCount(bitmaps[i][gl_LocalInvocationID.x]); + } + // element_count is number of elements covering bin for this invocation. + if (element_count > 0 && chunk_n > 0) { + uint new_chunk = instance_ref.offset; + if (new_chunk + min(32, element_count * 4) > chunk_limit) { + new_chunk = atomicAdd(alloc, BIN_ALLOC); + chunk_limit = new_chunk + BIN_ALLOC - BinInstance_size; + } + BinChunk_write(chunk_ref, BinChunk(chunk_n, BinChunkRef(new_chunk))); + chunk_ref = BinChunkRef(new_chunk); + instance_ref = BinInstanceRef(new_chunk + BinChunk_size); + chunk_n = 0; + } + // TODO: allocate output here + + // Iterate over bits set. + uint slice_ix = 0; + uint bitmap = bitmaps[0][gl_LocalInvocationID.x]; + while (true) { + if (bitmap == 0) { + slice_ix++; + if (slice_ix == N_SLICE) { + break; + } + bitmap = bitmaps[slice_ix][gl_LocalInvocationID.x]; + if (bitmap == 0) { + continue; + } + } + element_ix = my_tile * N_TILE + slice_ix * 32 + findLSB(bitmap); + // At this point, element_ix refers to an element that covers this bin. + + // TODO: batch allocated based on element_count; this is divergent + if (instance_ref.offset > chunk_limit) { + uint new_chunk = atomicAdd(alloc, BIN_ALLOC); + BinChunk_write(chunk_ref, BinChunk(chunk_n, BinChunkRef(new_chunk))); + chunk_ref = BinChunkRef(new_chunk); + instance_ref = BinInstanceRef(new_chunk + BinChunk_size); + chunk_n = 0; + chunk_limit = new_chunk + BIN_ALLOC - BinInstance_size; + } + BinInstance_write(instance_ref, BinInstance(element_ix)); + chunk_n++; + instance_ref.offset += BinInstance_size; + // clear LSB + bitmap &= bitmap - 1; + } + } + BinChunk_write(chunk_ref, BinChunk(chunk_n, BinChunkRef(0))); +} diff --git a/piet-gpu/shader/binning.spv b/piet-gpu/shader/binning.spv new file mode 100644 index 0000000000000000000000000000000000000000..4cc5d36f5d587a905b5158380672988de7e0d49d GIT binary patch literal 14804 zcma)?2bf(|xrR5Hk^q4aLazxuKucTJT;)ykbE>#a>V}D4>EURlxV%XMf4c;knOqH)myi-}nFPU;iq5?S0PBF>dqm zRkczzf&co(RrNKw8i!ITe!n+yYHmK>QzVWr_ZEneAPwWNm&(@>7Bgx`A~*lVJGbJ<9mnX{$nbgO*j25@+^i6%XQaP(;Wjud z=^LA|VlFe5?7Z_nwCSK#RgI9Gd92a2pF1!(Xu{^J=2hl91#Ue3v~}~Jn%BtL3wnF{ zh6jg7T6wCQ_ksn(7i#aSCRXcEr~PY^Y&vgcsDDSbHvX*~yQ}q@wqw0x{R`_^ZS|w> zwLH&>)kbjpseOCKde1H=BLSL@>}Ny#Tm7tFO&i`ANe zR`~W@FfuUKpZO^abXd<&vyBSN@;$U2v0C%iQEd+%rMRDVZ1r)#IZKAlGja1)^D6UZ zOPfpWr_KGY=H*RoJGuL4sQ*Il8so29&7@Ad*9;)Nwq;+-Xfu`%sPyEnXszRJ)EU$6 z;MSNrs=dIiIqa-v>C<~beJ**=&w(%7Z*!J#$D4ag7x!i_g57TiDsp*#97av7df4)# zTYQ(;Pi*mB!%uH<)2!R~wYaI)vCpMW&X<9c^Ht#GId@i9xA?MtuW9jR{a)MR%lf^( z#hZRRsvF_0embkW!HauGdKXbUhI?1w9o2pC8ze#rVYWns~6y;Ro{S3mv!5TY{=1bdKO*OGd3_-pTV5MXtM_g zhx>~4SMwU`Y3@Qs=$O?t+1eX3n&*12F{mB;q^7;OO#!DvZJWRcYQN5EI(Wgr*rMLW zqji2e!B@QRJF1yz+1LAkITi8kto8*jU%QTK7W_hXwtdV7U)10^>g*Qv1JrBWSse`a zxk)N@yhGs2<8@XS!_R7-7xs6_3igibCOAh^9rqUSMK$iKZUr;m(ZPYfWwAfg(iZiO zo`-*T^&mXs>>FM(yGPFvo)?& z&{}gW@2d8Mx5wRog*ctn>^6RQ8$Ys*pV7w8EciU;ZZ`RMQ#vUJlfxWpFP|G;^BB8Y zM~yq{b7nIQT(VN?Dh7t5D00vk7CeAGMTe_)LVW8^_pdP$%|euyNIlwK4k1m-FUlF8_#yy^x2bIA7i>+ zd)InvjkjyK&up*o*MW_*G5O40BU{I_G!5Hg_?0}Gne9}|H0JS)MGy! zyi&oB1()qlhP&6|-vf4i!#&WtD8?V4cAT!0d~w6wPx?FV5sLk3zkynR*HM03!`&~A zJNLBX_TH82_kC(}RCC;N?_V|d;qR#(qj~H92kMn5>iWxd_&?eB zWZX}|XHmlc3HBaYi!3_nY&yMLcTwzPRchzWK8z{%*{kNg=b4}_#Wt&>IVNqkk$XO< zrQbE-6DZ;9fF0~c_-{pRJT>{&)N1Bx{B6MYuWo#~&tJ8~+Y`Qh|&AaRM^87P@_o-zjbLIHW z(aSY{9>qQA7wei_)-1n2x-*=R7@3DgmZvVcg#O^)idrG)=-&4Zv-}jV~-%@b%_kAUHx$i0^ z_g$srzN3`f_mptg!vzcXOdMBm^V`k`wLwC(x%;C z!M0UTtf#^1Pf?DdKktLTQPf|@4tGDx?Z^4~J4MZBM)Lbdqo`QLzfz7R4_x0iGcQoVP0Bl=z$Gs7?m*dv9Aw|t`i_JZAtZr_ecaBrrrj4() z>o$#gGm4k?%^SOQ#Qlbf^enTLU=QmdPr&qg)(#(e6-DQf1PJ>+}J5sfW=M>c-0f9ZD= zy0(d&1-3gH%%|G2iJ?6)jse@3@9t%cW6`u-PR_~e;Ju%Jz zXS`*M9&~LP@0noZsAs&rVD)mm3*da}@oLX_`@q_=_vFc8AvohL=e!?XTl~&y{My&| zY;S&T^>;>NXitm*aK>BqbuPNLH#ci}9@seQ8Sfxiy&Uf%IG=ibv}e3SU~P_f zjyfed41+V?a?Tf{Ym49O8^8AT9YNQY@s5J|)Z;aV_QV(iXS`)!OVG7tyyt_Bqn`0z z09G%@dm)@pJznh@??qs3j@L6p?z!t3;aT9>kh9%rnb${B^-+RH@?9aU*FZY6b!f|;HUJG{4Co)U& z_ksD;e;3l`nSUL1)<%2Uz8~y(!ao2mV_gsDQ^(R*dt%v6d)j^w>{=v`4}tmAZMBto z=%+nxKMZ!9;U58)v2K9#sblG@J$cwpd)nRzo>lOVg3UGW#2*9eqwX{L6VzV5lWF@n zMa_FooVcF^n^V?A?wZ8cI=(l9%|YLrsO7PJ3Y<8%fNRdZ>$}mX;Y+D=u6BcOLsP#B zANd_%wc9Dizm-}o?~0!Vx5u~>P5oO^%d1jEFr{U=~!=vQRMI6YhDH0M%_04?zR#?HTk=jYeP3R7yFx~{(CXwz^a3P{C_tj+R|qyxa_kF zu4WA5%H!MJ_+}2q!_~^)(xZSOvkiyf!?)49mLv)1PCsoHYCuL@t<_^0oQXzID& zR|9*$yHB;nZ*{OX{r`H z6gB%5JI;(pJ#jY$8#nVm72H1m)6mp2|C@oGf7@zHtj)pNGXL_}wg8v&wk2FG^R_iO z`D@#XqGtYL^LNeU>hjF1b^4hOoYiF?W)Sa81Xrz{NcNcK`xw|WxdivfCT%Low!}U>j z{r8~ua{aZoLO_go>}^6OAZHs%RUc;s~N+%^7tOq_+}mE!qv(%YaU$P z_O5|koHHx24o0`%#5x45Rz3p`g{zm(05$h&*1|e54+FawoWE`8dp=xE`&RHHz~*E= zhlAzfBdPVxTpb0TfuBCw68~th<4DfOz}0fbXj98RJQm!(5067rPmJTiYK}4OPJpMK z`N{Q59w&mmZ}O}>39RORi_gi8Prmz{0$0=5n6_6d&w(`?K8f0WKDpuJm{Ym6{;$%H zd?k4LJGE}Je6OAkS38ZOk9)NToW1Jb;pFCWCbj;V|6Z_rRUd6Hb1Ljf@$bL>?z=k0 zYa(S2+E1WfjbcudsPnhqy};uOKC6u%*2a%+t37Rx0&6p_b10AP7;qW)IJla>NhR*_;KbFQwkLqK8P~Ot z$9599jC%@P?PN;go(fJ}?P+@&SetQOQ+aG>fXldN!qr;$V{3kU;fbp~Z5M#G8P~lb zk8L5ijC&Sbt)G&7&ju&1_Ov|*tj)OYF?np~foD;CHm*!PNKuP@5!f|}eF&`PGh`L& zVTx_k-KUGGUr%xGx_|6bu3ut~f?bcq90RMxz69*N#(q9nEqz`9wvBrFybx@kIXmV0 zCFVt7=QJ_@4XhUX#bDGKU>+o-3{H-ham_nKTkW3Ed5CW`h~I7h@ebEbgT zp?IxD@ywY_y*9-&XI<)?IhTRQ7yPO=esvqarj1|Q#;-5<4d5Fao-^ihxOupCnVT!X z>N#Vs1gmA-=BO6`w}91h#=I4*R-Q3$gJ-X5PusVHwYhH`t30-MfXle=gsYWj%)8); zt37Ss4c2B{=S&{kd%$Jf_rlf6Gv?pniK{(r{{yVexX!&iw(G!U-1ouNPNw9ZeLpyH zwWsX|z}k%K8p>n)Ah?YCA-Gz3#(Wr_xZ2b9BVcXDbuY+c`zY8s&tCW#_(qC*L4UbE z@&7p3xy(KD39wr1p9DK6vEKw%i~VM>V~hP3uv+Y&0-Iy(w}RD<;*MC8`qLEqG_Gy6 zoBM6lw^MGU;OW%cQ_On@>YT~-5`v<{l8RtV_wYik6 z1kH8N!KE|*wHT}x>UdKL@;K3#!$=He+E{|*nbXIE64r|xcQX*{1UF7vHuF3u^Yp&tLc}% rehn`B`VCy|aEfvEq<(~AUn7*Vzu&?ip`^dxfzzLH>`zU<%=Nzjd;0OI literal 0 HcmV?d00001 diff --git a/piet-gpu/shader/bins.h b/piet-gpu/shader/bins.h new file mode 100644 index 0000000..3ce06e0 --- /dev/null +++ b/piet-gpu/shader/bins.h @@ -0,0 +1,60 @@ +// Code auto-generated by piet-gpu-derive + +struct BinInstanceRef { + uint offset; +}; + +struct BinChunkRef { + uint offset; +}; + +struct BinInstance { + uint element_ix; +}; + +#define BinInstance_size 4 + +BinInstanceRef BinInstance_index(BinInstanceRef ref, uint index) { + return BinInstanceRef(ref.offset + index * BinInstance_size); +} + +struct BinChunk { + uint n; + BinChunkRef next; +}; + +#define BinChunk_size 8 + +BinChunkRef BinChunk_index(BinChunkRef ref, uint index) { + return BinChunkRef(ref.offset + index * BinChunk_size); +} + +BinInstance BinInstance_read(BinInstanceRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = bins[ix + 0]; + BinInstance s; + s.element_ix = raw0; + return s; +} + +void BinInstance_write(BinInstanceRef ref, BinInstance s) { + uint ix = ref.offset >> 2; + bins[ix + 0] = s.element_ix; +} + +BinChunk BinChunk_read(BinChunkRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = bins[ix + 0]; + uint raw1 = bins[ix + 1]; + BinChunk s; + s.n = raw0; + s.next = BinChunkRef(raw1); + return s; +} + +void BinChunk_write(BinChunkRef ref, BinChunk s) { + uint ix = ref.offset >> 2; + bins[ix + 0] = s.n; + bins[ix + 1] = s.next.offset; +} + diff --git a/piet-gpu/shader/build.ninja b/piet-gpu/shader/build.ninja index b429a71..4628fd2 100644 --- a/piet-gpu/shader/build.ninja +++ b/piet-gpu/shader/build.ninja @@ -20,4 +20,6 @@ build kernel3.spv: glsl kernel3.comp | scene.h tilegroup.h segment.h fill_seg.h build kernel4.spv: glsl kernel4.comp | ptcl.h segment.h fill_seg.h setup.h -build elements.spv: glsl elements.comp | scene.h state.h +build elements.spv: glsl elements.comp | scene.h state.h annotated.h + +build binning.spv: glsl binning.comp | annotated.h setup.h diff --git a/piet-gpu/shader/elements.comp b/piet-gpu/shader/elements.comp index 1061fab..c31dd2e 100644 --- a/piet-gpu/shader/elements.comp +++ b/piet-gpu/shader/elements.comp @@ -1,3 +1,9 @@ +// The element processing stage, first in the pipeline. +// +// This stage is primarily about applying transforms and computing bounding +// boxes. It is organized as a scan over the input elements, producing +// annotated output elements. + #version 450 #extension GL_GOOGLE_include_directive : enable @@ -208,6 +214,13 @@ void main() { anno_stroke.linewidth = st.linewidth * sqrt(st.mat.x * st.mat.w - st.mat.y * st.mat.z); Annotated_Stroke_write(out_ref, anno_stroke); break; + case Element_Fill: + Fill fill = Element_Fill_read(this_ref); + AnnoFill anno_fill; + anno_fill.rgba_color = fill.rgba_color; + anno_fill.bbox = st.bbox; + Annotated_Fill_write(out_ref, anno_fill); + break; default: Annotated_Nop_write(out_ref); break; diff --git a/piet-gpu/shader/elements.spv b/piet-gpu/shader/elements.spv index 41d9bc150159c4d8e83ae3898d8255e12b2a95c0..afb63b582b1547a2ecc25bf900952a97b01489fb 100644 GIT binary patch literal 35448 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 P%$>mUj?Xz%b`Jj+QCs&H literal 32624 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 BF?0X` diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 0753054..437a31a 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -32,6 +32,8 @@ const K2_PER_TILE_SIZE: usize = 8; const N_CIRCLES: usize = 1; +const N_WG: u32 = 16; + pub fn render_scene(rc: &mut impl RenderContext) { let mut rng = rand::thread_rng(); for _ in 0..N_CIRCLES { @@ -98,10 +100,10 @@ fn dump_scene(buf: &[u8]) { } #[allow(unused)] -fn dump_k1_data(k1_buf: &[u32]) { +pub fn dump_k1_data(k1_buf: &[u32]) { for i in 0..k1_buf.len() { if k1_buf[i] != 0 { - println!("{:4x}: {:8x}", i, k1_buf[i]); + println!("{:4x}: {:8x}", i * 4, k1_buf[i]); } } } @@ -114,10 +116,17 @@ pub struct Renderer { pub state_buf: D::Buffer, pub anno_buf: D::Buffer, + pub bin_buf: D::Buffer, el_pipeline: D::Pipeline, el_ds: D::DescriptorSet, + bin_pipeline: D::Pipeline, + bin_ds: D::DescriptorSet, + + bin_alloc_buf_host: D::Buffer, + bin_alloc_buf_dev: D::Buffer, + /* k1_alloc_buf_host: D::Buffer, k1_alloc_buf_dev: D::Buffer, @@ -149,6 +158,9 @@ impl Renderer { let host = MemFlags::host_coherent(); let dev = MemFlags::device_local(); + let n_elements = scene.len() / piet_gpu_types::scene::Element::fixed_size(); + println!("scene: {} elements", n_elements); + let scene_buf = device .create_buffer(std::mem::size_of_val(&scene[..]) as u64, host) .unwrap(); @@ -159,6 +171,7 @@ impl Renderer { let state_buf = device.create_buffer(64 * 1024 * 1024, dev)?; let anno_buf = device.create_buffer(64 * 1024 * 1024, dev)?; + let bin_buf = device.create_buffer(64 * 1024 * 1024, dev)?; let image_dev = device.create_image2d(WIDTH as u32, HEIGHT as u32, dev)?; let el_code = include_bytes!("../shader/elements.spv"); @@ -169,8 +182,25 @@ impl Renderer { &[], )?; - let n_elements = scene.len() / piet_gpu_types::scene::Element::fixed_size(); - println!("scene: {} elements", n_elements); + let bin_alloc_buf_host = device.create_buffer(12, host)?; + let bin_alloc_buf_dev = device.create_buffer(12, dev)?; + + // TODO: constants + let bin_alloc_start = 256 * 64 * N_WG; + device + .write_buffer(&bin_alloc_buf_host, &[ + n_elements as u32, + 0, + bin_alloc_start, + ]) + ?; + let bin_code = include_bytes!("../shader/binning.spv"); + let bin_pipeline = device.create_simple_compute_pipeline(bin_code, 3, 0)?; + let bin_ds = device.create_descriptor_set( + &bin_pipeline, + &[&anno_buf, &bin_alloc_buf_dev, &bin_buf], + &[], + )?; /* let tilegroup_buf = device.create_buffer(4 * 1024 * 1024, dev)?; @@ -253,14 +283,20 @@ impl Renderer { image_dev, el_pipeline, el_ds, + bin_pipeline, + bin_ds, state_buf, anno_buf, + bin_buf, + bin_alloc_buf_host, + bin_alloc_buf_dev, n_elements, }) } pub unsafe fn record(&self, cmd_buf: &mut impl CmdBuf, query_pool: &D::QueryPool) { cmd_buf.copy_buffer(&self.scene_buf, &self.scene_dev); + cmd_buf.copy_buffer(&self.bin_alloc_buf_host, &self.bin_alloc_buf_dev); cmd_buf.memory_barrier(); cmd_buf.image_barrier( &self.image_dev, @@ -276,6 +312,13 @@ impl Renderer { ); cmd_buf.write_timestamp(&query_pool, 1); cmd_buf.memory_barrier(); + cmd_buf.dispatch( + &self.bin_pipeline, + &self.bin_ds, + (N_WG, 1, 1), + ); + cmd_buf.write_timestamp(&query_pool, 2); + cmd_buf.memory_barrier(); cmd_buf.image_barrier(&self.image_dev, ImageLayout::General, ImageLayout::BlitSrc); } } From 64daf843b0dc4af10b5f0e8a56ff11b40fe31d73 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Tue, 12 May 2020 19:54:19 -0700 Subject: [PATCH 04/32] Better output allocation in binning --- piet-gpu/shader/binning.comp | 69 +++++++++++++++++++++++------------ piet-gpu/shader/binning.spv | Bin 14804 -> 16068 bytes 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/piet-gpu/shader/binning.comp b/piet-gpu/shader/binning.comp index bf7bbae..241d637 100644 --- a/piet-gpu/shader/binning.comp +++ b/piet-gpu/shader/binning.comp @@ -49,16 +49,16 @@ shared uint sh_my_tile; void main() { BinChunkRef chunk_ref = BinChunkRef((gl_LocalInvocationID.x * N_WG + gl_WorkGroupID.x) * BIN_INITIAL_ALLOC); - uint chunk_limit = chunk_ref.offset + BIN_INITIAL_ALLOC - BinInstance_size; + uint wr_limit = chunk_ref.offset + BIN_INITIAL_ALLOC; uint chunk_n = 0; - BinInstanceRef instance_ref = BinInstanceRef(chunk_ref.offset + BinChunk_size); + uint my_n_elements = n_elements; while (true) { if (gl_LocalInvocationID.x == 0) { sh_my_tile = atomicAdd(tile_ix, 1); } barrier(); uint my_tile = sh_my_tile; - if (my_tile * N_TILE >= n_elements) { + if (my_tile * N_TILE >= my_n_elements) { break; } @@ -70,7 +70,10 @@ void main() { // Read inputs and determine coverage of bins uint element_ix = my_tile * N_TILE + gl_LocalInvocationID.x; AnnotatedRef ref = AnnotatedRef(element_ix * Annotated_size); - uint tag = Annotated_tag(ref); + uint tag = Annotated_Nop; + if (element_ix < my_n_elements) { + tag = Annotated_tag(ref); + } int x0 = 0, y0 = 0, x1 = 0, y1 = 0; switch (tag) { case Annotated_Line: @@ -119,18 +122,43 @@ void main() { element_count += bitCount(bitmaps[i][gl_LocalInvocationID.x]); } // element_count is number of elements covering bin for this invocation. - if (element_count > 0 && chunk_n > 0) { - uint new_chunk = instance_ref.offset; - if (new_chunk + min(32, element_count * 4) > chunk_limit) { - new_chunk = atomicAdd(alloc, BIN_ALLOC); - chunk_limit = new_chunk + BIN_ALLOC - BinInstance_size; - } - BinChunk_write(chunk_ref, BinChunk(chunk_n, BinChunkRef(new_chunk))); - chunk_ref = BinChunkRef(new_chunk); - instance_ref = BinInstanceRef(new_chunk + BinChunk_size); - chunk_n = 0; + if (element_count == 0) { + continue; + } + uint chunk_end; + uint chunk_new_start; + // Refactor to reduce code duplication? + if (chunk_n > 0) { + uint next_chunk = chunk_ref.offset + BinChunk_size + chunk_n * 4; + if (next_chunk + BinChunk_size + min(24, element_count * 4) > wr_limit) { + uint alloc_amount = max(BIN_ALLOC, BinChunk_size + element_count * 4); + if (alloc_amount - BIN_ALLOC < 64) { + alloc_amount = BIN_ALLOC; + } + next_chunk = atomicAdd(alloc, alloc_amount); + wr_limit = next_chunk + alloc_amount; + } + BinChunk_write(chunk_ref, BinChunk(chunk_n, BinChunkRef(next_chunk))); + chunk_ref = BinChunkRef(next_chunk); + } + BinInstanceRef instance_ref = BinInstanceRef(chunk_ref.offset + BinChunk_size); + if (instance_ref.offset + element_count * 4 > wr_limit) { + chunk_end = wr_limit; + chunk_n = (wr_limit - instance_ref.offset) / 4; + uint alloc_amount = max(BIN_ALLOC, BinChunk_size + (element_count - chunk_n) * 4); + if (alloc_amount - BIN_ALLOC < 64) { + alloc_amount = BIN_ALLOC; + } + chunk_new_start = atomicAdd(alloc, alloc_amount); + wr_limit = chunk_new_start + alloc_amount; + BinChunk_write(chunk_ref, BinChunk(chunk_n, BinChunkRef(chunk_new_start))); + chunk_ref = BinChunkRef(chunk_new_start); + chunk_new_start += BinChunk_size; + chunk_n = element_count - chunk_n; + } else { + chunk_end = ~0; + chunk_n = element_count; } - // TODO: allocate output here // Iterate over bits set. uint slice_ix = 0; @@ -149,17 +177,10 @@ void main() { element_ix = my_tile * N_TILE + slice_ix * 32 + findLSB(bitmap); // At this point, element_ix refers to an element that covers this bin. - // TODO: batch allocated based on element_count; this is divergent - if (instance_ref.offset > chunk_limit) { - uint new_chunk = atomicAdd(alloc, BIN_ALLOC); - BinChunk_write(chunk_ref, BinChunk(chunk_n, BinChunkRef(new_chunk))); - chunk_ref = BinChunkRef(new_chunk); - instance_ref = BinInstanceRef(new_chunk + BinChunk_size); - chunk_n = 0; - chunk_limit = new_chunk + BIN_ALLOC - BinInstance_size; + if (instance_ref.offset == chunk_end) { + instance_ref.offset = chunk_new_start; } BinInstance_write(instance_ref, BinInstance(element_ix)); - chunk_n++; instance_ref.offset += BinInstance_size; // clear LSB bitmap &= bitmap - 1; diff --git a/piet-gpu/shader/binning.spv b/piet-gpu/shader/binning.spv index 4cc5d36f5d587a905b5158380672988de7e0d49d..52e04b35f6d4dd38ac7849309a3663f1f7b9e570 100644 GIT binary patch literal 16068 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 From 9a0b17ff5b8e7df518e22707f205a7661c917955 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Tue, 12 May 2020 21:26:44 -0700 Subject: [PATCH 05/32] Use different output strategy for binning Iterate over bin bounding box. Seems to work, and is a dramatic improvement. --- piet-gpu/shader/binning.comp | 111 +++++++++++++++++++---------------- piet-gpu/shader/binning.spv | Bin 16068 -> 17052 bytes 2 files changed, 59 insertions(+), 52 deletions(-) diff --git a/piet-gpu/shader/binning.comp b/piet-gpu/shader/binning.comp index 241d637..6e252c0 100644 --- a/piet-gpu/shader/binning.comp +++ b/piet-gpu/shader/binning.comp @@ -45,7 +45,11 @@ layout(set = 0, binding = 2) buffer BinsBuf { // Note: cudaraster has N_TILE + 1 to cut down on bank conflicts. shared uint bitmaps[N_SLICE][N_TILE]; +shared uint count[N_SLICE][N_TILE]; shared uint sh_my_tile; +shared uint sh_chunk_start[N_TILE]; +shared uint sh_chunk_end[N_TILE]; +shared uint sh_chunk_jump[N_TILE]; void main() { BinChunkRef chunk_ref = BinChunkRef((gl_LocalInvocationID.x * N_WG + gl_WorkGroupID.x) * BIN_INITIAL_ALLOC); @@ -120,70 +124,73 @@ void main() { uint element_count = 0; for (uint i = 0; i < N_SLICE; i++) { element_count += bitCount(bitmaps[i][gl_LocalInvocationID.x]); + count[i][gl_LocalInvocationID.x] = element_count; } // element_count is number of elements covering bin for this invocation. - if (element_count == 0) { - continue; - } - uint chunk_end; - uint chunk_new_start; - // Refactor to reduce code duplication? - if (chunk_n > 0) { - uint next_chunk = chunk_ref.offset + BinChunk_size + chunk_n * 4; - if (next_chunk + BinChunk_size + min(24, element_count * 4) > wr_limit) { - uint alloc_amount = max(BIN_ALLOC, BinChunk_size + element_count * 4); + if (element_count != 0) { + uint chunk_end; + uint chunk_new_start; + // Refactor to reduce code duplication? + if (chunk_n > 0) { + uint next_chunk = chunk_ref.offset + BinChunk_size + chunk_n * 4; + if (next_chunk + BinChunk_size + min(24, element_count * 4) > wr_limit) { + uint alloc_amount = max(BIN_ALLOC, BinChunk_size + element_count * 4); + if (alloc_amount - BIN_ALLOC < 64) { + alloc_amount = BIN_ALLOC; + } + next_chunk = atomicAdd(alloc, alloc_amount); + wr_limit = next_chunk + alloc_amount; + } + BinChunk_write(chunk_ref, BinChunk(chunk_n, BinChunkRef(next_chunk))); + chunk_ref = BinChunkRef(next_chunk); + } + BinInstanceRef instance_ref = BinInstanceRef(chunk_ref.offset + BinChunk_size); + if (instance_ref.offset + element_count * 4 > wr_limit) { + chunk_end = wr_limit; + chunk_n = (wr_limit - instance_ref.offset) / 4; + uint alloc_amount = max(BIN_ALLOC, BinChunk_size + (element_count - chunk_n) * 4); if (alloc_amount - BIN_ALLOC < 64) { alloc_amount = BIN_ALLOC; } - next_chunk = atomicAdd(alloc, alloc_amount); - wr_limit = next_chunk + alloc_amount; + chunk_new_start = atomicAdd(alloc, alloc_amount); + wr_limit = chunk_new_start + alloc_amount; + BinChunk_write(chunk_ref, BinChunk(chunk_n, BinChunkRef(chunk_new_start))); + chunk_ref = BinChunkRef(chunk_new_start); + chunk_new_start += BinChunk_size; + chunk_n = element_count - chunk_n; + } else { + chunk_end = ~0; + chunk_n = element_count; } - BinChunk_write(chunk_ref, BinChunk(chunk_n, BinChunkRef(next_chunk))); - chunk_ref = BinChunkRef(next_chunk); - } - BinInstanceRef instance_ref = BinInstanceRef(chunk_ref.offset + BinChunk_size); - if (instance_ref.offset + element_count * 4 > wr_limit) { - chunk_end = wr_limit; - chunk_n = (wr_limit - instance_ref.offset) / 4; - uint alloc_amount = max(BIN_ALLOC, BinChunk_size + (element_count - chunk_n) * 4); - if (alloc_amount - BIN_ALLOC < 64) { - alloc_amount = BIN_ALLOC; - } - chunk_new_start = atomicAdd(alloc, alloc_amount); - wr_limit = chunk_new_start + alloc_amount; - BinChunk_write(chunk_ref, BinChunk(chunk_n, BinChunkRef(chunk_new_start))); - chunk_ref = BinChunkRef(chunk_new_start); - chunk_new_start += BinChunk_size; - chunk_n = element_count - chunk_n; - } else { - chunk_end = ~0; - chunk_n = element_count; + sh_chunk_start[gl_LocalInvocationID.x] = instance_ref.offset; + sh_chunk_end[gl_LocalInvocationID.x] = chunk_end; + sh_chunk_jump[gl_LocalInvocationID.x] = chunk_new_start - chunk_end; } - // Iterate over bits set. - uint slice_ix = 0; - uint bitmap = bitmaps[0][gl_LocalInvocationID.x]; - while (true) { - if (bitmap == 0) { - slice_ix++; - if (slice_ix == N_SLICE) { - break; + barrier(); + // Use similar strategy as Laine & Karras paper; loop over bbox of bins + // touched by this element + x = x0; + y = y0; + while (y < y1) { + uint bin_ix = y * N_TILE_X + x; + uint out_mask = bitmaps[my_slice][bin_ix]; + if ((out_mask & my_mask) != 0) { + uint idx = bitCount(out_mask & (my_mask - 1)); + if (my_slice > 0) { + idx += count[my_slice - 1][bin_ix]; } - bitmap = bitmaps[slice_ix][gl_LocalInvocationID.x]; - if (bitmap == 0) { - continue; + uint out_offset = sh_chunk_start[bin_ix] + idx * 4; + if (out_offset >= sh_chunk_end[bin_ix]) { + out_offset += sh_chunk_jump[bin_ix]; } + BinInstance_write(BinInstanceRef(out_offset), BinInstance(element_ix)); } - element_ix = my_tile * N_TILE + slice_ix * 32 + findLSB(bitmap); - // At this point, element_ix refers to an element that covers this bin. - - if (instance_ref.offset == chunk_end) { - instance_ref.offset = chunk_new_start; + x++; + if (x == x1) { + x = x0; + y++; } - BinInstance_write(instance_ref, BinInstance(element_ix)); - instance_ref.offset += BinInstance_size; - // clear LSB - bitmap &= bitmap - 1; } } BinChunk_write(chunk_ref, BinChunk(chunk_n, BinChunkRef(0))); diff --git a/piet-gpu/shader/binning.spv b/piet-gpu/shader/binning.spv index 52e04b35f6d4dd38ac7849309a3663f1f7b9e570..fa33483768f6342f2191a639082cbad3b311ff96 100644 GIT binary patch delta 4984 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 From cc89d0e285b893964385ffba04e924d8f31c44e4 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Wed, 13 May 2020 15:35:19 -0700 Subject: [PATCH 06/32] Starting coarse rasterizer Working down the pipeline. WIP --- piet-gpu/bin/cli.rs | 7 +- piet-gpu/shader/binning.comp | 23 +------ piet-gpu/shader/binning.spv | Bin 17052 -> 16812 bytes piet-gpu/shader/build.ninja | 4 +- piet-gpu/shader/coarse.comp | 129 +++++++++++++++++++++++++++++++++++ piet-gpu/shader/coarse.spv | Bin 0 -> 9136 bytes piet-gpu/shader/setup.h | 19 +++++- piet-gpu/src/lib.rs | 38 +++++++++++ 8 files changed, 196 insertions(+), 24 deletions(-) create mode 100644 piet-gpu/shader/coarse.comp create mode 100644 piet-gpu/shader/coarse.spv diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index 4a4fed3..73f33ee 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -41,7 +41,7 @@ fn main() -> Result<(), Error> { let fence = device.create_fence(false)?; let mut cmd_buf = device.create_cmd_buf()?; - let query_pool = device.create_query_pool(3)?; + let query_pool = device.create_query_pool(4)?; let mut ctx = PietGpuRenderContext::new(); render_scene(&mut ctx); @@ -61,11 +61,16 @@ fn main() -> Result<(), Error> { let ts = device.reap_query_pool(&query_pool).unwrap(); println!("Element kernel time: {:.3}ms", ts[0] * 1e3); println!("Binning kernel time: {:.3}ms", (ts[1] - ts[0]) * 1e3); + println!("Coarse kernel time: {:.3}ms", (ts[2] - ts[1]) * 1e3); /* let mut data: Vec = Default::default(); device.read_buffer(&renderer.bin_buf, &mut data).unwrap(); piet_gpu::dump_k1_data(&data); + + let mut data: Vec = Default::default(); + device.read_buffer(&renderer.ptcl_buf, &mut data).unwrap(); + piet_gpu::dump_k1_data(&data); */ let mut img_data: Vec = Default::default(); diff --git a/piet-gpu/shader/binning.comp b/piet-gpu/shader/binning.comp index 6e252c0..c3067e7 100644 --- a/piet-gpu/shader/binning.comp +++ b/piet-gpu/shader/binning.comp @@ -3,20 +3,7 @@ #version 450 #extension GL_GOOGLE_include_directive : enable -#define N_ROWS 4 -#define WG_SIZE 32 -#define LG_WG_SIZE 5 -#define TILE_SIZE (WG_SIZE * N_ROWS) - -// TODO: move these to setup file -#define N_TILE_X 16 -#define N_TILE_Y 16 -#define N_TILE (N_TILE_X * N_TILE_Y) -#define N_SLICE (N_TILE / 32) -#define N_WG 16 // Number of workgroups, should be 1 per SM - -#define BIN_INITIAL_ALLOC 64 -#define BIN_ALLOC 256 +#include "setup.h" layout(local_size_x = N_TILE, local_size_y = 1) in; @@ -37,7 +24,6 @@ layout(set = 0, binding = 2) buffer BinsBuf { #include "annotated.h" #include "bins.h" -#include "setup.h" // scale factors useful for converting coordinates to bins #define SX (1.0 / float(N_TILE_X * TILE_WIDTH_PX)) @@ -135,9 +121,7 @@ void main() { uint next_chunk = chunk_ref.offset + BinChunk_size + chunk_n * 4; if (next_chunk + BinChunk_size + min(24, element_count * 4) > wr_limit) { uint alloc_amount = max(BIN_ALLOC, BinChunk_size + element_count * 4); - if (alloc_amount - BIN_ALLOC < 64) { - alloc_amount = BIN_ALLOC; - } + // could try to reduce fragmentation if BIN_ALLOC is only a bit above needed next_chunk = atomicAdd(alloc, alloc_amount); wr_limit = next_chunk + alloc_amount; } @@ -149,9 +133,6 @@ void main() { chunk_end = wr_limit; chunk_n = (wr_limit - instance_ref.offset) / 4; uint alloc_amount = max(BIN_ALLOC, BinChunk_size + (element_count - chunk_n) * 4); - if (alloc_amount - BIN_ALLOC < 64) { - alloc_amount = BIN_ALLOC; - } chunk_new_start = atomicAdd(alloc, alloc_amount); wr_limit = chunk_new_start + alloc_amount; BinChunk_write(chunk_ref, BinChunk(chunk_n, BinChunkRef(chunk_new_start))); diff --git a/piet-gpu/shader/binning.spv b/piet-gpu/shader/binning.spv index fa33483768f6342f2191a639082cbad3b311ff96..76148c2c44d71cb55a0d2ef55715d2d6f77ee77c 100644 GIT binary patch delta 3928 zcmZ9O>z9^O7{>QICmj`25+bFO$|NdN!gNxILM6o*)o9dAHB%(nP>2tv;f|*x^czoO>Ju$3E95UST$*J>7Jg0ip%oW z2RF5@&oWwzet%0_W6S2QncLy(Hf?NdZR%Lll}%-O46SA5=5l7cGW)vb=8je68LHAg zwEQafcjLz^(%EM?oaij{no=ly+gabcUv<-b-2denN`YF={NdsI}h~#xlnX3*q(BjiS+J8@KJF0BKS4%(cO4mH$FMXC*be? z#CYSoKf#GtOqMz)Buky=mO0NYb?{LHw9LWBz%6rd6QInexe1)ecmo^vd+OX$yL~`U z@my@ZN(;x;7JK7SP5Xm!HK={Sy=e2nR#5wOsWP2pH?&Qs!us_Es|7zBZk%o91D8@i zus-VL@r+UXJ5{7A`oFsRQok`hi~ZrJut%(O;Kp8%?dG{~wFSh!62DroW7Hkvjt>O? zO7C25vs~;J>t9dSbZHR(Ce!PqjqxMP3pyW?pL8%>%^(IIT>5^@g~ba|WYcJo?nP+o zvGgHewV~M(M&f%hT-|7%GPxV!DZ2!$mXA3Ut{!t-3jV3U33@84?=S?#Mgqjd!{H9p z76UH>pTy@7<0?sL1YAG$QzSEr-sd0csBI+e7MlAbjyPk$Cz-#z0^bXTFkW6Z{=cHJ z@a`)bho-(gTflg*l8sJ!Y9zwqf%Fa)$^6Q z8t(RJ>yw@QNnoVpdvz{AACvH<*XDLr`{GO~zc|+*X^I!89z2B>FOGJHdq-%CHB1Go z1)l~sPHf_}V13jb@8+q6|8y`*E}j;yLoi^xE!Tt9;%&JB>>zc6__oXd$J^qiC3i9} zEdAqcnFaQ3(MMY(F}s43ke_r8#@RH37+4<8H-hzz70v~#xdBljweX(@&TsNf@cbs< z44=1>e;N#+QPI1_^X%F63~TeK&ZkB2Tfqj8aq|2)$9LV9DJ?7&>S`C=jPW zcR$>BqNzKw7tt4i{bo7VndJIJy%jzEW%5*y8=5hOA!u>E%YAN+ni+XJobm z^m1$I-na+$f8yIr{{YQL`|8YYAF*1%#t8l(e3lp9QA+B?wFW^a4PI9ARWyS;nK%Zw zf(_;sV4`hswLQ5B*1=tnr&6EwaQ)P`65o&929LQ0YqFMp1I49)at$nRYO%%oecv9PD1{Tfsh7 zN!z0|HLE0!;E#jzlf_(dP_|(&Dte_w0#CplsPRW8uyotO>QS~Q!D_DXF?xAS`V`oz z#rJ3jTrHA+8th~8*otWI))xFl zu!(O=#jdiy1X0v6R;A`?j^ykkz{}vSiM#HD-Ei*%_3(KGtRB1scGCE6y$UW_{``mQ zHMj#cn#eACwFvS$SS|P-a6XYY;EQSM;qxX~{cv`k-U1s(z372@8{p%fXxvLvb4$b# z=pAr=vUlOjXzJnf9#}p2`(P7}kHZIGHT_s%v20Yd)G?`j1o#ka06%{tN%AAGTG&4Z zk7LiZkH-E9T+Qn3qt{=&pI%#h`aT7Z_Xm$o8ckp;y{oo&0#E+|?Mx6C$!Bo2c5LOq z*wn)RVCEnE5PYUSS@}PQ>!W_ENM>IEd@R4l!!$J$5l5gS;QUoB58F|&H)16DC0s3% z)TZVlBewkzg$DpmW-niv@#fNjNn@9G!u$fq?~SkEYR70!@9&MT!SQ?JI9P6NPSEQg SMgIou?+txUbdI0AuFrpu{Ga0h delta 4182 zcmZ9O>z9^O7{>QIGmVZ?Q<76Dm2_~*VI&$Qk(5JHVR~goQ<^eUgm@Fm`Sc>^UT__uj{(+ecyZEd++CYcidBW;l4tj>Z;*~ zq*O@V`TK5npXa;R53WjSMX{~8ETz;f)uf{i;S zO?m5*Rm)anGr9%+`j*zlmerk^*T9#rTG_a)*uJ!LHq~hZTFVWqD>K`a*_Ssrw>MQX z)TCW#`Bm=j!oR9YhaTjxr=!sG%tGPYj{07GyA|i+{x8o^3N#N~L*8M9lqP1}g{cK! z*oC_^?PK|W68#tOX%$|Zexd)iW9wl*6-w>}+fxa1D!qFVd=%Wh2!0lPTo*o}3!j|h zC*$w_#CYSoKf#GtN|rh&Buky=mO0NYb?{LHw9LUr!!2`gCqS7Ga3`=Q;|*-wuc_nq zx^)9elc-TGww~C!(biRtNJ_oHYW=DEF0^X;-f;Eb@!y}S)1hFWe~6*24^8c_R8_&} z;$uu>{EZeid*?7cA@(g7$JCV$L(=s(ChTCg55gphTBIdZ87csV71_NaO14a zmN5XVk9uW1vDBPS4ZTyPFC7Rl5OF_F5%6%h31(+!X%Jj(HVIC_?+CDC)E(n_I}*H? z-h>`9x!41ye?3d2OGoqX4En)UGdfRSBs~U0KIyS=HG>#f?h5{3Vy?j6f_h4amW!k7 zO2^?blooXu2Cit?QbyuA98KM*o^^RdIUcMw5xDSRcW+kfQE;_J&vqm} z0l|S9W8i4;J_7Gxpy%pDxPI!MtFiPxo-1u*Xjjo(syO0|2OGx)csb>rwo*zbWyV<1 z$?&c#It5KV4*jWM$EmN+R&^R!omEv5c{tQ!5~qW$Lhv)-`^kSG=$QyQX^2vt1y(m` zRH`06lt9{gXV>6tFjDe%dyDjOwZ3|^Mbe32wOB~YIS@q$#r2&8SC8v^9@xh+Yde>w zW|_s2$QYisu-RwVH#Jml@A)XEkJ~#%4_e$_?M9nSuPvrM6|5Hg0%0pqq_1XhdNdNJ5R>IU()UILEWIs+^>nXha8eP5;ZqA7^WE7Ca}|P48oa&o4H8shs!zQbd#!J6Dce+SJZ8t(*e&jQNtg6q?O)iY26kD%92TZ~%^ z)@IB#^zyLX4OR=jPRCsY`JP#tuB4v%G?W9pN8E}$$GGlRnm4JP0cEaBl!K` ze6k1N=8A*zAiSjLD=jAQ5Zr+pe;|RSdl;-9WqSmy<_b5^%OmNdV5=72!N=fgG5IIJ zK29E6DLsy$=@OcfazB4;duY--FUh+V_3Gi`GG`>Mob4$b#XbU)>>@9c` zO+9?Jg4Kg>13TgPIBW;2>Bj;~6{Di1j->VxU$+y93Vc!WJ%bsgL0sA{} zHLJIaUVrg!dTsIPdlx*8^H*`w=mgf&yJ~wAc=|iQ`4`E1aJ4pU2G*t){_ki0!9Rdc z!Dm}m{tw~$sPm($I(-cAvHTi8qNzC%aRmAVoPSlz!}clIH)6c)K7*^pB( 0 ? + BinInstance_read(BinInstanceRef(start_chunk + BinChunk_size)).element_ix : ~0; + } + uint probe = 0; // for debugging + do { + for (uint i = 0; i < N_SLICE; i++) { + sh_bitmaps[i][th_ix] = 0; + } + + while (wr_ix - rd_ix <= N_TILE) { + // Choose segment with least element. + uint my_min; + if (th_ix < N_WG) { + if (th_ix == 0) { + sh_selected_n = 0; + sh_min_buf = ~1; + } + } + barrier(); + // Tempting to do this with subgroups, but atomic should be good enough. + my_min = sh_first_el[th_ix]; + if (th_ix < N_WG) { + atomicMin(sh_min_buf, my_min); + } + barrier(); + if (th_ix < N_WG) { + if (sh_first_el[th_ix] == sh_min_buf) { + sh_elements_ref = sh_chunk[th_ix] + BinChunk_size; + uint selected_n = sh_chunk_n[th_ix]; + sh_selected_n = selected_n; + uint next_chunk = sh_chunk_next[th_ix]; + if (next_chunk == 0) { + sh_first_el[th_ix] = ~0; + } else { + sh_chunk[th_ix] = next_chunk; + BinChunk chunk = BinChunk_read(BinChunkRef(next_chunk)); + sh_chunk_n[th_ix] = chunk.n; + sh_chunk_next[th_ix] = chunk.next.offset; + sh_first_el[th_ix] = BinInstance_read( + BinInstanceRef(next_chunk + BinChunk_size)).element_ix; + } + } + } + barrier(); + uint chunk_n = sh_selected_n; + if (chunk_n == 0) { + // All chunks consumed + break; + } + BinInstanceRef inst_ref = BinInstanceRef(sh_elements_ref); + if (th_ix < chunk_n) { + uint el = BinInstance_read(BinInstance_index(inst_ref, th_ix)).element_ix; + sh_elements[(wr_ix + th_ix) % N_RINGBUF] = el; + probe = el; + } + wr_ix += chunk_n; + } + + // We've done the merge and filled the buffer. + uint tag = Annotated_Nop; + AnnotatedRef ref; + if (th_ix + rd_ix < wr_ix) { + uint element_ix = (sh_elements[rd_ix] + th_ix) % N_RINGBUF; + ref = AnnotatedRef(element_ix * Annotated_size); + tag = Annotated_tag(ref); + probe = tag; + } + rd_ix += N_TILE; + } while (wr_ix > rd_ix); + ptcl[bin_ix * N_TILE + th_ix] = probe; +} diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv new file mode 100644 index 0000000000000000000000000000000000000000..6bd6f61d36ec451435b82a1cfccd01f9752b4d9f GIT binary patch literal 9136 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 { pub state_buf: D::Buffer, pub anno_buf: D::Buffer, pub bin_buf: D::Buffer, + pub ptcl_buf: D::Buffer, el_pipeline: D::Pipeline, el_ds: D::DescriptorSet, @@ -127,6 +128,12 @@ pub struct Renderer { bin_alloc_buf_host: D::Buffer, bin_alloc_buf_dev: D::Buffer, + coarse_pipeline: D::Pipeline, + coarse_ds: D::DescriptorSet, + + coarse_alloc_buf_host: D::Buffer, + coarse_alloc_buf_dev: D::Buffer, + /* k1_alloc_buf_host: D::Buffer, k1_alloc_buf_dev: D::Buffer, @@ -172,6 +179,7 @@ impl Renderer { let state_buf = device.create_buffer(64 * 1024 * 1024, dev)?; let anno_buf = device.create_buffer(64 * 1024 * 1024, dev)?; let bin_buf = device.create_buffer(64 * 1024 * 1024, dev)?; + let ptcl_buf = device.create_buffer(48 * 1024 * 1024, dev)?; let image_dev = device.create_image2d(WIDTH as u32, HEIGHT as u32, dev)?; let el_code = include_bytes!("../shader/elements.spv"); @@ -202,6 +210,23 @@ impl Renderer { &[], )?; + let coarse_alloc_buf_host = device.create_buffer(4, host)?; + let coarse_alloc_buf_dev = device.create_buffer(4, dev)?; + + let coarse_alloc_start = 256 * 64 * N_WG; + device + .write_buffer(&coarse_alloc_buf_host, &[ + coarse_alloc_start, + ]) + ?; + let coarse_code = include_bytes!("../shader/coarse.spv"); + let coarse_pipeline = device.create_simple_compute_pipeline(coarse_code, 4, 0)?; + let coarse_ds = device.create_descriptor_set( + &coarse_pipeline, + &[&anno_buf, &bin_buf, &coarse_alloc_buf_dev, &ptcl_buf], + &[], + )?; + /* let tilegroup_buf = device.create_buffer(4 * 1024 * 1024, dev)?; let ptcl_buf = device.create_buffer(48 * 1024 * 1024, dev)?; @@ -285,11 +310,16 @@ impl Renderer { el_ds, bin_pipeline, bin_ds, + coarse_pipeline, + coarse_ds, state_buf, anno_buf, bin_buf, + ptcl_buf, bin_alloc_buf_host, bin_alloc_buf_dev, + coarse_alloc_buf_host, + coarse_alloc_buf_dev, n_elements, }) } @@ -297,6 +327,7 @@ impl Renderer { pub unsafe fn record(&self, cmd_buf: &mut impl CmdBuf, query_pool: &D::QueryPool) { cmd_buf.copy_buffer(&self.scene_buf, &self.scene_dev); cmd_buf.copy_buffer(&self.bin_alloc_buf_host, &self.bin_alloc_buf_dev); + cmd_buf.copy_buffer(&self.coarse_alloc_buf_host, &self.coarse_alloc_buf_dev); cmd_buf.memory_barrier(); cmd_buf.image_barrier( &self.image_dev, @@ -319,6 +350,13 @@ impl Renderer { ); cmd_buf.write_timestamp(&query_pool, 2); cmd_buf.memory_barrier(); + cmd_buf.dispatch( + &self.coarse_pipeline, + &self.coarse_ds, + (WIDTH as u32 / 256, HEIGHT as u32 / 256, 1), + ); + cmd_buf.write_timestamp(&query_pool, 3); + cmd_buf.memory_barrier(); cmd_buf.image_barrier(&self.image_dev, ImageLayout::General, ImageLayout::BlitSrc); } } From 06cad48dcaa13789fc1c6bdb26a715e8a9853c70 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Thu, 14 May 2020 17:06:45 -0700 Subject: [PATCH 07/32] Start output stage in coarse pass Still very much WIP but it's progress. --- piet-gpu/shader/coarse.comp | 82 ++++++++++++++++++++++++++++++++++-- piet-gpu/shader/coarse.spv | Bin 9136 -> 17556 bytes 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 3ca7b5f..4e4ff19 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -42,10 +42,16 @@ shared uint sh_elements_ref; shared uint sh_bitmaps[N_SLICE][N_TILE]; +// scale factors useful for converting coordinates to tiles +#define SX (1.0 / float(TILE_WIDTH_PX)) +#define SY (1.0 / float(TILE_HEIGHT_PX)) + void main() { // Could use either linear or 2d layouts for both dispatch and // invocations within the workgroup. We'll use variables to abstract. uint bin_ix = N_TILE_X * gl_WorkGroupID.y + gl_WorkGroupID.x; + // Top left coordinates of this bin. + vec2 xy0 = vec2(N_TILE_X * TILE_WIDTH_PX * gl_WorkGroupID.x, N_TILE_Y * TILE_HEIGHT_PX * gl_WorkGroupID.y); uint th_ix = gl_LocalInvocationID.x; uint wr_ix = 0; uint rd_ix = 0; @@ -109,20 +115,90 @@ void main() { if (th_ix < chunk_n) { uint el = BinInstance_read(BinInstance_index(inst_ref, th_ix)).element_ix; sh_elements[(wr_ix + th_ix) % N_RINGBUF] = el; - probe = el; } wr_ix += chunk_n; } // We've done the merge and filled the buffer. + + // Read one element, compute coverage. uint tag = Annotated_Nop; AnnotatedRef ref; if (th_ix + rd_ix < wr_ix) { - uint element_ix = (sh_elements[rd_ix] + th_ix) % N_RINGBUF; + uint element_ix = sh_elements[(rd_ix + th_ix) % N_RINGBUF]; ref = AnnotatedRef(element_ix * Annotated_size); tag = Annotated_tag(ref); - probe = tag; } + + int x0 = 0, y0 = 0, x1 = 0, y1 = 0; + switch (tag) { + case Annotated_Line: + AnnoLineSeg line = Annotated_Line_read(ref); + x0 = int(floor((min(line.p0.x, line.p1.x) - line.stroke.x - xy0.x) * SX)); + y0 = int(floor((min(line.p0.y, line.p1.y) - line.stroke.y - xy0.y) * SY)); + x1 = int(ceil((max(line.p0.x, line.p1.x) + line.stroke.x - xy0.x) * SX)); + y1 = int(ceil((max(line.p0.y, line.p1.y) + line.stroke.y - xy0.y) * SY)); + break; + case Annotated_Fill: + case Annotated_Stroke: + // Note: we take advantage of the fact that fills and strokes + // have compatible layout. + AnnoFill fill = Annotated_Fill_read(ref); + x0 = int(floor((fill.bbox.x - xy0.x) * SX)); + y0 = int(floor((fill.bbox.y - xy0.y) * SY)); + x1 = int(ceil((fill.bbox.z - xy0.x) * SX)); + y1 = int(ceil((fill.bbox.w - xy0.y) * SY)); + break; + } + // At this point, we run an iterator over the coverage area, + // trying to keep divergence low. + // Right now, it's just a bbox, but we'll get finer with + // segments. + x0 = clamp(x0, 0, N_TILE_X); + x1 = clamp(x1, x0, N_TILE_X); + y0 = clamp(y0, 0, N_TILE_Y); + y1 = clamp(y1, y0, N_TILE_Y); + // This loop draws a rectangle to the coverage bitmasks. For + // line segments, draw more precisely. + if (x0 == x1) y1 = y0; + int x = x0, y = y0; + uint my_slice = th_ix / 32; + uint my_mask = 1 << (th_ix & 31); + while (y < y1) { + atomicOr(sh_bitmaps[my_slice][y * N_TILE_X + x], my_mask); + x++; + if (x == x1) { + x = x0; + y++; + } + } + + // Output elements for this tile, based on bitmaps. + uint slice_ix = 0; + uint bitmap = sh_bitmaps[0][th_ix]; + while (true) { + if (bitmap == 0) { + slice_ix++; + if (slice_ix == N_SLICE) { + break; + } + bitmap = sh_bitmaps[slice_ix][th_ix]; + if (bitmap == 0) { + continue; + } + } + uint element_ref_ix = slice_ix * 32 + findLSB(bitmap); + uint element_ix = sh_elements[(rd_ix + element_ref_ix) % N_RINGBUF]; + + // At this point, we read the element again from global memory. + // If that turns out to be expensive, maybe we can pack it into + // shared memory (or perhaps just the tag). + probe += 1; + + // clear LSB + bitmap &= bitmap - 1; + } + rd_ix += N_TILE; } while (wr_ix > rd_ix); ptcl[bin_ix * N_TILE + th_ix] = probe; diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index 6bd6f61d36ec451435b82a1cfccd01f9752b4d9f..ded68da2d94dd24b0039ce453d730d2d17046c9b 100644 GIT binary patch literal 17556 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 Date: Fri, 15 May 2020 12:28:29 -0700 Subject: [PATCH 08/32] Start writing tiles This is the first checkpoint where it actually runs a pipeline end to end, though it's far from accurate. --- piet-gpu/bin/cli.rs | 5 +- piet-gpu/shader/coarse.comp | 35 +++++++++++- piet-gpu/shader/coarse.spv | Bin 17556 -> 21540 bytes piet-gpu/shader/kernel4.comp | 3 +- piet-gpu/shader/kernel4.spv | Bin 20180 -> 20172 bytes piet-gpu/src/lib.rs | 107 ++++++----------------------------- 6 files changed, 56 insertions(+), 94 deletions(-) diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index 73f33ee..672b42d 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -41,7 +41,7 @@ fn main() -> Result<(), Error> { let fence = device.create_fence(false)?; let mut cmd_buf = device.create_cmd_buf()?; - let query_pool = device.create_query_pool(4)?; + let query_pool = device.create_query_pool(5)?; let mut ctx = PietGpuRenderContext::new(); render_scene(&mut ctx); @@ -62,10 +62,11 @@ fn main() -> Result<(), Error> { println!("Element kernel time: {:.3}ms", ts[0] * 1e3); println!("Binning kernel time: {:.3}ms", (ts[1] - ts[0]) * 1e3); println!("Coarse kernel time: {:.3}ms", (ts[2] - ts[1]) * 1e3); + println!("Render kernel time: {:.3}ms", (ts[3] - ts[2]) * 1e3); /* let mut data: Vec = Default::default(); - device.read_buffer(&renderer.bin_buf, &mut data).unwrap(); + device.read_buffer(&renderer.ptcl_buf, &mut data).unwrap(); piet_gpu::dump_k1_data(&data); let mut data: Vec = Default::default(); diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 4e4ff19..da25ce4 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -46,6 +46,17 @@ shared uint sh_bitmaps[N_SLICE][N_TILE]; #define SX (1.0 / float(TILE_WIDTH_PX)) #define SY (1.0 / float(TILE_HEIGHT_PX)) +// Perhaps cmd_limit should be a global? This is a style question. +void alloc_cmd(inout CmdRef cmd_ref, inout uint cmd_limit) { + if (cmd_ref.offset > cmd_limit) { + uint new_cmd = atomicAdd(alloc, PTCL_INITIAL_ALLOC); + CmdJump jump = CmdJump(new_cmd); + Cmd_Jump_write(cmd_ref, jump); + cmd_ref = CmdRef(new_cmd); + cmd_limit = new_cmd + PTCL_INITIAL_ALLOC - 2 * Cmd_size; + } +} + void main() { // Could use either linear or 2d layouts for both dispatch and // invocations within the workgroup. We'll use variables to abstract. @@ -53,6 +64,13 @@ void main() { // Top left coordinates of this bin. vec2 xy0 = vec2(N_TILE_X * TILE_WIDTH_PX * gl_WorkGroupID.x, N_TILE_Y * TILE_HEIGHT_PX * gl_WorkGroupID.y); uint th_ix = gl_LocalInvocationID.x; + + uint tile_x = N_TILE_X * gl_WorkGroupID.x + gl_LocalInvocationID.x % N_TILE_X; + uint tile_y = N_TILE_Y * gl_WorkGroupID.y + gl_LocalInvocationID.x / N_TILE_X; + uint tile_ix = tile_y * WIDTH_IN_TILES + tile_x; + CmdRef cmd_ref = CmdRef(tile_ix * PTCL_INITIAL_ALLOC); + uint cmd_limit = cmd_ref.offset + PTCL_INITIAL_ALLOC - 2 * Cmd_size; + uint wr_ix = 0; uint rd_ix = 0; uint first_el; @@ -172,6 +190,7 @@ void main() { y++; } } + barrier(); // Output elements for this tile, based on bitmaps. uint slice_ix = 0; @@ -193,13 +212,25 @@ void main() { // At this point, we read the element again from global memory. // If that turns out to be expensive, maybe we can pack it into // shared memory (or perhaps just the tag). - probe += 1; + ref = AnnotatedRef(element_ix * Annotated_size); + tag = Annotated_tag(ref); + + switch (tag) { + case Annotated_Fill: + case Annotated_Stroke: + // Note: we take advantage of the fact that fills and strokes + // have compatible layout. + AnnoFill fill = Annotated_Fill_read(ref); + alloc_cmd(cmd_ref, cmd_limit); + Cmd_Solid_write(cmd_ref, CmdSolid(fill.rgba_color)); + break; + } // clear LSB bitmap &= bitmap - 1; } rd_ix += N_TILE; + break; } while (wr_ix > rd_ix); - ptcl[bin_ix * N_TILE + th_ix] = probe; } diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index ded68da2d94dd24b0039ce453d730d2d17046c9b..ed005fcf53c3c20dd1d3a805994839aeea4d576a 100644 GIT binary patch literal 21540 zcma)@2bf+})rBvanGhgALhmH>-g^%%lmLOy+c23-k_l77WC97j7X=hW6cq&vD54ZW z0RWc=dSJT6>>;_SyH`_DyUXvF_qkwOBQZ|D4}e zHIA{>2$ZT?sv6noCr+9&X^T0-)3(@d`>k|XrfNFsvuw3^)j{7*>+k9vpsQwMxR^GE z@#(3RSl7xd5o#Y@88?kXTs*I=27OmJluHtX&cFZ)I5e}OzrCI9_$+&YUZhK z-czRz&ePsejjmRr&-iyFSx?@~Q2(}SMf{sNj;vOx#}0Q5_e^V4wKJp;p?y_{M1A6s*g`OE$H|4h%Aw#tjTId@03ZFBrSGv^MR z>Ox(on#aO*c2wKLiP!Mc=JlcG;Z4ACa`)Xp&pfU&^BZ05M4xesoQa4jOvAse+L1oF z?E>DXf7+D6zTU>Ra}G6+GPm8}@zdtDq2^Iu8y(!4CZNT)^NgY1;hvnVs7a{4h$9z! zc2DCruHUw5FYp|i=WQQ)+z*=DKbs)Uny7g!%(uPT4_^B<>L&LX=sANDO()Wr>L7US z^Nuv@2^twK%u=xOlXljxh*R(mxKJk)iDmkYL$6{mFJy>0Kg(#BqE0B~3C zdthVD)Pcii)gV2wyj&Z6Zj)~lepZt=EYUrF+napBymvSGf_d+2@&)rg*yQ!RZPlaj<~;4ybKu!sLtXv!=J@;~ysdf>o;+Rw zFRWdA^%|T@evW1HI+lA(MYDHJZwNTVBe;=deWSpMI~rWZ9oyvf`r4|Mi*e(MaqEL8 z4|n%ve!0hNG<)aVu-3$z7i0GXm+RiEg->eXhqUlx;9lG8J?81Cj)hNfzb@EAP0c(S z`?${Mc>Fx9^~vB_TsPre@G__F7TyP^w)JyjWaAxeMqlSKgF~lI92%TE`@nshYo3SS zy!l%-=C}wvJhL$bKC1Cf$Cycj-CcbL4xCOm+&ehn$YxHL>EFxCTjN>OUcD!Le$)QJ za890UxB*^Xe>c^9;q}*E-Hdj|P;zLmZh;R?3-73IgY#G(8t&}&kwx5&>K?V3wNU4F zKU&kHnacz4h4t#F9xBFk@&NPtesmG7`4BVy<4t{f@6ep#&YnJ$_Uea=#A~m9E*~14 z%4;F^w(2=}Zv%H!e`$`N+B@9eHJeTCTqAgqBv?ZZ5e-j3qNTQ-cfbKsYTX9jJB#Djkm|fd~?CIvEI*Z)!AsxXAJjB z@WTAts!QPWxC0vJQZU=QfiF|%PN9CedIPstSAqThU@6$ztE=G)thug1M|F6i=l*yCTA6znyv%)Si@m3X&uHO;E&Q~CPb60tHj>s(JAiESEL<1v zF_BuUF$~<^cqXo+fg^l;lQ#IP;4)sG6JR_x&=STH^T( zDC5b;6HB}6^WN=Ij#tZg@7d*e`FQ+C&|I(I%6><#R&&3b<$ecgKaW82RcXsJ%5md~ zZhY~I__&^NG>^5g_}$yo{A#WmTx}(6OVE#_t<1P3>E*^*i?uk9TH1JgM#I&OW9%{X zi9Hr^_78%w*f zHlRkHP=3#b#7eS^wZ|}E$Ex}_G(M8T)VbyYn!or5i+)#YxD)oadPw8 zfnH5N$L>O}-LbpUJ627b-z2f^Ufa~naSwXWi(|CuzYl%&I-1;C&8U6UcCGo$S~HHh&!Tzg&!eUhQ$6<6z{x}XjM^vmbKqV_;TM73yWv-W zJ7~tgiryNz2j$n++-pUD*L@w$`LsVmumAEjf4b&g3$FWBdUJB!uhGjL_d30`QFGmL z?_p}%7h?kBxwQeF-v;!?P&2OFdzo6+yEEL_;rqi!!B@a*3fP$5 z+sxaT$AKNMW=y&Fw=(95a7+tg&VsvFGDkl=_m2Uvu{|foAB6MYO5ON!@0V)YPph$G z5^ugnTKv7AD#d<1GOaD{go>24YqBlRaaqy}1`l>r#?z>w#{#^KG_~?Hz*!|VKN3>Vh)ZAx= z`PkopR#oTkF4ynhaQ8>@U4qRquHZ|<{k?Bvc8z;7Mb~X)V1E*mmG*HoWNiig<8Nx=H@EOxTDadk z8Sl04cTKp@9KUPAjsH}^H-!70Qri7aDfx3P+;5H2KCXrPJrTS4_$?7`K7LDtZwU8W zBHaA^mI&AGw?w#hzazr6`z;Z!-EWC-?S4yyYxldMhem{h}zx{p)H$T50!nOPT5bnE$-w*N$T!r4xhiKm8 zyhKAC)duecCwY<2VXUZj?J zE&=;|E7yD}TwQ4sKoF6=>?o>s?@LtDbT1)=n$uxe~5^MP1vgz-o?7y{`rv zPn-F8uT)Fi_kvsNbPbw%@_8TFnCcn#e(kg}pKIajm)7}w0IXK#a~<4x+RVp$wOaPe zhrnu?`@>+h>uK@%2-tOGjUNT;r*2H|_iFL~7+5VaKMqzqkv5tA<2(0_V0Fj)e2}|V zub)qV)qICe%umAAlf$RLYR0^a%FZ^*N&^_8DXC#6DljI(`Q3zPW=qQ|LcS zQ?rgQ9NE|p--H{}Z;dkUV`$nE z_gmmcX^uCpT%W}KHn`07JMh+ApFmT;t=+@u}Um|ck>ln17(092Ufa*fSuR=CD!#T`e$h#+J9Nw?H&6Z zea8N}w%I%OxAecGW$f>3yM6Nh1K1e3R{scAv#y>K`EO{(GB0t){uS)l@E5?@XI?jd zqy2@ZpEmPVOTOl2O#8(6JJ^^x&;J0cxmUAZHEWUenw$Ke^v1Jy{r{$ak(TxUr?%T? zzL&r^7yM=LEwtPNUjgf*epkJRUj?hrr&%NUzi7_q{Nlua4Qx$P-`Bxv{WSOgoAe&W z(e?&S%{b!3c?)cuTk9O&2CL;dT8#I+#L>p^O3gU_*V_4Ar@y%IO$WDb@w1m_e*5Rh z%ila~e&;2h#lg*U-MmMRg1g_<ftMc%{%9J6|g?)?vvH%Gq<)?X==_bPMkHs<=kt+?_qBB_^bt1&o#C- zSj}tRbLIEDTKva@%b4rHecq_YXI-$m;lJ~2=+HV$7s{%BzpCn+mpebTlJjVPO!R%V~zo2Q72^Bp<^tbPhDab|*zo&D1b?xyMMymEa!H>rz##?Jy<-|$nx=9N79;A;9A zPn(+Ye21~l_VT>vxCiv@cO2_a>;ZUrZU^DM(`2sMaDCJ><}|Q#=KK%A^;7r!57T?N zC$-I?shO8JaZU$2SNIw5vflIH`lzSg^TFz;&{E$s!PZw>Vx0w6cg=I@<*}UuR?9cv zd0@43XZ(~ZI{q{pA}zP z;L8epd5x3%6>!g?b-t3`<8^)&QkPTC>Rt5y#{6#j_PV$B>RH2kz*)o9V7Y7X?@jLo zkE2&Nx2x#YCa}V5=-)?EGxzKzf0ufHZHwQvwV(I>((eQ4+D7vp=D6#?=DmI$LwjO; z5bRu2TVi|&P1{A}Y>ex{#!+`)nCFLS^9Wqd{gGPFxR1getA0rx>tkT`8)&0hv++L; zHpfvk?E_TSzZcvH)|T@rPwqE?Qzzdst-Y~6fvzonpRE0?Q|b39bZx2Ar@^U{F|;Sf z&EV9jjByLPwygIvVB@H#PM-x^r*hn_XzCgFIk02ZQ>WX&>Sdii4^N%6r%qo0Ys+4j zC->XIsZ)8b?m*WTzdLKc*8OxBy0+Bmi(qr`ea0Bt6XR}h>Qv5k54yGs>OJr!uyNE= zr!RwB_tU*->Z#LxV8^PbPWOY=%Q}4pZjSB;?WxmO!P=~o_W^lwe*m01mFMa~bZzl_ zsP=2!PhUgVmO6bMoH`jpdty8cPMylR9zoZZIz0+Dj(Y0!4RGsz`X-us>hu`cvFfSQ zx4`OUogRm$PTEtaZ-cd2r_1T(-ebK7dav`|mwVk);InD^K1nb4UgzJ+zYAWEUS0bW z^lGWY(_ppMJ@-8{ZCUg8!L57a2WaY9^AEv}Q_q@z1XeHC{A0Ll_FkhsYyJsXn|WSB zFHfG&fRkr=zJ7|XEqVS7+ zJYEH7thO=_{j_K7Yhc$I{yMmf^#(k#^wpj`9H%{F-vqmc@VCI`n(wK%!TPBCPPG_+ zAN26uRU1-6^PVD3oDpDi$~lpHP8_eVeSF)&=Af^CUzNu;5}Y{xtyQkgcgay;--q)z zrX}HO{+k42`L|fL_>Ttb{}}f+$1ej{^WQ8yispV;7VN!D+u8LzW5DXh^zXTH*D{vg zwb=U`>GJd|&^)xSSljKho|V9^BYb7B=j2Z2pF(b{!1YnL#`5K8#&#}o*0U;EV3rPgm8SYPj_ zzAI?YJmbOTJnO*KtZmM-TKw0o{lnLTm-}#ixIXIc!wu;@tdX`2Xlm9-oH!eS%e^I! zZDX+YN-Z`4t0hNmYM#r)wwHUJoy)%53!bOe-!WXTfA`P(l(pL&?p{pIw}4x7uX}Cg zsNa@gZN~853do&rD|+X*ckgXOzb(x}`*yY6-WvJuaJHuD=QwfZ*a2+pT=P4E)y&sg z?+o@Zj<%g>YQ_;K&MsirX+6z-SFl>@sZA~ZyMfg*@9to={i%cV?g{pAUTu5O)SOrB zI(Zus8dvex^+t+jsI;G!529B~ z+#|r{?+sJn`egi(V13l{d&5y+^;5XV_{{hIaWuRet}U^T0joRj;q>y@js>e_&BuY& z&Lu{EZ#W*V?)bCO<&O9Fh7-YR+D-t=^LxWdV1I8=*WQnxTIN0(tmb|@1?=%U_cwL9 zzY}!Qx7WSrPoBBE!I^t1Se|$1Y2fu3r|#Tc^lFav-x*8?-$1>z^?>EcYbN-n+BO3$ z_xC+gt$M*H&^tz3;>`l98PB-##5)zNuQub#JzstU_JPgKdr$oP;p*1ke*--LR?A$o z!O49PEI0OP^!g^(A@Jdh*GHTC{uFxaXrKA!g5A^kTjA+owY;l)KRKiJ$@6<2Tuoo+ zcD!17t$EGKx1x7W?-!YKKDhiX?@YLwe~UDpHZ{llw`0dYOE1sgmet?n>f4Ftzi0IK zp=D?uqiH)5$M2M-X^vl(KL57yB=F(|pVq?t|0I_7xh?#x7JfkszpUU_fv>K4&ig5F z^D57K7hL^e)?&@OX=+nx*2J9EQo~-bTJ902g4Mh)`fTf?@2AbAIgj>?9RO=Hu4|Xa zHXB^V9fGU59};&CIB~US>@ZlHajl^|w$s67+<9;{zqJx~J~(l;XY83^ZN~Ln$YVPj zT*f^Yu67PBanA!MuJ(*QAFR!|o>O^j7lO;U7sJ&qq9yJn;KbFQv6q6i8P~lbkL_}B z8TVaqwJT_e`)+XJYR}j!!P<=L9+SuR99zD9pt*P5Kh7!FFEKv|_IxDfhrnvFUk|pfv40q>mN`EHc8q%F{3zHt^X?+o zFEMWbThqk+7+5X#kAtmE>^FkdGUrWT$J|KEoSy(YXRbB5e#Tsi{*yHAZ*d1q9`jkSTGnljYVrRZSS|OM+rVnGXxXcu2WPKp&)6@3wYhIxt30+lz-8P!;cBPR z68A1};%d*>FM_oh*P6*=y9Zpx{SsWQyvKYQp19gG_Fk|y<63)pZ1;o9xL<**okL5$ zUj-+w_KbZ1tj)NdLwRfufy=mGgR7PIn6JYVS9``j4Ay2`_kujOZ-A|N_QE&8?hp5Z z{&Icd{}|X>=9>8ySS|L)!PX@9Z-doh{|?x-#r_0XE%qnD<{0}^V719y5zEnkm*$+t zb*y%Cf13V#G;`1WK(4>Bz5c#W)9$q?&b2ubyavr*clb zV>I<#D?b6Nd9CD}KLgIW(Vq4G6s*mh#K~zX@Hm>sTD0V}HrSlT(|f-dMgJUaaawZx z1z5i_$6vzLbAA2_tmgjl{n7Ey($qcg&(%DCPy01|8JfDe%9E?LU61Cm4lTK^3pUsF z>CJU4{qwXk*WbbQD|7ulTs^t|0j!o>$AJGxQ#V&*%JWYDC-6!%b#s*`*A2j%(L6S$ znd^r1o78wC`b}x(y*Yhq`d6?%Wu7m<)pIWY26k+|k^T-=Gw-bBA7IC6v+m~ePnx>9 zij(UW;H_yMThfy2R$%knhCa`X7s1vw{FQ<)wiy35gQh*}`48AQ<$7L%t7jj-3|4a= zTW9A`%X;4gt7UC(fz@WwvhUvpzd>^z?HTLGur}kGt30+guv&6$hnv4K^q1?SzcuQp z?bc76`fZPGCz{6&H0!rrqi@!4XK?B@ig9KAMz`>>1z!oga?O+D67U^~XO3CNl4$Cw z=Tcy`)YCcD;=eRlE%jUmtX9@@S$OKHJ!8j!wOJo?m&djoxQx3zTYaZY+Z7mR>g;dtTN6JN`Pb^U3vbyytSw z+U|K4=RA+VHi71`2hH=mEB&4|-kp9gn%B(U^f~9_h*4e>N$t&g4J>k zjiHt`ZUk1#Iouem);x#JZ~0B&=2OnIDVloD$7bLSY5Ew$dZ_7Fe#?%)eh|%LBF(k$ zOMgI(_oqLQ=GqUY&)PR9PPz6i;EpMC+7eAYYu^g2mbDv0Eo?A|$wX8n$#w|?%eNyJJ0^f{#F zS>rx%W0dvV7fn6&+YhW()^8#__0#Tp)cmbEu@3;Z#y${DJ@q>Xtd{!iLoNry)s1QX za_hGrwnM?LQ=4m%r+$Zly}lfyt+jq9VDmVZmiir6W8Wo?r&+%f>2vKJL!7dHCl}oP z+f{Ju+gxr&{u5Bmw= z<7v+IXR;nie-d2X=ZDw!iC}Bzy0j&SPOzHyd+U4(SWREoEZ27uwy9v(q|NoqbAG0Q z_19)C$;FSxaL#A>a%FZu(oRc L&X~H{XMg-3Y(pb` literal 17556 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 { coarse_alloc_buf_host: D::Buffer, coarse_alloc_buf_dev: D::Buffer, - /* - k1_alloc_buf_host: D::Buffer, - k1_alloc_buf_dev: D::Buffer, - k2s_alloc_buf_host: D::Buffer, - k2s_alloc_buf_dev: D::Buffer, - k2f_alloc_buf_host: D::Buffer, - k2f_alloc_buf_dev: D::Buffer, - k3_alloc_buf_host: D::Buffer, - k3_alloc_buf_dev: D::Buffer, - tilegroup_buf: D::Buffer, - ptcl_buf: D::Buffer, - - k1_pipeline: D::Pipeline, - k1_ds: D::DescriptorSet, - k2s_pipeline: D::Pipeline, - k2s_ds: D::DescriptorSet, - k2f_pipeline: D::Pipeline, - k2f_ds: D::DescriptorSet, - k3_pipeline: D::Pipeline, - k3_ds: D::DescriptorSet, k4_pipeline: D::Pipeline, k4_ds: D::DescriptorSet, - */ + n_elements: usize, } @@ -213,10 +195,10 @@ impl Renderer { let coarse_alloc_buf_host = device.create_buffer(4, host)?; let coarse_alloc_buf_dev = device.create_buffer(4, dev)?; - let coarse_alloc_start = 256 * 64 * N_WG; + let coarse_alloc_start = WIDTH_IN_TILES * HEIGHT_IN_TILES * PTCL_INITIAL_ALLOC; device .write_buffer(&coarse_alloc_buf_host, &[ - coarse_alloc_start, + coarse_alloc_start as u32, ]) ?; let coarse_code = include_bytes!("../shader/coarse.spv"); @@ -227,72 +209,11 @@ impl Renderer { &[], )?; - /* - let tilegroup_buf = device.create_buffer(4 * 1024 * 1024, dev)?; - let ptcl_buf = device.create_buffer(48 * 1024 * 1024, dev)?; + // These will probably be combined with the ptcl buf, as they're all written by the + // same kernel now. let segment_buf = device.create_buffer(64 * 1024 * 1024, dev)?; let fill_seg_buf = device.create_buffer(64 * 1024 * 1024, dev)?; - let k1_alloc_buf_host = device.create_buffer(4, host)?; - let k1_alloc_buf_dev = device.create_buffer(4, dev)?; - let k1_alloc_start = WIDTH_IN_TILEGROUPS * HEIGHT_IN_TILEGROUPS * TILEGROUP_STRIDE; - device.write_buffer(&k1_alloc_buf_host, &[k1_alloc_start as u32])?; - let k1_code = include_bytes!("../shader/kernel1.spv"); - let k1_pipeline = device.create_simple_compute_pipeline(k1_code, 3, 0)?; - let k1_ds = device.create_descriptor_set( - &k1_pipeline, - &[&scene_dev, &tilegroup_buf, &k1_alloc_buf_dev], - &[], - )?; - - let k2s_alloc_buf_host = device.create_buffer(4, host)?; - let k2s_alloc_buf_dev = device.create_buffer(4, dev)?; - let k2s_alloc_start = WIDTH_IN_TILES * HEIGHT_IN_TILES * K2_PER_TILE_SIZE; - device.write_buffer(&k2s_alloc_buf_host, &[k2s_alloc_start as u32])?; - let k2s_code = include_bytes!("../shader/kernel2s.spv"); - let k2s_pipeline = device.create_simple_compute_pipeline(k2s_code, 4, 0)?; - let k2s_ds = device.create_descriptor_set( - &k2s_pipeline, - &[&scene_dev, &tilegroup_buf, &segment_buf, &k2s_alloc_buf_dev], - &[], - )?; - - let k2f_alloc_buf_host = device.create_buffer(4, host)?; - let k2f_alloc_buf_dev = device.create_buffer(4, dev)?; - let k2f_alloc_start = WIDTH_IN_TILES * HEIGHT_IN_TILES * K2_PER_TILE_SIZE; - device.write_buffer(&k2f_alloc_buf_host, &[k2f_alloc_start as u32])?; - let k2f_code = include_bytes!("../shader/kernel2f.spv"); - let k2f_pipeline = device.create_simple_compute_pipeline(k2f_code, 4, 0)?; - let k2f_ds = device.create_descriptor_set( - &k2f_pipeline, - &[ - &scene_dev, - &tilegroup_buf, - &fill_seg_buf, - &k2f_alloc_buf_dev, - ], - &[], - )?; - - let k3_alloc_buf_host = device.create_buffer(4, host)?; - let k3_alloc_buf_dev = device.create_buffer(4, dev)?; - let k3_alloc_start = WIDTH_IN_TILES * HEIGHT_IN_TILES * PTCL_INITIAL_ALLOC; - device.write_buffer(&k3_alloc_buf_host, &[k3_alloc_start as u32])?; - let k3_code = include_bytes!("../shader/kernel3.spv"); - let k3_pipeline = device.create_simple_compute_pipeline(k3_code, 6, 0)?; - let k3_ds = device.create_descriptor_set( - &k3_pipeline, - &[ - &scene_dev, - &tilegroup_buf, - &segment_buf, - &fill_seg_buf, - &ptcl_buf, - &k3_alloc_buf_dev, - ], - &[], - )?; - let k4_code = include_bytes!("../shader/kernel4.spv"); let k4_pipeline = device.create_simple_compute_pipeline(k4_code, 3, 1)?; let k4_ds = device.create_descriptor_set( @@ -300,7 +221,6 @@ impl Renderer { &[&ptcl_buf, &segment_buf, &fill_seg_buf], &[&image_dev], )?; - */ Ok(Renderer { scene_buf, @@ -312,6 +232,8 @@ impl Renderer { bin_ds, coarse_pipeline, coarse_ds, + k4_pipeline, + k4_ds, state_buf, anno_buf, bin_buf, @@ -339,7 +261,7 @@ impl Renderer { cmd_buf.dispatch( &self.el_pipeline, &self.el_ds, - ((self.n_elements / 128) as u32, 1, 1), + (((self.n_elements + 127) / 128) as u32, 1, 1), ); cmd_buf.write_timestamp(&query_pool, 1); cmd_buf.memory_barrier(); @@ -357,6 +279,13 @@ impl Renderer { ); cmd_buf.write_timestamp(&query_pool, 3); cmd_buf.memory_barrier(); + cmd_buf.dispatch( + &self.k4_pipeline, + &self.k4_ds, + ((WIDTH / TILE_W) as u32, (HEIGHT / TILE_H) as u32, 1), + ); + cmd_buf.write_timestamp(&query_pool, 4); + cmd_buf.memory_barrier(); cmd_buf.image_barrier(&self.image_dev, ImageLayout::General, ImageLayout::BlitSrc); } } From 1240da3870736b6f24e7772c43ea0f9177992d34 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Fri, 15 May 2020 15:20:25 -0700 Subject: [PATCH 09/32] Delete old-style kernels and buffers Pave the way for the coarse raster pass to write to the ptcl buffer. --- piet-gpu-types/src/fill_seg.rs | 37 -------- piet-gpu-types/src/lib.rs | 2 - piet-gpu-types/src/main.rs | 2 - piet-gpu-types/src/ptcl.rs | 18 +++- piet-gpu-types/src/segment.rs | 32 ------- piet-gpu/shader/build.ninja | 12 +-- piet-gpu/shader/fill_seg.h | 130 ------------------------- piet-gpu/shader/kernel1.comp | 161 ------------------------------- piet-gpu/shader/kernel1.spv | Bin 18416 -> 0 bytes piet-gpu/shader/kernel2f.comp | 167 --------------------------------- piet-gpu/shader/kernel2f.spv | Bin 21108 -> 0 bytes piet-gpu/shader/kernel2s.comp | 137 --------------------------- piet-gpu/shader/kernel2s.spv | Bin 17688 -> 0 bytes piet-gpu/shader/kernel3.comp | 135 -------------------------- piet-gpu/shader/kernel3.spv | Bin 26532 -> 0 bytes piet-gpu/shader/kernel4.comp | 22 +---- piet-gpu/shader/kernel4.spv | Bin 20172 -> 17856 bytes piet-gpu/shader/ptcl.h | 66 +++++++++++++ piet-gpu/shader/segment.h | 126 ------------------------- piet-gpu/src/lib.rs | 9 +- 20 files changed, 91 insertions(+), 965 deletions(-) delete mode 100644 piet-gpu-types/src/fill_seg.rs delete mode 100644 piet-gpu-types/src/segment.rs delete mode 100644 piet-gpu/shader/fill_seg.h delete mode 100644 piet-gpu/shader/kernel1.comp delete mode 100644 piet-gpu/shader/kernel1.spv delete mode 100644 piet-gpu/shader/kernel2f.comp delete mode 100644 piet-gpu/shader/kernel2f.spv delete mode 100644 piet-gpu/shader/kernel2s.comp delete mode 100644 piet-gpu/shader/kernel2s.spv delete mode 100644 piet-gpu/shader/kernel3.comp delete mode 100644 piet-gpu/shader/kernel3.spv delete mode 100644 piet-gpu/shader/segment.h diff --git a/piet-gpu-types/src/fill_seg.rs b/piet-gpu-types/src/fill_seg.rs deleted file mode 100644 index 2242a84..0000000 --- a/piet-gpu-types/src/fill_seg.rs +++ /dev/null @@ -1,37 +0,0 @@ -use piet_gpu_derive::piet_gpu; - -// Structures representing segments for fill items. - -// There is some cut'n'paste here from stroke segments, which can be -// traced to the fact that buffers in GLSL are basically global. -// Maybe there's a way to address that, but in the meantime living -// with the duplication is easiest. - -piet_gpu! { - #[gpu_write] - mod fill_seg { - struct FillTileHeader { - n: u32, - items: Ref, - } - - struct FillItemHeader { - backdrop: i32, - segments: Ref, - } - - // TODO: strongly consider using f16. If so, these would be - // relative to the tile. We're doing f32 for now to minimize - // divergence from piet-metal originals. - struct FillSegment { - start: [f32; 2], - end: [f32; 2], - } - - struct FillSegChunk { - n: u32, - next: Ref, - // Segments follow (could represent this as a variable sized array). - } - } -} diff --git a/piet-gpu-types/src/lib.rs b/piet-gpu-types/src/lib.rs index 29ed806..75a7731 100644 --- a/piet-gpu-types/src/lib.rs +++ b/piet-gpu-types/src/lib.rs @@ -3,10 +3,8 @@ pub mod annotated; pub mod bins; pub mod encoder; -pub mod fill_seg; pub mod ptcl; pub mod scene; -pub mod segment; pub mod state; pub mod test; pub mod tilegroup; diff --git a/piet-gpu-types/src/main.rs b/piet-gpu-types/src/main.rs index 41ae021..9c40051 100644 --- a/piet-gpu-types/src/main.rs +++ b/piet-gpu-types/src/main.rs @@ -9,8 +9,6 @@ fn main() { "annotated" => print!("{}", piet_gpu_types::annotated::gen_gpu_annotated()), "bins" => print!("{}", piet_gpu_types::bins::gen_gpu_bins()), "tilegroup" => print!("{}", piet_gpu_types::tilegroup::gen_gpu_tilegroup()), - "segment" => print!("{}", piet_gpu_types::segment::gen_gpu_segment()), - "fill_seg" => print!("{}", piet_gpu_types::fill_seg::gen_gpu_fill_seg()), "ptcl" => print!("{}", piet_gpu_types::ptcl::gen_gpu_ptcl()), "test" => print!("{}", piet_gpu_types::test::gen_gpu_test()), _ => println!("Oops, unknown module name"), diff --git a/piet-gpu-types/src/ptcl.rs b/piet-gpu-types/src/ptcl.rs index 911f2c8..534cf85 100644 --- a/piet-gpu-types/src/ptcl.rs +++ b/piet-gpu-types/src/ptcl.rs @@ -13,13 +13,13 @@ piet_gpu! { end: [f32; 2], } struct CmdStroke { - // Should be Ref if we had cross-module references. + // Should be Ref seg_ref: u32, half_width: f32, rgba_color: u32, } struct CmdFill { - // Should be Ref if we had cross-module references. + // Should be Ref seg_ref: u32, backdrop: i32, rgba_color: u32, @@ -51,5 +51,19 @@ piet_gpu! { Jump(CmdJump), Bail, } + + // TODO: strongly consider using f16. If so, these would be + // relative to the tile. We're doing f32 for now to minimize + // divergence from piet-metal originals. + struct Segment { + start: [f32; 2], + end: [f32; 2], + } + + struct SegChunk { + n: u32, + next: Ref, + // Segments follow (could represent this as a variable sized array). + } } } diff --git a/piet-gpu-types/src/segment.rs b/piet-gpu-types/src/segment.rs deleted file mode 100644 index 0b18ab8..0000000 --- a/piet-gpu-types/src/segment.rs +++ /dev/null @@ -1,32 +0,0 @@ -use piet_gpu_derive::piet_gpu; - -// Structures representing segments for stroke/fill items. - -piet_gpu! { - #[gpu_write] - mod segment { - struct TileHeader { - n: u32, - items: Ref, - } - - // Note: this is only suitable for strokes, fills require backdrop. - struct ItemHeader { - segments: Ref, - } - - // TODO: strongly consider using f16. If so, these would be - // relative to the tile. We're doing f32 for now to minimize - // divergence from piet-metal originals. - struct Segment { - start: [f32; 2], - end: [f32; 2], - } - - struct SegChunk { - n: u32, - next: Ref, - // Segments follow (could represent this as a variable sized array). - } - } -} diff --git a/piet-gpu/shader/build.ninja b/piet-gpu/shader/build.ninja index 6a57917..3b6b963 100644 --- a/piet-gpu/shader/build.ninja +++ b/piet-gpu/shader/build.ninja @@ -9,19 +9,11 @@ rule glsl build image.spv: glsl image.comp | scene.h -build kernel1.spv: glsl kernel1.comp | scene.h tilegroup.h setup.h - -build kernel2s.spv: glsl kernel2s.comp | scene.h tilegroup.h segment.h setup.h - -build kernel2f.spv: glsl kernel2f.comp | scene.h tilegroup.h fill_seg.h setup.h - -build kernel3.spv: glsl kernel3.comp | scene.h tilegroup.h segment.h fill_seg.h ptcl.h setup.h - -build kernel4.spv: glsl kernel4.comp | ptcl.h segment.h fill_seg.h setup.h - build elements.spv: glsl elements.comp | scene.h state.h annotated.h build binning.spv: glsl binning.comp | annotated.h bins.h setup.h build coarse.spv: glsl coarse.comp | annotated.h bins.h ptcl.h setup.h + +build kernel4.spv: glsl kernel4.comp | ptcl.h setup.h diff --git a/piet-gpu/shader/fill_seg.h b/piet-gpu/shader/fill_seg.h deleted file mode 100644 index abe199f..0000000 --- a/piet-gpu/shader/fill_seg.h +++ /dev/null @@ -1,130 +0,0 @@ -// Code auto-generated by piet-gpu-derive - -struct FillTileHeaderRef { - uint offset; -}; - -struct FillItemHeaderRef { - uint offset; -}; - -struct FillSegmentRef { - uint offset; -}; - -struct FillSegChunkRef { - uint offset; -}; - -struct FillTileHeader { - uint n; - FillItemHeaderRef items; -}; - -#define FillTileHeader_size 8 - -FillTileHeaderRef FillTileHeader_index(FillTileHeaderRef ref, uint index) { - return FillTileHeaderRef(ref.offset + index * FillTileHeader_size); -} - -struct FillItemHeader { - int backdrop; - FillSegChunkRef segments; -}; - -#define FillItemHeader_size 8 - -FillItemHeaderRef FillItemHeader_index(FillItemHeaderRef ref, uint index) { - return FillItemHeaderRef(ref.offset + index * FillItemHeader_size); -} - -struct FillSegment { - vec2 start; - vec2 end; -}; - -#define FillSegment_size 16 - -FillSegmentRef FillSegment_index(FillSegmentRef ref, uint index) { - return FillSegmentRef(ref.offset + index * FillSegment_size); -} - -struct FillSegChunk { - uint n; - FillSegChunkRef next; -}; - -#define FillSegChunk_size 8 - -FillSegChunkRef FillSegChunk_index(FillSegChunkRef ref, uint index) { - return FillSegChunkRef(ref.offset + index * FillSegChunk_size); -} - -FillTileHeader FillTileHeader_read(FillTileHeaderRef ref) { - uint ix = ref.offset >> 2; - uint raw0 = fill_seg[ix + 0]; - uint raw1 = fill_seg[ix + 1]; - FillTileHeader s; - s.n = raw0; - s.items = FillItemHeaderRef(raw1); - return s; -} - -void FillTileHeader_write(FillTileHeaderRef ref, FillTileHeader s) { - uint ix = ref.offset >> 2; - fill_seg[ix + 0] = s.n; - fill_seg[ix + 1] = s.items.offset; -} - -FillItemHeader FillItemHeader_read(FillItemHeaderRef ref) { - uint ix = ref.offset >> 2; - uint raw0 = fill_seg[ix + 0]; - uint raw1 = fill_seg[ix + 1]; - FillItemHeader s; - s.backdrop = int(raw0); - s.segments = FillSegChunkRef(raw1); - return s; -} - -void FillItemHeader_write(FillItemHeaderRef ref, FillItemHeader s) { - uint ix = ref.offset >> 2; - fill_seg[ix + 0] = uint(s.backdrop); - fill_seg[ix + 1] = s.segments.offset; -} - -FillSegment FillSegment_read(FillSegmentRef ref) { - uint ix = ref.offset >> 2; - uint raw0 = fill_seg[ix + 0]; - uint raw1 = fill_seg[ix + 1]; - uint raw2 = fill_seg[ix + 2]; - uint raw3 = fill_seg[ix + 3]; - FillSegment s; - s.start = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); - s.end = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); - return s; -} - -void FillSegment_write(FillSegmentRef ref, FillSegment s) { - uint ix = ref.offset >> 2; - fill_seg[ix + 0] = floatBitsToUint(s.start.x); - fill_seg[ix + 1] = floatBitsToUint(s.start.y); - fill_seg[ix + 2] = floatBitsToUint(s.end.x); - fill_seg[ix + 3] = floatBitsToUint(s.end.y); -} - -FillSegChunk FillSegChunk_read(FillSegChunkRef ref) { - uint ix = ref.offset >> 2; - uint raw0 = fill_seg[ix + 0]; - uint raw1 = fill_seg[ix + 1]; - FillSegChunk s; - s.n = raw0; - s.next = FillSegChunkRef(raw1); - return s; -} - -void FillSegChunk_write(FillSegChunkRef ref, FillSegChunk s) { - uint ix = ref.offset >> 2; - fill_seg[ix + 0] = s.n; - fill_seg[ix + 1] = s.next.offset; -} - diff --git a/piet-gpu/shader/kernel1.comp b/piet-gpu/shader/kernel1.comp deleted file mode 100644 index 6b76c53..0000000 --- a/piet-gpu/shader/kernel1.comp +++ /dev/null @@ -1,161 +0,0 @@ -// This is "kernel 1" in a 4-kernel pipeline. It traverses the scene graph -// and outputs "instances" (references to item + translation) for each item -// that intersects the tilegroup. -// -// This implementation is simplistic and leaves a lot of performance on the -// table. A fancier implementation would use threadgroup shared memory or -// subgroups (or possibly both) to parallelize the reading of the input and -// the computation of tilegroup intersection. -// -// In addition, there are some features currently missing, such as support -// for clipping. - -#version 450 -#extension GL_GOOGLE_include_directive : enable - -// It's possible we should lay this out with x and do our own math. -layout(local_size_x = 1, local_size_y = 32) in; - -layout(set = 0, binding = 0) readonly buffer SceneBuf { - uint[] scene; -}; - -layout(set = 0, binding = 1) buffer TilegroupBuf { - uint[] tilegroup; -}; - -layout(set = 0, binding = 2) buffer AllocBuf { - uint alloc; -}; - -#include "scene.h" -#include "tilegroup.h" - -#include "setup.h" - -#define MAX_STACK 8 - -struct StackElement { - PietItemRef group; - uint index; - vec2 offset; -}; - -void main() { - StackElement stack[MAX_STACK]; - uint stack_ix = 0; - uint tilegroup_ix = gl_GlobalInvocationID.y * WIDTH_IN_TILEGROUPS + gl_GlobalInvocationID.x; - TileGroupRef tg_ref = TileGroupRef(tilegroup_ix * TILEGROUP_STRIDE); - uint tg_limit = tg_ref.offset + TILEGROUP_INITIAL_ALLOC - 2 * TileGroup_size; - - // State for stroke references. - TileGroupRef stroke_start = TileGroupRef(tg_ref.offset + TILEGROUP_STROKE_START); - ChunkRef stroke_chunk_start = ChunkRef(stroke_start.offset + 4); - InstanceRef stroke_ref = InstanceRef(stroke_chunk_start.offset + Chunk_size); - uint stroke_limit = stroke_start.offset + TILEGROUP_INITIAL_STROKE_ALLOC - Instance_size; - uint stroke_chunk_n = 0; - uint stroke_n = 0; - - // State for fill references. All this is a bit cut'n'paste, but making a - // proper abstraction isn't easy. - TileGroupRef fill_start = TileGroupRef(tg_ref.offset + TILEGROUP_FILL_START); - ChunkRef fill_chunk_start = ChunkRef(fill_start.offset + 4); - InstanceRef fill_ref = InstanceRef(fill_chunk_start.offset + Chunk_size); - uint fill_limit = fill_start.offset + TILEGROUP_INITIAL_FILL_ALLOC - Instance_size; - uint fill_chunk_n = 0; - uint fill_n = 0; - - vec2 xy0 = vec2(gl_GlobalInvocationID.xy) * vec2(TILEGROUP_WIDTH_PX, TILEGROUP_HEIGHT_PX); - PietItemRef root = PietItemRef(0); - SimpleGroup group = PietItem_Group_read(root); - StackElement tos = StackElement(root, 0, group.offset.xy); - - while (true) { - if (tos.index < group.n_items) { - Bbox bbox = Bbox_read(Bbox_index(group.bboxes, tos.index)); - vec4 bb = vec4(bbox.bbox) + tos.offset.xyxy; - bool hit = max(bb.x, xy0.x) < min(bb.z, xy0.x + float(TILEGROUP_WIDTH_PX)) - && max(bb.y, xy0.y) < min(bb.w, xy0.y + float(TILEGROUP_HEIGHT_PX)); - bool is_group = false; - uint tag; - if (hit) { - PietItemRef item_ref = PietItem_index(group.items, tos.index); - tag = PietItem_tag(item_ref); - is_group = tag == PietItem_Group; - } - if (hit && !is_group) { - PietItemRef item_ref = PietItem_index(group.items, tos.index); - Instance ins = Instance(item_ref.offset, tos.offset); - if (tg_ref.offset > tg_limit) { - // Allocation exceeded; do atomic bump alloc. - uint new_tg = atomicAdd(alloc, TILEGROUP_INITIAL_ALLOC); - Jump jump = Jump(TileGroupRef(new_tg)); - TileGroup_Jump_write(tg_ref, jump); - tg_ref = TileGroupRef(new_tg); - tg_limit = tg_ref.offset + TILEGROUP_INITIAL_ALLOC - 2 * TileGroup_size; - } - TileGroup_Instance_write(tg_ref, ins); - tg_ref.offset += TileGroup_size; - if (tag == PietItem_Poly) { - if (stroke_ref.offset > stroke_limit) { - uint new_stroke = atomicAdd(alloc, TILEGROUP_STROKE_ALLOC); - Chunk_write(stroke_chunk_start, Chunk(stroke_chunk_n, ChunkRef(new_stroke))); - stroke_chunk_start = ChunkRef(new_stroke); - stroke_ref = InstanceRef(new_stroke + Chunk_size); - stroke_n += stroke_chunk_n; - stroke_chunk_n = 0; - stroke_limit = new_stroke + TILEGROUP_STROKE_ALLOC - Instance_size; - } - Instance_write(stroke_ref, ins); - stroke_chunk_n++; - stroke_ref.offset += Instance_size; - } else if (tag == PietItem_Fill) { - if (fill_ref.offset > fill_limit) { - uint new_fill = atomicAdd(alloc, TILEGROUP_FILL_ALLOC); - Chunk_write(fill_chunk_start, Chunk(fill_chunk_n, ChunkRef(new_fill))); - fill_chunk_start = ChunkRef(new_fill); - fill_ref = InstanceRef(new_fill + Chunk_size); - fill_n += fill_chunk_n; - fill_chunk_n = 0; - fill_limit = new_fill + TILEGROUP_FILL_ALLOC - Instance_size; - } - Instance_write(fill_ref, ins); - fill_chunk_n++; - fill_ref.offset += Instance_size; - - } - } - if (is_group) { - PietItemRef item_ref = PietItem_index(group.items, tos.index); - tos.index++; - if (tos.index < group.n_items) { - stack[stack_ix++] = tos; - } - group = PietItem_Group_read(item_ref); - tos = StackElement(item_ref, 0, tos.offset + group.offset.xy); - } else { - tos.index++; - } - } else { - // processed all items in this group; pop the stack - if (stack_ix == 0) { - break; - } - tos = stack[--stack_ix]; - group = PietItem_Group_read(tos.group); - } - } - TileGroup_End_write(tg_ref); - - stroke_n += stroke_chunk_n; - if (stroke_n > 0) { - Chunk_write(stroke_chunk_start, Chunk(stroke_chunk_n, ChunkRef(0))); - } - tilegroup[stroke_start.offset >> 2] = stroke_n; - - fill_n += fill_chunk_n; - if (fill_n > 0) { - Chunk_write(fill_chunk_start, Chunk(fill_chunk_n, ChunkRef(0))); - } - tilegroup[fill_start.offset >> 2] = fill_n; -} diff --git a/piet-gpu/shader/kernel1.spv b/piet-gpu/shader/kernel1.spv deleted file mode 100644 index 358151d2c1cc1b4fad315b3039ac3abce100f60b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18416 zcmai)2b^71wZ>1#ObESq2oq{3q4%1E5(0q)LKm1Dl1Uht%*138f*^!02neWv3W@=x z3Me3mibxSe1w|B5n)vM9_W(h}_y3=JzL_)E_kJF-+28lAwbx#Ioqf*T_a?)a+IZ=r z7*>qrzn?Evl-DZ7QYb~STrr~TCr+L=d7JtDGq>4k=k0V@u^3+Z>9Z0(?euN5In%p) zH5`axWpuSwX!>MzK7xnc#l zKAqjYGrJazncqKe!HoW~)u^!xw%>lw1>5h5Pdhm;M?a5P#$Fy=4k>Nz)SxJ)c6aqp z>hGG9`P94y=Ubsz9Ui}-b4F!!%e<>HCiAWa9-OzWSQ~D(bmbc5UME*tJM~=;UaoOo z*YuhH*AlV2zIExF^RyQmfT#9#_x8IOF7L3K*Wh~EiqY`WkKVg2_gZv@F_$ei0hjj9 z`s9gRF0`C`WBi(Pk0>?+PwSpDx2J33yuJl<%M6@f-D`0E_F@dY=3C~e=GEKT&E1$k zpt0SGrqW!Ge(pnS)ir-mb7v3Eu|lyGyf(*>mDF_d99++qjA^cC`C?me?rvxQ^x3%u z(FO+M=Y7~_VC)LTj`gu+IfhLCZy_AN!@&6VVrTHA-ueC0duMc6Nb9KPHMqw1Vpn+l zw2k0DH7{$%tONT%-C7QgU$xi^p7EWh&tphd88xe{9_dO>f;r`8a1C;=!5l^u-2J|?Zs-v zfpC3>uBXbPyw%lOl*U%Lo`60q%(d)KtgPz*aC2Sl#X;cxPFc`<8s!0&xu|*7^F0_| z`q7)O+-rtoJ9`H-?@MpjBHnJ+ZS`UbeK~IEA~I!t6{Y6ws8&+<8F&XCO1!M&FmQ7n zYZOO;2bSKMn>%#U0Xu80PaJROuC_v1vuU-tjvSb4t>XA0bN%}r`v2zn54WkD^Vr&) z#|_N6VsX-tIS=Ta`5%^sdl~OU`qax>b@QfnKl{R-H~sYTv$P%M1Mq1xx_Y}NEEw1a zYF_iz>D@6LN?Tj;*CBj(@dmt^--zNJu=i}XZ~KAuH+4)! ze{5ja`K}_DcqKM`GtTg0?E&0YYy+;(zikWOqlND|gtr$H;Jh_woYrY{=NeJ$hc=MQ zfIhN3>&))yoY>QM^7Niby$kzhOz-dR>z(8?@4Y_EakHCmQ1A7$ChzH<(_KBMBZ}k7 z`R4VV*2Mv6`n*bCx;SZ2pHV*QmDM;i25oM!GRIQ}wIT6)2lZwgPHD#I8`R5lo7!Bo zS=~K7$)uf=)WT>(vS`LQGqy5|rnWd*m4)@YFxo%{`nDApMK5z2UR(@cbjEh2?Mm>x zzP_p+ZN=5_a-USS{V06!K94A_flu?k9njM?r>nOd=lfFKD`TT6=R6h7Gr{1ycX)9f zPW^rJ%Y5zt51y^siaX(h^S2duxA1$1@ZrS+<#^XUym$nB3R}RMzYOl4-+$?-NIc*B^>}jQu|>=H ze&_T%+WIu}%U-O zYNJtHhurzJ`F$>PjRKpe+M3ulr0<{^(_ZeH9XFc3gJ#T)%3i%`+l)RjHwPP2Z4~O5 z%H|r>jbn^0=(XuDA4_ix*D3d)qFwI%et*dvyMT?QHVR+An>20a*jDUT+10J#?)0w1 zaoUWx4}Ie83pSpbwsG{@#N+AZ#+^W~pPF&^qc^VOwdsEled106>#t^>p5L{}jduvW zc6H-TrZ=ABwCQs=y*}e;`W!*8kGgA7JEo?YhxI$5rfmgxe>7-ba`Tu;8(HI?+d3+) z|3aE`ZNR$DpifPF-sR?ZCcQptdsh6cO3OTF!@cxhOs~JOV!sePjHdk(de`foP2QJ* z-3Qt~MX%lcEx)Vcqbq)Y#XIN||7oyu>i-PAmq-*(zLfRp?Ua>`Mm89H;(gaKLF03Qg^)Ecb%H{i50Hb zajHgcX!1N-PNLzb$w?}FzMKHg{~@rk%vrm6U8bEB!Y?oR;_+Lv=Q=Q40oO-;YsUDD zUj_DlkHW|Emzr@q%GOp~i{`qutAN$9_<~bAYyw||ZL*4cI96ttj zy<0Pj_wY5i``Ks7ei!P1gm?2Y$G@Yr)0@LG^b;6o+;G2hg!`QPT_Rk6ze|K`_j^RR z?+(8|g!|6(J43kR{k~A=eov@#zaNAf-|q(D#$Qh4ITe#=-*qzVwdYyY-4|lw0_BwyFg?nC)-FTkY!~G2KydJLIGkf?1xaaS1@2BVQ zaP6MI!?k<<4lh4rYTWpqzhj>S_v{_+=ay&haQBC2?{MQCQsc(+>>a!DJa>m1&vSRU z@jQ2j8_#ohxb^hhUFV*;!=2AFceu~AXYO#vd*-fl&)nhG$1``h`FiFKH($@(;o3cO zhimuDE#HUtlh3~IkMX<_e5W5o^F8moV{hIMzPlz>`UUvN4~DDxIh1jSz#V6-jGGKs zUtEnl6zo`a=k`6L7XK+=wajxkSk2FsspK+_*hhfX9q)TdZVuMxv7l74`>F0$! zYd9UO)*ycN#Ui+RVx9q3tJnGiaCKuYq?ae=Szxt#%n!oV6Z34a+Dzs(PiuV+T-}&{ zZpjn#TySfi=b@=5=J{Z?daW;jtEbj}{;4^KpM9zIMHRLN`-3l``OHq?ZQ%Lq(n?eJ zvYU%<2mOa3+Arjpk$(iNb_LCO{0vnSUrB!z&3Qb3eGIIo&CgEx?!*~`?c?Ab>D9Gg zO|O=*p8z{{JZ~O9t3L_W$1`$+re6pBr_i)b#qS#W>uHX4?b?ieU8TRx`R4|(=OFtx ziSZfw8);tJKU>-D-@$$p{ikXAIZmAWb~D)6p5x8OcjV{b>hbwJ*t{DxpIhMS_fU%^ z^taL+r~VfC-cIl3{Mv4#-Ojt)`NhWe{O|>^u@{1!-*@|6aCO&xC%rtm+yidS_ls!i z`rl12-<_2h>r3Dj=#8UIpL^-my^m{SyAQk`y}I^0=+&Il?@SMZ)f!-P^!w9S;Oc$w z`{^H|sX4}b{Z+7eW?dal4G6#^QYX`j@O>?-vT>_-vu+rlW_I;JOyr@)JPlU6jFx%71LjY$Db1MrJc$1i`tQ=rOCR^tGxT2WDQ(}Qskx`b$>mwF`Q{wp z+~0?*$L9xN^R4?l2UkySKLqoqtcl~bXZ(-A&hb1gHTf}EJw7jhTj%%*n)+k2|ufaYO@%LTx8@Rgu zzoM7>>>BI0;MM7kqfMXJ>DA3!pFe=rwEdo5?sIMIKZ3WVSJ(bKdNs#t{}Wj4cKnyn z|Cy$i&m3!{W}UNs`_$|&U~3lsSFmGlt!nx=u-YwE{Qm|!M%_BULGNXqwfzrG%{q%y z=f8tn>-@HUG}rYOy*zdP2e`G)@1m*e{|>!8b^a&VIvYouK5x>iC+5GvY7P8SKhiAJ z_3@oI4DP)3civKH>QnLYH^t##$EiEdd-$tmo;L7dE%UUasb}61;A!~WS=DuExIXIE zbs2gu>#A)eP0hNBoog}n6~X?7(E$IBXU9FU5?tN)g8SfT<}pvd7ivomtAN#VhFcY^ zCLhnZ)xc_w^*f~8vE#6<0d|er{5~ny=D4-M`fKyMrQG{OSFFwdj;D8ww&b!7xSq?p za5ZB%k398V58PVc_0iOGPTK&i=Gt>69tBsgf6q|!*+^~ev&If^*64R)xi*`IP;7J&tOdrd=q;cH1+Up!KdJpeY+i8A9d&2f!@oxv~5pQb1t!S zEylhRxOLy|jHceYZ~eVNTXNV1+)EDOyTbKx-})VGH?W$|tG})94t9*XWAxb*d{TA) z{QW|1ZT;;+|J=*H!Bg2*E<(}5k86cez=-`j@PCpc3k~A zn}=@R89N`WR)5a=;p%zLyx(f>zuZfE^Yb}d2sUTuTL6}8^Eq1t&U1D;Sf1LR0roj_ zjJB-z17Pz`j%UKv@|uTrK%(Q`7$%@F%s? zo7=VYpMqbjMzcn8eZ1G1&))IY^#-tY)c1ONd49Y4G}zy+)U~^2wT%4?*s;!iKYllY z&82>}|16q%e&1RGHcspBTQ{L=JC|4;#JU;mSoQqo^*OL}`X113PR^(9`rND5)BTxy zbql!le%^|vo*Lf|t6>6L?~}GiXlkxQoH&ny%_E=7 zkHgg-t9<-i{uVgr-zUIwb9$0q|9o~l1@?1UA8pnubJ}Mge;a%pb2jiz9#5mGKT6Gf zS9}Momgi6HyKvX+^S2oOJ@~uy4VwPuq^4iuJ_Gi%Dg0S@{d4;Ja5eoLuT3rfKLD$x z&dZ$X~V6}RkUxB;k)cI9->a4#xsp~CWJ+Zb!JB7O3l4%VkZOP(i#)x&3i9h<*z>;kLJq~+Z=3+y;; zp5e@AHckCK<`gGaKQH_Y_F9EzuD(ZBt+2VSMl;tn=#%TIV0{|2e(}=gVo$K?gi&mi~k3~YFXSetRp zT^`#7;CkGP;A(z1N!*LUiK{(hKLplhTx%eY?ZaT_dc1n~Tn<*djCL0HW=;AlXzGvQ zBmYRH>618Df{l|gSAo?s=4!B7eauJU=99TU23D)j{c*T@;#>n(tH-$(Zk)`09at@6 zJ^@y9%v5&BTJ)c!xeoI(ratZi{|@MSntP)`vu1MrUF+KPH_)`ZSH;<@qrscdyf&h_ zS4YurOmnYpN}s)XBe=d-Z*JkY)%acDyDOf(`dPSf8?@YmC1CaJ)tkU-3u&(7MtZgQ ze-5mcz502uT79qH0?(e(p0T%rwHepk<+0rkzLA!=cYxI{q-C$(2~J$?8T$pWHse|Y zd2IK9oy+^|-}8KtrWX5`z>bOiUa*>%wcN8uz-slq@J)E`nf8o*6s*nqxi)!hkAv%RpMa~? z_rkZ}iK{(hp9E_&t~HX!_HA%I?swp7Pt&r;z6(xV?HT($ur}jbYk6$n2fK#U_6K0= z-=OI)*C+lz1e;qv$DRkPIeumOAJH8D9L@1^eKP*XU~|Z4_6uM&$B(4{3C;1jZ*qN- z-%r7<_x@*S>baLMgVkQ5InM_4uh7)Z$^9hv%p+Zei^eAxIWi#xLV#DZQypAb3I2h2ghzmKccevIpF?V8m^Z4M}jlIah+dHzs$c3 zxIX`~aJ9T&oPRl5<~Og*zkFrO{42oKGXIL;%x_%hSJSWlox*#6AkAw(n)iNR`u!_B zf&KuR_kJRMK5tecPW|4m40lX}mi4WIrk;DhDp)P|-WY1}Uk$95d%rqZt$FW9lz->H z2Hbp{$Cj~cqHD|g*8*q##&P{>`n9fq3URz9(_H@~`a>&x2>oF+*FTj$>tCBV_4Tg< zcT9tpoYqBC&-&K`t7ZMhP>cWiV707&1F%|s?~j6;Pko*Z(bTi{4sh0P4A-uvU*;MO zuFtg*TrGQl8Rpm+?p*Hu`h1(9Ys-9_f-|3SoKH=^)>%AqsHTG6$ z>bXx_gVpjb-kCYZ!qtsw{&I8M729@T*Qw35$n)F7_Tas+IYwJ+{f@`xbqp=_JGR0; ztH;r--}~sTm(TJZ#7X`1*|XxQ-wtqNG-#>cj%ezs-%en)di{2Wr+(U9k6LpNHUEvy zF7VdayP~P5e!GFyQonu3V|Tc^G0k6YZsW1-1$Ldvs}1uM=pg q--%#r)=8h5?88{~1}!z&7fn4i83$IY*JM0AHPLR4YQEEx%l`rb3nCW) diff --git a/piet-gpu/shader/kernel2f.comp b/piet-gpu/shader/kernel2f.comp deleted file mode 100644 index 7ea93bd..0000000 --- a/piet-gpu/shader/kernel2f.comp +++ /dev/null @@ -1,167 +0,0 @@ -// This is "kernel 2" (fill) in a 4-kernel pipeline. It processes the fill -// (polyline) items in the scene and generates a list of segments for each, for -// each tile. - -#version 450 -#extension GL_GOOGLE_include_directive : enable - -layout(local_size_x = 32) in; - -layout(set = 0, binding = 0) readonly buffer SceneBuf { - uint[] scene; -}; - -layout(set = 0, binding = 1) buffer TilegroupBuf { - uint[] tilegroup; -}; - -layout(set = 0, binding = 2) buffer FillSegBuf { - uint[] fill_seg; -}; - -layout(set = 0, binding = 3) buffer AllocBuf { - uint alloc; -}; - -#include "scene.h" -#include "tilegroup.h" -#include "fill_seg.h" - -#include "setup.h" - -// Ensure that there is space to encode a segment. -void alloc_chunk(inout uint chunk_n_segs, inout FillSegChunkRef seg_chunk_ref, - inout FillSegChunkRef first_seg_chunk, inout uint seg_limit) -{ - if (chunk_n_segs == 0) { - if (seg_chunk_ref.offset + 40 > seg_limit) { - seg_chunk_ref.offset = atomicAdd(alloc, SEG_CHUNK_ALLOC); - seg_limit = seg_chunk_ref.offset + SEG_CHUNK_ALLOC - FillSegment_size; - } - first_seg_chunk = seg_chunk_ref; - } else if (seg_chunk_ref.offset + FillSegChunk_size + FillSegment_size * chunk_n_segs > seg_limit) { - uint new_chunk_ref = atomicAdd(alloc, SEG_CHUNK_ALLOC); - seg_limit = new_chunk_ref + SEG_CHUNK_ALLOC - FillSegment_size; - FillSegChunk_write(seg_chunk_ref, FillSegChunk(chunk_n_segs, FillSegChunkRef(new_chunk_ref))); - seg_chunk_ref.offset = new_chunk_ref; - chunk_n_segs = 0; - } - -} - -void main() { - uint tile_ix = gl_GlobalInvocationID.y * WIDTH_IN_TILES + gl_GlobalInvocationID.x; - uint tilegroup_ix = gl_GlobalInvocationID.y * WIDTH_IN_TILEGROUPS - + (gl_GlobalInvocationID.x / TILEGROUP_WIDTH_TILES); - vec2 xy0 = vec2(gl_GlobalInvocationID.xy) * vec2(TILE_WIDTH_PX, TILE_HEIGHT_PX); - TileGroupRef fill_start = TileGroupRef(tilegroup_ix * TILEGROUP_STRIDE + TILEGROUP_FILL_START); - uint fill_n = tilegroup[fill_start.offset >> 2]; - - FillTileHeaderRef tile_header_ref = FillTileHeaderRef(tile_ix * FillTileHeader_size); - if (fill_n > 0) { - ChunkRef chunk_ref = ChunkRef(fill_start.offset + 4); - Chunk chunk = Chunk_read(chunk_ref); - InstanceRef fill_ref = InstanceRef(chunk_ref.offset + Chunk_size); - FillItemHeaderRef item_header = FillItemHeaderRef(atomicAdd(alloc, fill_n * FillItemHeader_size)); - FillTileHeader_write(tile_header_ref, FillTileHeader(fill_n, item_header)); - FillSegChunkRef seg_chunk_ref = FillSegChunkRef(0); - uint seg_limit = 0; - // Iterate through items; fill_n holds count remaining. - while (true) { - if (chunk.chunk_n == 0) { - chunk_ref = chunk.next; - if (chunk_ref.offset == 0) { - break; - } - chunk = Chunk_read(chunk_ref); - fill_ref = InstanceRef(chunk_ref.offset + Chunk_size); - } - Instance ins = Instance_read(fill_ref); - PietFill fill = PietItem_Fill_read(PietItemRef(ins.item_ref)); - - // Process the fill polyline item. - uint max_n_segs = fill.n_points - 1; - uint chunk_n_segs = 0; - int backdrop = 0; - FillSegChunkRef seg_chunk_ref; - FillSegChunkRef first_seg_chunk = FillSegChunkRef(0); - vec2 start = Point_read(fill.points).xy; - for (uint j = 0; j < max_n_segs; j++) { - fill.points.offset += Point_size; - vec2 end = Point_read(fill.points).xy; - - // Process one segment. - - // TODO: I think this would go more smoothly (and be easier to - // make numerically robust) if it were based on clipping the line - // to the tile box. See: - // https://tavianator.com/fast-branchless-raybounding-box-intersections/ - vec2 xymin = min(start, end); - vec2 xymax = max(start, end); - float a = end.y - start.y; - float b = start.x - end.x; - float c = -(a * start.x + b * start.y); - vec2 xy1 = xy0 + vec2(TILE_WIDTH_PX, TILE_HEIGHT_PX); - float ytop = max(xy0.y, xymin.y); - float ybot = min(xy1.y, xymax.y); - float s00 = sign(b * ytop + a * xy0.x + c); - float s01 = sign(b * ytop + a * xy1.x + c); - float s10 = sign(b * ybot + a * xy0.x + c); - float s11 = sign(b * ybot + a * xy1.x + c); - float sTopLeft = sign(b * xy0.y + a * xy0.x + c); - if (sTopLeft == sign(a) && xymin.y <= xy0.y && xymax.y > xy0.y) { - backdrop -= int(s00); - } - - // This is adapted from piet-metal but could be improved. - - if (max(xymin.x, xy0.x) < min(xymax.x, xy1.x) - && ytop < ybot - && s00 * s01 + s00 * s10 + s00 * s11 < 3.0) - { - // avoid overwriting `end` so that it can be used as start - vec2 enc_end = end; - if (xymin.x < xy0.x) { - float yEdge = mix(start.y, end.y, (start.x - xy0.x) / b); - if (yEdge >= xy0.y && yEdge < xy1.y) { - // This is encoded the same as a general fill segment, but could be - // special-cased, either here or in rendering. (It was special-cased - // in piet-metal). - FillSegment edge_seg; - if (b > 0.0) { - enc_end = vec2(xy0.x, yEdge); - edge_seg.start = enc_end; - edge_seg.end = vec2(xy0.x, xy1.y); - } else { - start = vec2(xy0.x, yEdge); - edge_seg.start = vec2(xy0.x, xy1.y); - edge_seg.end = start; - } - alloc_chunk(chunk_n_segs, seg_chunk_ref, first_seg_chunk, seg_limit); - FillSegment_write(FillSegmentRef(seg_chunk_ref.offset + FillSegChunk_size + FillSegment_size * chunk_n_segs), edge_seg); - chunk_n_segs++; - } - } - alloc_chunk(chunk_n_segs, seg_chunk_ref, first_seg_chunk, seg_limit); - FillSegment seg = FillSegment(start, enc_end); - FillSegment_write(FillSegmentRef(seg_chunk_ref.offset + FillSegChunk_size + FillSegment_size * chunk_n_segs), seg); - chunk_n_segs++; - } - - start = end; - } - FillItemHeader_write(item_header, FillItemHeader(backdrop, first_seg_chunk)); - if (chunk_n_segs != 0) { - FillSegChunk_write(seg_chunk_ref, FillSegChunk(chunk_n_segs, FillSegChunkRef(0))); - seg_chunk_ref.offset += FillSegChunk_size + FillSegment_size * chunk_n_segs; - } - - fill_ref.offset += Instance_size; - chunk.chunk_n--; - item_header.offset += FillItemHeader_size; - } - } else { - // As an optimization, we could just write 0 for the size. - FillTileHeader_write(tile_header_ref, FillTileHeader(fill_n, FillItemHeaderRef(0))); - } -} diff --git a/piet-gpu/shader/kernel2f.spv b/piet-gpu/shader/kernel2f.spv deleted file mode 100644 index 75a7a39616532d313668ff49c7fbd7b0ec2b8681..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21108 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 2]; - - TileHeaderRef tile_header_ref = TileHeaderRef(tile_ix * TileHeader_size); - if (stroke_n > 0) { - ChunkRef chunk_ref = ChunkRef(stroke_start.offset + 4); - Chunk chunk = Chunk_read(chunk_ref); - InstanceRef stroke_ref = InstanceRef(chunk_ref.offset + Chunk_size); - ItemHeaderRef item_header = ItemHeaderRef(atomicAdd(alloc, stroke_n * ItemHeader_size)); - TileHeader_write(tile_header_ref, TileHeader(stroke_n, item_header)); - SegChunkRef seg_chunk_ref = SegChunkRef(0); - uint seg_limit = 0; - // Iterate through items; stroke_n holds count remaining. - while (true) { - if (chunk.chunk_n == 0) { - chunk_ref = chunk.next; - if (chunk_ref.offset == 0) { - break; - } - chunk = Chunk_read(chunk_ref); - stroke_ref = InstanceRef(chunk_ref.offset + Chunk_size); - } - Instance ins = Instance_read(stroke_ref); - PietStrokePolyLine poly = PietItem_Poly_read(PietItemRef(ins.item_ref)); - - // Process the stroke polyline item. - uint max_n_segs = poly.n_points - 1; - uint chunk_n_segs = 0; - SegChunkRef seg_chunk_ref; - vec2 start = Point_read(poly.points).xy; - for (uint j = 0; j < max_n_segs; j++) { - poly.points.offset += Point_size; - vec2 end = Point_read(poly.points).xy; - - // Process one segment. - - // This logic just tests for collision. What we probably want to do - // is a clipping algorithm like Liang-Barsky, and then store coords - // relative to the tile in f16. See also: - // https://tavianator.com/fast-branchless-raybounding-box-intersections/ - - // Also note that when we go to the fancy version, we want to compute - // the (horizontal projection of) the bounding box of the intersection - // once per tilegroup, so we can assign work to individual tiles. - - float a = end.y - start.y; - float b = start.x - end.x; - float c = -(a * start.x + b * start.y); - float half_width = 0.5 * poly.width; - // Tile boundaries padded by half-width. - float xmin = xy0.x - half_width; - float ymin = xy0.y - half_width; - float xmax = xy0.x + float(TILE_WIDTH_PX) + half_width; - float ymax = xy0.y + float(TILE_HEIGHT_PX) + half_width; - float s00 = sign(b * ymin + a * xmin + c); - float s01 = sign(b * ymin + a * xmax + c); - float s10 = sign(b * ymax + a * xmin + c); - float s11 = sign(b * ymax + a * xmax + c); - // If bounding boxes intersect and not all four corners are on the same side, hit. - // Also note: this is designed to be false on NAN input. - if (max(min(start.x, end.x), xmin) < min(max(start.x, end.x), xmax) - && max(min(start.y, end.y), ymin) < min(max(start.y, end.y), ymax) - && s00 * s01 + s00 * s10 + s00 * s11 < 3.0) - { - // Allocate a chunk if needed. - if (chunk_n_segs == 0) { - if (seg_chunk_ref.offset + 40 > seg_limit) { - seg_chunk_ref.offset = atomicAdd(alloc, SEG_CHUNK_ALLOC); - seg_limit = seg_chunk_ref.offset + SEG_CHUNK_ALLOC - Segment_size; - } - ItemHeader_write(item_header, ItemHeader(seg_chunk_ref)); - } else if (seg_chunk_ref.offset + SegChunk_size + Segment_size * chunk_n_segs > seg_limit) { - uint new_chunk_ref = atomicAdd(alloc, SEG_CHUNK_ALLOC); - seg_limit = new_chunk_ref + SEG_CHUNK_ALLOC - Segment_size; - SegChunk_write(seg_chunk_ref, SegChunk(chunk_n_segs, SegChunkRef(new_chunk_ref))); - seg_chunk_ref.offset = new_chunk_ref; - chunk_n_segs = 0; - } - Segment seg = Segment(start, end); - Segment_write(SegmentRef(seg_chunk_ref.offset + SegChunk_size + Segment_size * chunk_n_segs), seg); - chunk_n_segs++; - } - - start = end; - } - if (chunk_n_segs == 0) { - ItemHeader_write(item_header, ItemHeader(SegChunkRef(0))); - } else { - SegChunk_write(seg_chunk_ref, SegChunk(chunk_n_segs, SegChunkRef(0))); - seg_chunk_ref.offset += SegChunk_size + Segment_size * chunk_n_segs; - } - - stroke_ref.offset += Instance_size; - chunk.chunk_n--; - item_header.offset += ItemHeader_size; - } - } else { - // As an optimization, we could just write 0 for the size. - TileHeader_write(tile_header_ref, TileHeader(stroke_n, ItemHeaderRef(0))); - } -} diff --git a/piet-gpu/shader/kernel2s.spv b/piet-gpu/shader/kernel2s.spv deleted file mode 100644 index f7c27f04f27c45e785ab91f33da2144f4b59923b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17688 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 diff --git a/piet-gpu/shader/kernel3.comp b/piet-gpu/shader/kernel3.comp deleted file mode 100644 index bd6d809..0000000 --- a/piet-gpu/shader/kernel3.comp +++ /dev/null @@ -1,135 +0,0 @@ -// This is "kernel 3" in a 4-kernel pipeline. It walks the active items -// for the tilegroup and produces a per-tile command list for each tile. - -#version 450 -#extension GL_GOOGLE_include_directive : enable - -layout(local_size_x = 32, local_size_y = 1) in; - -layout(set = 0, binding = 0) readonly buffer SceneBuf { - uint[] scene; -}; - -// TODO: this should have a `readonly` qualifier, but then inclusion -// of ptcl.h would fail because of the writers. -layout(set = 0, binding = 1) buffer TilegroupBuf { - uint[] tilegroup; -}; - -// Used readonly -layout(set = 0, binding = 2) buffer SegmentBuf { - uint[] segment; -}; - -// Used readonly -layout(set = 0, binding = 3) buffer FillSegmentBuf { - uint[] fill_seg; -}; - -layout(set = 0, binding = 4) buffer PtclBuf { - uint[] ptcl; -}; - -layout(set = 0, binding = 5) buffer AllocBuf { - uint alloc; -}; - -#include "scene.h" -#include "tilegroup.h" -#include "segment.h" -#include "fill_seg.h" -#include "ptcl.h" - -#include "setup.h" - -void alloc_cmd(inout CmdRef cmd_ref, inout uint cmd_limit) { - if (cmd_ref.offset > cmd_limit) { - uint new_cmd = atomicAdd(alloc, PTCL_INITIAL_ALLOC); - CmdJump jump = CmdJump(new_cmd); - Cmd_Jump_write(cmd_ref, jump); - cmd_ref = CmdRef(new_cmd); - cmd_limit = new_cmd + PTCL_INITIAL_ALLOC - 2 * Cmd_size; - } -} - -void main() { - uint tile_ix = gl_GlobalInvocationID.y * WIDTH_IN_TILES + gl_GlobalInvocationID.x; - uint tilegroup_ix = gl_GlobalInvocationID.y * WIDTH_IN_TILEGROUPS - + (gl_GlobalInvocationID.x / TILEGROUP_WIDTH_TILES); - vec2 xy0 = vec2(gl_GlobalInvocationID.xy) * vec2(TILE_WIDTH_PX, TILE_HEIGHT_PX); - TileGroupRef tg_ref = TileGroupRef(tilegroup_ix * TILEGROUP_STRIDE); - CmdRef cmd_ref = CmdRef(tile_ix * PTCL_INITIAL_ALLOC); - uint cmd_limit = cmd_ref.offset + PTCL_INITIAL_ALLOC - 2 * Cmd_size; - - TileHeader stroke_th = TileHeader_read(TileHeaderRef(tile_ix * TileHeader_size)); - FillTileHeader fill_th = FillTileHeader_read(FillTileHeaderRef(tile_ix * FillTileHeader_size)); - - while (true) { - uint tg_tag = TileGroup_tag(tg_ref); - if (tg_tag == TileGroup_End) { - break; - } - if (tg_tag == TileGroup_Jump) { - tg_ref = TileGroup_Jump_read(tg_ref).new_ref; - continue; - } - // Assume tg_tag is `Instance`, though there will be more cases. - Instance ins = TileGroup_Instance_read(tg_ref); - PietItemRef item_ref = PietItemRef(ins.item_ref); - uint item_tag = PietItem_tag(item_ref); - switch (item_tag) { - case PietItem_Circle: - PietCircle circle = PietItem_Circle_read(item_ref); - vec2 center = ins.offset + circle.center.xy; - float r = circle.radius; - if (max(center.x - r, xy0.x) < min(center.x + r, xy0.x + float(TILE_WIDTH_PX)) - && max(center.y - r, xy0.y) < min(center.y + r, xy0.y + float(TILE_HEIGHT_PX))) - { - CmdCircle cmd = CmdCircle(center, r, circle.rgba_color); - alloc_cmd(cmd_ref, cmd_limit); - Cmd_Circle_write(cmd_ref, cmd); - cmd_ref.offset += Cmd_size; - } - break; - case PietItem_Poly: - ItemHeader stroke_item = ItemHeader_read(stroke_th.items); - stroke_th.items.offset += ItemHeader_size; - if (stroke_item.segments.offset != 0) { - PietStrokePolyLine poly = PietItem_Poly_read(item_ref); - CmdStroke cmd = CmdStroke( - stroke_item.segments.offset, - 0.5 * poly.width, - poly.rgba_color - ); - alloc_cmd(cmd_ref, cmd_limit); - Cmd_Stroke_write(cmd_ref, cmd); - cmd_ref.offset += Cmd_size; - } - break; - case PietItem_Fill: - FillItemHeader fill_item = FillItemHeader_read(fill_th.items); - fill_th.items.offset += FillItemHeader_size; - // TODO: handle segments == 0 but backdrop != specially, it's a solid tile. - if (fill_item.segments.offset != 0) { - PietFill fill = PietItem_Fill_read(item_ref); - CmdFill cmd = CmdFill( - fill_item.segments.offset, - fill_item.backdrop, - fill.rgba_color - ); - alloc_cmd(cmd_ref, cmd_limit); - Cmd_Fill_write(cmd_ref, cmd); - cmd_ref.offset += Cmd_size; - } else if (fill_item.backdrop != 0) { - // TODO: truncate existing cmd list if alpha is opaque - PietFill fill = PietItem_Fill_read(item_ref); - alloc_cmd(cmd_ref, cmd_limit); - Cmd_Solid_write(cmd_ref, CmdSolid(fill.rgba_color)); - cmd_ref.offset += Cmd_size; - } - break; - } - tg_ref.offset += TileGroup_size; - } - Cmd_End_write(cmd_ref); -} diff --git a/piet-gpu/shader/kernel3.spv b/piet-gpu/shader/kernel3.spv deleted file mode 100644 index c182337cae88ffa81428a1a076cd6f601b3e588e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26532 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 VVD;b7jA3on{P%5BH~XBA{|8p;_f7x+ diff --git a/piet-gpu/shader/kernel4.comp b/piet-gpu/shader/kernel4.comp index 2df43ec..bdc540c 100644 --- a/piet-gpu/shader/kernel4.comp +++ b/piet-gpu/shader/kernel4.comp @@ -9,26 +9,14 @@ layout(local_size_x = 16, local_size_y = 16) in; -// Same concern that this should be readonly as in kernel 3. +// This should be annotated readonly but infra doesn't support that yet. layout(set = 0, binding = 0) buffer PtclBuf { uint[] ptcl; }; -// Used readonly -layout(set = 0, binding = 1) buffer SegmentBuf { - uint[] segment; -}; - -// Used readonly -layout(set = 0, binding = 2) buffer FillSegBuf { - uint[] fill_seg; -}; - -layout(rgba8, set = 0, binding = 3) uniform writeonly image2D image; +layout(rgba8, set = 0, binding = 1) uniform writeonly image2D image; #include "ptcl.h" -#include "segment.h" -#include "fill_seg.h" #include "setup.h" @@ -79,11 +67,11 @@ void main() { CmdFill fill = Cmd_Fill_read(cmd_ref); // Probably better to store as float, but conversion is no doubt cheap. float area = float(fill.backdrop); - FillSegChunkRef fill_seg_chunk_ref = FillSegChunkRef(fill.seg_ref); + SegChunkRef fill_seg_chunk_ref = SegChunkRef(fill.seg_ref); do { - FillSegChunk seg_chunk = FillSegChunk_read(fill_seg_chunk_ref); + SegChunk seg_chunk = SegChunk_read(fill_seg_chunk_ref); for (int i = 0; i < seg_chunk.n; i++) { - FillSegment seg = FillSegment_read(FillSegmentRef(fill_seg_chunk_ref.offset + FillSegChunk_size + FillSegment_size * i)); + Segment seg = Segment_read(SegmentRef(fill_seg_chunk_ref.offset + SegChunk_size + Segment_size * i)); vec2 start = seg.start - xy; vec2 end = seg.end - xy; vec2 window = clamp(vec2(start.y, end.y), 0.0, 1.0); diff --git a/piet-gpu/shader/kernel4.spv b/piet-gpu/shader/kernel4.spv index 00e1ac3435d0ed00ba24077d6e6477adbced8dd6..6658915fb85538479cd894533e38ac794acb58b7 100644 GIT binary patch literal 17856 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 QG2XSjqqMuuV%Pb90cT2MegFUf diff --git a/piet-gpu/shader/ptcl.h b/piet-gpu/shader/ptcl.h index 133b47a..56d4d17 100644 --- a/piet-gpu/shader/ptcl.h +++ b/piet-gpu/shader/ptcl.h @@ -36,6 +36,14 @@ struct CmdRef { uint offset; }; +struct SegmentRef { + uint offset; +}; + +struct SegChunkRef { + uint offset; +}; + struct CmdCircle { vec2 center; float radius; @@ -141,6 +149,28 @@ CmdRef Cmd_index(CmdRef ref, uint index) { return CmdRef(ref.offset + index * Cmd_size); } +struct Segment { + vec2 start; + vec2 end; +}; + +#define Segment_size 16 + +SegmentRef Segment_index(SegmentRef ref, uint index) { + return SegmentRef(ref.offset + index * Segment_size); +} + +struct SegChunk { + uint n; + SegChunkRef next; +}; + +#define SegChunk_size 8 + +SegChunkRef SegChunk_index(SegChunkRef ref, uint index) { + return SegChunkRef(ref.offset + index * SegChunk_size); +} + CmdCircle CmdCircle_read(CmdCircleRef ref) { uint ix = ref.offset >> 2; uint raw0 = ptcl[ix + 0]; @@ -362,3 +392,39 @@ void Cmd_Bail_write(CmdRef ref) { ptcl[ref.offset >> 2] = Cmd_Bail; } +Segment Segment_read(SegmentRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = ptcl[ix + 0]; + uint raw1 = ptcl[ix + 1]; + uint raw2 = ptcl[ix + 2]; + uint raw3 = ptcl[ix + 3]; + Segment s; + s.start = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); + s.end = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + return s; +} + +void Segment_write(SegmentRef ref, Segment s) { + uint ix = ref.offset >> 2; + ptcl[ix + 0] = floatBitsToUint(s.start.x); + ptcl[ix + 1] = floatBitsToUint(s.start.y); + ptcl[ix + 2] = floatBitsToUint(s.end.x); + ptcl[ix + 3] = floatBitsToUint(s.end.y); +} + +SegChunk SegChunk_read(SegChunkRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = ptcl[ix + 0]; + uint raw1 = ptcl[ix + 1]; + SegChunk s; + s.n = raw0; + s.next = SegChunkRef(raw1); + return s; +} + +void SegChunk_write(SegChunkRef ref, SegChunk s) { + uint ix = ref.offset >> 2; + ptcl[ix + 0] = s.n; + ptcl[ix + 1] = s.next.offset; +} + diff --git a/piet-gpu/shader/segment.h b/piet-gpu/shader/segment.h deleted file mode 100644 index 2843b64..0000000 --- a/piet-gpu/shader/segment.h +++ /dev/null @@ -1,126 +0,0 @@ -// Code auto-generated by piet-gpu-derive - -struct TileHeaderRef { - uint offset; -}; - -struct ItemHeaderRef { - uint offset; -}; - -struct SegmentRef { - uint offset; -}; - -struct SegChunkRef { - uint offset; -}; - -struct TileHeader { - uint n; - ItemHeaderRef items; -}; - -#define TileHeader_size 8 - -TileHeaderRef TileHeader_index(TileHeaderRef ref, uint index) { - return TileHeaderRef(ref.offset + index * TileHeader_size); -} - -struct ItemHeader { - SegChunkRef segments; -}; - -#define ItemHeader_size 4 - -ItemHeaderRef ItemHeader_index(ItemHeaderRef ref, uint index) { - return ItemHeaderRef(ref.offset + index * ItemHeader_size); -} - -struct Segment { - vec2 start; - vec2 end; -}; - -#define Segment_size 16 - -SegmentRef Segment_index(SegmentRef ref, uint index) { - return SegmentRef(ref.offset + index * Segment_size); -} - -struct SegChunk { - uint n; - SegChunkRef next; -}; - -#define SegChunk_size 8 - -SegChunkRef SegChunk_index(SegChunkRef ref, uint index) { - return SegChunkRef(ref.offset + index * SegChunk_size); -} - -TileHeader TileHeader_read(TileHeaderRef ref) { - uint ix = ref.offset >> 2; - uint raw0 = segment[ix + 0]; - uint raw1 = segment[ix + 1]; - TileHeader s; - s.n = raw0; - s.items = ItemHeaderRef(raw1); - return s; -} - -void TileHeader_write(TileHeaderRef ref, TileHeader s) { - uint ix = ref.offset >> 2; - segment[ix + 0] = s.n; - segment[ix + 1] = s.items.offset; -} - -ItemHeader ItemHeader_read(ItemHeaderRef ref) { - uint ix = ref.offset >> 2; - uint raw0 = segment[ix + 0]; - ItemHeader s; - s.segments = SegChunkRef(raw0); - return s; -} - -void ItemHeader_write(ItemHeaderRef ref, ItemHeader s) { - uint ix = ref.offset >> 2; - segment[ix + 0] = s.segments.offset; -} - -Segment Segment_read(SegmentRef ref) { - uint ix = ref.offset >> 2; - uint raw0 = segment[ix + 0]; - uint raw1 = segment[ix + 1]; - uint raw2 = segment[ix + 2]; - uint raw3 = segment[ix + 3]; - Segment s; - s.start = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); - s.end = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); - return s; -} - -void Segment_write(SegmentRef ref, Segment s) { - uint ix = ref.offset >> 2; - segment[ix + 0] = floatBitsToUint(s.start.x); - segment[ix + 1] = floatBitsToUint(s.start.y); - segment[ix + 2] = floatBitsToUint(s.end.x); - segment[ix + 3] = floatBitsToUint(s.end.y); -} - -SegChunk SegChunk_read(SegChunkRef ref) { - uint ix = ref.offset >> 2; - uint raw0 = segment[ix + 0]; - uint raw1 = segment[ix + 1]; - SegChunk s; - s.n = raw0; - s.next = SegChunkRef(raw1); - return s; -} - -void SegChunk_write(SegChunkRef ref, SegChunk s) { - uint ix = ref.offset >> 2; - segment[ix + 0] = s.n; - segment[ix + 1] = s.next.offset; -} - diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 2527b50..0ac8299 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -209,16 +209,11 @@ impl Renderer { &[], )?; - // These will probably be combined with the ptcl buf, as they're all written by the - // same kernel now. - let segment_buf = device.create_buffer(64 * 1024 * 1024, dev)?; - let fill_seg_buf = device.create_buffer(64 * 1024 * 1024, dev)?; - let k4_code = include_bytes!("../shader/kernel4.spv"); - let k4_pipeline = device.create_simple_compute_pipeline(k4_code, 3, 1)?; + let k4_pipeline = device.create_simple_compute_pipeline(k4_code, 1, 1)?; let k4_ds = device.create_descriptor_set( &k4_pipeline, - &[&ptcl_buf, &segment_buf, &fill_seg_buf], + &[&ptcl_buf], &[&image_dev], )?; From 868b0320a477b16338cab9eb6a743e07865dbe42 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Fri, 15 May 2020 16:51:37 -0700 Subject: [PATCH 10/32] Render strokes As of this point, it mostly renders stroke outlines for tiger. Some dropouts are because the scan in the elements pass doesn't do lookback yet, others are probably a bug. --- piet-gpu/shader/coarse.comp | 53 ++++++++++++++++++++++++++++++---- piet-gpu/shader/coarse.spv | Bin 21540 -> 28508 bytes piet-gpu/shader/elements.comp | 4 +-- piet-gpu/shader/elements.spv | Bin 35448 -> 35448 bytes piet-gpu/src/pico_svg.rs | 3 +- 5 files changed, 52 insertions(+), 8 deletions(-) diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index da25ce4..f94dc6b 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -57,6 +57,26 @@ void alloc_cmd(inout CmdRef cmd_ref, inout uint cmd_limit) { } } +// Ensure that there is space to encode a segment. +void alloc_chunk(inout uint chunk_n_segs, inout SegChunkRef seg_chunk_ref, + inout SegChunkRef first_seg_chunk, inout uint seg_limit) +{ + // TODO: Reduce divergence of atomic alloc? + if (chunk_n_segs == 0) { + if (seg_chunk_ref.offset + 40 > seg_limit) { + seg_chunk_ref.offset = atomicAdd(alloc, SEG_CHUNK_ALLOC); + seg_limit = seg_chunk_ref.offset + SEG_CHUNK_ALLOC - Segment_size; + } + first_seg_chunk = seg_chunk_ref; + } else if (seg_chunk_ref.offset + SegChunk_size + Segment_size * chunk_n_segs > seg_limit) { + uint new_chunk_ref = atomicAdd(alloc, SEG_CHUNK_ALLOC); + seg_limit = new_chunk_ref + SEG_CHUNK_ALLOC - Segment_size; + SegChunk_write(seg_chunk_ref, SegChunk(chunk_n_segs, SegChunkRef(new_chunk_ref))); + seg_chunk_ref.offset = new_chunk_ref; + chunk_n_segs = 0; + } +} + void main() { // Could use either linear or 2d layouts for both dispatch and // invocations within the workgroup. We'll use variables to abstract. @@ -71,6 +91,12 @@ void main() { CmdRef cmd_ref = CmdRef(tile_ix * PTCL_INITIAL_ALLOC); uint cmd_limit = cmd_ref.offset + PTCL_INITIAL_ALLOC - 2 * Cmd_size; + // Allocation and management of segment output + SegChunkRef seg_chunk_ref = SegChunkRef(0); + SegChunkRef first_seg_chunk = SegChunkRef(0); + uint seg_limit = 0; + uint chunk_n_segs = 0; + uint wr_ix = 0; uint rd_ix = 0; uint first_el; @@ -216,13 +242,30 @@ void main() { tag = Annotated_tag(ref); switch (tag) { + case Annotated_Line: + AnnoLineSeg line = Annotated_Line_read(ref); + Segment seg = Segment(line.p0, line.p1); + alloc_chunk(chunk_n_segs, seg_chunk_ref, first_seg_chunk, seg_limit); + Segment_write(SegmentRef(seg_chunk_ref.offset + SegChunk_size + Segment_size * chunk_n_segs), seg); + chunk_n_segs++; + break; case Annotated_Fill: + chunk_n_segs = 0; + break; case Annotated_Stroke: - // Note: we take advantage of the fact that fills and strokes - // have compatible layout. - AnnoFill fill = Annotated_Fill_read(ref); - alloc_cmd(cmd_ref, cmd_limit); - Cmd_Solid_write(cmd_ref, CmdSolid(fill.rgba_color)); + if (chunk_n_segs > 0) { + AnnoStroke stroke = Annotated_Stroke_read(ref); + SegChunk_write(seg_chunk_ref, SegChunk(chunk_n_segs, SegChunkRef(0))); + seg_chunk_ref.offset += SegChunk_size + Segment_size * chunk_n_segs; + CmdStroke cmd_stroke; + cmd_stroke.seg_ref = first_seg_chunk.offset; + cmd_stroke.half_width = 0.5 * stroke.linewidth; + cmd_stroke.rgba_color = stroke.rgba_color; + alloc_cmd(cmd_ref, cmd_limit); + Cmd_Stroke_write(cmd_ref, cmd_stroke); + cmd_ref.offset += Cmd_size; + chunk_n_segs = 0; + } break; } diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index ed005fcf53c3c20dd1d3a805994839aeea4d576a..92ecbaeaacb77cb54ebebc5636530de4b14dbc82 100644 GIT binary patch literal 28508 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:nxHOwSzGU}vye{%&g* zux-?BqtCA3tkZO`+uKV literal 21540 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 L&X~H{XMg-3Y(pb` diff --git a/piet-gpu/shader/elements.comp b/piet-gpu/shader/elements.comp index c31dd2e..94084c8 100644 --- a/piet-gpu/shader/elements.comp +++ b/piet-gpu/shader/elements.comp @@ -71,7 +71,7 @@ State map_element(ElementRef ref) { c.bbox = vec4(0.0, 0.0, 0.0, 0.0); c.mat = vec4(1.0, 0.0, 0.0, 1.0); c.translate = vec2(0.0, 0.0); - c.linewidth = 0.0; + c.linewidth = 1.0; // TODO should be 0.0 c.flags = 0; switch (tag) { case Element_Line: @@ -169,7 +169,7 @@ void main() { exclusive.bbox = vec4(0.0, 0.0, 0.0, 0.0); exclusive.mat = vec4(1.0, 0.0, 0.0, 1.0); exclusive.translate = vec2(0.0, 0.0); - exclusive.linewidth = 0.0; + exclusive.linewidth = 1.0; //TODO should be 0.0 exclusive.flags = 0; // TODO: do decoupled look-back diff --git a/piet-gpu/shader/elements.spv b/piet-gpu/shader/elements.spv index afb63b582b1547a2ecc25bf900952a97b01489fb..f8581291afe66b3857e17afb0579e5de40d76d18 100644 GIT binary patch delta 23 dcmew{h3Ur>rVTdwjORDo>OT(vQmPHXTmXR|3F80& delta 23 dcmew{h3Ur>rVTdwjAu66>OT(vQmPHXTmXRP3Ecny diff --git a/piet-gpu/src/pico_svg.rs b/piet-gpu/src/pico_svg.rs index 4ddf94b..a630f70 100644 --- a/piet-gpu/src/pico_svg.rs +++ b/piet-gpu/src/pico_svg.rs @@ -61,7 +61,8 @@ impl PicoSvg { for item in &self.items { match item { Item::Fill(fill_item) => { - rc.fill(&fill_item.path, &fill_item.color); + //rc.fill(&fill_item.path, &fill_item.color); + rc.stroke(&fill_item.path, &fill_item.color, 1.0); } Item::Stroke(stroke_item) => { rc.stroke(&stroke_item.path, &stroke_item.color, stroke_item.width); From 93044b469b3ba6ef3d3adc49bfa719d582c7b19b Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Fri, 15 May 2020 20:09:39 -0700 Subject: [PATCH 11/32] Fix prefix sum First, add decoupled lookback. Second, fix problem with monoid that was overly aggressive in resetting the bbox. --- piet-gpu/bin/cli.rs | 2 +- piet-gpu/shader/elements.comp | 94 +++++++++++++++++++++++++++++----- piet-gpu/shader/elements.spv | Bin 35448 -> 43616 bytes piet-gpu/src/lib.rs | 3 +- 4 files changed, 84 insertions(+), 15 deletions(-) diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index 672b42d..f37f0cd 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -70,7 +70,7 @@ fn main() -> Result<(), Error> { piet_gpu::dump_k1_data(&data); let mut data: Vec = Default::default(); - device.read_buffer(&renderer.ptcl_buf, &mut data).unwrap(); + device.read_buffer(&renderer.anno_buf, &mut data).unwrap(); piet_gpu::dump_k1_data(&data); */ diff --git a/piet-gpu/shader/elements.comp b/piet-gpu/shader/elements.comp index 94084c8..76d56b6 100644 --- a/piet-gpu/shader/elements.comp +++ b/piet-gpu/shader/elements.comp @@ -18,10 +18,10 @@ layout(set = 0, binding = 0) readonly buffer SceneBuf { uint[] scene; }; -// This will be used for inter-workgroup aggregates. In the -// meantime, for development, it has been used to store the -// scan of the state objects. -layout(set = 0, binding = 1) buffer StateBuf { +// It would be better to use the Vulkan memory model than +// "volatile" but shooting for compatibility here rather +// than doing things right. +layout(set = 0, binding = 1) volatile buffer StateBuf { uint[] state; }; @@ -34,8 +34,28 @@ layout(set = 0, binding = 2) buffer AnnotatedBuf { #include "state.h" #include "annotated.h" +#define StateBuf_stride (4 + 2 * State_size) + +StateRef state_aggregate_ref(uint partition_ix) { + return StateRef(8 + partition_ix * StateBuf_stride); +} + +StateRef state_prefix_ref(uint partition_ix) { + return StateRef(8 + partition_ix * StateBuf_stride + State_size); +} + +uint state_flag_index(uint partition_ix) { + return 1 + partition_ix * (StateBuf_stride / 4); +} + +// These correspond to X, A, P respectively in the prefix sum paper. +#define FLAG_NOT_READY 0 +#define FLAG_AGGREGATE_READY 1 +#define FLAG_PREFIX_READY 2 + #define FLAG_SET_LINEWIDTH 1 -#define FLAG_RESET_BBOX 2 +#define FLAG_SET_BBOX 2 +#define FLAG_RESET_BBOX 4 // This is almost like a monoid (the interaction between transformation and // bounding boxes is approximate) @@ -47,7 +67,9 @@ State combine_state(State a, State b) { c.bbox.w = max(a.mat.y * b.bbox.x, a.mat.y * b.bbox.z) + max(a.mat.w * b.bbox.y, a.mat.w * b.bbox.w) + a.translate.y; if ((a.flags & FLAG_RESET_BBOX) == 0 && b.bbox.z <= b.bbox.x && b.bbox.w <= b.bbox.y) { c.bbox = a.bbox; - } else if ((a.flags & FLAG_RESET_BBOX) == 0 && (a.bbox.z > a.bbox.x || a.bbox.w > a.bbox.y)) { + } else if ((a.flags & FLAG_RESET_BBOX) == 0 && (b.flags & FLAG_SET_BBOX) == 0 && + (a.bbox.z > a.bbox.x || a.bbox.w > a.bbox.y)) + { c.bbox.xy = min(a.bbox.xy, c.bbox.xy); c.bbox.zw = max(a.bbox.zw, c.bbox.zw); } @@ -59,7 +81,7 @@ State combine_state(State a, State b) { c.translate.x = a.mat.x * b.translate.x + a.mat.z * b.translate.y + a.translate.x; c.translate.y = a.mat.y * b.translate.x + a.mat.w * b.translate.y + a.translate.y; c.linewidth = (b.flags & FLAG_SET_LINEWIDTH) == 0 ? a.linewidth : b.linewidth; - c.flags = a.flags | b.flags; + c.flags = (a.flags & (FLAG_SET_LINEWIDTH | FLAG_SET_BBOX)) | b.flags; return c; } @@ -122,10 +144,18 @@ shared vec4 sh_bbox[WG_SIZE]; shared float sh_width[WG_SIZE]; shared uint sh_flags[WG_SIZE]; +shared uint sh_tile_ix; +shared State sh_prefix; + void main() { State th_state[N_ROWS]; - // this becomes an atomic counter - uint tile_ix = gl_WorkGroupID.x; + // Determine partition to process by atomic counter (described in Section + // 4.4 of prefix sum paper). + if (gl_LocalInvocationID.x == 0) { + sh_tile_ix = atomicAdd(state[0], 1); + } + barrier(); + uint tile_ix = sh_tile_ix; uint ix = tile_ix * TILE_SIZE + gl_LocalInvocationID.x * N_ROWS; ElementRef ref = ElementRef(ix * Element_size); @@ -162,16 +192,54 @@ void main() { sh_flags[gl_LocalInvocationID.x] = agg.flags; } - // TODO: if last invocation in wg, publish agg. - - barrier(); State exclusive; exclusive.bbox = vec4(0.0, 0.0, 0.0, 0.0); exclusive.mat = vec4(1.0, 0.0, 0.0, 1.0); exclusive.translate = vec2(0.0, 0.0); exclusive.linewidth = 1.0; //TODO should be 0.0 exclusive.flags = 0; - // TODO: do decoupled look-back + + // Publish aggregate for this partition + if (gl_LocalInvocationID.x == WG_SIZE - 1) { + // Note: with memory model, we'd want to generate the atomic store version of this. + State_write(state_aggregate_ref(tile_ix), agg); + uint flag = FLAG_AGGREGATE_READY; + memoryBarrierBuffer(); + if (tile_ix == 0) { + State_write(state_prefix_ref(tile_ix), agg); + flag = FLAG_PREFIX_READY; + } + state[state_flag_index(tile_ix)] = flag; + if (tile_ix != 0) { + // step 4 of paper: decoupled lookback + uint look_back_ix = tile_ix - 1; + while (true) { + flag = state[state_flag_index(look_back_ix)]; + if (flag == FLAG_PREFIX_READY) { + State their_prefix = State_read(state_prefix_ref(look_back_ix)); + exclusive = combine_state(their_prefix, exclusive); + break; + } else if (flag == FLAG_AGGREGATE_READY) { + State their_agg = State_read(state_aggregate_ref(look_back_ix)); + exclusive = combine_state(their_agg, exclusive); + look_back_ix--; + } + // else spin + } + + // step 5 of paper: compute inclusive prefix + State inclusive_prefix = combine_state(exclusive, agg); + sh_prefix = exclusive; + State_write(state_prefix_ref(tile_ix), inclusive_prefix); + memoryBarrierBuffer(); + flag = FLAG_PREFIX_READY; + state[state_flag_index(tile_ix)] = flag; + } + } + barrier(); + if (tile_ix != 0) { + exclusive = sh_prefix; + } State row = exclusive; if (gl_LocalInvocationID.x > 0) { diff --git a/piet-gpu/shader/elements.spv b/piet-gpu/shader/elements.spv index f8581291afe66b3857e17afb0579e5de40d76d18..a19b5f82bbdd333d0d9161ab31e49d2ab49648f2 100644 GIT binary patch literal 43616 zcma)_2Y_BxwS_OtOz6ExC!vPkn=~n*Na!HlFi9pM5R#BWS9BT2PnbA-`Bhh2QTxnQhh57y3qD=oPTIcRqsFLjj4&&@+HAD`G~?Mi z_}|p@4?{Yu>8ni#57~65OeMo$`EI&|St=I^2+r!r`C&%QllCT`a=qH0xHPp=v~V#I`=i4|TmRx?fU8!~Fl z@SXz)PMA1;(y)okO&Pr0q?J}$d(uj4 zXG+e_Y7Thq*IFgHpK+|Nt0s4?#;s)B8LGL#wf~UuJ>A1o;*?yi67lV*=EApKUsp8` zc-u+c!^?Wq{FL>~2e18F`Q?66k398QpWI_OZsm4;3xezMed?Q%drEx^;G6mu25&TJ z=%`_Jf$k(VKU3=Kss_O0r_CDF{G>*CS))8P#@}5#Zk6_NUksd>eTt6A5o}kje@C?_ z{_VOut0lmjj2b-}7i#HhaipT=&T3%oqu!7I$^DEUIkbDou(6}ZjyLY~)iP+t>Qh4< zyv)C}V_W;Kt6C1c{lxKO_vx{M_TE#Ej+_xgg}FKT}F?Vbql*W&K4iN+k= zJ(0^2wa!>=K;JvIPocdk+1pjRkG_L{yT%!+LE!o_Gh|}-$eh-VD^Gj>ZQMI%J#?j1i%wMQ}jzwWdD5@+|`I5St>MV#KF{l8CzJ=0O`*&Cy)8V25eBKxV> zqiTMp?9tB3{nqlMcYn(LWPi$=`_uDF-Rt1cp<@rEYwcyV4%%q;PR_gddLJDzx_jgV zdaqBOldZgc=9P-4?CX*EwfFTLl@9^!F$atvHL)lAy3e2q?YnGCTWRCF|m3g4x<9?V#-(I_pYJYHhj$PG3UNe1hgq}zdp;}+FHA> zm}lEi&vdyFJPNkvdK~dG*Wq&JUPwC-o1ZZnNsr#8S*O{x+;MUag9kSKfabGVh zVfvgCr!uacd+zG2J~3K%gRgDNK0%79)#uLFHl@e%N4+^Zsx!gu`ORIO-zQ&tasH38 zi;Ul}n8Y*RxkbM7dh^X$UD79C>uUf1o?l~&w(?w5_ z-wxQ{xpcMy6PNB*k+GVc1`ab9if zuO23=$6fc0QiuP`LgX-*V0){#YL!$+po=gHU0?X!R`}i7$FH-xug!bcP%G!h?cC;TT9f&|-{xzD4{!68+%($vuDJzT`#5;^+F9WCT69#e z_2Pcjo8aF2XYX3Hb@O!R-de|e8-G8>pMeM9#9X!)cUD8d<#F7z!S`wK(G7k;gCE$3 zcT}gtht)OQ+R69H?vbte-v*wNx1+iP-uCOPo&fti)2^LsVISYl>Sg$Te2v4H!V z95>&%T}M|n82<@-4VgHKcY4{&+ZeAFJPzBza~yUBx9=Za)t>t8)xO1LUfpPAZ9R^y zYa7?c)>(~j@JS7RK!YFLhj&zm!FdvBt;sQ9=HXW@c)#k@HZNZVwEWI)>#av??@<@G zdA?$ybXC{1wS2`w;oghZo<~RZfVjTrJpvv(aj%~7^&DS-Pnmyb^->?+RecKYIgq!b z6I!oLiOqY_etpMG)8NxK_zZB1%bL&Du+Q1xb2a!peR#iW0DSb=vHJ`e+C6Na-sh&y zYUzgGDsZMcYW$G)b5!!L+Q+Z6TBE_&YVfuD@UCiOcss5~v%SX$_3`PfHf!+jHu&H^ ze41)oc)fb!yFR|1)ee1lSG6;oz0^BNkI}Aud^)Q=8vOeW-ra|HRQtfkk3FEZ zm&Sv$fA$BL`{$%S<2tKT`f#pIj%}?`M|B>!{T$X=UC_s`v%07c@2W0?Puy$N1fP9# z+%E6q(^*~F;Mc&%PMX*n;yG|LJlDco8oqbK%j@4gee7M;{cx}Kt%uF}7{A)br?Yya z!QX7~zrgvTQh(R)tlsHk@2cK|w{p}@*8BlluA3kB@#h(Dn!e}V>5U|*m}2ZzbSLl zhg%f5v-Rw}hz3641KPC3m*aj~#?LdzT=v3|Mn=vv&LQ~C3p`D?yB>nER=HV0n%4IrBF#B&kD z`Sz#zSqKY5YR#WgRV}X8AK&R}?wA3bozvG`KW&cp=R@t7?3voGHaE7}==;;YMXcHB z<<_wfd=7dw+W`2S^lHX1uE#iWosTuCEkqsu{3c^F-ZfCy*YW-wCgb~q9j~?!s%uz| zPmD#-we_bNV+s1iSQ2auHEjcHo3$?mmg}Q!x!UF$E)RB`nl^uiVNA#Q>?qeqn-8nW z=Yy)_)U^4a>iRj(hg7*f+WgX;aqEB`r{=n^OYb<(A=llX3#etz>x0!CXY7sWjpZ?x zZ%XfR3Eu+jI)`r!cFn@K2RozLwL8Xj-<#gCYVO^A=-qqTwV7)ieRAyw)?dv%HlDuRBgULi`)D&Jk5jFfj@4$& zgXk0UV6ZXOT>C?6o3$J7P zjo==d$HS)2t+kf@aR=Pvpxx`&J@ox)Q_+^G`MqF2a^pWpTdd~#KTdN_3o`eo==E_O zo~D<(Zoi<{M{Vtz`|d?8ai0bI(f`-<+Kd(Z@4%@`{g1SA{A73s&GB#1yY6$;{Jokx z*UamEa2aoQNM~&~{ygw5dgCnsHlF8Z>e&gctZ$FnNB`~`52*cJr%7n3@A%s8xvT$q zHTT?>UsrR_W%*4t_gW)=uI8TG@>gr_UiCQS-s^GjJ}cL61$x&^&Ep{VKB|^^?Fw;! zgzpJ=PlbvN~c`7Fy3b3N{YtCcOj_q2QuUq8P0!qws{UY1?pzPZ29Gk#?^ zNL8)xyKDBdcXiwuVnjfbbpQ zhhtv=ukV9wb;ry7E}Zda*4QzL?{{MD#=8*g@k_jmYMZ+La=$Yt-W4@=jN`9@yOtS$b!}I7 zyxi~Cj?a2Hz~`3m8R473m*Ob8ra`uo13_Y)=e{Y1F&d^ZtpJl{=(8_#zWCHLJ#$$d9ba^FXk-1iaT=JVY{xcPkd5NW`;6jyh;Z}y9wOZ5P~Ss@8_#zT z;o5x%5w6{L5b_PU>J8%D-h{R>&1b)Lx#m$`wP~&2fRESU&ERVKe8N@pyI?<`r3R@3JFM7|ced$0`y-%hXYJls>m!F$qNW5>w# z6MO%0eO=QLV2_dgzsR*0{V19r?R(dD`^;q@urY2WmwRC}Sk3kD_{&GqjAd+b#_kJt zZ1|X(A4x2a$ym6W^=0n+!83R7mGX?A05+cU^1dlwi%727MDR@Xj?t!%_fqw&-Tq+L zPCaXPAlS9D7W4FAKM1UD4DY{k@xk=QFlP^bhk$RP*GF4o9tt+5^LLzFU)R|6cAe}q zuOEP&SN7Kr!D{al+wt1e%46tyC*R><^ZETIb2tL7?s%UOiT$nJOgYV=V~6GO@9Pe*XK<7 zvuLN%9BUrAKFMIEI(9Whg);w~3lIId|GtZ@H z>iYN{@iMS^ax8o{z8tQu&lU7n(k`Ys);w~3lIL1*GtYHs>iT#eydG>Gzw2cm-2hkD zXAAlpY1hykYaY2i;+yEb_PVET2D>Niy)OKi{#KeF?YGr-`{cSEY>ZqNeganWJG<8f z`7JbK8C%@Ej%U7iU~A6zPBiu8y9;c-N12=7aqkAZXVecRpVyOn!0NZ)*KzD0{#ds*M7M^iTNZrF+G=_qIoW<>*M+MbgdiHbMhB( zV>%z}lywzj1}CQH$Zu$#BkKBi9{sk~ zjp@1e9Nd_mYsQr8lbFwg6LSmj?`WP6>hbw~tsB$x>JM;ZdR`e*u8;mb*k1%|*Y*Ov zJhnfAJtwujL@&?1;$`qHjCYJSee_lL9Lqkm|8M8XE7+R*@>MkTtn+JN*N0VC)hmYb^9Z@x<2mHk7@2x$C^j3kNf0SdaqaZ z*~9+=-$cvv!zW<1oWI)C%4_|P;pTIVKLdYC%eg7{on7po*Y;dv3^}e`-D7QKsSspyrLhYkdd3AFuV= zrlzU6cH*pE7kC@Sxz4`Zko&wijIsT|SJ10#_g#lt&iCoS9!qV$_mF$ObLpt22aln5 zj5d9I7vi(-WU#&Ox>CnXVC%?yW`?V|4$fy*upj56Z5EoE^ARV`?BLADcP;YFXAbby z#89^$-@T}1K68UJAK$~sGoNpPosVO*Wj=l9@{!#uTR>1KPS(9bzSf+jCYJS zee_lLSY*wePv-F*aJe2Ez|}m~uE$1TKdy(i4QXnwhd6OI0cSl1f#q3`O~GC})U9bV zdbO;_=3v)DUHiuLYMJvEU^Q*NQ16IP0`G*mbuikAvJA;%gt@(cm1beZcbE zYCovBLC#H2C4)@tuJ#`)dR!^Nrf{poj9aFAPVjcxfOrL{~hWi|>u8-f% zjsdG1(`UY8!N$xppIo2BJPw?gKJy+A_nB8cJ|}?Hjp=jZiC|;qIZ>{U{yo@F2D`6Z zmy_t_v7HKbthQ6=<+;wB2EK*yj?t!%an#+**;Dq~PN#PdYC8k$ezW&k;7t0nXnwSx zUEA$5uXDi0$h(ts!D@M@D*qA9SjHA-?D=5FW?mP9Gv^Dya@WiE!WV=0WKA6t|4YE? z&f_9_dDh`lu@xIFW+mf2kWDrck4HT)o-BrZr$JWxe2Upj%(=UssG1d^J=?=UY=|5 zt>8h7SJ!?sy;|zI4Xoz%@h*BlU$8UP@1(KqtJ~@MYE%7$o~cxK(0dHE8^`(vvLM!F z4u2jaw+6pAECoJ|Ufum;9>*IubGR3rIot!5JKo>5xevTKy}I%5rdP|k{s7oxTD~86 z5U##8N6Kr?Pr+(7Ki1{F$n*LkG-Ep6bHJKB|9^(2?NsV8#?QgVQBUrN!Oh%{ps6SK zqhK|g>&YX1Z|=v?lH2QaazBo)ExDfn8%OrHxjo(10qpM{TOw3X+q*8pR1Dp&7o1@B0& zuKh3cYS!+3?{8qWoU_2{eg35$pTC3Oqh(LZ^~rku1Dy5x5G>Dn{S)kZshj@;dNpIa zpFaXS_HlfS=d;eoaCNzB{x7iO4y*YmV71skt?k_FERzs|yMG9g?O*Pshs+cEW;^aDFqJ!7Wes4mCa}xk?`CPw_?gvd+Ot-(fL+VS@pb>m^>dz{TeE?4 zZu$ETa&1Rrn*;1v$N9St@|;_9g3rgtG1|&=>kh^k1G$=qf92fr_ao%)?Sa_l1@A(y zZVrE+Le1Jex8?_{xxU6DY_$MfJw6M9{XGkPj3L)YyXVJ3V2{z`bqx31!f5L8=?^wW z_M2RvtkELitdYONA=h>Yu@(b6*4+L+hdgVvIQSBL9HXsVqr0d{eipsY5cZklviP13 z&$HEXaJAH@P0jIV)Z@>j|B79)Ce7ca!`ynNqig*>>KfGN@2Jc`6T7}M(#$W9zm1VA;ps77h^SE1+ zTGr>6V708pufS^0(!3`4`!~O#Jwr2(_Kf{4SetR3n>@DX!DZav!_~a@B<>%;iK{(h zUjS<}u4^QZ?Imy-_hq=6*Q&&Q1)R9rGxk-mHsiY1^4MMnmvP^Kt9hMG+&_U6S9`|3 z3D#y@kC{BSKZ9Kxf0pZY^e;5E*xvy=$JpNmt8K&a^M3eOnq$;G?(fn6jpknPc$ibJ zUt<0p?EDh*1F%}`AA+qj_J4rYlJlQn$EYXgM__a2yq4={%sJ>krfK(l5$AlF54<4F z&-^scmwD+Ipn1M5M4$8HQ}8qe|FXfS;u=}n{rd|g_wO&1+`qq2a{vB9xc?o1e}6%q z^XW6Nb-AuttIxseIiJ1&tL1#MX0`Z#1y;-X^lz|Qc|QFIo_(Y}W4%#o^O!n!d2CaI z%eWnIweo!GgeR`{jO_wzGp=hWk8K)o8Q0%QS1Zq_>EMa0J!7W_YcsCLLLS?U;4*twd7nL>=^arTmfv(oNIFZjOle{#oF#QN1SWU0BnoV{47HAn$w?tQJUAB z#p!d+S%q=ACitA;-(D%Xe|x3m8#MSP4ZcN#Z`0sAHn@L#CGoq#Lu;OE(yDOlF0V*So&vi56&)pAW*3#?XNlfDhl-qxP6YlF49&s-aMZ0mx{xa(=7mDi;8 z;fdR_bL{#tpYNb+Gp=haPuvZ`W!#P6YUMR)V|e0f&)7j=ZN~Lj$z$6TT*loDt~Qx^ za*l5fPh9O8`(3a$<9f{Hu?+^7akqr4mDi-L;EAg}W48utGp>6_9^1CyGVXS8wep&@ zJv?!>XY3AOZN_yE%47Q;xQx3qTx};>)?pWL;%d*>UBTLn>t2?}wmZ0t`+d0D9<;FBod{M- zttWx4KYLTIkN&RF$+g|}6KDNaz_v2Y&x$nHZ+ZHaXs+KX^jW{t7+0>}nFT)&e16SS z)9G;Ilua8x zE`uAVT))fF)U$q9fYq{o=1|K#uL7%O{jLV9mFss6JnN@DW3L5kbN#GU9@`CIwVdNO zg4L}3Ci+-2Usn~$C}l$j(368a(wOvtCh#+ z9(azA_Kdw3tj*)&+~u*|4=&?A09PxYvmS&euJ(-mDOj6vT|;?nKLeL>e-2kGpR*o@ zC$9F4eFUt{xE>36Y>$DuJ(-mEm)gz-DC3Deh2ng=DhknSS|KH zfITL$zW`Q?{Y9{A8~aOOwb=g%c8y|x8LXC{WxN75r*R#t-TnS5{cAMOft=HF{S*6j zuvHuCImgj;u!H!Wk=X)Icx4`CnAMAR`^)sf|innXK*Aa29BmJ-q zqWRf~=5=IR`i*H`M>e6)dHfFJ)XVeuT{QKa$A1N@c^>EZz6a0o)t-6&4Xn+Y#Hq=5 zUYpbWY)VT_n}MzAyYyb$W~Kj#abDX};|Fm4${IgJQ_r>IA7C~2w(q28!T+CNb=S_A za(_P2djAHVyUt~;AH!3t-<7wd`PqV&S_gxzbt`¬OSg#+9{x3fHf!^)oc})cQGC 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 T+VlU{u|8Pc`MHL2*YJM;Sds-H literal 35448 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 P%$>mUj?Xz%b`Jj+f*SW7 diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 0ac8299..bf7a7c7 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -158,7 +158,7 @@ impl Renderer { .unwrap(); device.write_buffer(&scene_buf, &scene)?; - let state_buf = device.create_buffer(64 * 1024 * 1024, dev)?; + let state_buf = device.create_buffer(1 * 1024 * 1024, dev)?; let anno_buf = device.create_buffer(64 * 1024 * 1024, dev)?; let bin_buf = device.create_buffer(64 * 1024 * 1024, dev)?; let ptcl_buf = device.create_buffer(48 * 1024 * 1024, dev)?; @@ -245,6 +245,7 @@ impl Renderer { cmd_buf.copy_buffer(&self.scene_buf, &self.scene_dev); cmd_buf.copy_buffer(&self.bin_alloc_buf_host, &self.bin_alloc_buf_dev); cmd_buf.copy_buffer(&self.coarse_alloc_buf_host, &self.coarse_alloc_buf_dev); + cmd_buf.clear_buffer(&self.state_buf); cmd_buf.memory_barrier(); cmd_buf.image_barrier( &self.image_dev, From 9bb06ec34074072f9c37d5f6aa8e3b774f679271 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Fri, 15 May 2020 20:57:07 -0700 Subject: [PATCH 12/32] Correct rendering (on Intel) Handle multiple passes in coarse raster. Doesn't work on NV, WIP to find out why. --- piet-gpu/bin/cli.rs | 50 +++++++++++++++++++++++++++++++----- piet-gpu/shader/coarse.comp | 18 ++++++++----- piet-gpu/shader/coarse.spv | Bin 28508 -> 28728 bytes piet-gpu/src/lib.rs | 4 +-- piet-gpu/src/pico_svg.rs | 2 +- 5 files changed, 58 insertions(+), 16 deletions(-) diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index f37f0cd..c5c0b6b 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -34,6 +34,47 @@ fn dump_state(buf: &[u8]) { } +/// 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::>>(); + 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); + } + } + + } +} + fn main() -> Result<(), Error> { let (instance, _) = VkInstance::new(None)?; unsafe { @@ -66,12 +107,9 @@ fn main() -> Result<(), Error> { /* let mut data: Vec = Default::default(); - device.read_buffer(&renderer.ptcl_buf, &mut data).unwrap(); - piet_gpu::dump_k1_data(&data); - - let mut data: Vec = Default::default(); - device.read_buffer(&renderer.anno_buf, &mut data).unwrap(); - piet_gpu::dump_k1_data(&data); + device.read_buffer(&renderer.bin_buf, &mut data).unwrap(); + //piet_gpu::dump_k1_data(&data); + //trace_merge(&data); */ let mut img_data: Vec = Default::default(); diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index f94dc6b..2ca0cff 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -109,8 +109,8 @@ void main() { sh_first_el[th_ix] = chunk.n > 0 ? BinInstance_read(BinInstanceRef(start_chunk + BinChunk_size)).element_ix : ~0; } - uint probe = 0; // for debugging - do { + uint count = 0; + while (true) { for (uint i = 0; i < N_SLICE; i++) { sh_bitmaps[i][th_ix] = 0; } @@ -121,18 +121,18 @@ void main() { if (th_ix < N_WG) { if (th_ix == 0) { sh_selected_n = 0; - sh_min_buf = ~1; + sh_min_buf = ~0; } } barrier(); // Tempting to do this with subgroups, but atomic should be good enough. - my_min = sh_first_el[th_ix]; if (th_ix < N_WG) { + my_min = sh_first_el[th_ix]; atomicMin(sh_min_buf, my_min); } barrier(); if (th_ix < N_WG) { - if (sh_first_el[th_ix] == sh_min_buf) { + if (my_min == sh_min_buf && my_min != ~0) { sh_elements_ref = sh_chunk[th_ix] + BinChunk_size; uint selected_n = sh_chunk_n[th_ix]; sh_selected_n = selected_n; @@ -162,6 +162,7 @@ void main() { } wr_ix += chunk_n; } + barrier(); // We've done the merge and filled the buffer. @@ -272,8 +273,11 @@ void main() { // clear LSB bitmap &= bitmap - 1; } + barrier(); rd_ix += N_TILE; - break; - } while (wr_ix > rd_ix); + // The second disjunct is there as a strange workaround on Nvidia. If it is + // removed, then the kernel fails with ERROR_DEVICE_LOST. + if (rd_ix >= wr_ix || bin_ix == ~0) break; + } } diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index 92ecbaeaacb77cb54ebebc5636530de4b14dbc82..b0bec3f996366f3fc5f8e2a017621dd32622cb87 100644 GIT binary patch literal 28728 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 B%N76t literal 28508 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:nxHOwSzGU}vye{%&g* zux-?BqtCA3tkZO`+uKV diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index bf7a7c7..2dca39d 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -65,7 +65,7 @@ pub fn render_scene(rc: &mut impl RenderContext) { #[allow(unused)] fn render_cardioid(rc: &mut impl RenderContext) { - let n = 91; + let n = 601; let dth = std::f64::consts::PI * 2.0 / (n as f64); let center = Point::new(1024.0, 768.0); let r = 750.0; @@ -73,7 +73,7 @@ fn render_cardioid(rc: &mut impl RenderContext) { for i in 1..n { let p0 = center + Vec2::from_angle(i as f64 * dth) * r; let p1 = center + Vec2::from_angle(((i * 2) % n) as f64 * dth) * r; - rc.fill(&Circle::new(p0, 8.0), &Color::WHITE); + //rc.fill(&Circle::new(p0, 8.0), &Color::WHITE); path.move_to(p0); path.line_to(p1); //rc.stroke(Line::new(p0, p1), &Color::BLACK, 2.0); diff --git a/piet-gpu/src/pico_svg.rs b/piet-gpu/src/pico_svg.rs index a630f70..9cf5cc3 100644 --- a/piet-gpu/src/pico_svg.rs +++ b/piet-gpu/src/pico_svg.rs @@ -48,7 +48,7 @@ impl PicoSvg { } if let Some(stroke_color) = el.attribute("stroke") { let width = - f64::from_str(el.attribute("stroke-width").ok_or("missing width")?)?; + scale * f64::from_str(el.attribute("stroke-width").ok_or("missing width")?)?; let color = parse_color(stroke_color); items.push(Item::Stroke(StrokeItem { width, color, path })); } From fe1790e72481d8f4a9994299979933a365405f26 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Sat, 16 May 2020 21:20:25 -0700 Subject: [PATCH 13/32] Fix bbox bug Bounding boxes were being calculated as way too large in the element processing. Also wire up counters so winit binary is happy. --- piet-gpu/bin/winit.rs | 5 ++--- piet-gpu/shader/elements.comp | 1 + piet-gpu/shader/elements.spv | Bin 43616 -> 43780 bytes 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/piet-gpu/bin/winit.rs b/piet-gpu/bin/winit.rs index 1c263bb..fd30fa3 100644 --- a/piet-gpu/bin/winit.rs +++ b/piet-gpu/bin/winit.rs @@ -37,7 +37,7 @@ fn main() -> Result<(), Error> { .map(|_| device.create_cmd_buf()) .collect::, Error>>()?; let query_pools = (0..NUM_FRAMES) - .map(|_| device.create_query_pool(6)) + .map(|_| device.create_query_pool(5)) .collect::, Error>>()?; let mut ctx = PietGpuRenderContext::new(); @@ -70,12 +70,11 @@ fn main() -> Result<(), Error> { let timestamps = device.reap_query_pool(query_pool).unwrap(); window.set_title(&format!( - "k1: {:.3}ms, k2s: {:.3}ms, k2f: {:.3}ms, k3: {:.3}ms, k4: {:.3}ms", + "e: {:.3}ms, b: {:.3}ms, c: {:.3}ms, f: {:.3}ms", timestamps[0] * 1e3, (timestamps[1] - timestamps[0]) * 1e3, (timestamps[2] - timestamps[1]) * 1e3, (timestamps[3] - timestamps[2]) * 1e3, - (timestamps[4] - timestamps[3]) * 1e3, )); } diff --git a/piet-gpu/shader/elements.comp b/piet-gpu/shader/elements.comp index 76d56b6..8f87b87 100644 --- a/piet-gpu/shader/elements.comp +++ b/piet-gpu/shader/elements.comp @@ -82,6 +82,7 @@ State combine_state(State a, State b) { c.translate.y = a.mat.y * b.translate.x + a.mat.w * b.translate.y + a.translate.y; c.linewidth = (b.flags & FLAG_SET_LINEWIDTH) == 0 ? a.linewidth : b.linewidth; c.flags = (a.flags & (FLAG_SET_LINEWIDTH | FLAG_SET_BBOX)) | b.flags; + c.flags |= (a.flags & FLAG_RESET_BBOX) >> 1; return c; } diff --git a/piet-gpu/shader/elements.spv b/piet-gpu/shader/elements.spv index a19b5f82bbdd333d0d9161ab31e49d2ab49648f2..7828aa4732cc2ea3c5291afc93f45b4af815d3c3 100644 GIT binary patch literal 43780 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 O*%GYo{9Hr1YxqAgC=VY1 delta 14524 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 Date: Tue, 19 May 2020 08:20:45 -0700 Subject: [PATCH 14/32] Smarter line segment coverage Compute tile coverage of segments using optimized algorithm. This algorithm does a bit of setup, then uses an efficient formula to compute the span per scan-line. --- piet-gpu/shader/coarse.comp | 66 +++++++++++++++++++++++------------- piet-gpu/shader/coarse.spv | Bin 28728 -> 30280 bytes 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 2ca0cff..e331076 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -175,47 +175,67 @@ void main() { tag = Annotated_tag(ref); } - int x0 = 0, y0 = 0, x1 = 0, y1 = 0; + // Setup for coverage algorithm. + float a, b, c; + // Bounding box of element in pixel coordinates. + float xmin, xmax, ymin, ymax; switch (tag) { case Annotated_Line: AnnoLineSeg line = Annotated_Line_read(ref); - x0 = int(floor((min(line.p0.x, line.p1.x) - line.stroke.x - xy0.x) * SX)); - y0 = int(floor((min(line.p0.y, line.p1.y) - line.stroke.y - xy0.y) * SY)); - x1 = int(ceil((max(line.p0.x, line.p1.x) + line.stroke.x - xy0.x) * SX)); - y1 = int(ceil((max(line.p0.y, line.p1.y) + line.stroke.y - xy0.y) * SY)); + xmin = min(line.p0.x, line.p1.x) - line.stroke.x; + xmax = max(line.p0.x, line.p1.x) + line.stroke.x; + ymin = min(line.p0.y, line.p1.y) - line.stroke.y; + ymax = max(line.p0.y, line.p1.y) + line.stroke.y; + float dx = line.p1.x - line.p0.x; + float dy = line.p1.y - line.p0.y; + // Set up for per-scanline coverage formula, below. + float invslope = abs(dy) < 1e-9 ? 1e9 : dx / dy; + c = abs(invslope) * (0.5 * float(TILE_HEIGHT_PX) + line.stroke.y) * SX; + b = invslope; // Note: assumes square tiles, otherwise scale. + a = (line.p0.x - xy0.x - (line.p0.y - 0.5 * float(TILE_HEIGHT_PX) - xy0.y) * b) * SX; break; case Annotated_Fill: case Annotated_Stroke: // Note: we take advantage of the fact that fills and strokes // have compatible layout. AnnoFill fill = Annotated_Fill_read(ref); - x0 = int(floor((fill.bbox.x - xy0.x) * SX)); - y0 = int(floor((fill.bbox.y - xy0.y) * SY)); - x1 = int(ceil((fill.bbox.z - xy0.x) * SX)); - y1 = int(ceil((fill.bbox.w - xy0.y) * SY)); + xmin = fill.bbox.x; + xmax = fill.bbox.z; + ymin = fill.bbox.y; + ymax = fill.bbox.w; + // Just let the clamping to xmin and xmax determine the bounds. + a = 0.0; + b = 0.0; + c = 1e9; + break; + default: + ymin = 0; + ymax = 0; break; } - // At this point, we run an iterator over the coverage area, - // trying to keep divergence low. - // Right now, it's just a bbox, but we'll get finer with - // segments. + + // Draw the coverage area into the bitmaks. This uses an algorithm + // that computes the coverage of a span for given scanline. + + // Compute bounding box in tiles and clip to this bin. + int x0 = int(floor((xmin - xy0.x) * SX)); + int x1 = int(ceil((xmax - xy0.x) * SX)); + int y0 = int(floor((ymin - xy0.y) * SY)); + int y1 = int(ceil((ymax - xy0.y) * SY)); x0 = clamp(x0, 0, N_TILE_X); x1 = clamp(x1, x0, N_TILE_X); y0 = clamp(y0, 0, N_TILE_Y); y1 = clamp(y1, y0, N_TILE_Y); - // This loop draws a rectangle to the coverage bitmasks. For - // line segments, draw more precisely. - if (x0 == x1) y1 = y0; - int x = x0, y = y0; uint my_slice = th_ix / 32; uint my_mask = 1 << (th_ix & 31); - while (y < y1) { - atomicOr(sh_bitmaps[my_slice][y * N_TILE_X + x], my_mask); - x++; - if (x == x1) { - x = x0; - y++; + float t = a + b * float(y0); + for (uint y = y0; y < y1; y++) { + uint xx0 = clamp(int(floor(t - c)), x0, x1); + uint xx1 = clamp(int(ceil(t + c)), x0, x1); + for (uint x = xx0; x < xx1; x++) { + atomicOr(sh_bitmaps[my_slice][y * N_TILE_X + x], my_mask); } + t += b; } barrier(); diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index b0bec3f996366f3fc5f8e2a017621dd32622cb87..d61b227981fefe57ee154d9dd72928df4528cb29 100644 GIT binary patch delta 8669 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 delta 7198 zcmZvh37A%8702J%1QFMPEH?(p)R2_0a70kl6ikbP1XRQU1{|3gkQo&Oy}%%f3of6E zW*b=+Mi@;C`8?UCsVxR(X1394nJs34W>&x7oqNVR^YrQU{r~^xyyrdddC$G`eb>ic zsM@x(YVVrr5d%`HN(1?4!`8jm?Vr`8_tm7d^thDjQcZe4cv*p`s*mo`r>W<-Hq8XL zwzM}VK6R-!od;jsw4|x6;2YrcTUuMgeperRO?m>pH21AZ+w;IVsY_2{Xj?YBqqSvj zZ}6vkw6>;>g)CN=cENLp*)7J%6WEP5r=_#4X|a5dG^BE9{ejuRwJ9~XHn%mmcjmsP zdIVZI)lu;BR^HafUXw<{JDL~dtBmbqt4)pl__)42OXK?r>D~SK+5Px*ct_`wMGM)y z$!L{3=1<&#Y zZP9Qrf$b$eh6#3R*yVO(*{EfQY&@~P>cUFZ3H4PoD?7&4XBVN)<{HfYy?vU%rgH?>g<$OwVGh`+ zYq728GA-xKMbp+rCh~b;C)QVPX*?v06g!2Bi|&E%)0^Uz81fSux*Q~nQRi<;Qr^;<#}m{YODb}8AES1t^uo; ztFabtlAfLRsK&KmZC1nkM;^&v2ll+`XRZUu!g9S9t_O)$KC2thv_)xd1V?F%p*>>U z1kO7kTMv>&F>VGsQ$31t3%Gx0+=`|iwYUxJT=nRT3Rt~di`(H*3+!2MG48;i(PB(v zl)u6Qzz2j60v`;;gCN}vp2Vf^U5s*{1ND^TgWzKs)wOS6REw?O16H$q>r>^~v4bir zP8!&I7kn5~|AYSsntJT@qu>v5smESF239ZcbtBxpdY@{KbN@J4o9X#Hg=f9_-HQ;So$n5a!ss`jAA%?IH+3!9%6|k`dy30oPco_*O#6?)`afFu{{*h~4A;YG9`mzc zA70v4IFI$yPazr%`ZS~5-TaKvojdNr`4^18glKbA?`i(G0}NgRP*0+$<19=3mgtyom!pJ25}a(f0*^gJWDqujH0F~@R0 zcvL${>K>Bw+CeGZ@h*pC;?ivL?2GUr~rDh;;1gZtQ zQ%h>#I=EVtRGV7(?*Ueeg$KaZx_JQY_PgYs5I-)gaUhqP3ya-p>_|NV?*%q+@WJqM zx%Y-!ZvC`{-#%b%d9_(TDTHxfutyNt?gv-%T&>apU_U0SZGSE`lNFn+MEKbcEg{saTR39K!Sbuc2A<`MABvz;1UL+=llrCNyFTn3ngE;qn1cKQ~unl@iO$o=wa1&;)eU{u%Mh@V<4ew4mw z9^)}!Kd)x|u+X4jX^&>~r}SGH`8SQ^Kc1y}BylV_lJNb8Jbpu=yrN-pJQ2q;xXazostcTO8-=6LU`i zYcnAelaEj5_hd8?;v|h`CXa+p1shD8iOD1AY2w0WVsfkKi<#3atIny<-i2~HSG4vS zU^O2<5#>y{DMedOf)9nO$Gpj4=W4T4=d!|CV0AO|J(S#-p3ga8&Lqo?XG0VkX;k8- z_kb^9bdt76$zwBB z@2Hum(abZza+~=eZ0CVTGOD}8bVjv^$l>J??a6lhE`Y1Gv5G$S+J*3<*YbC5a$?QN zU$i+PYH|^{g9)=3eF+zfG=a;9GY9VBs7IaVf;~`eas2c0<8MZp#}%IQ!D`M3&jnyp z^qzE^a{a>I0=Dtup7mjK37UHNECgT76*1)a_|vrKB~}}P^;^qix90ndcDQ={G+G31 zHC0OUY)Y#-PaRoRMSq_$KJCjAO2D=I8 zUBxJmL^ALN_&7&fR3}>nF<`kotI^b>C%V9DE4gCg8gR^WLAicnyD{#xbJ4sD9m{u~ zcPEMW{CT|=;@|Pr^n%TE9b7G5QK+peeB#&3^>8(PUDWw%E)rib9Odyn(QipRsn+XX zOe*1H;vxW5i*?2QtAN81X3wR7Z y@xAF*xZ1td>5Oz=G4VD8Cu{Vt@a>F#{4%Aj!lh Date: Tue, 19 May 2020 08:21:09 -0700 Subject: [PATCH 15/32] Start implementing fills This should get the "right_edge" value for each segment plumbed through to the binning phase. It also needs to be plumbed to coarse raster and wired up there. Also considering WIP because none of this logic has been tested yet. --- piet-gpu-types/src/state.rs | 1 + piet-gpu/shader/binning.comp | 52 ++++++++++++++++++++++++++++++++-- piet-gpu/shader/binning.spv | Bin 16812 -> 21800 bytes piet-gpu/shader/elements.comp | 10 +++++-- piet-gpu/shader/elements.spv | Bin 43780 -> 45068 bytes piet-gpu/shader/setup.h | 5 ++++ piet-gpu/shader/state.h | 10 +++++-- piet-gpu/src/lib.rs | 4 +-- 8 files changed, 73 insertions(+), 9 deletions(-) diff --git a/piet-gpu-types/src/state.rs b/piet-gpu-types/src/state.rs index 35076f0..b93e9f3 100644 --- a/piet-gpu-types/src/state.rs +++ b/piet-gpu-types/src/state.rs @@ -8,6 +8,7 @@ piet_gpu! { translate: [f32; 2], bbox: [f32; 4], linewidth: f32, + right_edge: f32, flags: u32, } } diff --git a/piet-gpu/shader/binning.comp b/piet-gpu/shader/binning.comp index c3067e7..cba0217 100644 --- a/piet-gpu/shader/binning.comp +++ b/piet-gpu/shader/binning.comp @@ -11,24 +11,33 @@ layout(set = 0, binding = 0) buffer AnnotatedBuf { uint[] annotated; }; -layout(set = 0, binding = 1) buffer AllocBuf { +// This is for scanning forward for right_edge data. +layout(set = 0, binding = 1) buffer StateBuf { + uint[] state; +}; + +layout(set = 0, binding = 2) buffer AllocBuf { uint n_elements; // Will be incremented atomically to claim tiles uint tile_ix; uint alloc; }; -layout(set = 0, binding = 2) buffer BinsBuf { +layout(set = 0, binding = 3) buffer BinsBuf { uint[] bins; }; #include "annotated.h" +#include "state.h" #include "bins.h" // scale factors useful for converting coordinates to bins #define SX (1.0 / float(N_TILE_X * TILE_WIDTH_PX)) #define SY (1.0 / float(N_TILE_Y * TILE_HEIGHT_PX)) +// Constant not available in GLSL. Also consider uintBitsToFloat(0x7f800000) +#define INFINITY (1.0 / 0.0) + // Note: cudaraster has N_TILE + 1 to cut down on bank conflicts. shared uint bitmaps[N_SLICE][N_TILE]; shared uint count[N_SLICE][N_TILE]; @@ -37,6 +46,14 @@ shared uint sh_chunk_start[N_TILE]; shared uint sh_chunk_end[N_TILE]; shared uint sh_chunk_jump[N_TILE]; +shared float sh_right_edge[N_TILE]; + +#define StateBuf_stride (4 + 2 * State_size) + +StateRef state_aggregate_ref(uint partition_ix) { + return StateRef(8 + partition_ix * StateBuf_stride); +} + void main() { BinChunkRef chunk_ref = BinChunkRef((gl_LocalInvocationID.x * N_WG + gl_WorkGroupID.x) * BIN_INITIAL_ALLOC); uint wr_limit = chunk_ref.offset + BIN_INITIAL_ALLOC; @@ -65,6 +82,7 @@ void main() { tag = Annotated_tag(ref); } int x0 = 0, y0 = 0, x1 = 0, y1 = 0; + float my_right_edge = INFINITY; switch (tag) { case Annotated_Line: AnnoLineSeg line = Annotated_Line_read(ref); @@ -82,8 +100,37 @@ void main() { y0 = int(floor(fill.bbox.y * SY)); x1 = int(ceil(fill.bbox.z * SX)); y1 = int(ceil(fill.bbox.w * SY)); + my_right_edge = x1; break; } + + // If the last element in this partition is a fill edge, then we need to do a + // look-forward to find the right edge of its corresponding fill. That data is + // recorded in aggregates computed in the element processing pass. + if (gl_LocalInvocationID.x == N_TILE - 1 && tag == Annotated_Line) { + uint aggregate_ix = (my_tile + 1) * ELEMENT_BINNING_RATIO; + // This is sequential but the expectation is that the amount of + // look-forward is small (performance may degrade in the case + // of massively complex paths). + do { + StateRef agg_ref = state_aggregate_ref(aggregate_ix); + my_right_edge = State_read(agg_ref).right_edge; + aggregate_ix++; + } while (isinf(my_right_edge)); + } + + // Now propagate right_edge backward, from fill to segment. + for (uint i = 0; i < LG_N_TILE; i++) { + // Note: we could try to cut down on write bandwidth here if the value hasn't + // changed, but not sure it's worth the complexity to track. + sh_right_edge[gl_LocalInvocationID.x] = my_right_edge; + barrier(); + if (gl_LocalInvocationID.x + (1 << i) < N_TILE && isinf(my_right_edge)) { + my_right_edge = sh_right_edge[gl_LocalInvocationID.x + (1 << i)]; + } + barrier(); + } + // At this point, we run an iterator over the coverage area, // trying to keep divergence low. // Right now, it's just a bbox, but we'll get finer with @@ -141,6 +188,7 @@ void main() { chunk_n = element_count - chunk_n; } else { chunk_end = ~0; + chunk_new_start = ~0; chunk_n = element_count; } sh_chunk_start[gl_LocalInvocationID.x] = instance_ref.offset; diff --git a/piet-gpu/shader/binning.spv b/piet-gpu/shader/binning.spv index 76148c2c44d71cb55a0d2ef55715d2d6f77ee77c..a5379e6dad7ab1566cc2378b2fa9ed054c62b1a3 100644 GIT binary patch literal 21800 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 literal 16812 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 diff --git a/piet-gpu/shader/elements.comp b/piet-gpu/shader/elements.comp index 8f87b87..15ad80d 100644 --- a/piet-gpu/shader/elements.comp +++ b/piet-gpu/shader/elements.comp @@ -10,7 +10,7 @@ #define N_ROWS 4 #define WG_SIZE 32 #define LG_WG_SIZE 5 -#define TILE_SIZE (WG_SIZE * N_ROWS) +#define PARTITION_SIZE (WG_SIZE * N_ROWS) layout(local_size_x = WG_SIZE, local_size_y = 1) in; @@ -81,6 +81,7 @@ State combine_state(State a, State b) { c.translate.x = a.mat.x * b.translate.x + a.mat.z * b.translate.y + a.translate.x; c.translate.y = a.mat.y * b.translate.x + a.mat.w * b.translate.y + a.translate.y; c.linewidth = (b.flags & FLAG_SET_LINEWIDTH) == 0 ? a.linewidth : b.linewidth; + c.right_edge = (a.flags & FLAG_SET_BBOX) != 0 ? a.right_edge : (a.flags & FLAG_RESET_BBOX) != 0 ? a.bbox.z : c.right_edge; c.flags = (a.flags & (FLAG_SET_LINEWIDTH | FLAG_SET_BBOX)) | b.flags; c.flags |= (a.flags & FLAG_RESET_BBOX) >> 1; return c; @@ -143,6 +144,7 @@ shared vec4 sh_mat[WG_SIZE]; shared vec2 sh_translate[WG_SIZE]; shared vec4 sh_bbox[WG_SIZE]; shared float sh_width[WG_SIZE]; +shared float sh_right_edge[WG_SIZE]; shared uint sh_flags[WG_SIZE]; shared uint sh_tile_ix; @@ -158,7 +160,7 @@ void main() { barrier(); uint tile_ix = sh_tile_ix; - uint ix = tile_ix * TILE_SIZE + gl_LocalInvocationID.x * N_ROWS; + uint ix = tile_ix * PARTITION_SIZE + gl_LocalInvocationID.x * N_ROWS; ElementRef ref = ElementRef(ix * Element_size); th_state[0] = map_element(ref); @@ -172,6 +174,7 @@ void main() { sh_translate[gl_LocalInvocationID.x] = agg.translate; sh_bbox[gl_LocalInvocationID.x] = agg.bbox; sh_width[gl_LocalInvocationID.x] = agg.linewidth; + sh_right_edge[gl_LocalInvocationID.x] = agg.right_edge; sh_flags[gl_LocalInvocationID.x] = agg.flags; for (uint i = 0; i < LG_WG_SIZE; i++) { barrier(); @@ -190,6 +193,7 @@ void main() { sh_translate[gl_LocalInvocationID.x] = agg.translate; sh_bbox[gl_LocalInvocationID.x] = agg.bbox; sh_width[gl_LocalInvocationID.x] = agg.linewidth; + sh_right_edge[gl_LocalInvocationID.x] = agg.right_edge; sh_flags[gl_LocalInvocationID.x] = agg.flags; } @@ -198,6 +202,7 @@ void main() { exclusive.mat = vec4(1.0, 0.0, 0.0, 1.0); exclusive.translate = vec2(0.0, 0.0); exclusive.linewidth = 1.0; //TODO should be 0.0 + exclusive.right_edge = 0.0; exclusive.flags = 0; // Publish aggregate for this partition @@ -250,6 +255,7 @@ void main() { other.translate = sh_translate[ix]; other.bbox = sh_bbox[ix]; other.linewidth = sh_width[ix]; + other.right_edge = sh_right_edge[ix]; other.flags = sh_flags[ix]; row = combine_state(row, other); } diff --git a/piet-gpu/shader/elements.spv b/piet-gpu/shader/elements.spv index 7828aa4732cc2ea3c5291afc93f45b4af815d3c3..ff0ae2d456ea352ee946cbe946a3e9b56ffc4853 100644 GIT binary patch literal 45068 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 O*%GYo{9Hr1YxqAgC=VY1 diff --git a/piet-gpu/shader/setup.h b/piet-gpu/shader/setup.h index 5d8fb9b..6b00661 100644 --- a/piet-gpu/shader/setup.h +++ b/piet-gpu/shader/setup.h @@ -51,9 +51,14 @@ #define N_TILE_X 16 #define N_TILE_Y 16 #define N_TILE (N_TILE_X * N_TILE_Y) +#define LG_N_TILE 8 #define N_SLICE (N_TILE / 32) // Number of workgroups for binning kernel #define N_WG 16 +// This is the ratio of the number of elements in a binning workgroup +// over the number of elements in a partition workgroup. +#define ELEMENT_BINNING_RATIO 4 + #define BIN_INITIAL_ALLOC 64 #define BIN_ALLOC 256 diff --git a/piet-gpu/shader/state.h b/piet-gpu/shader/state.h index 2547b93..bc6192f 100644 --- a/piet-gpu/shader/state.h +++ b/piet-gpu/shader/state.h @@ -9,10 +9,11 @@ struct State { vec2 translate; vec4 bbox; float linewidth; + float right_edge; uint flags; }; -#define State_size 48 +#define State_size 52 StateRef State_index(StateRef ref, uint index) { return StateRef(ref.offset + index * State_size); @@ -32,12 +33,14 @@ State State_read(StateRef ref) { uint raw9 = state[ix + 9]; uint raw10 = state[ix + 10]; uint raw11 = state[ix + 11]; + uint raw12 = state[ix + 12]; State s; s.mat = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); s.translate = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); s.bbox = vec4(uintBitsToFloat(raw6), uintBitsToFloat(raw7), uintBitsToFloat(raw8), uintBitsToFloat(raw9)); s.linewidth = uintBitsToFloat(raw10); - s.flags = raw11; + s.right_edge = uintBitsToFloat(raw11); + s.flags = raw12; return s; } @@ -54,6 +57,7 @@ void State_write(StateRef ref, State s) { state[ix + 8] = floatBitsToUint(s.bbox.z); state[ix + 9] = floatBitsToUint(s.bbox.w); state[ix + 10] = floatBitsToUint(s.linewidth); - state[ix + 11] = s.flags; + state[ix + 11] = floatBitsToUint(s.right_edge); + state[ix + 12] = s.flags; } diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 2dca39d..70b02f5 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -185,10 +185,10 @@ impl Renderer { ]) ?; let bin_code = include_bytes!("../shader/binning.spv"); - let bin_pipeline = device.create_simple_compute_pipeline(bin_code, 3, 0)?; + let bin_pipeline = device.create_simple_compute_pipeline(bin_code, 4, 0)?; let bin_ds = device.create_descriptor_set( &bin_pipeline, - &[&anno_buf, &bin_alloc_buf_dev, &bin_buf], + &[&anno_buf, &state_buf, &bin_alloc_buf_dev, &bin_buf], &[], )?; From 076e6d600d828f7b7902b2603d22178faf25c3c9 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Wed, 20 May 2020 07:38:52 -0700 Subject: [PATCH 16/32] Progress on wiring up fills Write the right_edge to the binning output. More work on encoding the fill/stroke distinction and plumbing that through the pipeline. This is a bit unsatisfying because of the code duplication; having an extra fill/stroke bool might be better, but I want to avoid making the structs bigger (this could be solved by better packing in the struct encoding). Fills are plumbed through to the last stage. Backdrop is WIP. --- piet-gpu-types/src/annotated.rs | 13 +++-- piet-gpu-types/src/bins.rs | 3 ++ piet-gpu-types/src/scene.rs | 11 ++++- piet-gpu/shader/annotated.h | 81 +++++++++++++++++++++++++------- piet-gpu/shader/binning.comp | 23 ++++----- piet-gpu/shader/binning.spv | Bin 21800 -> 22016 bytes piet-gpu/shader/bins.h | 6 ++- piet-gpu/shader/coarse.comp | 34 ++++++++++---- piet-gpu/shader/coarse.spv | Bin 30280 -> 33272 bytes piet-gpu/shader/elements.comp | 24 +++++++--- piet-gpu/shader/elements.spv | Bin 45068 -> 45312 bytes piet-gpu/shader/scene.h | 21 +++++---- piet-gpu/src/pico_svg.rs | 4 +- piet-gpu/src/render_ctx.rs | 22 ++++++--- 14 files changed, 175 insertions(+), 67 deletions(-) diff --git a/piet-gpu-types/src/annotated.rs b/piet-gpu-types/src/annotated.rs index 247ab12..f7a6ad6 100644 --- a/piet-gpu-types/src/annotated.rs +++ b/piet-gpu-types/src/annotated.rs @@ -3,7 +3,14 @@ use piet_gpu_derive::piet_gpu; piet_gpu! { #[gpu_write] mod annotated { - struct AnnoLineSeg { + struct AnnoFillLineSeg { + p0: [f32; 2], + p1: [f32; 2], + // A note: the layout of this struct is shared with + // AnnoStrokeLineSeg. In that case, we actually write + // [0.0, 0.0] as the stroke field, to minimize divergence. + } + struct AnnoStrokeLineSeg { p0: [f32; 2], p1: [f32; 2], // halfwidth in both x and y for binning @@ -35,8 +42,8 @@ piet_gpu! { } enum Annotated { Nop, - // The segments need a flag to indicate fill/stroke - Line(AnnoLineSeg), + FillLine(AnnoFillLineSeg), + StrokeLine(AnnoStrokeLineSeg), Quad(AnnoQuadSeg), Cubic(AnnoCubicSeg), Stroke(AnnoStroke), diff --git a/piet-gpu-types/src/bins.rs b/piet-gpu-types/src/bins.rs index 88f16f1..1ac2413 100644 --- a/piet-gpu-types/src/bins.rs +++ b/piet-gpu-types/src/bins.rs @@ -7,6 +7,9 @@ piet_gpu! { mod bins { struct BinInstance { element_ix: u32, + // Right edge of the bounding box of the associated fill + // element; used in backdrop computation. + right_edge: f32, } struct BinChunk { diff --git a/piet-gpu-types/src/scene.rs b/piet-gpu-types/src/scene.rs index 7451c9c..5792c94 100644 --- a/piet-gpu-types/src/scene.rs +++ b/piet-gpu-types/src/scene.rs @@ -85,8 +85,15 @@ piet_gpu! { } enum Element { Nop, - // The segments need a flag to indicate fill/stroke - Line(LineSeg), + // Another approach to encoding would be to use a single + // variant but have a bool for fill/stroke. This could be + // packed into the tag, so the on-the-wire representation + // would be very similar to what's here. + StrokeLine(LineSeg), + FillLine(LineSeg), + + // Note: we'll need to handle the stroke/fill distinction + // for these as well, when we do flattening on the GPU. Quad(QuadSeg), Cubic(CubicSeg), Stroke(Stroke), diff --git a/piet-gpu/shader/annotated.h b/piet-gpu/shader/annotated.h index a3fc464..9812264 100644 --- a/piet-gpu/shader/annotated.h +++ b/piet-gpu/shader/annotated.h @@ -1,6 +1,10 @@ // Code auto-generated by piet-gpu-derive -struct AnnoLineSegRef { +struct AnnoFillLineSegRef { + uint offset; +}; + +struct AnnoStrokeLineSegRef { uint offset; }; @@ -24,16 +28,27 @@ struct AnnotatedRef { uint offset; }; -struct AnnoLineSeg { +struct AnnoFillLineSeg { + vec2 p0; + vec2 p1; +}; + +#define AnnoFillLineSeg_size 16 + +AnnoFillLineSegRef AnnoFillLineSeg_index(AnnoFillLineSegRef ref, uint index) { + return AnnoFillLineSegRef(ref.offset + index * AnnoFillLineSeg_size); +} + +struct AnnoStrokeLineSeg { vec2 p0; vec2 p1; vec2 stroke; }; -#define AnnoLineSeg_size 24 +#define AnnoStrokeLineSeg_size 24 -AnnoLineSegRef AnnoLineSeg_index(AnnoLineSegRef ref, uint index) { - return AnnoLineSegRef(ref.offset + index * AnnoLineSeg_size); +AnnoStrokeLineSegRef AnnoStrokeLineSeg_index(AnnoStrokeLineSegRef ref, uint index) { + return AnnoStrokeLineSegRef(ref.offset + index * AnnoStrokeLineSeg_size); } struct AnnoQuadSeg { @@ -87,18 +102,39 @@ AnnoStrokeRef AnnoStroke_index(AnnoStrokeRef ref, uint index) { } #define Annotated_Nop 0 -#define Annotated_Line 1 -#define Annotated_Quad 2 -#define Annotated_Cubic 3 -#define Annotated_Stroke 4 -#define Annotated_Fill 5 +#define Annotated_FillLine 1 +#define Annotated_StrokeLine 2 +#define Annotated_Quad 3 +#define Annotated_Cubic 4 +#define Annotated_Stroke 5 +#define Annotated_Fill 6 #define Annotated_size 44 AnnotatedRef Annotated_index(AnnotatedRef ref, uint index) { return AnnotatedRef(ref.offset + index * Annotated_size); } -AnnoLineSeg AnnoLineSeg_read(AnnoLineSegRef ref) { +AnnoFillLineSeg AnnoFillLineSeg_read(AnnoFillLineSegRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = annotated[ix + 0]; + uint raw1 = annotated[ix + 1]; + uint raw2 = annotated[ix + 2]; + uint raw3 = annotated[ix + 3]; + AnnoFillLineSeg s; + s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); + s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + return s; +} + +void AnnoFillLineSeg_write(AnnoFillLineSegRef ref, AnnoFillLineSeg s) { + uint ix = ref.offset >> 2; + annotated[ix + 0] = floatBitsToUint(s.p0.x); + annotated[ix + 1] = floatBitsToUint(s.p0.y); + annotated[ix + 2] = floatBitsToUint(s.p1.x); + annotated[ix + 3] = floatBitsToUint(s.p1.y); +} + +AnnoStrokeLineSeg AnnoStrokeLineSeg_read(AnnoStrokeLineSegRef ref) { uint ix = ref.offset >> 2; uint raw0 = annotated[ix + 0]; uint raw1 = annotated[ix + 1]; @@ -106,14 +142,14 @@ AnnoLineSeg AnnoLineSeg_read(AnnoLineSegRef ref) { uint raw3 = annotated[ix + 3]; uint raw4 = annotated[ix + 4]; uint raw5 = annotated[ix + 5]; - AnnoLineSeg s; + AnnoStrokeLineSeg s; s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); s.stroke = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); return s; } -void AnnoLineSeg_write(AnnoLineSegRef ref, AnnoLineSeg s) { +void AnnoStrokeLineSeg_write(AnnoStrokeLineSegRef ref, AnnoStrokeLineSeg s) { uint ix = ref.offset >> 2; annotated[ix + 0] = floatBitsToUint(s.p0.x); annotated[ix + 1] = floatBitsToUint(s.p0.y); @@ -239,8 +275,12 @@ uint Annotated_tag(AnnotatedRef ref) { return annotated[ref.offset >> 2]; } -AnnoLineSeg Annotated_Line_read(AnnotatedRef ref) { - return AnnoLineSeg_read(AnnoLineSegRef(ref.offset + 4)); +AnnoFillLineSeg Annotated_FillLine_read(AnnotatedRef ref) { + return AnnoFillLineSeg_read(AnnoFillLineSegRef(ref.offset + 4)); +} + +AnnoStrokeLineSeg Annotated_StrokeLine_read(AnnotatedRef ref) { + return AnnoStrokeLineSeg_read(AnnoStrokeLineSegRef(ref.offset + 4)); } AnnoQuadSeg Annotated_Quad_read(AnnotatedRef ref) { @@ -263,9 +303,14 @@ void Annotated_Nop_write(AnnotatedRef ref) { annotated[ref.offset >> 2] = Annotated_Nop; } -void Annotated_Line_write(AnnotatedRef ref, AnnoLineSeg s) { - annotated[ref.offset >> 2] = Annotated_Line; - AnnoLineSeg_write(AnnoLineSegRef(ref.offset + 4), s); +void Annotated_FillLine_write(AnnotatedRef ref, AnnoFillLineSeg s) { + annotated[ref.offset >> 2] = Annotated_FillLine; + AnnoFillLineSeg_write(AnnoFillLineSegRef(ref.offset + 4), s); +} + +void Annotated_StrokeLine_write(AnnotatedRef ref, AnnoStrokeLineSeg s) { + annotated[ref.offset >> 2] = Annotated_StrokeLine; + AnnoStrokeLineSeg_write(AnnoStrokeLineSegRef(ref.offset + 4), s); } void Annotated_Quad_write(AnnotatedRef ref, AnnoQuadSeg s) { diff --git a/piet-gpu/shader/binning.comp b/piet-gpu/shader/binning.comp index cba0217..60b12e0 100644 --- a/piet-gpu/shader/binning.comp +++ b/piet-gpu/shader/binning.comp @@ -84,8 +84,9 @@ void main() { int x0 = 0, y0 = 0, x1 = 0, y1 = 0; float my_right_edge = INFINITY; switch (tag) { - case Annotated_Line: - AnnoLineSeg line = Annotated_Line_read(ref); + case Annotated_FillLine: + case Annotated_StrokeLine: + AnnoStrokeLineSeg line = Annotated_StrokeLine_read(ref); x0 = int(floor((min(line.p0.x, line.p1.x) - line.stroke.x) * SX)); y0 = int(floor((min(line.p0.y, line.p1.y) - line.stroke.y) * SY)); x1 = int(ceil((max(line.p0.x, line.p1.x) + line.stroke.x) * SX)); @@ -107,7 +108,7 @@ void main() { // If the last element in this partition is a fill edge, then we need to do a // look-forward to find the right edge of its corresponding fill. That data is // recorded in aggregates computed in the element processing pass. - if (gl_LocalInvocationID.x == N_TILE - 1 && tag == Annotated_Line) { + if (gl_LocalInvocationID.x == N_TILE - 1 && tag == Annotated_FillLine) { uint aggregate_ix = (my_tile + 1) * ELEMENT_BINNING_RATIO; // This is sequential but the expectation is that the amount of // look-forward is small (performance may degrade in the case @@ -165,9 +166,9 @@ void main() { uint chunk_new_start; // Refactor to reduce code duplication? if (chunk_n > 0) { - uint next_chunk = chunk_ref.offset + BinChunk_size + chunk_n * 4; - if (next_chunk + BinChunk_size + min(24, element_count * 4) > wr_limit) { - uint alloc_amount = max(BIN_ALLOC, BinChunk_size + element_count * 4); + uint next_chunk = chunk_ref.offset + BinChunk_size + chunk_n * BinInstance_size; + if (next_chunk + BinChunk_size + min(24, element_count * BinInstance_size) > wr_limit) { + uint alloc_amount = max(BIN_ALLOC, BinChunk_size + element_count * BinInstance_size); // could try to reduce fragmentation if BIN_ALLOC is only a bit above needed next_chunk = atomicAdd(alloc, alloc_amount); wr_limit = next_chunk + alloc_amount; @@ -176,10 +177,10 @@ void main() { chunk_ref = BinChunkRef(next_chunk); } BinInstanceRef instance_ref = BinInstanceRef(chunk_ref.offset + BinChunk_size); - if (instance_ref.offset + element_count * 4 > wr_limit) { + if (instance_ref.offset + element_count * BinInstance_size > wr_limit) { chunk_end = wr_limit; - chunk_n = (wr_limit - instance_ref.offset) / 4; - uint alloc_amount = max(BIN_ALLOC, BinChunk_size + (element_count - chunk_n) * 4); + chunk_n = (wr_limit - instance_ref.offset) / BinInstance_size; + uint alloc_amount = max(BIN_ALLOC, BinChunk_size + (element_count - chunk_n) * BinInstance_size); chunk_new_start = atomicAdd(alloc, alloc_amount); wr_limit = chunk_new_start + alloc_amount; BinChunk_write(chunk_ref, BinChunk(chunk_n, BinChunkRef(chunk_new_start))); @@ -209,11 +210,11 @@ void main() { if (my_slice > 0) { idx += count[my_slice - 1][bin_ix]; } - uint out_offset = sh_chunk_start[bin_ix] + idx * 4; + uint out_offset = sh_chunk_start[bin_ix] + idx * BinInstance_size; if (out_offset >= sh_chunk_end[bin_ix]) { out_offset += sh_chunk_jump[bin_ix]; } - BinInstance_write(BinInstanceRef(out_offset), BinInstance(element_ix)); + BinInstance_write(BinInstanceRef(out_offset), BinInstance(element_ix, my_right_edge)); } x++; if (x == x1) { diff --git a/piet-gpu/shader/binning.spv b/piet-gpu/shader/binning.spv index a5379e6dad7ab1566cc2378b2fa9ed054c62b1a3..4fda673c4aea604390b10ba4ceb6b93a76378ee0 100644 GIT binary patch literal 22016 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 T3`LvYEyiC0R`>oAdw=~e9^Xm4 literal 21800 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 diff --git a/piet-gpu/shader/bins.h b/piet-gpu/shader/bins.h index 3ce06e0..85f7536 100644 --- a/piet-gpu/shader/bins.h +++ b/piet-gpu/shader/bins.h @@ -10,9 +10,10 @@ struct BinChunkRef { struct BinInstance { uint element_ix; + float right_edge; }; -#define BinInstance_size 4 +#define BinInstance_size 8 BinInstanceRef BinInstance_index(BinInstanceRef ref, uint index) { return BinInstanceRef(ref.offset + index * BinInstance_size); @@ -32,14 +33,17 @@ BinChunkRef BinChunk_index(BinChunkRef ref, uint index) { BinInstance BinInstance_read(BinInstanceRef ref) { uint ix = ref.offset >> 2; uint raw0 = bins[ix + 0]; + uint raw1 = bins[ix + 1]; BinInstance s; s.element_ix = raw0; + s.right_edge = uintBitsToFloat(raw1); return s; } void BinInstance_write(BinInstanceRef ref, BinInstance s) { uint ix = ref.offset >> 2; bins[ix + 0] = s.element_ix; + bins[ix + 1] = floatBitsToUint(s.right_edge); } BinChunk BinChunk_read(BinChunkRef ref) { diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index e331076..57abd73 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -30,6 +30,7 @@ layout(set = 0, binding = 3) buffer PtclBuf { #define N_RINGBUF 512 shared uint sh_elements[N_RINGBUF]; +shared float sh_right_edge[N_RINGBUF]; shared uint sh_chunk[N_WG]; shared uint sh_chunk_next[N_WG]; shared uint sh_chunk_n[N_WG]; @@ -41,6 +42,7 @@ shared uint sh_selected_n; shared uint sh_elements_ref; shared uint sh_bitmaps[N_SLICE][N_TILE]; +shared uint sh_backdrop[N_SLICE][N_TILE]; // scale factors useful for converting coordinates to tiles #define SX (1.0 / float(TILE_WIDTH_PX)) @@ -113,6 +115,7 @@ void main() { while (true) { for (uint i = 0; i < N_SLICE; i++) { sh_bitmaps[i][th_ix] = 0; + sh_backdrop[i][th_ix] = 0; } while (wr_ix - rd_ix <= N_TILE) { @@ -157,8 +160,10 @@ void main() { } BinInstanceRef inst_ref = BinInstanceRef(sh_elements_ref); if (th_ix < chunk_n) { - uint el = BinInstance_read(BinInstance_index(inst_ref, th_ix)).element_ix; - sh_elements[(wr_ix + th_ix) % N_RINGBUF] = el; + BinInstance inst = BinInstance_read(BinInstance_index(inst_ref, th_ix)); + uint wr_el_ix = (wr_ix + th_ix) % N_RINGBUF; + sh_elements[wr_el_ix] = inst.element_ix; + sh_right_edge[wr_el_ix] = inst.right_edge; } wr_ix += chunk_n; } @@ -180,8 +185,9 @@ void main() { // Bounding box of element in pixel coordinates. float xmin, xmax, ymin, ymax; switch (tag) { - case Annotated_Line: - AnnoLineSeg line = Annotated_Line_read(ref); + case Annotated_FillLine: + case Annotated_StrokeLine: + AnnoStrokeLineSeg line = Annotated_StrokeLine_read(ref); xmin = min(line.p0.x, line.p1.x) - line.stroke.x; xmax = max(line.p0.x, line.p1.x) + line.stroke.x; ymin = min(line.p0.y, line.p1.y) - line.stroke.y; @@ -214,7 +220,7 @@ void main() { break; } - // Draw the coverage area into the bitmaks. This uses an algorithm + // Draw the coverage area into the bitmasks. This uses an algorithm // that computes the coverage of a span for given scanline. // Compute bounding box in tiles and clip to this bin. @@ -263,15 +269,27 @@ void main() { tag = Annotated_tag(ref); switch (tag) { - case Annotated_Line: - AnnoLineSeg line = Annotated_Line_read(ref); + case Annotated_FillLine: + case Annotated_StrokeLine: + AnnoStrokeLineSeg line = Annotated_StrokeLine_read(ref); Segment seg = Segment(line.p0, line.p1); alloc_chunk(chunk_n_segs, seg_chunk_ref, first_seg_chunk, seg_limit); Segment_write(SegmentRef(seg_chunk_ref.offset + SegChunk_size + Segment_size * chunk_n_segs), seg); chunk_n_segs++; break; case Annotated_Fill: - chunk_n_segs = 0; + if (chunk_n_segs > 0) { + AnnoFill fill = Annotated_Fill_read(ref); + SegChunk_write(seg_chunk_ref, SegChunk(chunk_n_segs, SegChunkRef(0))); + seg_chunk_ref.offset += SegChunk_size + Segment_size * chunk_n_segs; + CmdFill cmd_fill; + cmd_fill.seg_ref = first_seg_chunk.offset; + cmd_fill.rgba_color = fill.rgba_color; + alloc_cmd(cmd_ref, cmd_limit); + Cmd_Fill_write(cmd_ref, cmd_fill); + cmd_ref.offset += Cmd_size; + chunk_n_segs = 0; + } break; case Annotated_Stroke: if (chunk_n_segs > 0) { diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index d61b227981fefe57ee154d9dd72928df4528cb29..e201c5ee594c6bd6ed8b29c3756015d10c16c85a 100644 GIT binary patch literal 33272 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 literal 30280 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 b8Mxeka(#?xuHJv{gUqLOxi8!wIY<5v^rU{n diff --git a/piet-gpu/shader/elements.comp b/piet-gpu/shader/elements.comp index 15ad80d..341952b 100644 --- a/piet-gpu/shader/elements.comp +++ b/piet-gpu/shader/elements.comp @@ -98,8 +98,9 @@ State map_element(ElementRef ref) { c.linewidth = 1.0; // TODO should be 0.0 c.flags = 0; switch (tag) { - case Element_Line: - LineSeg line = Element_Line_read(ref); + case Element_FillLine: + case Element_StrokeLine: + LineSeg line = Element_FillLine_read(ref); c.bbox.xy = min(line.p0, line.p1); c.bbox.zw = max(line.p0, line.p1); break; @@ -272,13 +273,22 @@ void main() { AnnotatedRef out_ref = AnnotatedRef((ix + i) * Annotated_size); uint tag = Element_tag(this_ref); switch (tag) { - case Element_Line: - LineSeg line = Element_Line_read(this_ref); - AnnoLineSeg anno_line; + case Element_FillLine: + case Element_StrokeLine: + LineSeg line = Element_StrokeLine_read(this_ref); + AnnoStrokeLineSeg anno_line; anno_line.p0 = st.mat.xz * line.p0.x + st.mat.yw * line.p0.y + st.translate; anno_line.p1 = st.mat.xz * line.p1.x + st.mat.yw * line.p1.y + st.translate; - anno_line.stroke = get_linewidth(st); - Annotated_Line_write(out_ref, anno_line); + if (tag == Element_StrokeLine) { + anno_line.stroke = get_linewidth(st); + } else { + anno_line.stroke = vec2(0.0); + } + // We do encoding a bit by hand to minimize divergence. Another approach + // would be to have a fill/stroke bool. + uint out_tag = tag == Element_FillLine ? Annotated_FillLine : Annotated_StrokeLine; + annotated[out_ref.offset >> 2] = out_tag; + AnnoStrokeLineSeg_write(AnnoStrokeLineSegRef(out_ref.offset + 4), anno_line); break; case Element_Stroke: Stroke stroke = Element_Stroke_read(this_ref); diff --git a/piet-gpu/shader/elements.spv b/piet-gpu/shader/elements.spv index ff0ae2d456ea352ee946cbe946a3e9b56ffc4853..c947240a374822edb8c39f605604b116253e8cd9 100644 GIT binary patch literal 45312 zcma)_2Y_Bx)wM6oObEUAmQX|Qz4sRBOm-Is=oZ+Q6N9FSJR?Y z)r?i2R=?H2T?Vc=se9OptFN)L_F1Y9yOwQMe7eA$w6Q}*k5eCyFdMqs?6iI~|_@H5dMyTWUxYUZhagGP@V zHvG^5le#A!JhXd-se@NIc$HPwJ9w4#@b6$vs%jzu%{ddeHHO}tv(gWw_FaaL?8&L- zXKK#QYEF3V*IFgHp9!q4t0s4?CahxI8LN4~wf~@r!-ot@iBog6O2oILnj7DCeO=Xj z;GGU0GOVme%}-g+0`S_em0#{B^~h6?^~pVk6IN~4w=lRK->bf~BIU7$Nj&Ck^Ox~l&0_-V5SH9x6QUe+j2jq!KaPFSsd+?N0+X0M_nas=B|>)%l= zj(@xE&T1*}mZQgv!G&77S{$jUxw9Hj`>6Nf|Kxrqj@*C9prPZ(jGt)S8LH*bjMb}# zI(V6XS;w~aU01aNc$e;p;}0Be1?|19=4a}3>w$NjIAq+U5#uM0Z7)+RpSqu^wRKe+!h3wd?iV#bV~2FZ`?R?GtJ|1k zhIDgTqSl$JP3U{Z_A0bTC40L{_tD1qw`-iS+5%i(W(IW+8JW|%aph_6zs-Bb%~EYs zjB6hx4?(Zw-xy=-BF4ZXhTH9bM`+ivT~Caes~w9Np6ma;g7)|wit)P^<6EcQ{|Mmv z?NW^2vl!oAhyNbH@p}~G_bJAI?Hv5t3byOtyJ!5Y)!<_M|G8iPk2r&R;>=PFE8_I* zn*Tl(_FhLdv?oSa<#k|}ZuVud*VX(?-Rqr|=UB^+-hD3jlYK64?sLymb+4!U??3)f zy4E?N<~4N;yD#Uj=Zd=b7^-=JU*vb^iKx)T&d>&(wK$ zRY${3q@OnTfSRB3UgJE~op<(8S2ag<0zBitKD|17>yQ<*QbzNbW**0xueC=ys^h`! zdGx7Hs^hnhB`XBC7Ii=6Tuy;cU9|RG)ciE(qF&C$d8>IG%X6ZunydOQ+SIsTpJh*N z&3U#B^-S5i^}hWM@iN!b!I|rsbsjGT!`f4A%~jpc)Va=5oeQ_7mhYhL$4~frSqams z%JE)z&Y^GTo~OF7SB!SC|0B$#_Nh5_HF{01&T)QEj*jXAaC?4pRTuZl=O+07&(_#7 zt+`xO6YS_Bs zef`Q;AA$dRact%7sBWYmQsbNG_iy1DtJ~6n6vVQGu=YF{r-}Zi)wYn2L za(H*^J6`Lak&*wS7~|Z**h$^(J@^pVRrhuP=`snZ+piI&oi@f?^KT#eiOr8f7`7~jt7)iz(P%{!{M0qs56QGERLLh zuiCsPZYLKE^7O==zRl~noz*OD-g<;K?_6!Z24Pw?F9=WGRlv!+M%%9^@49W?lXs&w z@5#G)oA>11rp5Hg7$yTi3VSe67?q7@oS00;jIy+J1Fi9n~p?-x-D9 zISs!H3%^SWzbhJk*A;%Z7Jhg1^6RYbZu6e?xWCPN*2DJ^9G9N;c&yEP*5j!*?>P?7 z!`t)itUd&DEf_Mkb&NWz&l`MNZV07)h6bOb!53)o#TtCsUc94P5uWv46~x+H_R^>cM@g&%r(S zzMi#d>x8L>vE|2r%20yOBkMG4hs*B)5>l`oh0L^#8 z*8E-pPmSAAy$)~tvG>3}YqV?YtUl`H+gVN5RaFP^-7NOL@TsvntHt0$eFxLZ+gUB% z%id9K0UvWnd!4sz*tUmv*W-5RW$W8|2RL%fpn>Cu4jHq}xXEz2s z4eB1v`@ZbmBaBxIuJcjwtn*3W_C2JlI$gg}?b}`E^=M!LRAXxhKMTDrg;>+rZ4juTStk)je%qz9wk-J=oS;kG-vXRGa5(Q)}$AZLR#; zgx*!X)YkL038kZY6|FtLj_NPs`d;`?@c8ag!zb2rpT1AIE}hkky?9r(B7FFvyiuLh zdeus7&i`IMybEdYjT(GYxW(mIY~8SL*Wd#ie1~4VPqim}%=qyK4%&an&;xs(tvahA z4Zj26Om+0cLG5R)NUuQM0!6!8M#9q9sIs)E~>rrm+{Udw%bXLbS_;C$>LN7jD zbt=5|=Gt{`)O%Vl-_GjvUc9S13(g+vnWV?)oL)Yi)p-qmeuIC%7w@R9gijoQNNX?M z0M7pT5xCqxkMZM-1t9lvUJ!(SYr=W7uD`!`R%`XLcU9}cTRCed=ddAKuCE*Q^5?$T;DZ}{7<@$io!dPz zyqBGKHt<%y+DV`By?i>WgBtwcf^UWYW2_BJ1*)p8`EQ=1=WFq)&(({={cOdY^yz5f z&ek*bVjB1u&u8xbE!O6%abGRt=h(!SnNMy$9?4qyKZ8%r z*VTFsz|?xbZ*ToR8zi3h{lzr!v3|Mn=vv&Lb+5)bmXq=Qz>Zg21l2Vx z$0x>O=-T?xjIk7bVk`|dhMKkkwawa>0n7E#=FdYiZbh);)U>Ts+Z^XJrCc9vKE!66 z53r6?)8+%L>*qKhUgi2|^NV`Mtq*pbn(Mv+z2iKGTz7xApq4pr1XgpLu{WbPmd9AW z6}`tLe0#9#9KJKyH4EPz?3}{)0uLzoeqd{leSfg|<<4aUedaO}>|E3q!DAGCYR|l! zoBr17y+9wwxqtmmKt*pLT8L^I1Llc#LZKtGUk(rZ4x2F(=nP+KkELX)C5< zwHed*l!aL*Q=k<_oGciTdw9m1^ba3|7Wx%YOenjn)NTtJRhUi$2mVv zFL#Z8Nw1IEdNud{pq98#f&J+J481mE#r^^~b*cZ3R*rui?zPwPZ_&HPo=fugYwlb# zuMfawyxAb0x5+mToJ)Rd`~qO(c`nAj8(LZ4-nEbU2GzKK?e98GLQ8!o*Y*ME`d?6U z&t>_wHD9ylH`d(qPy2H<_gt30QgipE$07GxkAwG2xqfTVyJl)02f6o6wajZU#QhOI z0`8s)9|QN^6h0B|eK7n`_)Il--WS2MuJTLauB&zC9&Mf8i{;k&1wH?3Y1S$Ceyo-} zC$pGi3Vs&c>v!zu!@W<<#i;2hK;89w!(#X>&k=RK-h``_ExvEHe2?8IzHh_T;wxUB z*|@LXDfEor*NIluM&297g559K1LNQvwwCT3=^lEI4N2&^q2eHS}k)v z9Nw1z;m5#_#l8?;r-4U--RH*p4%|Jh9{(SK`_=xpfL))q)>+*OHx08apQOAFMUwJqq^tCEjDTOIq(_a5T=l-T9IPbsEtN9@M;{YLmmxbHW@jpzG~aQ%J1QF7mJgpb6&UBMmi`;FKg@B59C`+lS3LkjMA z-)ofi0}DP9yYDk%*WdRUCHH+s$$g&@Zau!s2pOZxQY@vF|Oy9q)UKaGx=JZxL=j-&=&A3irK5xX( zKI8b_B6jon-Xh%RSl?TO8_#zZ;o5y?5w6{L7V=HF3VMG$h~_=>DYOl^2C`h$#9F@* zAFs)i;A;AO!d3KOu%FNI*Vau_^V%xjgeX3L9t!r^$md_%`(!r$Vd&btMjO+9L*jZb z*7g~363_k(^7yQ3^ZuMVj|4l$v0lgJu^kOoJCQk<^H{K&_w&sCIB;|B$D^rd+zDVc z@A=2pu}*{=Pn)^DXQ(Cj$>5e&`=0_=^SOUV=HmLF3Ric$_ZYe3wVeT0)8_p}z7DZQ zVLKCi8@;;oa8I2D_P*pAJ4UXb*!z*|>zbYe_88fJOs?bp1FJPlxO^9VgeTs?Vy4Sts9So6sBNuJ+=n|XeRrmm0o$KQj^<9AN;crASy zuCC8y`d4T#(Hv_Yxjy1o>Al9gcV7d$_wBv@yg~ma&5!m!)OP#idJAlfTz}pMtNCuh z>yP|(nz4-C!szAwBJ=$tKF#^QgQlK*e*&9t3Ul+j_Pb#Bruvcf{NDqs--3_V#P`9* zQTP7%0lgpV)AnbYn)QiO&tJjKdj5u{o_hWcF6;RR+i<~ggbkLUFlwQfw$ z{eQ!a>3pnHu1{kA2b`GJ`6cZ^ntJN|s@7Ad8`PMd=f;%llbF-M6Z2{GY2luW>iT#d zb%524={emAHm2vaG3ELsW*0ayJsYiiShxX0qDr=j7J`29heK{+dde(V1uyM=# z!|Z74`gpwN0K2aoYaMcZl4nkEGtXRT>iW1x<_4R`J>vY`NAsYm>*HRV7wlehta;@6 zB+q={W}f-c)b(+HF90@Ad4E_CO?}ddHeau21qT3~uIG1WjEZ_tK(Z^OX08 zerW3YxF`FA-II5=}jMmI6PEk7Lav*C%Bz zKvUPpeY_0VJmvjiSu}NhJRg<=dp9k6!=1F52f@Sst6$ygU!B09VU(LYrE7 zPk95)d>+e{u=yS%*8sWiC}Ur_w&z+V_r0Wc_uMMAJ^AGC)OL^Isl9<^L|HU@jFwfWvh?sbQ0R-1qiqIZloeKw`{ z+5Szi{bumgu{qc}GM_ErYOaIx*$V8(`DojcrsjOaiL(tj^Vu3K&wRE8UquXc>)DQ8 zE%W&nIP)0@mS;ZOgPo6Kv}Ha%SGey|i+yU@5u93f0L!g~uG)$JTt}}S|DD0EQNAzl z0?zkk{mrSSU&ibTHpflG_1R-LxLVe253nECP227?HP=m?INt^vC(jIf!PPuY=G_yl z=3Mutcf7sNZTr&iNAsh7P;Iw2?_m0UX!qRs(UQ5=FTVc zI0Rg-$DwdFkG1P@IM|Qtq3tl5n(HA>oTI>5k0ZhItjE#d%ZZ_GO~=ryWj&4syB_M= zkDyn}oR0^qX*-Ty9@~lFtj7spdDi13uR*PPSA^33H7uyb*YHfuYTUOnT_1}FAe zV0q?wE;!epbHMT(pYMU)uj4Vn;e&5E3ccE z!quLl7SDmp!G1gkv|UD1Gp;!6bOqRTwPILb*PP`2e_#`E&TQ#8gkr2f^xz`4HHcd5)Cplb8>K6VvDHU%-9NR*%mVu(~mQ z_Iw0v%shL__0iw^(qmxvs_XJ7y*##Gf*q^vae8^KT~C0$b~#3yKE_e^{K!7F*Y+g6 z`&!#mVE3@S&lk_oKTGqY{a3Z!KJ$7GY>d1YdLFEn_tNsGX~r_PIAebec5LSL8*t|Q zB3SNv`ELEU;L}-C$Hf14V0Gv561_a@@O!Z9U>t4wyg;w+`022{3O+&0Y|D2bpTgC5;D~uW`wXmR^V9mS#B97~e2!*J$9pbV zlh>9n(6pUN9%K9)Y#jCE{tvjB`%5(SnF zJtx>W>RGF~z}BQKYc)5xxyJLLsb}20V8^Ow+zk+ubC{b`;{V&i!4SOjc+=J4n2a?d6A!lK|i>D5zLKd^P>J}lSIbJd*2 zH&6T*1DF1C-^-N#j?2830GIPx60YX6>J7}?_xnr1)m&*wz8>Nw02>HMP+)r}e;U<@&7;SC7vIV1Jh)*9Eyg=6A1c49;HjcPixBYny=G zYwG6rcQDk9?KNdnuw!#JErDafYoB(vbOI;Ot0Bn!S(SIZR=X| zx)5y}G{^dhwr#EHwuf(-@A?=Qwg{8wj>ueKMzSfnBeh<8uA9d#~9Z?3~N@ z&O4x~=bo@*?cJ^UC_0iRIkadV8^Ow>~3J|Fh_Y!+#Ow8 z{PrmP^4-!Ld!lPgj&Fk{!PQrkCg3+8=x&K912=o?AZK7z4Q)!GCgY4F}8J z+XJwT1n)zyZjNE}YS!+xX*5{P^)()0s{`Qb@i`DYs`fF4Tp#V8A7jAH^J6TUdVI!# zjgkE(*C%T<0h~1&50-25-wjLzJJ#F>(aW<&lfW0_;}~t_8u|Pt_q&1j2K&tMaD2<> z+#}#>sZX1lZ$Kuuw(PgeIHoO-wDcf{C=?GwB`4N9ssLzj>!ZkqMY zOP}9qeF*H_!XIt$=NkN_27k5SZ-L*gdFKBx+`Q%de*ss|{qPa6TFxnJQp@^030BKm zJPlTRism)p8Tw~wPteSxJ!5|b*5-b4Zt~ck2bXbw4OjEplejN}6IXl2{syegxUP{r zw%>xwxW9+1d96y^m%)jvJ!4-1YcsBEEsyOra2fXvxSH45#C;Q-xNm?n_77lf#`T!V zWBVi6wek7J>)|^zwb=gzc8;;X3s&2a^TPY#do;(Wd)(ir|1-_K;PEi0T))Kp3)uN3 z=3l{TvHuNhow5HNtd^Yr06RuKIX?uOGv~EjKVy1Md_>dk`6ABwvH*Btnx6$}o-gy$ zFGTZvS%g04$H(CEeEGb=ziRN#X?pwncS_3f{+*JN`*%vh{dY0`of3J@r%%Av<+^6A zJ_W1ieEJNmHihQeTeDjHzW}S{eEK(7tvsLp1J6Fvp0QtowHepB%VYBj}HsiWC`N7(Z>mHNGwh-83nR9JnxLWLsfITL$FA7)7x!4cv81=^arTpDc7od0tD5_169HBHQAz-qBC3wCW{Ukk8WQR4Y;?*`XA*Q8b9)?HqcRzp+IHEDIQ zTGq)qsb%fg1gqtmv=&&cye6#;&)(LavFm`fxzAi1d2H)}%ed>q)yiwq2Jpnyp0OK> zX~uPpTb$$Chq-PHFXL_kS1Yeco5B-Ud*W^e)@EFfl{~gBz-8Pm;cDeIX)Ac*YR}lM z!P<=LF_*`-Ex3%k9bB!vCJlrquJ(-m7Fe5c-8=Hwb^w=gcZ92z*QA}`iK{(hcLr-S zu6s}(+pgd;?(T54-DtVi?Ey|)?HRi#SetR(%ktRv0+(_3fvfFJOWb|IiK{(h_XBG) zuIGe2wjp5mUalScgVkam3U&|0J`AjuYs+x3W7KnP83FcO%QaN4Ut*2~yMGdM6j&|x z(O~yM><56=lJh{YW7Ly#4A`8xhRXFz%&}niKw^#qtHnMZ?6Hh}0$43M4+1+zJvk?W z&6#V1T))KZ274S6^I))A?32MBkJt|Zt0m{5V8^H@=V4%TPNI1{<@y=3Kj-)1VC`PF z#kp?#J$pHtpJixXx0j}0mgaSPdHP(pk78V|n_jn%EBMLaQ)+Ha+K+}Cr@W3HgQlMA z=&@k6td%*`vKGgKTN*LFPMrW(E3cy`!n41%XY5H}ZLWv4%40hftd?3&16zOgrd%KW zU8C>RcGpjw^;-$ssx&_<(_Ft5=~tn-eyh=E{mx)qxqjyq`~vWWHBU`v!i`g|-&ttt zS--QvYFR&XsAZn#g4J>#`W{%VT)*?+SwHO=dp=m3>u0U<*uD=|ORYZuTYuJ1u8;n% z(M7f0^%H0P*2K0B&Cgn`#yyXIZJO)1E`8ST62_J5cSXUk1z%V5)O0D_IOY0XhNhnN zyBw^R^)rWB=6NMpE$ep`Sgl;YtKnHc?HPLwSexr-t@7Be2dm{AzX7af?KjfjL~}k_ zPq{vhcg=3D?XIUd>$xGeO=x~LqPZW}r{9?7dTvUe^}LmF<$B)H;CDCp{SE$bgFn{b zPZj(*@bfj#@wp9dUFGrlF`9ag&+TBf93N{|%R1f(R?G3Z3#?WipP#^Ue6(lmPr=$e zKF(bp+dbei?!9oe@;U2g@Wj=gvG;+s8P_$G$MyiYjQexATKSyyAUtukXY4~@ZN~Lj z$Yc8jxQsgmu2w#0JpxZ$?HT(hSetP@rt;Vx2bXbw30IqqmgkEnz=^9pW1m!~8P~lb zkL_u28TT2u+B~$xeHNU!+B5c7U~R^AkI7?u0qn8NdG%|sTI?@^JtndL2CNqQOJLVF z_TPflV*ef3HH!WBV72@#<7Kcpjq6zL?)O*dU!{2t=<=(zR$V!7TBC0fL$-Se#Z1#@pf(ZIwH<>#Cz=)G(VfsypHsx-<;-k zWJ~&-$A4m+dU+ndi>99Q_&u^3w%ThUU})?jPe zmfmaIZ1f*8&TCt0{3~3)vc|umsps19cd(j!+jr8l;{Ok@x@%`lxj&z|F5)-Q>pUs>x{=AflkLR4z0bw2#3K~uL@W6G`7 zS~|eKzf`wYd1~#CzAMeo&a~9J3)otBqqo+6^!`2FverIu{mNRWLsL(!eH}wft&8G6 zJzU*djVZTQYnc(eM4ii8<*C)@o;_)PcBiG*J;2ubZF+0ug}P)Vc!xv&(7LYD~GcTFabZpH$) zxPE1=^P;Jz*7?9{sdXv*=ZC9Xt1;!)YAp+b{a&JOt@70B^Uq+KpZ#d5br9HEhtONA z&ykBUuB>$txPE1=i=wHg)_!2M)VezU{o(4?YD~GcTFc@s+|FgK^3>`zb{Ngi{8;ggv}GAr*18m2zp~b)(bQAx0I*tWT@U|d;Of?DOu4mM%W~jN>Ri?;PpzZS z52X1SO|#aK^as$abqu|=`rXdIEp4sgt2X$04Zd-M`?sRY@!K}Ie=EAQ@7mzsZt(pY zd{~1Y(BS^9=raD~f{$fhN7g*|kCouALwPJ#MpMr{ZWXZF6#QI!*GMh*wY9)%Id*G< z)yikpb>R7ZaP1kpE?Ap!U2A!4>x0X<8^G0GppM*YH-smy_Ke*Ktj)L{GkI*AfXld> z!qv)W)y?3Et36{k2WvB~$6g-Wmf$k(R&cfQS#@i8;%d*>ZNS=$>mHKFwjH>PI}ons zxsi4F7CdpaXYBT1ZN_yk%46FRT*loAt~O5*cV~FwYR}kRz}k%Ko|eb98@PJv-LbRN--NEs_4GUS&0w{h&p!gMNpqZWT_ZL9lK&QPng3R}TJG=WzYU)J z>dF6ObZyChJ6J9G?*J#iam}x$U-I7xF7w|7SIcvN`F{dWe)Z)4DY~}gzZ`5yqQCI8RC$!}cqtLc~g4}#1555d*) zTw(r);pX?6QXc&i`3-ZOQ*Duv+pz2Tp$DnqN)7 zu|LZwB&ySoc!hfe-m9>^8W#>mi%vl zli#@JSJN-~-v*cY{|HwbL(6^P9kBVmuQ<+Y-=EObllxt;T5`V!PHtnGTTQ>@eji-s z{xe){A}zT;0GsaB=Dw6Z z<30kbC)Yp0$z=?4sp(h#PA;_d?-A%)f4kiKTsQbons_q(VKnb~N6_b<_b&#P_q>nc zjw$bXpP;Gdp7$wOE%!WQsAUfS0juSn_a#`(bK7~j=f8qmPxGEfsV!U9ej2!1)?-@m zzZvT|1t-68&9A0k@=p&g^UnZR z^W4t-XM~%-dCi{*U0d?c3|342S-{C}T=T2xm;AGW%lxy!)yjL`?C|6-=RXI!w&b4^ ztd{(9fs^03=2z1%`R4|g`R9SFmG``P;pT5%^XEg?mi+UB)slY!aPk}1{A&6o|AOE$ z|3YxJ@}9RaJo(G{FM_Tu`4cWx$!cG0df=Uve!AE^{phS1ZrG<>BUP?)??ewPo#B1gj*sq4JE%{dmt0n&$;N&;1`PKAG{x!j6{w?vie?4&W8`u15`X&GR;4=RPaJBN>+Yp}o<^6smbZyDMF<346HvuQV zam}x$Uvuw&m-+ZPjpp7znf^O9K9&A-ntT5Y`t1EpiBse>5SfYq}1 zjiHwH*b=Oky}uP$t=#)t!>y;xvkjVh=DsaBb2o-_SJN-KwgZ>B2Ex_Kz5gw^xte=_ zdvtAC`yIe)$-g5w`HgFSHT{x*Cvcg6XSiCq_jiFOf4TQ}Mc0=6yMfh`e|K>58`u15 z`X&D!;4=T7aJ6#pe;c0s<=)>5U0d?+4OUD3eZa|YT=T2xm;C#J%l!Mn)ylm;2%h}q z-XDyvE%}Fl)slaIaPk}1{A&6o|4?w5e;8b?ecx%nLm3V?zx&m3p7$fr)RTK8SS`6n zfs@;q=2p|MdET8%JU?gCJnzn;KZoYI_C5MMdyZzTdb#HhKvU1Tbs$*HT2spycxusZ zEoz=m%~~!*^K%|8wVYpLuK^d(tn2&qUIV;F984^)0djpN*F1HPg&U)+dmNg2>K+eP zE9;&BPu<$BTP?@N_e}@Eo3SUNsptAJ39ROx^5^^~F|He~ZcOW!du&g^b_m#cYI82~ z{2RVQ!CogFqpi7q7i07D16tPaq8cAV|3jMVcL}}g)q1`lPS#JK<7=MvI}C1&a{UfR zQ_uPx0ah#5??`yoPrLI`Yk!Y#KSLY^Z^k|vO+D*(3|KAecO*q1PjOLnML7(&XB*v=e-yEI-S38-Ox=#fU zo|gB=^d5KPp9WU<{i8VVAGKXY^Hb)&5^nCR>681rj8jkUGvI1v?la-$)|PtC2CJV% zOFid+)vZUIdioImI+~wrXjzME!Pav(I5u?|N|Zm-qM^(6uH1jbO*BXY5VjZ_05qy0-ZJNM5hw z>-1UgThO&7$E{$;s%MV3fip*I(C0Yb`8)p~gIxoClIwP`HGM!UkL?}k+A{V|u;bM; z_Aan{E63aGh?>6f`3X34F4ya)=-T3UH#l=H>$nG9TjqQ(*si z3jU@XkD+Ue-{auqC~Ns8y0+wa0_<4z%<)NZ=4cK2pFmD4f-VRE8yfW>B8kT_u{0&`O=KOcC zW7RYEAK-7w@gcgl_KXfQ@Hgf754yJaeF;vEvX-yVwIv6S4wYloGskJrGDmCBC&zzU zaBB_dldA)4P41m?KAq^=GPVosc=e3!1J2&DPWO(QzMF8L^f|UK*!bno4yH#_e+(a= z`DOsC`K+D4Sv4bE-F@^4ac2hm@o!dXn~A38^O-m`%>vHamHT5>bZzmQt?(=N>g?#+ z?xVKp=;xq0R^7iflAQMc;#o+aIce79bE5v%W6g8Z`>|$y=Ax-tvpBWP15VB5@tzl5 zTm0rL{K{iBKf1Qmya3p->eif`_NjS6ur)ak{jJBE7p70m`Yc52PqSvR$JO6ySp@8H zokH`OY6JR3(bVJ951jqs-jM5KP3AGCv9nhe12^}|;%MsGD@%aY%Du8ATs?bbX|NyH zUE5MLHS>v+ZvfbJFZbp$XzIzgELhFGl)bqeT-|)mC9&p8d$Sj{;6kE7rECkU)S`}(Y} zK8d+4xH;eL(9~1!K(LyPulKE38UHP`W=-3psb~BSU^QFDXTCe4IbWaW%%g8&?*wkf z-Wg3j^W6okW*k4p5;||MIr{43e#!B+m**N}pMAS4*!5_BKi>^qTl{thd+liY?SZZ> z_vk&rj#batZ-bMgJh%5k*A~CM!O2mcL;Ij>OOAcPj#WR7nfv{8Kd`yACEg&g>zSHe zA2ogBGZ>uO%A7;cwZ(6LaB3^BJ44a6rM6*U$Es)SaBy;z*N_qD+Tu47oE&8>qtLY_ z$7rx))ieJCz~~XS+7j;s zaOQ73=dY%3d`<+Xw(|I&gsv@qCxfl+16t{K3c9w`cB+`Bp0THalcQX_@1Sdo-|67w zD38T=(X}PV8DPh%XZ~k`&8;o*&H|gaS@YTG+A{VWu;bM;_FS-Qm$9y$n!fS*9ys$V z>pc%$Tl~%kXI|ynU4X7F^STi1SoMtkJ~%nbJ@Er{ZSlJZoE&8>KSbA-92bKftDgB^ z0yej{#Jd!nwJYOYhORARF9$nbJ!7u`XYCy8+NtTg3D;}CCtV4)-tt-WDm3*xi(U;@ zGe(|8uYs$(jy~sH5BB5prMBy6H_?2~5u4BV&^LgcPx-rSH=?Oq-?iFl-Y1Rec;jUJ z&0y;-$NvaTJ>%u&c;ocEe+8Q(@oojXM){p>`7O2G^|=jfz2*A+7)?Fvb30hgb1MJ; zbnbwwXMKJG_T&0!yNh-Y&H0H_&riYTD}UeaZZvi0cPG8v`5Dvk#&Nv$-V1Kl`!h84 z)O#OT&F?a)_kOr~>U|LG$9lE>oc0Tv`NY{{4}r}$g;riu9!A$@%@5Gat=YK7FjnR~ z1?({>=llqoddADk@y5w9eH3httdG20AJ-|qkAYpoV#)llA;1xVfHBps8m) zp9HIUUC6kn;LY>uX*BgmI99%Ycn0iP^^AKKY(4Hb>yhgh`>(*wdY(g5Pd(3r)jYoW zndJ*`^$&^~e+^GA*Tt_sqzV+MM?$5r&<%+*Zei`7WC@cox7Ur z?K_V@fSvD@dc6EiTI_!WYxnuu@$$E8`}=5rs_h{Oj{|zaD#! zoyR)IZhCAvcAkTBkKF>qm;t;ay}GeHXVo&l8Nq7hv6~659-oZyAHaE`s>wP*Z-;O4v+LQ~K9g~1tbEbW>1BH-q{7e!Ogy!(OG zjN`{xnZL(dUwz8G!YbB#|Gf!aJ=@|HY{w`qOY-Cw~fHfb=w$CJ?pj! zSj{+o%#-zVzv-(_xp%#%nZtYNX4uS;d#K#!v(n#tUiSJ{*t6Gt){wi`{hJtDg9p>A zn|lj-HFJ4C-v+Ffz3zLtZQ<(i*$!+yx#!FENxcJWd+L?@{jaRodvogD5qs+00W7y( z|1QQ(;9>OY*1J8unz>y6oxy6U*ZbuzaP|1?3btN-j3L)YyZ80oz{V)|;O=ns`0N2T zhCarS>r>9jdzNz=h0k8Kzt6GaJjZ&@-bwRwJI&`JF3=>wdemI(E(s}=jR&AUBmweKx4^i literal 45068 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 2]; } -LineSeg Element_Line_read(ElementRef ref) { +LineSeg Element_StrokeLine_read(ElementRef ref) { + return LineSeg_read(LineSegRef(ref.offset + 4)); +} + +LineSeg Element_FillLine_read(ElementRef ref) { return LineSeg_read(LineSegRef(ref.offset + 4)); } diff --git a/piet-gpu/src/pico_svg.rs b/piet-gpu/src/pico_svg.rs index 9cf5cc3..b2f054c 100644 --- a/piet-gpu/src/pico_svg.rs +++ b/piet-gpu/src/pico_svg.rs @@ -61,8 +61,8 @@ impl PicoSvg { for item in &self.items { match item { Item::Fill(fill_item) => { - //rc.fill(&fill_item.path, &fill_item.color); - rc.stroke(&fill_item.path, &fill_item.color, 1.0); + rc.fill(&fill_item.path, &fill_item.color); + //rc.stroke(&fill_item.path, &fill_item.color, 1.0); } Item::Stroke(stroke_item) => { rc.stroke(&stroke_item.path, &stroke_item.color, stroke_item.width); diff --git a/piet-gpu/src/render_ctx.rs b/piet-gpu/src/render_ctx.rs index e01a6ae..8d68b0c 100644 --- a/piet-gpu/src/render_ctx.rs +++ b/piet-gpu/src/render_ctx.rs @@ -94,7 +94,7 @@ impl RenderContext for PietGpuRenderContext { } let brush = brush.make_brush(self, || shape.bounding_box()).into_owned(); let path = shape.to_bez_path(TOLERANCE); - self.encode_path(path); + self.encode_path(path, false); match brush { PietGpuBrush::Solid(rgba_color) => { let stroke = Stroke { rgba_color }; @@ -116,7 +116,7 @@ impl RenderContext for PietGpuRenderContext { fn fill(&mut self, shape: impl Shape, brush: &impl IntoBrush) { let brush = brush.make_brush(self, || shape.bounding_box()).into_owned(); let path = shape.to_bez_path(TOLERANCE); - self.encode_path(path); + self.encode_path(path, true); match brush { PietGpuBrush::Solid(rgba_color) => { let fill = Fill { rgba_color }; @@ -198,7 +198,15 @@ impl RenderContext for PietGpuRenderContext { } impl PietGpuRenderContext { - fn encode_path(&mut self, path: impl Iterator) { + fn encode_line_seg(&mut self, seg: LineSeg, is_fill: bool) { + if is_fill { + self.elements.push(Element::FillLine(seg)); + } else { + self.elements.push(Element::StrokeLine(seg)); + } + } + + fn encode_path(&mut self, path: impl Iterator, is_fill: bool) { let flatten = true; if flatten { let mut start_pt = None; @@ -215,7 +223,7 @@ impl PietGpuRenderContext { p0: last_pt.unwrap(), p1: scene_pt, }; - self.elements.push(Element::Line(seg)); + self.encode_line_seg(seg, is_fill); last_pt = Some(scene_pt); } PathEl::ClosePath => { @@ -224,7 +232,7 @@ impl PietGpuRenderContext { p0: last, p1: start, }; - self.elements.push(Element::Line(seg)); + self.encode_line_seg(seg, is_fill); } } _ => (), @@ -246,7 +254,7 @@ impl PietGpuRenderContext { p0: last_pt.unwrap(), p1: scene_pt, }; - self.elements.push(Element::Line(seg)); + self.encode_line_seg(seg, is_fill); last_pt = Some(scene_pt); } PathEl::QuadTo(p1, p2) => { @@ -279,7 +287,7 @@ impl PietGpuRenderContext { p0: last, p1: start, }; - self.elements.push(Element::Line(seg)); + self.encode_line_seg(seg, is_fill); } } } From ed4ed307089c3d77b6b2468343a1c612cd2c01f9 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Wed, 20 May 2020 11:48:05 -0700 Subject: [PATCH 17/32] Adding backdrop logic Calculation of backdrops kinda works but with issues, so WIP. --- piet-gpu/shader/binning.comp | 11 +++++- piet-gpu/shader/binning.spv | Bin 22016 -> 22592 bytes piet-gpu/shader/coarse.comp | 64 ++++++++++++++++++++++++++++++----- piet-gpu/shader/coarse.spv | Bin 33272 -> 38016 bytes piet-gpu/src/lib.rs | 2 +- 5 files changed, 67 insertions(+), 10 deletions(-) diff --git a/piet-gpu/shader/binning.comp b/piet-gpu/shader/binning.comp index 60b12e0..138621e 100644 --- a/piet-gpu/shader/binning.comp +++ b/piet-gpu/shader/binning.comp @@ -35,6 +35,8 @@ layout(set = 0, binding = 3) buffer BinsBuf { #define SX (1.0 / float(N_TILE_X * TILE_WIDTH_PX)) #define SY (1.0 / float(N_TILE_Y * TILE_HEIGHT_PX)) +#define TSY (1.0 / float(TILE_HEIGHT_PX)) + // Constant not available in GLSL. Also consider uintBitsToFloat(0x7f800000) #define INFINITY (1.0 / 0.0) @@ -83,6 +85,7 @@ void main() { } int x0 = 0, y0 = 0, x1 = 0, y1 = 0; float my_right_edge = INFINITY; + bool crosses_edge = false; switch (tag) { case Annotated_FillLine: case Annotated_StrokeLine: @@ -91,6 +94,7 @@ void main() { y0 = int(floor((min(line.p0.y, line.p1.y) - line.stroke.y) * SY)); x1 = int(ceil((max(line.p0.x, line.p1.x) + line.stroke.x) * SX)); y1 = int(ceil((max(line.p0.y, line.p1.y) + line.stroke.y) * SY)); + crosses_edge = tag == Annotated_FillLine && ceil(line.p0.y * TSY) != ceil(line.p1.y * TSY); break; case Annotated_Fill: case Annotated_Stroke: @@ -101,7 +105,9 @@ void main() { y0 = int(floor(fill.bbox.y * SY)); x1 = int(ceil(fill.bbox.z * SX)); y1 = int(ceil(fill.bbox.w * SY)); - my_right_edge = x1; + // It probably makes more sense to track x1, to avoid having to redo + // the rounding to tile coords. + my_right_edge = fill.bbox.z; break; } @@ -131,6 +137,9 @@ void main() { } barrier(); } + if (crosses_edge) { + x1 = int(ceil(my_right_edge * SX)); + } // At this point, we run an iterator over the coverage area, // trying to keep divergence low. diff --git a/piet-gpu/shader/binning.spv b/piet-gpu/shader/binning.spv index 4fda673c4aea604390b10ba4ceb6b93a76378ee0..dc1713b5505929835d055744fc3124cd12d0d6e1 100644 GIT binary patch literal 22592 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 literal 22016 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 T3`LvYEyiC0R`>oAdw=~e9^Xm4 diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 57abd73..2389e27 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -43,6 +43,7 @@ shared uint sh_elements_ref; shared uint sh_bitmaps[N_SLICE][N_TILE]; shared uint sh_backdrop[N_SLICE][N_TILE]; +shared uint sh_bd_sign[N_SLICE]; // scale factors useful for converting coordinates to tiles #define SX (1.0 / float(TILE_WIDTH_PX)) @@ -111,7 +112,10 @@ void main() { sh_first_el[th_ix] = chunk.n > 0 ? BinInstance_read(BinInstanceRef(start_chunk + BinChunk_size)).element_ix : ~0; } - uint count = 0; + if (th_ix < N_SLICE) { + sh_bd_sign[th_ix] = 0; + } + int backdrop = 0; while (true) { for (uint i = 0; i < N_SLICE; i++) { sh_bitmaps[i][th_ix] = 0; @@ -174,8 +178,11 @@ void main() { // Read one element, compute coverage. uint tag = Annotated_Nop; AnnotatedRef ref; + float right_edge = 0.0; if (th_ix + rd_ix < wr_ix) { - uint element_ix = sh_elements[(rd_ix + th_ix) % N_RINGBUF]; + uint rd_el_ix = (rd_ix + th_ix) % N_RINGBUF; + uint element_ix = sh_elements[rd_el_ix]; + right_edge = sh_right_edge[rd_el_ix]; ref = AnnotatedRef(element_ix * Annotated_size); tag = Annotated_tag(ref); } @@ -184,6 +191,8 @@ void main() { float a, b, c; // Bounding box of element in pixel coordinates. float xmin, xmax, ymin, ymax; + uint my_slice = th_ix / 32; + uint my_mask = 1 << (th_ix & 31); switch (tag) { case Annotated_FillLine: case Annotated_StrokeLine: @@ -194,6 +203,14 @@ void main() { ymax = max(line.p0.y, line.p1.y) + line.stroke.y; float dx = line.p1.x - line.p0.x; float dy = line.p1.y - line.p0.y; + if (tag == Annotated_FillLine) { + // Set bit for backdrop sign calculation, 1 is +1, 0 is -1. + if (dy < 0) { + atomicOr(sh_bd_sign[my_slice], my_mask); + } else { + atomicAnd(sh_bd_sign[my_slice], ~my_mask); + } + } // Set up for per-scanline coverage formula, below. float invslope = abs(dy) < 1e-9 ? 1e9 : dx / dy; c = abs(invslope) * (0.5 * float(TILE_HEIGHT_PX) + line.stroke.y) * SX; @@ -226,14 +243,14 @@ void main() { // Compute bounding box in tiles and clip to this bin. int x0 = int(floor((xmin - xy0.x) * SX)); int x1 = int(ceil((xmax - xy0.x) * SX)); + int xr = int(ceil((right_edge - xy0.x) * SX)); int y0 = int(floor((ymin - xy0.y) * SY)); int y1 = int(ceil((ymax - xy0.y) * SY)); x0 = clamp(x0, 0, N_TILE_X); x1 = clamp(x1, x0, N_TILE_X); + xr = clamp(xr, 0, N_TILE_X); y0 = clamp(y0, 0, N_TILE_Y); y1 = clamp(y1, y0, N_TILE_Y); - uint my_slice = th_ix / 32; - uint my_mask = 1 << (th_ix & 31); float t = a + b * float(y0); for (uint y = y0; y < y1; y++) { uint xx0 = clamp(int(floor(t - c)), x0, x1); @@ -241,6 +258,15 @@ void main() { for (uint x = xx0; x < xx1; x++) { atomicOr(sh_bitmaps[my_slice][y * N_TILE_X + x], my_mask); } + if (tag == Annotated_FillLine && ymin <= xy0.y + float(y * TILE_HEIGHT_PX)) { + // Assign backdrop to all tiles to the right of the ray crossing the + // top edge of this tile, up to the right edge of the fill bbox. + float xray = t - 0.5 * b; + xx0 = max(int(ceil(xray)), 0); + for (uint x = xx0; x < xr; x++) { + atomicOr(sh_backdrop[my_slice][y * N_TILE_X + x], my_mask); + } + } t += b; } barrier(); @@ -248,20 +274,34 @@ void main() { // Output elements for this tile, based on bitmaps. uint slice_ix = 0; uint bitmap = sh_bitmaps[0][th_ix]; + uint bd_bitmap = sh_backdrop[0][th_ix]; + uint combined = bitmap | bd_bitmap; while (true) { - if (bitmap == 0) { + if (combined == 0) { slice_ix++; if (slice_ix == N_SLICE) { break; } bitmap = sh_bitmaps[slice_ix][th_ix]; - if (bitmap == 0) { + bd_bitmap = sh_backdrop[slice_ix][th_ix]; + combined = bitmap | bd_bitmap; + if (combined == 0) { continue; } } - uint element_ref_ix = slice_ix * 32 + findLSB(bitmap); + uint element_ref_ix = slice_ix * 32 + findLSB(combined); uint element_ix = sh_elements[(rd_ix + element_ref_ix) % N_RINGBUF]; + // TODO: use bit magic to aggregate this calculation. + if ((bd_bitmap & (1 << (element_ref_ix & 31))) != 0) { + if ((sh_bd_sign[slice_ix] & (1 << (element_ref_ix & 31))) != 0) { + backdrop += 1; + } else { + backdrop -= 1; + } + } + + if ((bitmap & (1 << (element_ref_ix & 31))) != 0) { // At this point, we read the element again from global memory. // If that turns out to be expensive, maybe we can pack it into // shared memory (or perhaps just the tag). @@ -284,12 +324,19 @@ void main() { seg_chunk_ref.offset += SegChunk_size + Segment_size * chunk_n_segs; CmdFill cmd_fill; cmd_fill.seg_ref = first_seg_chunk.offset; + cmd_fill.backdrop = backdrop; cmd_fill.rgba_color = fill.rgba_color; alloc_cmd(cmd_ref, cmd_limit); Cmd_Fill_write(cmd_ref, cmd_fill); cmd_ref.offset += Cmd_size; chunk_n_segs = 0; + } else if (backdrop != 0) { + AnnoFill fill = Annotated_Fill_read(ref); + alloc_cmd(cmd_ref, cmd_limit); + Cmd_Solid_write(cmd_ref, CmdSolid(fill.rgba_color)); + cmd_ref.offset += Cmd_size; } + backdrop = 0; break; case Annotated_Stroke: if (chunk_n_segs > 0) { @@ -307,9 +354,10 @@ void main() { } break; } + } // clear LSB - bitmap &= bitmap - 1; + combined &= combined - 1; } barrier(); diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index e201c5ee594c6bd6ed8b29c3756015d10c16c85a..bc097e44388cf90bb53859a8801d1fe1b4f343e3 100644 GIT binary patch literal 38016 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

3dIb`ZoUe;A+OOA9;NDs(rIh_lB!E zuA`U-$Gk6CeIJT3oLe>jhA8u9-MT+6!q4kmiu=PoabAs2qCUUC7t~mv3n{L}PpGqR z4kv#0jcrdTxcBu*HP5*AgWFgBrm7RJb`6?)U?|wjJ)muW%4CZDinHH_fz7krZ^O~l zGiM{fYR*sk?*coH?7@+6{nYJ$G_{xgYa2yTvwv~+U^lpR4~{`o&mKIW_IK{af|IXn zCD%XW8CTeo>v%NvjAsH^&A8bE2ZGfnQjF`^)Z7Ef+uE2XVw(gu4}D!*xxUW3V|1>Q zQ%~(%&hbHL>dEO~u$u2e$@>tvYn<=vQ{d*Oo_15gw$+w)-v^g#^8>hk>aNXU)LyQQ zwnHgu<}J?J{1Du_Hb7TRaWU$XxeYB;|Q^59_wLBGE?mxLc V#%$h?&3%yhv@Z9B`y=Pb{{z)BoU8x< diff --git a/piet-gpu/shader/elements.comp b/piet-gpu/shader/elements.comp index 341952b..bdb4e0d 100644 --- a/piet-gpu/shader/elements.comp +++ b/piet-gpu/shader/elements.comp @@ -34,14 +34,14 @@ layout(set = 0, binding = 2) buffer AnnotatedBuf { #include "state.h" #include "annotated.h" -#define StateBuf_stride (4 + 2 * State_size) +#define StateBuf_stride (8 + 2 * State_size) StateRef state_aggregate_ref(uint partition_ix) { - return StateRef(8 + partition_ix * StateBuf_stride); + return StateRef(12 + partition_ix * StateBuf_stride); } StateRef state_prefix_ref(uint partition_ix) { - return StateRef(8 + partition_ix * StateBuf_stride + State_size); + return StateRef(12 + partition_ix * StateBuf_stride + State_size); } uint state_flag_index(uint partition_ix) { @@ -81,13 +81,12 @@ State combine_state(State a, State b) { c.translate.x = a.mat.x * b.translate.x + a.mat.z * b.translate.y + a.translate.x; c.translate.y = a.mat.y * b.translate.x + a.mat.w * b.translate.y + a.translate.y; c.linewidth = (b.flags & FLAG_SET_LINEWIDTH) == 0 ? a.linewidth : b.linewidth; - c.right_edge = (a.flags & FLAG_SET_BBOX) != 0 ? a.right_edge : (a.flags & FLAG_RESET_BBOX) != 0 ? a.bbox.z : c.right_edge; c.flags = (a.flags & (FLAG_SET_LINEWIDTH | FLAG_SET_BBOX)) | b.flags; c.flags |= (a.flags & FLAG_RESET_BBOX) >> 1; return c; } -State map_element(ElementRef ref) { +State map_element(ElementRef ref, inout bool is_fill) { // TODO: it would *probably* be more efficient to make the memory read patterns less // divergent, though it would be more wasted memory. uint tag = Element_tag(ref); @@ -97,6 +96,7 @@ State map_element(ElementRef ref) { c.translate = vec2(0.0, 0.0); c.linewidth = 1.0; // TODO should be 0.0 c.flags = 0; + is_fill = false; switch (tag) { case Element_FillLine: case Element_StrokeLine: @@ -115,6 +115,8 @@ State map_element(ElementRef ref) { c.bbox.zw = max(max(cubic.p0, cubic.p1), max(cubic.p2, cubic.p3)); break; case Element_Fill: + is_fill = true; + // fall-through case Element_Stroke: c.flags = FLAG_RESET_BBOX; break; @@ -145,9 +147,10 @@ shared vec4 sh_mat[WG_SIZE]; shared vec2 sh_translate[WG_SIZE]; shared vec4 sh_bbox[WG_SIZE]; shared float sh_width[WG_SIZE]; -shared float sh_right_edge[WG_SIZE]; shared uint sh_flags[WG_SIZE]; +shared uint sh_min_fill; + shared uint sh_tile_ix; shared State sh_prefix; @@ -157,6 +160,7 @@ void main() { // 4.4 of prefix sum paper). if (gl_LocalInvocationID.x == 0) { sh_tile_ix = atomicAdd(state[0], 1); + sh_min_fill = ~0; } barrier(); uint tile_ix = sh_tile_ix; @@ -164,18 +168,24 @@ void main() { uint ix = tile_ix * PARTITION_SIZE + gl_LocalInvocationID.x * N_ROWS; ElementRef ref = ElementRef(ix * Element_size); - th_state[0] = map_element(ref); + bool is_fill; + uint my_min_fill = ~0; + th_state[0] = map_element(ref, is_fill); + if (is_fill) my_min_fill = ix; for (uint i = 1; i < N_ROWS; i++) { // discussion question: would it be faster to load using more coherent patterns // into thread memory? This is kinda strided. - th_state[i] = combine_state(th_state[i - 1], map_element(Element_index(ref, i))); + th_state[i] = combine_state(th_state[i - 1], map_element(Element_index(ref, i), is_fill)); + if (is_fill && my_min_fill == ~0) { + my_min_fill = ix + i; + } } + atomicMin(sh_min_fill, my_min_fill); State agg = th_state[N_ROWS - 1]; sh_mat[gl_LocalInvocationID.x] = agg.mat; sh_translate[gl_LocalInvocationID.x] = agg.translate; sh_bbox[gl_LocalInvocationID.x] = agg.bbox; sh_width[gl_LocalInvocationID.x] = agg.linewidth; - sh_right_edge[gl_LocalInvocationID.x] = agg.right_edge; sh_flags[gl_LocalInvocationID.x] = agg.flags; for (uint i = 0; i < LG_WG_SIZE; i++) { barrier(); @@ -194,7 +204,6 @@ void main() { sh_translate[gl_LocalInvocationID.x] = agg.translate; sh_bbox[gl_LocalInvocationID.x] = agg.bbox; sh_width[gl_LocalInvocationID.x] = agg.linewidth; - sh_right_edge[gl_LocalInvocationID.x] = agg.right_edge; sh_flags[gl_LocalInvocationID.x] = agg.flags; } @@ -203,7 +212,6 @@ void main() { exclusive.mat = vec4(1.0, 0.0, 0.0, 1.0); exclusive.translate = vec2(0.0, 0.0); exclusive.linewidth = 1.0; //TODO should be 0.0 - exclusive.right_edge = 0.0; exclusive.flags = 0; // Publish aggregate for this partition @@ -244,6 +252,7 @@ void main() { } } barrier(); + my_min_fill = sh_min_fill; if (tile_ix != 0) { exclusive = sh_prefix; } @@ -256,12 +265,17 @@ void main() { other.translate = sh_translate[ix]; other.bbox = sh_bbox[ix]; other.linewidth = sh_width[ix]; - other.right_edge = sh_right_edge[ix]; other.flags = sh_flags[ix]; row = combine_state(row, other); } + if (my_min_fill == ~0 && gl_LocalInvocationID.x == 0) { + state[state_flag_index(tile_ix) + 1] = 0x7f800000; // infinity + } for (uint i = 0; i < N_ROWS; i++) { State st = combine_state(row, th_state[i]); + if (my_min_fill == ix + i) { + state[state_flag_index(tile_ix) + 1] = floatBitsToUint(st.bbox.z); + } // We write the state now for development purposes, but the // actual goal is to write transformed and annotated elements. //State_write(StateRef((ix + i) * State_size), st); diff --git a/piet-gpu/shader/elements.spv b/piet-gpu/shader/elements.spv index c947240a374822edb8c39f605604b116253e8cd9..962bd0ac664fc8283b64a2a94fa207832f65fff5 100644 GIT binary patch literal 45536 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 literal 45312 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 diff --git a/piet-gpu/shader/setup.h b/piet-gpu/shader/setup.h index 6b00661..b913086 100644 --- a/piet-gpu/shader/setup.h +++ b/piet-gpu/shader/setup.h @@ -58,7 +58,7 @@ // This is the ratio of the number of elements in a binning workgroup // over the number of elements in a partition workgroup. -#define ELEMENT_BINNING_RATIO 4 +#define ELEMENT_BINNING_RATIO 2 #define BIN_INITIAL_ALLOC 64 #define BIN_ALLOC 256 diff --git a/piet-gpu/shader/state.h b/piet-gpu/shader/state.h index bc6192f..2547b93 100644 --- a/piet-gpu/shader/state.h +++ b/piet-gpu/shader/state.h @@ -9,11 +9,10 @@ struct State { vec2 translate; vec4 bbox; float linewidth; - float right_edge; uint flags; }; -#define State_size 52 +#define State_size 48 StateRef State_index(StateRef ref, uint index) { return StateRef(ref.offset + index * State_size); @@ -33,14 +32,12 @@ State State_read(StateRef ref) { uint raw9 = state[ix + 9]; uint raw10 = state[ix + 10]; uint raw11 = state[ix + 11]; - uint raw12 = state[ix + 12]; State s; s.mat = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); s.translate = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); s.bbox = vec4(uintBitsToFloat(raw6), uintBitsToFloat(raw7), uintBitsToFloat(raw8), uintBitsToFloat(raw9)); s.linewidth = uintBitsToFloat(raw10); - s.right_edge = uintBitsToFloat(raw11); - s.flags = raw12; + s.flags = raw11; return s; } @@ -57,7 +54,6 @@ void State_write(StateRef ref, State s) { state[ix + 8] = floatBitsToUint(s.bbox.z); state[ix + 9] = floatBitsToUint(s.bbox.w); state[ix + 10] = floatBitsToUint(s.linewidth); - state[ix + 11] = floatBitsToUint(s.right_edge); - state[ix + 12] = s.flags; + state[ix + 11] = s.flags; } diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 65bbe5c..70b02f5 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -46,8 +46,8 @@ pub fn render_scene(rc: &mut impl RenderContext) { let circle = Circle::new(center, radius); rc.fill(circle, &color); } - /* let mut path = BezPath::new(); + /* path.move_to((100.0, 1150.0)); path.line_to((200.0, 1200.0)); path.line_to((150.0, 1250.0)); diff --git a/piet-gpu/src/render_ctx.rs b/piet-gpu/src/render_ctx.rs index 8d68b0c..da234de 100644 --- a/piet-gpu/src/render_ctx.rs +++ b/piet-gpu/src/render_ctx.rs @@ -215,6 +215,7 @@ impl PietGpuRenderContext { match el { PathEl::MoveTo(p) => { let scene_pt = to_f32_2(p); + start_pt = Some(scene_pt); last_pt = Some(scene_pt); } PathEl::LineTo(p) => { @@ -228,11 +229,13 @@ impl PietGpuRenderContext { } PathEl::ClosePath => { if let (Some(start), Some(last)) = (start_pt.take(), last_pt.take()) { - let seg = LineSeg { - p0: last, - p1: start, - }; - self.encode_line_seg(seg, is_fill); + if last != start { + let seg = LineSeg { + p0: last, + p1: start, + }; + self.encode_line_seg(seg, is_fill); + } } } _ => (), @@ -246,6 +249,7 @@ impl PietGpuRenderContext { match el { PathEl::MoveTo(p) => { let scene_pt = to_f32_2(p); + start_pt = Some(scene_pt); last_pt = Some(scene_pt); } PathEl::LineTo(p) => { @@ -283,11 +287,13 @@ impl PietGpuRenderContext { } PathEl::ClosePath => { if let (Some(start), Some(last)) = (start_pt.take(), last_pt.take()) { - let seg = LineSeg { - p0: last, - p1: start, - }; - self.encode_line_seg(seg, is_fill); + if last != start { + let seg = LineSeg { + p0: last, + p1: start, + }; + self.encode_line_seg(seg, is_fill); + } } } } From 7d040dff37d98ba759303a23ab29aeac65384576 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Fri, 22 May 2020 07:13:27 -0700 Subject: [PATCH 19/32] Bit magic for backdrop accumulation Use bit counting rather than iterating backdrop increments one by one. A nice if not huge speedup. --- piet-gpu/shader/coarse.comp | 38 +++++++++++++++++++----------------- piet-gpu/shader/coarse.spv | Bin 43040 -> 43268 bytes 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 03c4535..c77c6b8 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -80,6 +80,14 @@ void alloc_chunk(inout uint chunk_n_segs, inout SegChunkRef seg_chunk_ref, } } +// Accumulate delta to backdrop. +// +// Each bit for which bd_bitmap is 1 and bd_sign is 1 counts as +1, and each +// bit for which bd_bitmap is 1 and bd_sign is 0 counts as -1. +int count_backdrop(uint bd_bitmap, uint bd_sign) { + return bitCount(bd_bitmap & bd_sign) - bitCount(bd_bitmap & ~bd_sign); +} + void main() { // Could use either linear or 2d layouts for both dispatch and // invocations within the workgroup. We'll use variables to abstract. @@ -275,33 +283,31 @@ void main() { uint slice_ix = 0; uint bitmap = sh_bitmaps[0][th_ix]; uint bd_bitmap = sh_backdrop[0][th_ix]; - uint combined = bitmap | bd_bitmap; + uint bd_sign = sh_bd_sign[0]; while (true) { - if (combined == 0) { + if (bitmap == 0) { + backdrop += count_backdrop(bd_bitmap, bd_sign); slice_ix++; if (slice_ix == N_SLICE) { break; } bitmap = sh_bitmaps[slice_ix][th_ix]; bd_bitmap = sh_backdrop[slice_ix][th_ix]; - combined = bitmap | bd_bitmap; - if (combined == 0) { + bd_sign = sh_bd_sign[slice_ix]; + if (bitmap == 0) { continue; } } - uint element_ref_ix = slice_ix * 32 + findLSB(combined); + uint element_ref_ix = slice_ix * 32 + findLSB(bitmap); uint element_ix = sh_elements[(rd_ix + element_ref_ix) % N_RINGBUF]; - // TODO: use bit magic to aggregate this calculation. - if ((bd_bitmap & (1 << (element_ref_ix & 31))) != 0) { - if ((sh_bd_sign[slice_ix] & (1 << (element_ref_ix & 31))) != 0) { - backdrop += 1; - } else { - backdrop -= 1; - } - } + // Bits up to and including the lsb + uint bd_mask = (bitmap - 1) ^ bitmap; + backdrop += count_backdrop(bd_bitmap & bd_mask, bd_sign); + // Clear bits that have been consumed. + bd_bitmap &= ~bd_mask; + bitmap &= ~bd_mask; - if ((bitmap & (1 << (element_ref_ix & 31))) != 0) { // At this point, we read the element again from global memory. // If that turns out to be expensive, maybe we can pack it into // shared memory (or perhaps just the tag). @@ -378,10 +384,6 @@ void main() { } break; } - } - - // clear LSB - combined &= combined - 1; } barrier(); diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index 3d3f3ff34e9f936435e516410382d85c0fb9e92a..f74d0a003e966fa7050ab07d9a61957a6364f762 100644 GIT binary patch literal 43268 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 I_dNK20E7kjt^fc4 literal 43040 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 D@a*Ai From 55df3e6cc8fd2877b846482e9a91af46147771aa Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Sat, 23 May 2020 11:25:22 -0700 Subject: [PATCH 20/32] Fix linewidth math Coarse rasterization wasn't entirely taking line width into account. Also fix swizzle in matrix (not yet used). And fix missing End command in ptcl output (hasn't been a problem because buffer was cleared). --- piet-gpu/shader/coarse.comp | 3 ++- piet-gpu/shader/coarse.spv | Bin 43268 -> 43620 bytes piet-gpu/shader/elements.comp | 4 ++-- piet-gpu/shader/elements.spv | Bin 45536 -> 45536 bytes 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index c77c6b8..0519b2e 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -221,7 +221,7 @@ void main() { } // Set up for per-scanline coverage formula, below. float invslope = abs(dy) < 1e-9 ? 1e9 : dx / dy; - c = abs(invslope) * (0.5 * float(TILE_HEIGHT_PX) + line.stroke.y) * SX; + c = (line.stroke.x + abs(invslope) * (0.5 * float(TILE_HEIGHT_PX) + line.stroke.y)) * SX; b = invslope; // Note: assumes square tiles, otherwise scale. a = (line.p0.x - xy0.x - (line.p0.y - 0.5 * float(TILE_HEIGHT_PX) - xy0.y) * b) * SX; break; @@ -392,4 +392,5 @@ void main() { // removed, then the kernel fails with ERROR_DEVICE_LOST. if (rd_ix >= wr_ix || bin_ix == ~0) break; } + Cmd_End_write(cmd_ref); } diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index f74d0a003e966fa7050ab07d9a61957a6364f762..1329bb6f475f7cc836cc7715a5792d8410f48850 100644 GIT binary patch literal 43620 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 I_dNK20E7kjt^fc4 diff --git a/piet-gpu/shader/elements.comp b/piet-gpu/shader/elements.comp index bdb4e0d..43bb9cc 100644 --- a/piet-gpu/shader/elements.comp +++ b/piet-gpu/shader/elements.comp @@ -291,8 +291,8 @@ void main() { case Element_StrokeLine: LineSeg line = Element_StrokeLine_read(this_ref); AnnoStrokeLineSeg anno_line; - anno_line.p0 = st.mat.xz * line.p0.x + st.mat.yw * line.p0.y + st.translate; - anno_line.p1 = st.mat.xz * line.p1.x + st.mat.yw * line.p1.y + st.translate; + anno_line.p0 = st.mat.xy * line.p0.x + st.mat.zw * line.p0.y + st.translate; + anno_line.p1 = st.mat.xy * line.p1.x + st.mat.zw * line.p1.y + st.translate; if (tag == Element_StrokeLine) { anno_line.stroke = get_linewidth(st); } else { diff --git a/piet-gpu/shader/elements.spv b/piet-gpu/shader/elements.spv index 962bd0ac664fc8283b64a2a94fa207832f65fff5..a2d439c77fc4575f6aff7e1dbe84b56bdfd68bae 100644 GIT binary patch delta 34 ocmaFxnCZb|rVS=ujEs{_y~-JxCa?8+4P?*s2C_E``kYS%0P Date: Fri, 22 May 2020 14:18:39 -0700 Subject: [PATCH 21/32] Start work on parallel segment output Output of segments is in parallel. Getting closer, some problems with chaining but mostly correct. --- piet-gpu/bin/cli.rs | 4 +- piet-gpu/shader/coarse.comp | 196 +++++++++++++++++++++++++----------- piet-gpu/shader/coarse.spv | Bin 43620 -> 44012 bytes piet-gpu/src/lib.rs | 4 +- piet-gpu/src/pico_svg.rs | 6 +- 5 files changed, 142 insertions(+), 68 deletions(-) diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index c5c0b6b..3fdc5f8 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -107,8 +107,8 @@ fn main() -> Result<(), Error> { /* let mut data: Vec = Default::default(); - device.read_buffer(&renderer.bin_buf, &mut data).unwrap(); - //piet_gpu::dump_k1_data(&data); + device.read_buffer(&renderer.ptcl_buf, &mut data).unwrap(); + piet_gpu::dump_k1_data(&data); //trace_merge(&data); */ diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 0519b2e..8130e39 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -44,6 +44,14 @@ shared uint sh_elements_ref; shared uint sh_bitmaps[N_SLICE][N_TILE]; shared uint sh_backdrop[N_SLICE][N_TILE]; shared uint sh_bd_sign[N_SLICE]; +shared uint sh_is_segment[N_SLICE]; + +// Shared state for parallel segment output stage + +// Count of total number of segments in each tile, then +// inclusive prefix sum of same. +shared uint sh_seg_count[N_TILE]; +shared uint sh_seg_alloc; // scale factors useful for converting coordinates to tiles #define SX (1.0 / float(TILE_WIDTH_PX)) @@ -60,26 +68,6 @@ void alloc_cmd(inout CmdRef cmd_ref, inout uint cmd_limit) { } } -// Ensure that there is space to encode a segment. -void alloc_chunk(inout uint chunk_n_segs, inout SegChunkRef seg_chunk_ref, - inout SegChunkRef first_seg_chunk, inout uint seg_limit) -{ - // TODO: Reduce divergence of atomic alloc? - if (chunk_n_segs == 0) { - if (seg_chunk_ref.offset + 40 > seg_limit) { - seg_chunk_ref.offset = atomicAdd(alloc, SEG_CHUNK_ALLOC); - seg_limit = seg_chunk_ref.offset + SEG_CHUNK_ALLOC - Segment_size; - } - first_seg_chunk = seg_chunk_ref; - } else if (seg_chunk_ref.offset + SegChunk_size + Segment_size * chunk_n_segs > seg_limit) { - uint new_chunk_ref = atomicAdd(alloc, SEG_CHUNK_ALLOC); - seg_limit = new_chunk_ref + SEG_CHUNK_ALLOC - Segment_size; - SegChunk_write(seg_chunk_ref, SegChunk(chunk_n_segs, SegChunkRef(new_chunk_ref))); - seg_chunk_ref.offset = new_chunk_ref; - chunk_n_segs = 0; - } -} - // Accumulate delta to backdrop. // // Each bit for which bd_bitmap is 1 and bd_sign is 1 counts as +1, and each @@ -128,6 +116,7 @@ void main() { for (uint i = 0; i < N_SLICE; i++) { sh_bitmaps[i][th_ix] = 0; sh_backdrop[i][th_ix] = 0; + sh_is_segment[th_ix] = 0; } while (wr_ix - rd_ix <= N_TILE) { @@ -219,6 +208,7 @@ void main() { atomicAnd(sh_bd_sign[my_slice], ~my_mask); } } + atomicOr(sh_is_segment[my_slice], my_mask); // Set up for per-scanline coverage formula, below. float invslope = abs(dy) < 1e-9 ? 1e9 : dx / dy; c = (line.stroke.x + abs(invslope) * (0.5 * float(TILE_HEIGHT_PX) + line.stroke.y)) * SX; @@ -279,14 +269,102 @@ void main() { } barrier(); - // Output elements for this tile, based on bitmaps. + // We've computed coverage and other info for each element in the input, now for + // the output stage. We'll do segments first using a more parallel algorithm. + + uint seg_count = 0; + for (uint i = 0; i < N_SLICE; i++) { + // Count each segment as 1 and each non-segment element as 1. A finer + // approach would be to count bytes accurately (non-segment elements that + // are not strokes and fills wouldn't count). + seg_count += bitCount(sh_bitmaps[i][th_ix]); + } + sh_seg_count[th_ix] = seg_count; + // Prefix sum of sh_seg_count + for (uint i = 0; i < LG_N_TILE; i++) { + barrier(); + if (th_ix >= (1 << i)) { + seg_count += sh_seg_count[th_ix - (1 << i)]; + } + barrier(); + sh_seg_count[th_ix] = seg_count; + } + if (th_ix == N_TILE - 1) { + sh_seg_alloc = atomicAdd(alloc, seg_count * Segment_size + SegChunk_size); + } + barrier(); + uint total_seg_count = sh_seg_count[N_TILE - 1]; + uint seg_alloc = sh_seg_alloc; + + // Output buffer is allocated as segments for each tile laid end-to-end, + // but with gaps for non-segment elements (to fit the linked list headers). + + for (uint ix = th_ix; ix < total_seg_count; ix += N_TILE) { + // Find the work item; this thread is now not bound to an element or tile. + // First find the tile (by binary search) + uint tile_ix = 0; + for (uint i = 0; i < LG_N_TILE; i++) { + uint probe = tile_ix + ((N_TILE / 2) >> i); + if (ix >= sh_seg_count[probe - 1]) { + tile_ix = probe; + } + } + // Now, sh_seg_count[tile_ix - 1] <= ix < sh_seg_count[tile_ix]. + // (considering sh_seg_count[-1] == 0) + + // Index of segment within tile's segments + uint seq_ix = ix; + // Maybe consider a sentinel value to avoid the conditional? + if (tile_ix > 0) { + seq_ix -= sh_seg_count[tile_ix - 1]; + } + // Find the segment. This is done by linear scan through the bitmaps of the + // tile, accelerated by bit counting. Binary search might help, maybe not. + uint slice_ix = 0; + uint seq_bits; + while (true) { + seq_bits = sh_bitmaps[slice_ix][tile_ix]; + uint this_count = bitCount(seq_bits); + if (this_count > seq_ix) { + break; + } + seq_ix -= this_count; + slice_ix++; + } + // Now find position of nth bit set (n = seq_ix) in seq_bits; binary search + uint bit_ix = 0; + for (int i = 0; i < 5; i++) { + uint probe = bit_ix + (16 >> i); + if (seq_ix >= bitCount(seq_bits & ((1 << probe) - 1))) { + bit_ix = probe; + } + } + if ((sh_is_segment[slice_ix] & (1 << bit_ix)) != 0) { + uint out_offset = seg_alloc + Segment_size * ix + SegChunk_size; + uint rd_el_ix = (rd_ix + slice_ix * 32 + bit_ix) % N_RINGBUF; + uint element_ix = sh_elements[rd_el_ix]; + ref = AnnotatedRef(element_ix * Annotated_size); + AnnoStrokeLineSeg line = Annotated_StrokeLine_read(ref); + Segment seg = Segment(line.p0, line.p1); + Segment_write(SegmentRef(seg_alloc + Segment_size * ix + SegChunk_size), seg); + } + } + + // Output non-segment elements for this tile. The thread does a sequential walk + // through the non-segment elements, and for segments, count and backdrop are + // aggregated using bit counting. uint slice_ix = 0; uint bitmap = sh_bitmaps[0][th_ix]; uint bd_bitmap = sh_backdrop[0][th_ix]; uint bd_sign = sh_bd_sign[0]; + uint is_segment = sh_is_segment[0]; + uint seg_start = th_ix == 0 ? 0 : sh_seg_count[th_ix - 1]; + seg_count = 0; while (true) { - if (bitmap == 0) { + uint nonseg_bitmap = bitmap & ~is_segment; + if (nonseg_bitmap == 0) { backdrop += count_backdrop(bd_bitmap, bd_sign); + seg_count += bitCount(bitmap & is_segment); slice_ix++; if (slice_ix == N_SLICE) { break; @@ -294,16 +372,19 @@ void main() { bitmap = sh_bitmaps[slice_ix][th_ix]; bd_bitmap = sh_backdrop[slice_ix][th_ix]; bd_sign = sh_bd_sign[slice_ix]; - if (bitmap == 0) { + is_segment = sh_is_segment[slice_ix]; + nonseg_bitmap = bitmap & ~is_segment; + if (nonseg_bitmap == 0) { continue; } } - uint element_ref_ix = slice_ix * 32 + findLSB(bitmap); + uint element_ref_ix = slice_ix * 32 + findLSB(nonseg_bitmap); uint element_ix = sh_elements[(rd_ix + element_ref_ix) % N_RINGBUF]; // Bits up to and including the lsb - uint bd_mask = (bitmap - 1) ^ bitmap; + uint bd_mask = (nonseg_bitmap - 1) ^ nonseg_bitmap; backdrop += count_backdrop(bd_bitmap & bd_mask, bd_sign); + seg_count += bitCount(bitmap & bd_mask & is_segment); // Clear bits that have been consumed. bd_bitmap &= ~bd_mask; bitmap &= ~bd_mask; @@ -315,40 +396,8 @@ void main() { tag = Annotated_tag(ref); switch (tag) { - case Annotated_FillLine: - AnnoFillLineSeg fill_line = Annotated_FillLine_read(ref); - // This is basically the same logic as piet-metal, but should be made numerically robust. - vec2 tile_xy = vec2(tile_x * TILE_WIDTH_PX, tile_y * TILE_HEIGHT_PX); - float yEdge = mix(fill_line.p0.y, fill_line.p1.y, (tile_xy.x - fill_line.p0.x) / (fill_line.p1.x - fill_line.p0.x)); - if (min(fill_line.p0.x, fill_line.p1.x) < tile_xy.x && yEdge >= tile_xy.y && yEdge < tile_xy.y + TILE_HEIGHT_PX) { - Segment edge_seg; - if (fill_line.p0.x > fill_line.p1.x) { - fill_line.p1 = vec2(tile_xy.x, yEdge); - edge_seg.start = fill_line.p1; - edge_seg.end = vec2(tile_xy.x, tile_xy.y + TILE_HEIGHT_PX); - } else { - fill_line.p0 = vec2(tile_xy.x, yEdge); - edge_seg.start = vec2(tile_xy.x, tile_xy.y + TILE_HEIGHT_PX); - edge_seg.end = fill_line.p0; - } - alloc_chunk(chunk_n_segs, seg_chunk_ref, first_seg_chunk, seg_limit); - Segment_write(SegmentRef(seg_chunk_ref.offset + SegChunk_size + Segment_size * chunk_n_segs), edge_seg); - chunk_n_segs++; - } - Segment fill_seg = Segment(fill_line.p0, fill_line.p1); - alloc_chunk(chunk_n_segs, seg_chunk_ref, first_seg_chunk, seg_limit); - Segment_write(SegmentRef(seg_chunk_ref.offset + SegChunk_size + Segment_size * chunk_n_segs), fill_seg); - chunk_n_segs++; - break; - case Annotated_StrokeLine: - AnnoStrokeLineSeg line = Annotated_StrokeLine_read(ref); - Segment seg = Segment(line.p0, line.p1); - alloc_chunk(chunk_n_segs, seg_chunk_ref, first_seg_chunk, seg_limit); - Segment_write(SegmentRef(seg_chunk_ref.offset + SegChunk_size + Segment_size * chunk_n_segs), seg); - chunk_n_segs++; - break; case Annotated_Fill: - if (chunk_n_segs > 0) { + if (seg_count > 0) { AnnoFill fill = Annotated_Fill_read(ref); SegChunk_write(seg_chunk_ref, SegChunk(chunk_n_segs, SegChunkRef(0))); seg_chunk_ref.offset += SegChunk_size + Segment_size * chunk_n_segs; @@ -367,12 +416,21 @@ void main() { cmd_ref.offset += Cmd_size; } backdrop = 0; + seg_count = 0; break; case Annotated_Stroke: - if (chunk_n_segs > 0) { + if (chunk_n_segs > 0 || seg_count > 0) { + uint chunk_offset = seg_count > 0 ? seg_alloc + seg_start * Segment_size : 0; + SegChunkRef chunk_start = SegChunkRef(chunk_offset); + if (chunk_n_segs > 0) { + SegChunk_write(seg_chunk_ref, SegChunk(chunk_n_segs, chunk_start)); + } else { + first_seg_chunk = chunk_start; + } + if (seg_count > 0) { + SegChunk_write(chunk_start, SegChunk(seg_count, SegChunkRef(0))); + } AnnoStroke stroke = Annotated_Stroke_read(ref); - SegChunk_write(seg_chunk_ref, SegChunk(chunk_n_segs, SegChunkRef(0))); - seg_chunk_ref.offset += SegChunk_size + Segment_size * chunk_n_segs; CmdStroke cmd_stroke; cmd_stroke.seg_ref = first_seg_chunk.offset; cmd_stroke.half_width = 0.5 * stroke.linewidth; @@ -382,9 +440,25 @@ void main() { cmd_ref.offset += Cmd_size; chunk_n_segs = 0; } + seg_start += seg_count + 1; + seg_count = 0; + break; + default: + // This shouldn't happen, but just in case. + seg_start++; break; } } + if (seg_count > 0) { + SegChunkRef chunk_start = SegChunkRef(seg_alloc + seg_start * Segment_size); + if (chunk_n_segs > 0) { + SegChunk_write(seg_chunk_ref, SegChunk(chunk_n_segs, chunk_start)); + } else { + first_seg_chunk = chunk_start; + } + seg_chunk_ref = chunk_start; + chunk_n_segs = seg_count; + } barrier(); rd_ix += N_TILE; diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index 1329bb6f475f7cc836cc7715a5792d8410f48850..bd07113e408eafba66282984f99011952cd47a95 100644 GIT binary patch literal 44012 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

c+g4TArA9g3Z%?aSyfE*Zd}q`rQ<6 zuj;qdbla$ncNd&b|Bc!`qo1)~pmy(yU#+oYH?Drh$voZ<&OF`+mOGEr5%&S`lGN(P zx|dpQBSz!>{t#HrGsOGGn6CT7XxifUNbTo*lzxw*Ynz_;VB0+gcASgXF|;Sf<6!%` ztR==1Xxfe;XJb4GHjcVEKS-^XI8TFrDCggyYm47AwO?z_&!THf&c6qnv%kM%4DE^W z9QcQF{sX$Uqw1WW2OCG-oS&jrOPoJ~le0e~WS(C{*A~B*YCm%>{r-fmEjj-goOw2e z_QZG@oSe%Te?ixld42_K9CdScKOU%@|=^K0nZ;`e&(*P8R+(6uG!H^7-^V`xu| zzk`1$=Qq){WuE^5HjcVEdvB;E&RbyPw66KT(6q(x?b@%0c`oACY6 z*vsdUwofT)o*CloyU)SKzMcKM6$jE6V6}6siSre8;%NJl@*TxE;^gx+_(n>eLH_~! zElfQ=-+*tWe8d^ynfEPNKXZ2OwR^VNr}6D4Z5hU7@uz?rUp+okf{p(eqVe0{Y9H49 zwS#S!{zmaka&7VX(Pqi{GMP^+hO-$=_K~OJ9qF%~jiC)bixF zB-s2~&y%IlwRwN|yj>c6DEqgZlV#AT3x%p7o?VXSr@FEGYW|MrkHOl^bu(&t^4S*b z{3W05;H~*=kEWh{b^zN>J^AbiRxk6}32r`~o!XPn&R}ikvkkR8`RoclyU1rZcxyhp zqp2sKJ;1h8Pd~n zo_t1twVBTlYPtFNbJs|)dun=ioqROd7|YkT9_mqOw$)F2+U^f_{lmwA%UEOKiKVah z#Il|Cv>gX_?UKiMaN24s^UzOw+8zLQp40X~aN24s+v=x1Z6|=8lkkJUWvqkYiKVah zjKOx=ZR0rkaeg=3hO7{GjV13kcKAr`3Ej%B!XMdj!)|Nca0dH3D zpMl+P*&pYEv!=GyU!RQYJg`2_;c3+J-d0!0H zmb@|6G~d0!7#^Uvqv za|7J|^DXg4uzu>k5BPfmH-Y`L@J4)a+TIK{x)#=^W<;fYWD5V9e04eoHK2=Q`F2uY#jH{o#67B_FH)CGvqEb z_4Iu=xSZE};A+n6=sNDbaP{ov`@qIgzl-(oXV?3|_OH!xCO_*>`59232f*g<*!4Ge z$M+Dmm*dmtL5iB=6K8ynfDbA7W8lRoIVT?nKT1*0dGQ3;cG_HvhpE*P^GUE;?#ZXX zxhJ*do_rdt&F?5a4}VwtEd$opL!JBW8Mt=)JB?bN`TjjvP2027a_8$gYR7Bs{q{Wd z3luNyf2{4+iS;7b7;dHe~imizC|VB4rW*YZD5?90BziTyITy#M|JFZanS zaJBOOdlhazx&QtO)=xe6-)rDD?rY;~Pk*n2%lq$daD8+CX?Gr+6LYmr+c&^vzki3T z<^I#AR?f3ymS>+?8_)abP4Hp`{|DH8m;L`wu$q3Zqc%0;ok(qd*2(WJuydDr`4?C% z`Ds(L{c+T`KY{v({%qxKbk8IEeW%8@d$(?9?f!Y6`U8rW_77{jb@KWMY>e!We}mQX zJ4f>OD8@1;aoTa40G}sQz`w;u zKl736Blho)xK_q)1Dl(*>(fEqhvKDus@iUyzNQ8nBkMB_SS{-#Z>Jc`*y7eblUUPY zE5|V%+}QbZmg&LHiF(#z2C(||^%*}S*f{Eq?_tzlo+a95qRdP&7jgQY1zh$$E4=J` zHn@GOr|;Rp>iJ!)Il#tI&)m-mcJ8&MuereL;d6t_{WA|-O~33PHP;}yTmQFtoEKX; zmigeuPfqj0%}GDw%k?q7>+RfFC%*;2&PDd;f?&1mPi<=D9&)|Yej%{=@A6eC#e&ZdfQ_T>*fyb7OUy07<(zE=*C+S))?hXFOxFL$aMxd3;%oy}&zZC> z*x2gs_wA{@oOf;8QPdo>IB|9W+ke(U?%c=MTJF5t);eu>1Uo+&`%YlB+_T!$%C&Uf z(|#AQb79~9oj-YOyMmkhqWNCE8(htO;`sIedpSOByHnK6QJgq?f{l~;{RvpDoZr3R z&abw#+Z(JN-U&9x#MuX|reE?`bF9h3TH65XJpTrQ&C%LDJ(#+S;$_<*wcXmW`Fqxb z!1~!voScS%jUC<%cC2~#*ca@(hkn}Pw;x!WxecY3$2J12miLm8V72V`QD8OO4yTr< z?P#!C`2Jw$EPoeZ3|Jp^*Ki!Qm*dnnmZIi3#m+~@t)93CfXjXkgqLeG0j{QB)Oy*ml;g^AXfXQoOVuRoku2 z%k$-EurY4pyz%G6W5H_2P@E(A;S^&TTbwZ+4>p&?Ist5~oLwh^N3q8nIw_ITLJb z_3Xp5!0KspHdrm^-#K9OQ+IEjOYP;nY5N&P&3O|i=kvhkoVAp@KJm4dyLPs} zna_z}=l(LVTKc<>g&QwQ%Pu{agoDyP5e)%bCdK zALX|9&m?~f&Ywx%36|$~s_p_WiI2K%@1RyQSO0C0d%$XWpY`vQ-V0Zc&waIz`^Y}^ zxgSk^JK`J7i~{a%l+M1x~iT6pF(XLZQh63)7J9Usomq&8OzgPb5Bmc1FQLW zUF=7jn(bGiw*6|<+Mc2Io=U9W!+qb*Gx<5V&t%s_Tm1e2)@F=nspYY~04`(v5$?Am zV`z)ti(qZWc%E9GJ^LrHzpIjc;Jx~1G>qHo@?GwqaNp&$rQKU# z_4FYx`>?P0{tIj#`o2mn7yI*Ha(f%i+^l`Jzf1id#ml(w*LG{i>pR>#VEt?-PJbVQ zUGMOZz|L)cf9T&}wVdT2gKeX3ULR1arTr&hwfr3XDOfG*@HyDaytI8rQ8O=b;(Q5q z581~T)biNA2D@LheMK$zp7R;_AMhpAw$WxA^>4ubeM9dl`L}R2{f(ndEq#0kF6YIE zi}R9sm;$bkdfpMH1gqyc*alWh%yw}34Wt9Erk^=zQ;UBeu=jJ$fvMnXdA@5?i~rQM zf98G~xSD5~?A!KJeUdYnWCSz_{|K~=GdG| zd2F+Q%b2sm%b2slJ!ACKmUgp)wHed>A&+eiurYIn%nA3*QIF4DV0GKOhvc!%4KCZy z1NRJ4kI%edb=$iiGy7o%27+~vUKd)D%BebRmfus-T}Us@5Y?)y^KdL_8;MA{O2Ww5&a z`ftX_V_OyMcygAn23PYekI(A0&m)`}z-kTtM?ZCA`tQ!D#eYq(dop}2xIS6$wZZzR z+kPEtwfL_G&bj8lStHN6wm#T%%{JO><2q~rF3+_M;cEKZhc>nJ(GOhi(T(6`jvK@E zQBRvqz~$NZBe;I*Ir}yRt7ji<23AXq{^0WL+Z?W@pLuFii~km2^UA%mC0H%?t-#K8 z=5}kintsO9rWXGngUfi^z|~^k7VNwv-ga;`{fwtgE&khs-BY<|b^xoD@6U&U>cH@6Yx!RrD z`(HinzX1Dgk$db*eJFXZXjAjNFuv`Lm-asayI%ROvKL$}=Taxw%jb-?y(yw>FYgQk!F?&nbW)o|Mn0n5e1 zsP)f#dN=qS{Poe6KK2FMN7i^hxIaUvXYPiBeZFe*ELxQ~Bf$1szPpY@Q%}25U^UyP z-Dq&ynWtR8*vEjcZHYS;O+7y2!246|+Zb|v9Fyn8c(7-J{{Gu!2f+2u+RODxyaU0` zeL0p1aP{~c1UANHb)Ry5?9+c+<`8fXeVWg~V0q^FF!1HI?NG2h>vA~w0@~X~TjCu7 zRx_S)<%xGBSYK_%m8ad&;N)=>Sgvh*`ZyNs7;JY8wLEzo2Of)$ZM0?FCxDGt&drHv z>RIQLz-q@+((Yt%+S#96Ke78Q@A=+G-ZS1i=IQ$V6zuxtJIbkWHFNMhI34Wey{GLo zikfl6#_{|)3!HoJOt3un-q~R9J#}+BgIX=~_%m?k@f@&R8&!24|C9AR7c9>_o)30C zZKEymE&!|9mvQBZ_j9nm+KemDJYEPkw{re3f~)(pyZ`3+#bCAc^$T!vzXU8d_C#uZ zlWPyy{|=-++S2Ex;H+J8{3V)t*8DQCTDj(z!_%%Y7Px+%u~{E$dDh)pU)S3)iks)| zF8J;D8n|t>xv#FH_HtioyOyHn zz7i+)4d62Njc~O*8*Tw7j<%aAw^NKGPMll8EASi&|25p_QqJPr;A-Wcf&K<=J8jO@ zP1I`6sr}lAeZ`jhEH>-(cL&(^`d&{hkL|Z$HTRg$nY+Mh`rb(`kMG^!ay{;Wt7Sg! z17|+8-Aj3lVy@!ExgTsCxqUtWx34_^9t7*79-oK7dHy{FmYd@v)cR+yKMMBwr;j%0 z%e>6NToUhbuzecC=a9VgwN3JU0$lEcC*f)t_tRi6_mj4#C~Ed8PMqI?jgxUc16M1b z^S_5DuV=w>b9|0k|BUkwV8^MCHgiv(*7oVy_(yQg#uvbH&qn{<_ZPv-Qmfm?^VDiN zAN~YxeMbKoO+7v@gI}WLo|fyQ-M<&}7qD@Co|==t3-t^W`x@A|<$3u!Ts=O20~^D>9iLnu^YB^zcW}nARs<=3IPx2+TZW3YQW@jrp9l|O@i3NPQ)KZC33 zYaiOw#LfGyxv%#`vp;M1dA))qKlqmS>^E?=+_T?-y}W0&eM?cZA93PL zG1cT}_muGR>~4drm1lQ5+;-Y>u6BUc9UDL1RPyxE2V9=3Q^D1e>onlxs%>h@EEIDU zC(g9s@?4z`ZeKZXrU&bzZX3_u8NoSM{kKKs=IFmGs(<#{%wW$OeY836=4B4%l6bR% z?b8^Z9rDuGHu0UU@HGc{#xgtD^)rrRk{c&$WZn8ZK507#zU9CFFeh9sf99Y~E&g-W z{^4`O{kI|V=b`h!^-+({yx`IJ8&+_u_$?^ux9%RQ%U0g9S)D0X}w z(q67E|ET8H^7m`5pSgIBEsSr@kG^2JXFM*|BH)qK>fi8{Yb}cZF24VUz>E~H=_zZH zpMTe328#X9M4f*le)ms8X(qqt`#QmbXXt^up%e7X*-<~`=0C;9toH&Cvo*pK$Ky%DUj6dPut&twHenvCXem+VAs-be7=)BM^TIY4`A0M z_UFNBo~6@Lzd*5#y8F~WAAXVI-gW=jr(D0p{1e#qNX$Qj)nb1c?7YVQ7qD9Td`%$W^5C&g=aif7I&)N@cgbLOJXne!I7JTu;H;UBf|&szA`E!+?MiPr)4 zznd=48S^i&dGt^+H*bU0bH=;_R?E1}Q7!)Ofz`5C-v_IeXUqri>{ab)`yp7H^XXXS zvHcrd#{C$sb_*rW4?qZuJ*M33arhz&b>Ug z|A5Q5-@w(%Gv-@(;%ZOZ@4(uO>l(^qn}Rhf<4y@zE6z82oMg)iR1mucZEw(!*o?!URePR+AEbHmN6hmtv)2TeWq>bzjJ z+^goS7XSIdYPnYz0IQYv>Voj>CGBau5Llag$+630TNqr%T?DRH-m8nk6IXlME(X?S zT<1_8+Y;b1?vik|@?KpEp19i6c4@FSyCahHRumG|oM@Wj=gwkv?O8P_$H z$F>r>GpCV&4Sp*kk_@ zSS@G3reOOtu5GpF4A=~8?l}YG`X_dOu;Wkc&B1E6--3EeitTgn$n~+k=fqaE-7`d- zGo%l;MJZkjQ#?cZQZGXB3|Wji^Scf0)O#q&Yg;t++{4>})yyN$x$WUuTkUDP16Z3m ziIbD>BTG`e7N;bqCBWvi6t(Bt)YQAvu6!rh8LnRsCH?Gzrk-Hb6t+wTs@Zu(5}pNFSvd^l;pZMntF2W z1gj<2x$)fxu5PZzl;`{4K(NmQb#s*`SDz&-QoNR@B-a(d=DHHKx%z(BO}jGJF1UU@ zl;k=DO+C2|1*;|3neiP4S2tH<%FWeW_67THscx?F-;0?tm|2{E7$ql7JgyDCxUxwo-v*cH*OCl<2naTJ?s24uv*sHKGouX z9#}2wd_GvMT;~hmSr6@L`*W~1*TdZ9v0Vf%<6aC`E7$oFc;aeL+h2gS8P_?G$95^$ zzA^{D1UoN1%z^%Led2!^*cjfQ8{l&}Sk3mX%N1bTXPxEx*uEe3D{H&!EY3PliEV3& z*A^7lc{A!QYrHx2RutF#$JAN#tBFybnb*K=(?dzl*P^Ls4X*>MWets?7XO>UYFWcu zz-rAkZ2rE-t#I?{Df;;}ntGnWw}Ee>^x$I*=Tl9;^z|EX+1KrGwdT4u@0~l~_T`+~ zZW`)4(bT=)e4qF&SS@|u1y0|_v~M;2()ZoqvhRD~YRz}$<~Z+#+qZL-cK4yFr?30L z>B|`QrKVr`E!8#Nh2pg%#WmiRdZ!xiK)o}?HQtpvYy1Fl$~ArvZkrxTa(W0&J!||h zSS@R847K<_0#?f!KMGbW*Z48G`ScY1JdUQGu|EOM*p1=X)$~hWPlC(7o`S1ojh_bp zj$&U~Pw@nWvWBC)Bde->QV708V zG1TJ!GFUBZ{1>oVxyG-+&8Mg6=T$WIjQy|RjNKTHT}{8%u@57TR~N;x52PMa~DhA%CY|gZazIlKmSBi z&)DArXY9ss>}vY8j(sF?yoOU8`+n3TD2{g&b)GT*qOE!lCHcOMrk?S=16Ff<$>m*m za?x%sYL2Njm$7JGqbbQ{{~CMFjG>s-p+}!#D z+rW-fn`4n@{@TG?W3!F6*7-XOo7cgV%-?fiL0&PbfhpFT6yJY(#D z8>5Gk`RjwGp81;!tX9t7H1N!ycE_VOH6^j91-HhY4oyAtH$7M_^S2v$%m7z6ruoaw zZ4YcSgB_wp0VH<6&^LI2huOld#zawirnEEJ+^LGrj^EZ%sUgBi_^qH^b z8RM+*BhkzGn+;7p^EW$K?K1pw?#uzt{AqVQYNOdVi9IL0HTGO+O}+VNuXDrIGJgZ; zV;;D=G0k7@{0+i3KiF|DM~f7ZA(qTuRQ)^J;9L=Tn^PpHpY9wK_+a>+HP2bGIov+VF>HaR zp841kY~T4iBwNGvQU9FrY(wqk@BL`|F-6T>#rAbQ_8q}HQ0y!J?$OS0b?4T#+zIR) zIWBFzL*G?u%^?uw&BZ_~qJc_Y<)G+MEk{&gQ+qN8@7~ZRvY& zaQZfWCtS@K_9Kt)KDBT5=>WKz<2sso@Mr!(VD*6%V>q{J{{BLjrF)(RY literal 43620 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 { - rc.fill(&fill_item.path, &fill_item.color); - //rc.stroke(&fill_item.path, &fill_item.color, 1.0); + //rc.fill(&fill_item.path, &fill_item.color); + rc.stroke(&fill_item.path, &fill_item.color, 1.0); } Item::Stroke(stroke_item) => { rc.stroke(&stroke_item.path, &stroke_item.color, stroke_item.width); From 8eaf49a04d8ccdd5e817b6d5de65ed140d505950 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Mon, 25 May 2020 09:08:21 -0700 Subject: [PATCH 22/32] Checkpoint parallel output Parallel segment output seems to be working for strokes. --- piet-gpu-types/src/ptcl.rs | 7 +-- piet-gpu/bin/cli.rs | 54 +++++++++++++++++++- piet-gpu/shader/coarse.comp | 96 +++++++++++++++++++++-------------- piet-gpu/shader/coarse.spv | Bin 44012 -> 40472 bytes piet-gpu/shader/kernel4.comp | 5 +- piet-gpu/shader/kernel4.spv | Bin 17856 -> 18428 bytes piet-gpu/shader/ptcl.h | 12 +++-- piet-gpu/src/lib.rs | 6 +-- piet-gpu/src/pico_svg.rs | 2 +- 9 files changed, 130 insertions(+), 52 deletions(-) diff --git a/piet-gpu-types/src/ptcl.rs b/piet-gpu-types/src/ptcl.rs index 534cf85..2aa869e 100644 --- a/piet-gpu-types/src/ptcl.rs +++ b/piet-gpu-types/src/ptcl.rs @@ -13,8 +13,8 @@ piet_gpu! { end: [f32; 2], } struct CmdStroke { - // Should be Ref - seg_ref: u32, + // Consider a specialization to one segment. + seg_ref: Ref, half_width: f32, rgba_color: u32, } @@ -63,7 +63,8 @@ piet_gpu! { struct SegChunk { n: u32, next: Ref, - // Segments follow (could represent this as a variable sized array). + // Actually a reference to a variable-sized slice. + segs: Ref, } } } diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index 3fdc5f8..347cf01 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -75,6 +75,58 @@ fn trace_merge(buf: &[u32]) { } } +/// 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, + 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 * 4]); + let y0 = f32::from_bits(buf[segs / 4 + i * 4 + 1]); + let x1 = f32::from_bits(buf[segs / 4 + i * 4 + 2]); + let y1 = f32::from_bits(buf[segs / 4 + i * 4 + 3]); + println!(" ({:.3}, {:.3}) - ({:.3}, {:.3})", x0, y0, x1, y1); + } + loop { + seg_chunk = buf[seg_chunk / 4 + 1] as usize; + if seg_chunk == 0 { + break; + } + } + } + _ => { + 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 (instance, _) = VkInstance::new(None)?; unsafe { @@ -109,7 +161,7 @@ fn main() -> Result<(), Error> { let mut data: Vec = Default::default(); device.read_buffer(&renderer.ptcl_buf, &mut data).unwrap(); piet_gpu::dump_k1_data(&data); - //trace_merge(&data); + //trace_ptcl(&data); */ let mut img_data: Vec = Default::default(); diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 8130e39..8d593d8 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -51,6 +51,7 @@ shared uint sh_is_segment[N_SLICE]; // Count of total number of segments in each tile, then // inclusive prefix sum of same. shared uint sh_seg_count[N_TILE]; +shared uint sh_orig_seg_count[N_TILE]; shared uint sh_seg_alloc; // scale factors useful for converting coordinates to tiles @@ -68,6 +69,11 @@ void alloc_cmd(inout CmdRef cmd_ref, inout uint cmd_limit) { } } +// TODO: aggregate rather than doing an atomic every time +SegChunkRef alloc_seg_chunk() { + return SegChunkRef(atomicAdd(alloc, SegChunk_size)); +} + // Accumulate delta to backdrop. // // Each bit for which bd_bitmap is 1 and bd_sign is 1 counts as +1, and each @@ -91,10 +97,10 @@ void main() { uint cmd_limit = cmd_ref.offset + PTCL_INITIAL_ALLOC - 2 * Cmd_size; // Allocation and management of segment output - SegChunkRef seg_chunk_ref = SegChunkRef(0); SegChunkRef first_seg_chunk = SegChunkRef(0); - uint seg_limit = 0; - uint chunk_n_segs = 0; + SegChunkRef last_chunk_ref = SegChunkRef(0); + uint last_chunk_n = 0; + SegmentRef last_chunk_segs = SegmentRef(0); uint wr_ix = 0; uint rd_ix = 0; @@ -274,12 +280,10 @@ void main() { uint seg_count = 0; for (uint i = 0; i < N_SLICE; i++) { - // Count each segment as 1 and each non-segment element as 1. A finer - // approach would be to count bytes accurately (non-segment elements that - // are not strokes and fills wouldn't count). - seg_count += bitCount(sh_bitmaps[i][th_ix]); + seg_count += bitCount(sh_bitmaps[i][th_ix] & sh_is_segment[i]); } sh_seg_count[th_ix] = seg_count; + sh_orig_seg_count[th_ix] = seg_count; // Prefix sum of sh_seg_count for (uint i = 0; i < LG_N_TILE; i++) { barrier(); @@ -290,14 +294,13 @@ void main() { sh_seg_count[th_ix] = seg_count; } if (th_ix == N_TILE - 1) { - sh_seg_alloc = atomicAdd(alloc, seg_count * Segment_size + SegChunk_size); + sh_seg_alloc = atomicAdd(alloc, seg_count * Segment_size); } barrier(); uint total_seg_count = sh_seg_count[N_TILE - 1]; uint seg_alloc = sh_seg_alloc; - // Output buffer is allocated as segments for each tile laid end-to-end, - // but with gaps for non-segment elements (to fit the linked list headers). + // Output buffer is allocated as segments for each tile laid end-to-end. for (uint ix = th_ix; ix < total_seg_count; ix += N_TILE) { // Find the work item; this thread is now not bound to an element or tile. @@ -322,8 +325,9 @@ void main() { // tile, accelerated by bit counting. Binary search might help, maybe not. uint slice_ix = 0; uint seq_bits; + while (true) { - seq_bits = sh_bitmaps[slice_ix][tile_ix]; + seq_bits = sh_bitmaps[slice_ix][tile_ix] & sh_is_segment[slice_ix]; uint this_count = bitCount(seq_bits); if (this_count > seq_ix) { break; @@ -339,15 +343,13 @@ void main() { bit_ix = probe; } } - if ((sh_is_segment[slice_ix] & (1 << bit_ix)) != 0) { - uint out_offset = seg_alloc + Segment_size * ix + SegChunk_size; - uint rd_el_ix = (rd_ix + slice_ix * 32 + bit_ix) % N_RINGBUF; - uint element_ix = sh_elements[rd_el_ix]; - ref = AnnotatedRef(element_ix * Annotated_size); - AnnoStrokeLineSeg line = Annotated_StrokeLine_read(ref); - Segment seg = Segment(line.p0, line.p1); - Segment_write(SegmentRef(seg_alloc + Segment_size * ix + SegChunk_size), seg); - } + uint out_offset = seg_alloc + Segment_size * ix + SegChunk_size; + uint rd_el_ix = (rd_ix + slice_ix * 32 + bit_ix) % N_RINGBUF; + uint element_ix = sh_elements[rd_el_ix]; + ref = AnnotatedRef(element_ix * Annotated_size); + AnnoStrokeLineSeg line = Annotated_StrokeLine_read(ref); + Segment seg = Segment(line.p0, line.p1); + Segment_write(SegmentRef(seg_alloc + Segment_size * ix), seg); } // Output non-segment elements for this tile. The thread does a sequential walk @@ -397,6 +399,7 @@ void main() { switch (tag) { case Annotated_Fill: + /* if (seg_count > 0) { AnnoFill fill = Annotated_Fill_read(ref); SegChunk_write(seg_chunk_ref, SegChunk(chunk_n_segs, SegChunkRef(0))); @@ -415,32 +418,45 @@ void main() { Cmd_Solid_write(cmd_ref, CmdSolid(fill.rgba_color)); cmd_ref.offset += Cmd_size; } + */ backdrop = 0; seg_count = 0; break; case Annotated_Stroke: - if (chunk_n_segs > 0 || seg_count > 0) { - uint chunk_offset = seg_count > 0 ? seg_alloc + seg_start * Segment_size : 0; - SegChunkRef chunk_start = SegChunkRef(chunk_offset); - if (chunk_n_segs > 0) { - SegChunk_write(seg_chunk_ref, SegChunk(chunk_n_segs, chunk_start)); - } else { - first_seg_chunk = chunk_start; - } + if (last_chunk_n > 0 || seg_count > 0) { + // TODO: noncontiguous case + + SegChunkRef chunk_ref = SegChunkRef(0); if (seg_count > 0) { - SegChunk_write(chunk_start, SegChunk(seg_count, SegChunkRef(0))); + chunk_ref = alloc_seg_chunk(); + SegChunk chunk; + chunk.n = seg_count; + chunk.next = SegChunkRef(0); + uint seg_offset = seg_alloc + seg_start * Segment_size; + chunk.segs = SegmentRef(seg_offset); + SegChunk_write(chunk_ref, chunk); } + if (last_chunk_n > 0) { + SegChunk chunk; + chunk.n = last_chunk_n; + chunk.next = chunk_ref; + chunk.segs = last_chunk_segs; + SegChunk_write(last_chunk_ref, chunk); + } else { + first_seg_chunk = chunk_ref; + } + AnnoStroke stroke = Annotated_Stroke_read(ref); CmdStroke cmd_stroke; - cmd_stroke.seg_ref = first_seg_chunk.offset; + cmd_stroke.seg_ref = first_seg_chunk; cmd_stroke.half_width = 0.5 * stroke.linewidth; cmd_stroke.rgba_color = stroke.rgba_color; alloc_cmd(cmd_ref, cmd_limit); Cmd_Stroke_write(cmd_ref, cmd_stroke); cmd_ref.offset += Cmd_size; - chunk_n_segs = 0; + last_chunk_n = 0; } - seg_start += seg_count + 1; + seg_start += seg_count; seg_count = 0; break; default: @@ -450,14 +466,18 @@ void main() { } } if (seg_count > 0) { - SegChunkRef chunk_start = SegChunkRef(seg_alloc + seg_start * Segment_size); - if (chunk_n_segs > 0) { - SegChunk_write(seg_chunk_ref, SegChunk(chunk_n_segs, chunk_start)); + SegChunkRef chunk_ref = alloc_seg_chunk(); + if (last_chunk_n > 0) { + SegChunk_write(last_chunk_ref, SegChunk(last_chunk_n, chunk_ref, last_chunk_segs)); } else { - first_seg_chunk = chunk_start; + first_seg_chunk = chunk_ref; } - seg_chunk_ref = chunk_start; - chunk_n_segs = seg_count; + // TODO: free two registers by writing count and segments ref now, + // as opposed to deferring SegChunk write until all fields are known. + last_chunk_ref = chunk_ref; + last_chunk_n = seg_count; + uint seg_offset = seg_alloc + seg_start * Segment_size; + last_chunk_segs = SegmentRef(seg_offset); } barrier(); diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index bd07113e408eafba66282984f99011952cd47a95..ac0895068da7b6c3cde6bbdf42f33f2d10928539 100644 GIT binary patch literal 40472 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

c+g4TArA9g3Z%?aSyfE*Zd}q`rQ<6 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 diff --git a/piet-gpu/shader/kernel4.comp b/piet-gpu/shader/kernel4.comp index bdc540c..4c4aba3 100644 --- a/piet-gpu/shader/kernel4.comp +++ b/piet-gpu/shader/kernel4.comp @@ -47,11 +47,12 @@ void main() { case Cmd_Stroke: CmdStroke stroke = Cmd_Stroke_read(cmd_ref); float df = 1e9; - SegChunkRef seg_chunk_ref = SegChunkRef(stroke.seg_ref); + SegChunkRef seg_chunk_ref = stroke.seg_ref; do { SegChunk seg_chunk = SegChunk_read(seg_chunk_ref); + SegmentRef segs = seg_chunk.segs; for (int i = 0; i < seg_chunk.n; i++) { - Segment seg = Segment_read(SegmentRef(seg_chunk_ref.offset + SegChunk_size + Segment_size * i)); + Segment seg = Segment_read(Segment_index(segs, i)); vec2 line_vec = seg.end - seg.start; vec2 dpos = xy + vec2(0.5, 0.5) - seg.start; float t = clamp(dot(line_vec, dpos) / dot(line_vec, line_vec), 0.0, 1.0); diff --git a/piet-gpu/shader/kernel4.spv b/piet-gpu/shader/kernel4.spv index 6658915fb85538479cd894533e38ac794acb58b7..02bd1378c14a8746b89c6c235c86c14f8e34f01a 100644 GIT binary patch literal 18428 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 Dn}E@X literal 17856 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 2; - ptcl[ix + 0] = s.seg_ref; + ptcl[ix + 0] = s.seg_ref.offset; ptcl[ix + 1] = floatBitsToUint(s.half_width); ptcl[ix + 2] = s.rgba_color; } @@ -416,9 +417,11 @@ SegChunk SegChunk_read(SegChunkRef ref) { uint ix = ref.offset >> 2; uint raw0 = ptcl[ix + 0]; uint raw1 = ptcl[ix + 1]; + uint raw2 = ptcl[ix + 2]; SegChunk s; s.n = raw0; s.next = SegChunkRef(raw1); + s.segs = SegmentRef(raw2); return s; } @@ -426,5 +429,6 @@ void SegChunk_write(SegChunkRef ref, SegChunk s) { uint ix = ref.offset >> 2; ptcl[ix + 0] = s.n; ptcl[ix + 1] = s.next.offset; + ptcl[ix + 2] = s.segs.offset; } diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 2bfe5b1..65bbe5c 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -46,8 +46,8 @@ pub fn render_scene(rc: &mut impl RenderContext) { let circle = Circle::new(center, radius); rc.fill(circle, &color); } - let mut path = BezPath::new(); /* + let mut path = BezPath::new(); path.move_to((100.0, 1150.0)); path.line_to((200.0, 1200.0)); path.line_to((150.0, 1250.0)); @@ -59,8 +59,8 @@ pub fn render_scene(rc: &mut impl RenderContext) { &Color::WHITE, 5.0, ); - render_cardioid(rc); - //render_tiger(rc); + //render_cardioid(rc); + render_tiger(rc); } #[allow(unused)] diff --git a/piet-gpu/src/pico_svg.rs b/piet-gpu/src/pico_svg.rs index bc2503f..9cf5cc3 100644 --- a/piet-gpu/src/pico_svg.rs +++ b/piet-gpu/src/pico_svg.rs @@ -58,7 +58,7 @@ impl PicoSvg { } pub fn render(&self, rc: &mut impl RenderContext) { - for item in self.items.iter().take(30) { + for item in &self.items { match item { Item::Fill(fill_item) => { //rc.fill(&fill_item.path, &fill_item.color); From 3d422d924345b95fdd7711d0fdfab5bc6d4a7f48 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Mon, 25 May 2020 12:22:29 -0700 Subject: [PATCH 23/32] Allocate segment chunks in slabs Another speedup might be to special-case when the number of chunks in a stroke or fill command is 1, then the segment header doesn't need allocation and memory traffic is reduced. But right now we'll avoid the complexity. --- piet-gpu/shader/coarse.comp | 16 ++++++++++++++-- piet-gpu/shader/coarse.spv | Bin 40472 -> 40892 bytes 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 8d593d8..81ea890 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -69,9 +69,20 @@ void alloc_cmd(inout CmdRef cmd_ref, inout uint cmd_limit) { } } -// TODO: aggregate rather than doing an atomic every time +#define CHUNK_ALLOC_SLAB 16 + +uint alloc_chunk_remaining; +uint alloc_chunk_offset; + SegChunkRef alloc_seg_chunk() { - return SegChunkRef(atomicAdd(alloc, SegChunk_size)); + if (alloc_chunk_remaining == 0) { + alloc_chunk_offset = atomicAdd(alloc, CHUNK_ALLOC_SLAB * SegChunk_size); + alloc_chunk_remaining = CHUNK_ALLOC_SLAB; + } + uint offset = alloc_chunk_offset; + alloc_chunk_offset += SegChunk_size; + alloc_chunk_remaining--; + return SegChunkRef(offset); } // Accumulate delta to backdrop. @@ -101,6 +112,7 @@ void main() { SegChunkRef last_chunk_ref = SegChunkRef(0); uint last_chunk_n = 0; SegmentRef last_chunk_segs = SegmentRef(0); + alloc_chunk_remaining = 0; uint wr_ix = 0; uint rd_ix = 0; diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index ac0895068da7b6c3cde6bbdf42f33f2d10928539..069209e9fc4104d855cf5680c2673a39454a7ac8 100644 GIT binary patch literal 40892 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 m#x`(c;SeOb;l}qtp5k5-$cUz literal 40472 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 k+!* Date: Mon, 25 May 2020 15:01:52 -0700 Subject: [PATCH 24/32] Reinstate fills Add fills back in. --- piet-gpu-types/src/ptcl.rs | 7 +++-- piet-gpu/bin/cli.rs | 38 ++++++++++++++++++----- piet-gpu/shader/coarse.comp | 58 ++++++++++++++++++++++++++--------- piet-gpu/shader/coarse.spv | Bin 40892 -> 49152 bytes piet-gpu/shader/kernel4.comp | 6 ++-- piet-gpu/shader/kernel4.spv | Bin 18428 -> 19028 bytes piet-gpu/shader/ptcl.h | 12 +++++--- piet-gpu/src/pico_svg.rs | 4 +-- 8 files changed, 94 insertions(+), 31 deletions(-) diff --git a/piet-gpu-types/src/ptcl.rs b/piet-gpu-types/src/ptcl.rs index 2aa869e..bdf342b 100644 --- a/piet-gpu-types/src/ptcl.rs +++ b/piet-gpu-types/src/ptcl.rs @@ -19,8 +19,7 @@ piet_gpu! { rgba_color: u32, } struct CmdFill { - // Should be Ref - seg_ref: u32, + seg_ref: Ref, backdrop: i32, rgba_color: u32, } @@ -58,6 +57,10 @@ piet_gpu! { struct Segment { start: [f32; 2], end: [f32; 2], + + // This is used for fills only, but we're including it in + // the general structure for simplicity. + y_edge: f32, } struct SegChunk { diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index 347cf01..fe8c4ac 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -87,6 +87,29 @@ fn trace_ptcl(buf: &[u32]) { 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]; @@ -96,11 +119,12 @@ fn trace_ptcl(buf: &[u32]) { 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 * 4]); - let y0 = f32::from_bits(buf[segs / 4 + i * 4 + 1]); - let x1 = f32::from_bits(buf[segs / 4 + i * 4 + 2]); - let y1 = f32::from_bits(buf[segs / 4 + i * 4 + 3]); - println!(" ({:.3}, {:.3}) - ({:.3}, {:.3})", x0, y0, x1, y1); + 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; @@ -160,8 +184,8 @@ fn main() -> Result<(), Error> { /* let mut data: Vec = Default::default(); device.read_buffer(&renderer.ptcl_buf, &mut data).unwrap(); - piet_gpu::dump_k1_data(&data); - //trace_ptcl(&data); + //piet_gpu::dump_k1_data(&data); + trace_ptcl(&data); */ let mut img_data: Vec = Default::default(); diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 81ea890..3a73417 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -51,7 +51,6 @@ shared uint sh_is_segment[N_SLICE]; // Count of total number of segments in each tile, then // inclusive prefix sum of same. shared uint sh_seg_count[N_TILE]; -shared uint sh_orig_seg_count[N_TILE]; shared uint sh_seg_alloc; // scale factors useful for converting coordinates to tiles @@ -295,7 +294,6 @@ void main() { seg_count += bitCount(sh_bitmaps[i][th_ix] & sh_is_segment[i]); } sh_seg_count[th_ix] = seg_count; - sh_orig_seg_count[th_ix] = seg_count; // Prefix sum of sh_seg_count for (uint i = 0; i < LG_N_TILE; i++) { barrier(); @@ -359,8 +357,23 @@ void main() { uint rd_el_ix = (rd_ix + slice_ix * 32 + bit_ix) % N_RINGBUF; uint element_ix = sh_elements[rd_el_ix]; ref = AnnotatedRef(element_ix * Annotated_size); - AnnoStrokeLineSeg line = Annotated_StrokeLine_read(ref); - Segment seg = Segment(line.p0, line.p1); + AnnoFillLineSeg line = Annotated_FillLine_read(ref); + float y_edge = 0.0; + // This is basically the same logic as piet-metal, but should be made numerically robust. + if (Annotated_tag(ref) == Annotated_FillLine) { + vec2 tile_xy = xy0 + vec2((tile_ix % N_TILE_X) * TILE_WIDTH_PX, (tile_ix / N_TILE_X) * TILE_HEIGHT_PX); + y_edge = mix(line.p0.y, line.p1.y, (tile_xy.x - line.p0.x) / (line.p1.x - line.p0.x)); + if (min(line.p0.x, line.p1.x) < tile_xy.x && y_edge >= tile_xy.y && y_edge < tile_xy.y + TILE_HEIGHT_PX) { + if (line.p0.x > line.p1.x) { + line.p1 = vec2(tile_xy.x, y_edge); + } else { + line.p0 = vec2(tile_xy.x, y_edge); + } + } else { + y_edge = 1e9; + } + } + Segment seg = Segment(line.p0, line.p1, y_edge); Segment_write(SegmentRef(seg_alloc + Segment_size * ix), seg); } @@ -411,33 +424,50 @@ void main() { switch (tag) { case Annotated_Fill: - /* - if (seg_count > 0) { + if (last_chunk_n > 0 || seg_count > 0) { + SegChunkRef chunk_ref = SegChunkRef(0); + if (seg_count > 0) { + chunk_ref = alloc_seg_chunk(); + SegChunk chunk; + chunk.n = seg_count; + chunk.next = SegChunkRef(0); + uint seg_offset = seg_alloc + seg_start * Segment_size; + chunk.segs = SegmentRef(seg_offset); + SegChunk_write(chunk_ref, chunk); + } + if (last_chunk_n > 0) { + SegChunk chunk; + chunk.n = last_chunk_n; + chunk.next = chunk_ref; + chunk.segs = last_chunk_segs; + SegChunk_write(last_chunk_ref, chunk); + } else { + first_seg_chunk = chunk_ref; + } + AnnoFill fill = Annotated_Fill_read(ref); - SegChunk_write(seg_chunk_ref, SegChunk(chunk_n_segs, SegChunkRef(0))); - seg_chunk_ref.offset += SegChunk_size + Segment_size * chunk_n_segs; CmdFill cmd_fill; - cmd_fill.seg_ref = first_seg_chunk.offset; + cmd_fill.seg_ref = first_seg_chunk; cmd_fill.backdrop = backdrop; cmd_fill.rgba_color = fill.rgba_color; alloc_cmd(cmd_ref, cmd_limit); Cmd_Fill_write(cmd_ref, cmd_fill); cmd_ref.offset += Cmd_size; - chunk_n_segs = 0; + last_chunk_n = 0; } else if (backdrop != 0) { AnnoFill fill = Annotated_Fill_read(ref); alloc_cmd(cmd_ref, cmd_limit); Cmd_Solid_write(cmd_ref, CmdSolid(fill.rgba_color)); cmd_ref.offset += Cmd_size; } - */ - backdrop = 0; + seg_start += seg_count; seg_count = 0; + backdrop = 0; break; case Annotated_Stroke: + // TODO: reduce divergence & code duplication? Much of the + // fill and stroke processing is in common. if (last_chunk_n > 0 || seg_count > 0) { - // TODO: noncontiguous case - SegChunkRef chunk_ref = SegChunkRef(0); if (seg_count > 0) { chunk_ref = alloc_seg_chunk(); diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index 069209e9fc4104d855cf5680c2673a39454a7ac8..56337efe2c287438514a7be4724992bd6a4f6b2b 100644 GIT binary patch literal 49152 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 m#x`(c;SeOb;l}qtp5k5-$cUz diff --git a/piet-gpu/shader/kernel4.comp b/piet-gpu/shader/kernel4.comp index 4c4aba3..1abcc2b 100644 --- a/piet-gpu/shader/kernel4.comp +++ b/piet-gpu/shader/kernel4.comp @@ -68,11 +68,12 @@ void main() { CmdFill fill = Cmd_Fill_read(cmd_ref); // Probably better to store as float, but conversion is no doubt cheap. float area = float(fill.backdrop); - SegChunkRef fill_seg_chunk_ref = SegChunkRef(fill.seg_ref); + SegChunkRef fill_seg_chunk_ref = fill.seg_ref; do { SegChunk seg_chunk = SegChunk_read(fill_seg_chunk_ref); + SegmentRef segs = seg_chunk.segs; for (int i = 0; i < seg_chunk.n; i++) { - Segment seg = Segment_read(SegmentRef(fill_seg_chunk_ref.offset + SegChunk_size + Segment_size * i)); + Segment seg = Segment_read(Segment_index(segs, i)); vec2 start = seg.start - xy; vec2 end = seg.end - xy; vec2 window = clamp(vec2(start.y, end.y), 0.0, 1.0); @@ -87,6 +88,7 @@ void main() { float a = (b + 0.5 * (d * d - c * c) - xmin) / (xmax - xmin); area += a * (window.x - window.y); } + area += sign(end.x - start.x) * clamp(xy.y - seg.y_edge + 1.0, 0.0, 1.0); } fill_seg_chunk_ref = seg_chunk.next; } while (fill_seg_chunk_ref.offset != 0); diff --git a/piet-gpu/shader/kernel4.spv b/piet-gpu/shader/kernel4.spv index 02bd1378c14a8746b89c6c235c86c14f8e34f01a..8060f1ff2b700288356a5af27af23f9511c73062 100644 GIT binary patch literal 19028 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 Dn}E@X diff --git a/piet-gpu/shader/ptcl.h b/piet-gpu/shader/ptcl.h index 2026b46..dd1f9a8 100644 --- a/piet-gpu/shader/ptcl.h +++ b/piet-gpu/shader/ptcl.h @@ -80,7 +80,7 @@ CmdStrokeRef CmdStroke_index(CmdStrokeRef ref, uint index) { } struct CmdFill { - uint seg_ref; + SegChunkRef seg_ref; int backdrop; uint rgba_color; }; @@ -152,9 +152,10 @@ CmdRef Cmd_index(CmdRef ref, uint index) { struct Segment { vec2 start; vec2 end; + float y_edge; }; -#define Segment_size 16 +#define Segment_size 20 SegmentRef Segment_index(SegmentRef ref, uint index) { return SegmentRef(ref.offset + index * Segment_size); @@ -238,7 +239,7 @@ CmdFill CmdFill_read(CmdFillRef ref) { uint raw1 = ptcl[ix + 1]; uint raw2 = ptcl[ix + 2]; CmdFill s; - s.seg_ref = raw0; + s.seg_ref = SegChunkRef(raw0); s.backdrop = int(raw1); s.rgba_color = raw2; return s; @@ -246,7 +247,7 @@ CmdFill CmdFill_read(CmdFillRef ref) { void CmdFill_write(CmdFillRef ref, CmdFill s) { uint ix = ref.offset >> 2; - ptcl[ix + 0] = s.seg_ref; + ptcl[ix + 0] = s.seg_ref.offset; ptcl[ix + 1] = uint(s.backdrop); ptcl[ix + 2] = s.rgba_color; } @@ -399,9 +400,11 @@ Segment Segment_read(SegmentRef ref) { uint raw1 = ptcl[ix + 1]; uint raw2 = ptcl[ix + 2]; uint raw3 = ptcl[ix + 3]; + uint raw4 = ptcl[ix + 4]; Segment s; s.start = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.end = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.y_edge = uintBitsToFloat(raw4); return s; } @@ -411,6 +414,7 @@ void Segment_write(SegmentRef ref, Segment s) { ptcl[ix + 1] = floatBitsToUint(s.start.y); ptcl[ix + 2] = floatBitsToUint(s.end.x); ptcl[ix + 3] = floatBitsToUint(s.end.y); + ptcl[ix + 4] = floatBitsToUint(s.y_edge); } SegChunk SegChunk_read(SegChunkRef ref) { diff --git a/piet-gpu/src/pico_svg.rs b/piet-gpu/src/pico_svg.rs index 9cf5cc3..b2f054c 100644 --- a/piet-gpu/src/pico_svg.rs +++ b/piet-gpu/src/pico_svg.rs @@ -61,8 +61,8 @@ impl PicoSvg { for item in &self.items { match item { Item::Fill(fill_item) => { - //rc.fill(&fill_item.path, &fill_item.color); - rc.stroke(&fill_item.path, &fill_item.color, 1.0); + rc.fill(&fill_item.path, &fill_item.color); + //rc.stroke(&fill_item.path, &fill_item.color, 1.0); } Item::Stroke(stroke_item) => { rc.stroke(&stroke_item.path, &stroke_item.color, stroke_item.width); From e16f68d89d1c4941e2b85020340061e0b61c823f Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Tue, 26 May 2020 22:46:33 -0700 Subject: [PATCH 25/32] Fix buffer overrun Was a little too eager zeroing out sh_is_segment[] --- piet-gpu/shader/coarse.comp | 2 ++ piet-gpu/shader/coarse.spv | Bin 49152 -> 49152 bytes 2 files changed, 2 insertions(+) diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 3a73417..1f73318 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -133,6 +133,8 @@ void main() { for (uint i = 0; i < N_SLICE; i++) { sh_bitmaps[i][th_ix] = 0; sh_backdrop[i][th_ix] = 0; + } + if (th_ix < N_SLICE) { sh_is_segment[th_ix] = 0; } diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index 56337efe2c287438514a7be4724992bd6a4f6b2b..d951b247c1f2e6116832ab6fbd677a0e3ef937c0 100644 GIT binary patch delta 17 YcmZo@U~Xt&-Z00ODTI0RJlmuu0638bTL1t6 delta 17 YcmZo@U~Xt&-Z00O=^4}JdA3PS06d8X>;M1& From 319aa703c4ab10e36348fa5f1a1aa3598b5aa7d8 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Mon, 25 May 2020 15:45:06 -0700 Subject: [PATCH 26/32] Output multiple pixels per thread in k4 In kernel 4, compute a chunk of pixels rather than just one per thread. This is a dramatic speedup. (This commit cherry-picked from another working branch) --- piet-gpu/shader/kernel4.comp | 92 ++++++++++++++++++++++------------- piet-gpu/shader/kernel4.spv | Bin 19028 -> 23052 bytes 2 files changed, 59 insertions(+), 33 deletions(-) diff --git a/piet-gpu/shader/kernel4.comp b/piet-gpu/shader/kernel4.comp index 1abcc2b..2c068aa 100644 --- a/piet-gpu/shader/kernel4.comp +++ b/piet-gpu/shader/kernel4.comp @@ -6,10 +6,13 @@ #version 450 #extension GL_GOOGLE_include_directive : enable +#extension GL_KHR_shader_subgroup_basic : enable -layout(local_size_x = 16, local_size_y = 16) in; +#define CHUNK 8 +#define CHUNK_DY (16 / CHUNK) +layout(local_size_x = 16, local_size_y = 2) in; -// This should be annotated readonly but infra doesn't support that yet. +// Same concern that this should be readonly as in kernel 3. layout(set = 0, binding = 0) buffer PtclBuf { uint[] ptcl; }; @@ -24,11 +27,14 @@ void main() { uint tile_ix = gl_WorkGroupID.y * WIDTH_IN_TILES + gl_WorkGroupID.x; CmdRef cmd_ref = CmdRef(tile_ix * PTCL_INITIAL_ALLOC); - uvec2 xy_uint = gl_GlobalInvocationID.xy; + uvec2 xy_uint = uvec2(gl_GlobalInvocationID.x, gl_LocalInvocationID.y + TILE_HEIGHT_PX * gl_WorkGroupID.y); vec2 xy = vec2(xy_uint); vec2 uv = xy * vec2(1.0 / IMAGE_WIDTH, 1.0 / IMAGE_HEIGHT); //vec3 rgb = uv.xyy; - vec3 rgb = vec3(0.75); + vec3 rgb[CHUNK]; + for (uint i = 0; i < CHUNK; i++) { + rgb[i] = vec3(0.5); + } while (true) { uint tag = Cmd_tag(cmd_ref); @@ -38,15 +44,19 @@ void main() { switch (tag) { case Cmd_Circle: CmdCircle circle = Cmd_Circle_read(cmd_ref); - float r = length(xy + vec2(0.5, 0.5) - circle.center.xy); - float alpha = clamp(0.5 + circle.radius - r, 0.0, 1.0); vec4 fg_rgba = unpackUnorm4x8(circle.rgba_color).wzyx; - // TODO: sRGB - rgb = mix(rgb, fg_rgba.rgb, alpha * fg_rgba.a); + for (uint i = 0; i < CHUNK; i++) { + float dy = float(i * CHUNK_DY); + float r = length(vec2(xy.x, xy.y + dy) + vec2(0.5, 0.5) - circle.center.xy); + float alpha = clamp(0.5 + circle.radius - r, 0.0, 1.0); + // TODO: sRGB + rgb[i] = mix(rgb[i], fg_rgba.rgb, alpha * fg_rgba.a); + } break; case Cmd_Stroke: CmdStroke stroke = Cmd_Stroke_read(cmd_ref); - float df = 1e9; + float df[CHUNK]; + for (uint k = 0; k < CHUNK; k++) df[k] = 1e9; SegChunkRef seg_chunk_ref = stroke.seg_ref; do { SegChunk seg_chunk = SegChunk_read(seg_chunk_ref); @@ -54,52 +64,65 @@ void main() { for (int i = 0; i < seg_chunk.n; i++) { Segment seg = Segment_read(Segment_index(segs, i)); vec2 line_vec = seg.end - seg.start; - vec2 dpos = xy + vec2(0.5, 0.5) - seg.start; - float t = clamp(dot(line_vec, dpos) / dot(line_vec, line_vec), 0.0, 1.0); - df = min(df, length(line_vec * t - dpos)); + for (uint k = 0; k < CHUNK; k++) { + vec2 dpos = xy + vec2(0.5, 0.5) - seg.start; + dpos.y += float(k * CHUNK_DY); + float t = clamp(dot(line_vec, dpos) / dot(line_vec, line_vec), 0.0, 1.0); + df[k] = min(df[k], length(line_vec * t - dpos)); + } } seg_chunk_ref = seg_chunk.next; } while (seg_chunk_ref.offset != 0); fg_rgba = unpackUnorm4x8(stroke.rgba_color).wzyx; - alpha = clamp(stroke.half_width + 0.5 - df, 0.0, 1.0); - rgb = mix(rgb, fg_rgba.rgb, alpha * fg_rgba.a); + for (uint k = 0; k < CHUNK; k++) { + float alpha = clamp(stroke.half_width + 0.5 - df[k], 0.0, 1.0); + rgb[k] = mix(rgb[k], fg_rgba.rgb, alpha * fg_rgba.a); + } break; case Cmd_Fill: CmdFill fill = Cmd_Fill_read(cmd_ref); // Probably better to store as float, but conversion is no doubt cheap. - float area = float(fill.backdrop); + float area[CHUNK]; + for (uint k = 0; k < CHUNK; k++) area[k] = float(fill.backdrop); SegChunkRef fill_seg_chunk_ref = fill.seg_ref; do { SegChunk seg_chunk = SegChunk_read(fill_seg_chunk_ref); SegmentRef segs = seg_chunk.segs; for (int i = 0; i < seg_chunk.n; i++) { Segment seg = Segment_read(Segment_index(segs, i)); - vec2 start = seg.start - xy; - vec2 end = seg.end - xy; - vec2 window = clamp(vec2(start.y, end.y), 0.0, 1.0); - if (window.x != window.y) { - vec2 t = (window - start.y) / (end.y - start.y); - vec2 xs = vec2(mix(start.x, end.x, t.x), mix(start.x, end.x, t.y)); - float xmin = min(min(xs.x, xs.y), 1.0) - 1e-6; - float xmax = max(xs.x, xs.y); - float b = min(xmax, 1.0); - float c = max(b, 0.0); - float d = max(xmin, 0.0); - float a = (b + 0.5 * (d * d - c * c) - xmin) / (xmax - xmin); - area += a * (window.x - window.y); + for (uint k = 0; k < CHUNK; k++) { + vec2 my_xy = vec2(xy.x, xy.y + float(k * CHUNK_DY)); + vec2 start = seg.start - my_xy; + vec2 end = seg.end - my_xy; + vec2 window = clamp(vec2(start.y, end.y), 0.0, 1.0); + if (window.x != window.y) { + vec2 t = (window - start.y) / (end.y - start.y); + vec2 xs = vec2(mix(start.x, end.x, t.x), mix(start.x, end.x, t.y)); + float xmin = min(min(xs.x, xs.y), 1.0) - 1e-6; + float xmax = max(xs.x, xs.y); + float b = min(xmax, 1.0); + float c = max(b, 0.0); + float d = max(xmin, 0.0); + float a = (b + 0.5 * (d * d - c * c) - xmin) / (xmax - xmin); + area[k] += a * (window.x - window.y); + } + area[k] += sign(end.x - start.x) * clamp(my_xy.y - seg.y_edge + 1.0, 0.0, 1.0); } - area += sign(end.x - start.x) * clamp(xy.y - seg.y_edge + 1.0, 0.0, 1.0); } fill_seg_chunk_ref = seg_chunk.next; } while (fill_seg_chunk_ref.offset != 0); fg_rgba = unpackUnorm4x8(fill.rgba_color).wzyx; - alpha = min(abs(area), 1.0); - rgb = mix(rgb, fg_rgba.rgb, alpha * fg_rgba.a); + for (uint k = 0; k < CHUNK; k++) { + float alpha = min(abs(area[k]), 1.0); + rgb[k] = mix(rgb[k], fg_rgba.rgb, alpha * fg_rgba.a); + } break; case Cmd_Solid: CmdSolid solid = Cmd_Solid_read(cmd_ref); fg_rgba = unpackUnorm4x8(solid.rgba_color).wzyx; - rgb = mix(rgb, fg_rgba.rgb, fg_rgba.a); + for (uint k = 0; k < CHUNK; k++) { + rgb[k] = mix(rgb[k], fg_rgba.rgb, fg_rgba.a); + } break; case Cmd_Jump: cmd_ref = CmdRef(Cmd_Jump_read(cmd_ref).new_ref); @@ -108,5 +131,8 @@ void main() { cmd_ref.offset += Cmd_size; } - imageStore(image, ivec2(xy_uint), vec4(rgb, 1.0)); + // TODO: sRGB + for (uint i = 0; i < CHUNK; i++) { + imageStore(image, ivec2(xy_uint.x, xy_uint.y + CHUNK_DY * i), vec4(rgb[i], 1.0)); + } } diff --git a/piet-gpu/shader/kernel4.spv b/piet-gpu/shader/kernel4.spv index 8060f1ff2b700288356a5af27af23f9511c73062..5215e2f556e23cde480c5c9abb0565ff024d5cfc 100644 GIT binary patch literal 23052 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

}mVUT*vZ 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 g@19r+Rx{r3!ZNV&ao^ Date: Thu, 28 May 2020 11:48:36 -0700 Subject: [PATCH 27/32] Improve SVG parsing WIP --- piet-gpu/src/pico_svg.rs | 88 ++++++++++++++++++++++++++++++++-------- 1 file changed, 71 insertions(+), 17 deletions(-) diff --git a/piet-gpu/src/pico_svg.rs b/piet-gpu/src/pico_svg.rs index a4c92d0..7b52f5a 100644 --- a/piet-gpu/src/pico_svg.rs +++ b/piet-gpu/src/pico_svg.rs @@ -2,7 +2,7 @@ use std::str::FromStr; -use roxmltree::Document; +use roxmltree::{Document, Node}; use piet::kurbo::{Affine, BezPath}; @@ -28,27 +28,18 @@ pub struct FillItem { path: BezPath, } +struct Parser<'a> { + items: &'a mut Vec, +} + impl PicoSvg { pub fn load(xml_string: &str, scale: f64) -> Result> { let doc = Document::parse(xml_string)?; let root = doc.root_element(); - let g = root.first_element_child().ok_or("no root element")?; let mut items = Vec::new(); - for el in g.children() { - if el.is_element() { - let d = el.attribute("d").ok_or("missing 'd' attribute")?; - let bp = BezPath::from_svg(d)?; - let path = Affine::scale(scale) * bp; - if let Some(fill_color) = el.attribute("fill") { - let color = parse_color(fill_color); - items.push(Item::Fill(FillItem { color, path: path.clone() })); - } - if let Some(stroke_color) = el.attribute("stroke") { - let width = f64::from_str(el.attribute("stroke-width").ok_or("missing width")?)?; - let color = parse_color(stroke_color); - items.push(Item::Stroke(StrokeItem { width, color, path })); - } - } + let mut parser = Parser::new(&mut items); + for node in root.children() { + parser.rec_parse(node)?; } Ok(PicoSvg { items }) } @@ -67,6 +58,49 @@ impl PicoSvg { } } +impl<'a> Parser<'a> { + fn new(items: &'a mut Vec) -> Parser<'a> { + Parser { + items + } + } + + fn rec_parse(&mut self, node: Node) -> Result<(), Box> { + if node.is_element() { + match node.tag_name().name() { + "g" => { + for child in node.children() { + self.rec_parse(child)?; + } + } + "path" => { + let d = node.attribute("d").ok_or("missing 'd' attribute")?; + let bp = BezPath::from_svg(d)?; + let path = Affine::new([1.5, 0.0, 0.0, -1.5, 0.0, 1500.0]) * bp; + // TODO: default fill color is black, but this is overridden in tiger to this logic. + if let Some(fill_color) = node.attribute("fill") { + if fill_color != "none" { + let color = parse_color(fill_color); + let color = modify_opacity(color, "fill-opacity", node); + self.items.push(Item::Fill(FillItem { color, path: path.clone() })); + } + } + if let Some(stroke_color) = node.attribute("stroke") { + if stroke_color != "none" { + let width = 1.5 * f64::from_str(node.attribute("stroke-width").ok_or("missing width")?)?; + let color = parse_color(stroke_color); + let color = modify_opacity(color, "stroke-opacity", node); + self.items.push(Item::Stroke(StrokeItem { width, color, path })); + } + } + } + _ => (), + } + } + Ok(()) + } +} + fn parse_color(color: &str) -> Color { if color.as_bytes()[0] == b'#' { let mut hex = u32::from_str_radix(&color[1..], 16).unwrap(); @@ -74,7 +108,27 @@ fn parse_color(color: &str) -> Color { hex = (hex >> 8) * 0x110000 + ((hex >> 4) & 0xf) * 0x1100 + (hex & 0xf) * 0x11; } Color::from_rgba32_u32((hex << 8) + 0xff) + } else if color.starts_with("rgb(") { + let mut iter = color[4..color.len() - 1].split(','); + let r = u8::from_str(iter.next().unwrap()).unwrap(); + let g = u8::from_str(iter.next().unwrap()).unwrap(); + let b = u8::from_str(iter.next().unwrap()).unwrap(); + Color::rgb8(r, g, b) } else { Color::from_rgba32_u32(0xff00ff80) } } + +fn modify_opacity(color: Color, attr_name: &str, node: Node) -> Color { + if let Some(opacity) = node.attribute(attr_name) { + let alpha = if opacity.ends_with("%") { + let pctg = opacity[..opacity.len() - 1].parse().unwrap_or(100.0); + pctg * 0.01 + } else { + opacity.parse().unwrap_or(1.0) + }; + color.with_alpha(alpha) + } else { + color + } +} From 894ef156e14e028c6c7ef5ea3acf85696d4efebe Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Fri, 29 May 2020 20:06:16 -0700 Subject: [PATCH 28/32] Change to new merge strategy in binning WIP We get "device lost" on NV :/ --- piet-gpu-hal/src/vulkan.rs | 2 +- piet-gpu/bin/cli.rs | 8 +++--- piet-gpu/shader/binning.comp | 48 +++++------------------------------ piet-gpu/shader/binning.spv | Bin 19600 -> 16024 bytes piet-gpu/shader/coarse.spv | Bin 49152 -> 49240 bytes piet-gpu/src/lib.rs | 10 +++++--- 6 files changed, 18 insertions(+), 50 deletions(-) diff --git a/piet-gpu-hal/src/vulkan.rs b/piet-gpu-hal/src/vulkan.rs index 2fac015..402b13d 100644 --- a/piet-gpu-hal/src/vulkan.rs +++ b/piet-gpu-hal/src/vulkan.rs @@ -1016,7 +1016,7 @@ unsafe fn choose_compute_device( devices: &[vk::PhysicalDevice], surface: Option<&VkSurface>, ) -> Option<(vk::PhysicalDevice, u32)> { - for pdevice in devices { + for pdevice in &devices[1..] { let props = instance.get_physical_device_queue_family_properties(*pdevice); for (ix, info) in props.iter().enumerate() { // Check for surface presentation support diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index fe8c4ac..cc4cb44 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -181,12 +181,10 @@ fn main() -> Result<(), Error> { println!("Coarse kernel time: {:.3}ms", (ts[2] - ts[1]) * 1e3); println!("Render kernel time: {:.3}ms", (ts[3] - ts[2]) * 1e3); - /* let mut data: Vec = Default::default(); - device.read_buffer(&renderer.ptcl_buf, &mut data).unwrap(); - //piet_gpu::dump_k1_data(&data); - trace_ptcl(&data); - */ + device.read_buffer(&renderer.bin_buf, &mut data).unwrap(); + piet_gpu::dump_k1_data(&data); + //trace_ptcl(&data); let mut img_data: Vec = Default::default(); // Note: because png can use a `&[u8]` slice, we could avoid an extra copy diff --git a/piet-gpu/shader/binning.comp b/piet-gpu/shader/binning.comp index 713a654..d193dd2 100644 --- a/piet-gpu/shader/binning.comp +++ b/piet-gpu/shader/binning.comp @@ -45,8 +45,6 @@ shared uint bitmaps[N_SLICE][N_TILE]; shared uint count[N_SLICE][N_TILE]; shared uint sh_my_tile; shared uint sh_chunk_start[N_TILE]; -shared uint sh_chunk_end[N_TILE]; -shared uint sh_chunk_jump[N_TILE]; shared float sh_right_edge[N_TILE]; @@ -57,8 +55,6 @@ uint state_right_edge_index(uint partition_ix) { } void main() { - BinChunkRef chunk_ref = BinChunkRef((gl_LocalInvocationID.x * N_WG + gl_WorkGroupID.x) * BIN_INITIAL_ALLOC); - uint wr_limit = chunk_ref.offset + BIN_INITIAL_ALLOC; uint chunk_n = 0; uint my_n_elements = n_elements; while (true) { @@ -169,41 +165,15 @@ void main() { count[i][gl_LocalInvocationID.x] = element_count; } // element_count is number of elements covering bin for this invocation. + uint chunk_start = 0; if (element_count != 0) { - uint chunk_end; - uint chunk_new_start; - // Refactor to reduce code duplication? - if (chunk_n > 0) { - uint next_chunk = chunk_ref.offset + BinChunk_size + chunk_n * BinInstance_size; - if (next_chunk + BinChunk_size + min(24, element_count * BinInstance_size) > wr_limit) { - uint alloc_amount = max(BIN_ALLOC, BinChunk_size + element_count * BinInstance_size); - // could try to reduce fragmentation if BIN_ALLOC is only a bit above needed - next_chunk = atomicAdd(alloc, alloc_amount); - wr_limit = next_chunk + alloc_amount; - } - BinChunk_write(chunk_ref, BinChunk(chunk_n, BinChunkRef(next_chunk))); - chunk_ref = BinChunkRef(next_chunk); - } - BinInstanceRef instance_ref = BinInstanceRef(chunk_ref.offset + BinChunk_size); - if (instance_ref.offset + element_count * BinInstance_size > wr_limit) { - chunk_end = wr_limit; - chunk_n = (wr_limit - instance_ref.offset) / BinInstance_size; - uint alloc_amount = max(BIN_ALLOC, BinChunk_size + (element_count - chunk_n) * BinInstance_size); - chunk_new_start = atomicAdd(alloc, alloc_amount); - wr_limit = chunk_new_start + alloc_amount; - BinChunk_write(chunk_ref, BinChunk(chunk_n, BinChunkRef(chunk_new_start))); - chunk_ref = BinChunkRef(chunk_new_start); - chunk_new_start += BinChunk_size; - chunk_n = element_count - chunk_n; - } else { - chunk_end = ~0; - chunk_new_start = ~0; - chunk_n = element_count; - } - sh_chunk_start[gl_LocalInvocationID.x] = instance_ref.offset; - sh_chunk_end[gl_LocalInvocationID.x] = chunk_end; - sh_chunk_jump[gl_LocalInvocationID.x] = chunk_new_start - chunk_end; + // TODO: aggregate atomic adds (subgroup is probably fastest) + chunk_start = atomicAdd(alloc, element_count * BinInstance_size); + sh_chunk_start[gl_LocalInvocationID.x] = chunk_start; } + uint out_ix = (my_tile * N_TILE + gl_LocalInvocationID.x) * 2; + bins[out_ix] = element_count; + bins[out_ix + 1] = chunk_start; barrier(); // Use similar strategy as Laine & Karras paper; loop over bbox of bins @@ -219,9 +189,6 @@ void main() { idx += count[my_slice - 1][bin_ix]; } uint out_offset = sh_chunk_start[bin_ix] + idx * BinInstance_size; - if (out_offset >= sh_chunk_end[bin_ix]) { - out_offset += sh_chunk_jump[bin_ix]; - } BinInstance_write(BinInstanceRef(out_offset), BinInstance(element_ix, my_right_edge)); } x++; @@ -231,5 +198,4 @@ void main() { } } } - BinChunk_write(chunk_ref, BinChunk(chunk_n, BinChunkRef(0))); } diff --git a/piet-gpu/shader/binning.spv b/piet-gpu/shader/binning.spv index e932e4d3b944d2352e9ea514e2b4b4250c3e9a46..9f22a33b7a8812ba864f15fbee6d4b490ce04a31 100644 GIT binary patch literal 16024 zcmai)2Yg>;xyKKr38fS$6v{3wtA!4rY>^RGK`S6d87d)7(h{2{HA#V@$i{&J4n)Dd zP~6vfuk*T4+DzV{YYwOKWt z|6aa%RX^KRo1;|KR#i`Jm#;L<}6JID&#R9$sF>ugJ%ZtOJ5y8hu&9lxQ> zLRZ^Pfr{3(bn&0vd>k^Zno%uZ(YO4JGnTJ7xo=?o`o4+DjUz*SBg3OZeS^c}Lj#k; z7Y|u)dQoq9bYNt|U|FG?8fE|8)Mrpt@95~*%E|GuwL>e2xN>OCSwpLl>x}jLjICZh zF*I2*hAkNHBeX$tB>H^TGXvp{XZ^uVJpGPqqVCUlvzVtxh}Snh)IYe8Y8wV7mpoF2 zOEw&Q@X@sIsXDZK` z)$VZnsq6Ml_OB_YBmvrv>}NOXH~nl|&3jnesTKaWd>c2YJbAw^+S?i|nJ`V_nr^ z@C2p1+Ok?EcK-UNGUyu~9UQuJ;fAJdPsP@+*7uK34o?n`jnl7@p?6&4B8g8mJKda$~grD1R)2aOz zH2koJcU4SjJKnD9a&U6Ks;SrJJgvIE;hXxsq2ZhQy}99=`n{#$ZNFXB?eJz^r&aF< zGiUwlu#V}Y593|cC*aBBGhh~O!fA3($$eI#X`P?D1w6+39(LfO4Wn!OM&Uix_u=a{ z_Ko(no2$v8yZQlo@5sp5Kr#PnK4oS5RCgav4s$V+STygteq&(G;~uP!cdz&VnvuR0 zV*~vor;T2Waf6(8QseKg{sNe|sE^c)+T{KKS_I>D(djUnOe4gO&x+dr=DsV z`oQ?u#Kh18|cTU+H<=v}TQ_zdomRy{_ub)bla+oezuMRlNt^%xO>c5io1eF|Mxa&**mQJlqG~ zSmW;MelYVeF)}=`X>1QQ+PeOUwbbva=CUcWE(2p5MtQB)arQ*dS{?+R@^x27%IiJL zD8?u2I47WyTPdSYtCpjWZD{UDc^{m!P|uQcG}QX*gxbE>qYV#UinqJEy7f=p$rQVCBY{%beMdTFQLt%!I2O$Jn#5iM<`zxN64R9&0@3b}s*)jkT`2 zv39_!+0IxyV-w5q8B5K&yS6@c`CM$~LB1Em1ll-#pqJh&6^W?T_oQ#_GHO8aKr@KZF}opE2e4P}Fwd|G&WQPafJ|Qrxq@!WzT*F~+a4n^Dw_A@_`{ z>HlquZ6p6P*142hU+#OnoZtK5(+QAq{2gw<@EPFgNW0T&CfHckk#7xGvyT03gEh9g z{mK0fDEpfWUrZhA9{_e9T{ru4J`X~xsw=P1e{kz}P7Y}2<`A^hJ*laCbof}qk4f&g z!v9HI*E)BfYwaB;w~serT>~|9mV3{sImQox&C4-c|HE*{qi%h_U|`cxc7qJbm98_rVBTJzv)VTX9xGYEPnI%8?5AhhlLx@ zZ?Tg59aeI`!%FV=SIPb6D!JcPCHK3klH*A1G?p`762n3F~8QeSf5=8Cz`Z6S4PE9Fy;l zUovvX^jC_yXUce5$LAfY&-@ch`$PKh{buoAO^$y9+s3vJVCC^W2v)n6JnZu!ikjy+ zb^ZZ%PT$^+(L2q0>W*=<%>X=(QC|gDbBtp1%zUbwhv&JMs`{pZt*dp7dazqieDwQ% z(63ESTY`=8&Nip5z-lun<{|H<7|XWew4DWR`ft{CJ9y{1ZjYwk%dGg`nho|{Vq1NV zL9MsV(R}Skn|^ix+s~El-0lcgbB!|xYULc5@Bg)qJJofk)@oEW4tq zCy(90wpBL|?^3noy$4t=dty(pn(xjtnOna@=YiF2@7*gGd++w*PprM*#tPpX>|V;c z?E|-tx_fCqtdDD`Z(oXQBO{4&U0dE_4yv~9ufNv z2=@LD_U;e%-uD~9`ri3|4`ghIf*rr(I0EbQAYY5>a;3xgAE%h;CWbebMbwn=6?14^k}e}dG}I`>3762X!=s`Sg`fnlf6Z~ssw(u=+`qndEN0Q^5Sx z-+lT=iRX9Ba;z33eI@FCzHcz==xIcF>O7^Th@Cly1tBe1(=_DyvER<7>@&I zyk(5j(e-7#XMl~Pp7EXuRxiiBqHHfY+;~fGU zM?K?R4OTD5y9UlrJzo78??qsJj@LUz?!NV27zWR$hRI-xp3Ydbf8h?Y&Lv>SdKks}j?=kYhSg`gBe2fF;aKO=cg54eyHl)t8CLE+ zIvd|J!24j;^>4(gWvrKj)jH45Gtu;AtXF{5%5}dI&QE==^=I9$0_(HCMObaa?b$|NP-}PX9_O}=-Pk+w>r$4_Nb8kKyU0?cp4p^=1@40Y( z>b2CL{%!#4vp>(VJpDZnoc{c?m-P31bbaaXMzC7h-%W6S>i+bnznj7O>~ASnp8j3{ zPJiY2UWl&GHV0zm<^FLW<(ujj@Nek1x6Rk{`4TksoX?kn)o!Ka48IIqp5fczYOd=Y z?Y_SqZd-kx>sMfXJlFbOPEm7Dixc}*V87e*P4{ZJnz8fU_8Pdl<93{{1gkmD*J6#S z^_%7^LDVlXV9bKlTF@AN~K?`n4Iue}RpWZ{81r)n-u4L;gOBv5YOw-uo!H zbMJi&O+91$Z?I#u&Bw8ypnQa4J@c1ar}OuU^zli2oqc=?O+91yG}tkGl0N(vy9?|Z zsJo7yiO+!5vsRx48%I53zZNu zm%zqzF0E@U`}+zu{aNSB6gB%3XHLEf?wpgap{Zw1z78(u_o8FbCeQDJ%`-Xt4_M7yv%l4BpZ)D#w7qBM zf5FQr&foX3KCYMg_b6)iDNg=B0J~nf&wmJ3JBgC-i+jNQ)aO)x?(-jk^_kletlZqv zkJf(Nhd%}{qRiy2C;thUpZecC==1Km7i%o*=}+6Af{h>kGjJK}=Wu@NSk~2_Shmxj zw!Z)`rX-JFg88Z2>MQfGp8mA`6?kdEe+@2U{RYlY9m~4i1ylsJotj#QM0N`u;#sb3Mh0^Cz%5Igj=!cTG}Po4S7in}c=#jFrdtS8(Fo2bSl| zJpk@JbALlqzn*)?JLd0TwFfE2zaOiX82(^%7TY?>L_zbx7_%3qtjBN$Cj=F0h@22!n z>`QE0=U}E<5o>F>v3>7*2DX8#xku99ws6h9NFu|Cd`zFjD4 z&XG8Ab_17tP#)jzVCO4yu?Ji&IqFk$EfZTScb)A^TkZwdsq;Q|-}vY1&XL$ZQy1HA z9@scq_t0M0y(vEW_i6oF^LF0%1sfyp(*3|{GsxHZkk6+W%h=+yT>x(SZ~n}4A-waP z?2o3N{cr%-{a{;tiM0rcQ%{>i!OdJZd-*W9e~)D>>)MZ+^_*Yl+BwoD#^GT5O`b=9)v_=2sg>*JTpQcJ zA8~CH>nOPSyrn(gj{>V%$9?>0u#fAe?`VpeIf=c`{DwXTd=J(?TTAR?(Ttt%{NuoC z?t}c@!SQh0=8V>L+C25;zC01!IkzXFsi(h_!Oi%(>wDr9_z4v2Id-{qoR^G2oA#&H zzNvgqgR7N)Zv7Z|XC9A5Q_p&=0NYkQV}Bf2J-_2D2dky;)4^)tXMml%{El)a*uPu% z{cK&wqh`Idc|6z{$>S`rnsb{vE8)!?HNX3u4NqL_8&}PGo%e(>JV#H!SKe9j_?`$Z z-4&S8@19lg@|`dMx1M_Lj6txv^VElx+lTkwYH;3n@-=Yl^)^YeQ7rg)@RHi ztUSJ_g4M#;f_=y38({=)9rc{ybzt>ga_|g~fqk4KeWMgL=SZB`PXm{Ijl=EhYRwY`5Xg{vj^%fLS7u5Tkn&D_O_ z^K`W$*Jr@h%DsO%yj+iG!o7Q~t1q#x0P73C5^UZ%S67KC*2|dG+{ejHoA%d$n|_;n z=~}q=l6vY~2UZWi9&DevKb{3v^X^O@&xYGhpLtx3RZEQLfYtoFu>F|B=YrMZzX3dl zcyj*?;(71`;b*cI3*pa)TSwjaH)4$;z6q=UD0T*>x|#ni!CFV3bCEr!wGZ!(7l6w% z_CmN?-d+0CQvXG5{qPsV%kQpR;MP%huiuLGalhz$2}RBQB2Ju_g3CKh9^cEr-WATX zIo}3XOOE=~9Ajc@<>zDVODjL8<>$5hXT;O5)qiixe~NvG-=2=7`0rBuyR&U5KK}c? zBN*Fs?A8?9Z;Q>pNjVg}MZu5m;HPx(6&?Jn4t{P2zo6iQ;GvfLZuZ}796>QJ-%ZY; zcg2wu^^a3W{-{>-`@sEU&T6TDJXkGz`vkCBZ(W*q)k)yvDE6a2ZBGX4Gp=Ko$G03@ z#yt(L=Dm=2>tn!)t3PcY3)W{`=TIKsC$9dqeF|8gaorp8`1-(Q z+0chee*TKpG*U6c5S!D`-JGq6vk*hbxb zx)wV^aqqf+>{D*N#2f{?9*H>yR*Qc<*m;frX<)VVIS#gsditCI+vhrpeafwun3G`V zG%+`T)#ASx?A*kE30N(CUJAC2divZ5w$Ge3x%G_cy>b~v|3A1##JO{3gXd6ucA$9Y zY=_;E;+?Y-Hh0eD;4KP%RR_PmgWu4>Z|>l?6#O>u?Jdt8^GvvTxOSPFE5PcxW3B|N zW!&bdmikwN8?CvMu7RtScg(f$>{b0~dmUJxYv@?z@jVM%#(g$it-NEN15aH2Y5QET zKI1xP^7x(yF5^BQu2$YLH^LKFf7;#z)@NMjULM~Iz-8PQ!qv{E& z%)AP$7XPck&Pn{Q0jtITTCih_|8-!s_+Jk;$N1j>Ry&guu|4*U6#F!;ZS|Y`9oRQf z%suyk-1^4${Joi?-?J&s+3W%DPVw25;@R8!@!eqSl{vl#uAXmz z_kz{jKYqj7{(Tg6*WH-%{9B_BfVZKjo2xvzI=A~!eDb&9&@(R1>RR!iUC zgr{#~+P9kZ%5QGlE~EGyL~-5^#2!p>t`ET`*Kf6Yen0yTTa^+8wjLUuz4Z<|ZmYwJRm)MIm8xZ`PU;TIf}Xy9 zs_F&GtCX>{S06_)kp7lY{BLRMyCEIba@Evn-BS-ebn3MIyJsz2*gZ6S#{Ayy`F;Jp z-Lv}!duI*zo!YD4=)$kBf7bj(vrC6gJjyP^|`;Z}<#Pu`vOqpB5XKS-B5>u9Z52lUOK?=T$0SnNI{W9Y0_fg4pn zZC(6N&1Z1#%%1L91M>$4TYXk{3^QjAoUXmI8dI%Co%V0)YLmRxL;XiptK#44V^_6C z({{LLxOcXl_CxPwm@1+%vbFk_2cHnP+YMTX|NhCcL5T$O^xyzyBf5 z`b*-hSZ(-*I1cXri_jYLq$M%Bs*S-@`uY#*9~$oIpVeEB+A*v9jEuRn+7w>+)@!5Y z(>uR+L2v(XcONU^nyI@sgMD*P9`5d)J(u^Ovl?G*g;vDqK5ejXxHofG80bhnd(B2G zEXZ5Uaf{XKw}m{=q@s6s_ z2dB>$f=Bw?QC-~POY&aQ;!E;g*5XU@UfJSJ-ci-H@K&CV>P|3o*0X@xG2H(KKB{^E zo_;(6X5ELJCijNiXC|7p^Yf^HN4%K2)wZ*G7Cg%h$!qE)d(QiLGk1RXw1HVY^AGAj zmCA)X$R)zwReu&?pD{2vZ|dN{qJ>)A-yLko(+2sdS_S{bas5|nuh!MU}GTB{H*9P2DRfH)3mp5>wt$&?k2UZ zw0Be!&?4x+IlQlK+fnTVp4m6Npl9I_zMa(`@bWy-e_yn`69D3xXH@ctixYhYj zf{*0ysIG#~X+FCg_tkH(cUAYod*;p^?49d-?9MPwXZ0iW>Au@b)}*CruiyFFZ&dX# zem=&2M9l3ujX2VO{VUHSVm|2Qv>t^ZRBk8QX>}Z9&h_ zJp8+={oz@cSp$pu-FU`106l9t13Y5utmeW!H@oYNsee~>23oy$x_cHhVbD9O3((8C zyzmXYtGWozYgT_yHLnouI;+dkTl>DZe|FucuId`JH|OZ8u3zHY-+LPOHIB+U_pRt< z?%UtMyQ+KO%x5c2s~PROsvnekD}Aj$RO+WJTClM0S4XuNowK4|dHenZd|=UVcRk6? zd_IHLH~Vy~%r4he?IWLA9o5=sWnb56<6E@xtqMMsdETEqU6c;Wf#f)V+GkR$s;0W8 zV48tD>hpesCAJoySn!9zWxSk;WjwXS^L#Ah$&DAgXQKDfwJGNJJ}zI2Vt;d1C4Mxv zd_9JYOFyQv)<#o1F16(G%qsgKpNOAr%=;j`9GCB?Px2a1E%AIe%XspM_*2#Kw!zVH ztVvmgEasU=Yuk%gC8zl&P<+le8y7K*LBmTE?Um4jgQ*y4L`8ajAQ}IYpAumw(^@B?jCa74^!JG$NdPkT)!Vv+ebCWEq}Dpv_A$m zu6rQ;d>ZUNbMHP!ZCuBt|MS#KQPhnm_l!}~{$hj6d3yzIEG7P)LHZ}(QjopjzH6Q- zwoNhS@@S4hn=#~`FKXlY?|ATL^w4@$ifz`WcJ7U18@YF>ay{0Ed(TThCV}l(_+;%! z>(FX@u(9-!??A1lkNI|_HnzI?pIw8?%l%n;od1q?p;E@CI9uD+Ki3ud%q>C>z?*=#qs*CTXtek(xh%f#SB*L@ulnc z*c{u!pQX5#a?dTlPn6s}R&u{flzc)P-@J``hmXJK`o0A>pLh7uen`RHXWrvumwT5l z`H^khJALe)58ms;-Iw0y!#&5m$A{a$lL~$f{HlU$zq;V|-+O%8%O7du-o<0L|K7Vx z?%g}w_TIls?%liO-n~oiox9}TvrFzBJKXj5ZXNFDn0M>&P2k?G!|jiE>XLh(F1h#U zaN~P_4mZB{=WydKZsXp2OS^a8l6&tB--PzwdBZn@d*=-|zjxkn^Lyu&@58Qtk@_Wy zXTI;v9_)F~#g{4S@5M*{??&_NPrKK^p8aJz&nWe?n|2lJYg_fiS{hB=&xymx=X*N} ztiC62r0<{HJkC!iSZyy#`qzb~o;=Hd)r@J3`kigC@2h<@p7($i;C|ke@m7SZ+t&A4 z{u1ZHO4ObY*3Z+|anvhQe6+99*sWj2?&sB5ihi~eXTDYgx6kwHXzJI~H$NNJps3m3 z7wF4cU>{>^Ta%(@Y;nf4F4!@7$9Rg>bwg<8J}0xyG3TwQ>&Z@Bg)qdtxis zYA?8&Yn8F=4Q?OHK4|Lc#}u$_)$NC$XKLy9equ`Y!~tNn{VBfhYta9xV0GK?ODz{4 zNbQ^?)V#YD>jb#4+WdooQF`$cV#d5-W&$@ z-!RnECpG6ev8=WES?oC?_H#Jc&){G`e}nz(ok7w680upw8QXDS$L}~!r1p7*Zxrfs zrQ`Vz9|v`9Ur&Jh9mLu(YR?#Zz!~F7V7X)TJIYM(1Zs8r;%B~^`_((bEU=pW&U$zc zoQ1L*MmV0OqH@>uc|4&Aq$!gSBPdq6uQ9YI#xOYJEn_S~*Ou{~3O0^<#(NrAy&Ui9aDM9XYR`Dj0Bg(Ml&25x1ZTYE zy1ffsTl~&!{My&|-RRmf-uHm{secDy4DE?=7C7TAbDfQ@?VM&U&jA}pJ>xxBJEa`& zd2oK}_0gX3z89>`@%sMB(}(wgGv0E}&qvo5zxOwO?dy91y0(n>17LpY@ft&WVq6H$ zc*|TLMAw$_eh6$F^^Et!VD)mm7s2_d$E!W#y%?;`@p{I{bC!Grd>|$FijRWT?2qTm z$H6|HliEH;QS%HDXDpY1|I@sE0$p4B{YkLerIfsfp91q!_gi~nUIsR1`y787O&0(#`*=YTDk7m!TG7*YwcP0 zFM_q1?+9vn@_h-MeC6J|9$j1V-2hfA^W6yNr_QH6`MwO+X1?jv^5nY-oP6FJb8dbG zU0d?q3|1@i-2&&QUQ6xC_f@bq^BqYoPrk2#lh5DJCEwT4wI$!JV6`&eZE$|-eA<)m zcCa?{9YrlqzHfk&uN>bu(Y4v;Xli-6f80m8Q+*5Eey932ntI;P?|{|rpyVCC6I{N- zcfr+M*Xyd*zP}r8TW!AA_fY%zUTga3}Zb)Ezh0nQSk1xRd??Fp7s;)!xZlz+7sue zU~OlT*U#vmfz`4W^7#H7Y|f1H7hpBt$BaqMKHGoCWNmw6{}Sx_UI8CZ{VR%^^DB1z zj@@?F#>sqJo8vKRKlil#y79Gk-hNB{JBp9?#f{xMWB5JT7`gNQ0jxHeVn5`+p%}}4 zh_m;e0JrbGKclH zZ}+0@eP>2tJBHf%>je9_Ug{kbHFJv7|1PlWmGgWVxY|6l+%HDMq4j&JJ?HtdU~P{5 zNF3z$EqScX<33ytd_)~{bj`=Wjp^TFY4hyycizUT{lICv0@(QBE5gfIE5Q>>U+sxy zJMC#Z7Caqo44=OCYaBdnwUzzQPkY*~3_c1id=+>ZYdk!$^wpkz*iL)et_pSx;j6*z zYd)t|hwG#6dagb-?yPUw=0&k8M40;`n=F zdEU7R;P!WJBAWX7oIAdQlfY{JyM^)n-LhI@Yy@tPu`!zZeNBu_!D{|nN6xd&z`hUK z^6qX9RyU8ovz9yNEvTIz>-zUq)LT=0v~SbctuyXz!Hze4GT3>%g+7g^-VUyhx@#fd zlCl-WTw>cg2it=kcVg`THnyMp?!6tsYVMKv>;!f#JOlLE8LXeWpVw>Chh5~<#@C*F zyMi76Gu*??zZ+Oz?;4(u+LLE@aGB>VaJ8H@+SKB|2Usn9PjIZ^4RtTJ71ZL{lIGJqc%0yGO?}YuCuwU%e~+_wV%iCoBh$9Bk=(Z zw%t^)aje}#2Wg}DXn$*Cx3=HT`!ujI@>%*euv+dB@&hTxGPXEv4*}aY@5!Oy_V?s4 zH1+I0q_IH%Efi;(rv_dC2F_(O@=GB>oM^5 zz8;IFo;JsU&5^zz51v6WmcHgu)6e;JuAL+6#5e(L-t_ZCuv+$oHnnp7oa?mj2HS@l zns@pnu$t@d-kk~daqnvDp{UspapKGZk7nL)Y~sv@t0m4HaN=m|rKlN4>>B&;E+>QA z|J|hzO+8~g1+3;CO8$9p^XGfSe7KrEX}17wTW!wmTxzw%=m)FCe*mmj=6MI)`AVKa zu$n&pp2j^t1c%mtBeM{!Pht*(jjK=cE&_MZAai#rTt9W!;B;yq*Ff886gB5goH*|U zmwkH|T&6iZSVZ3xsNk;>$HC_*!2&8AKdu)x4!eiYKJkF$@KsI zVB4tM#(xj|0NA}}z6-!|@rBg-@5<^@RUhO(J5lSSEjd2~E^~euu4WA5%I$yly><5e z#o+dR{}D9x%*jW=YObf_+L)LhgR7h4B5HYJejKdkJuZE`1g;+b32?b*E`_U=d*+jH z+i6R?Pl3xldl_6mb@%LNsD0eC+CELWlj5EgC(adMvo0{*g``SHxXJd2E z-qm39h?D1Tuz3;EF-xfk4iU-%xHdiMW);P(B0KbpEe-iN;rzK3F4^UC!} zo*#nS^E`m2o_;?Fc3g@1Bk&I>`WaKMPhvg{Zjbp0ntEdX80`FnKMFQS-m#y6^-)jW zpMoEv7|XnJ{hY&@)UJbd`yJLC-tm5ht-MdmWBWO{{7xq~?k}i~YwaEPSJaPDe6;_% zv0EqBZ@}vo{I}rpd(!XV`lx#z%6~~Qwze&3}N^b4UCq*vC22_B2J!IT9!Kzrba#|ACt;d+AxQn&)KlKL@v+wtNS8 z9;}`@djYKG_@1GbC+CY`=PY-$m%wW2`zv4{`>yR}ikf{FC(ggY#z|ja1*_TD_`C)$ z*W-2g42r(m;zu_tZQ)D7?RU<>rQvG&WlU=BQe-}kHtHjcXYkFnG~ zu7|djC~B^UIB~{-%UmnNJ#*CKvkF)}d_36xW}a6CtLc~VsX5QdX`S|~fo-2V-0EPp z*w+yA{>lCKfHmR%UeeE7|E_*5xIXIU^KbEtA@=X^wCD46U9i7b)JL0r$~$Fk4$qMF zu&oWxnX*1yE#D)xsl|Un;~zc|Uj7bb1GqlwuE&PdKCXwhNfb5LL!3Apfy*;n9^1xX z&&Kp|6S!LXs7=i=CbqTwF>L0tmgk)J43@9V{A#zBuhj5y)aLix+>}_}1#<7%9Iocy z|LWttXG?JIJ^l@(+;#EqAN9|=Yz_9_qmQ;%xh3vG@o$CwTN(eq-aca}JJa63Azy(~ z?_cWto$wyuWeUDu8$Yy-AJxW>YvVm_d~U)0J6-?(pyu;4+VAUoQ|wpy9b=zHzZ)Ot zZC{Go6pC|UpVcz22i3T>uKK@~qUPD^XOiDN-$wEOYhfPkX?qA*n{gexJhsDXJQDW^ zxSD5e;!X!AuJ*J&60FU*&Y?WEqiZ}8_w8`C8I;652AsIs)Am@fHsiV$^4N~A@krbg z;cEVybK-V`6IXlMo&?rrT-Q_{+sqn|#GMUSn?*_7UU1@SPun?QZN_zP$YVRX#v^f0 zfvfdV5_cXrakZ!Ie6Tj-y2s?P4S-!sKbJlK7E;t=e+Sq#iG2{P=6ksu^$^82>h9BF z>O~ayuKUNFa{UtXG_dQDn5To)Vm|}yyvF`cuv&7y3v3(p&YVwj{Sx!tVCOV3 z-vd^Q{VcF^6Z_dhmbtU*n7u=ge6Jyc)%4JjF9- z9QCRc&z#k%bLN~6_6!KWu#I2b#xH5(m$mUL3w{mw+J@(hc|Y8KxOSPF3&83*V?F>@ z%ed{MTKqobztW* z@5~p$YO#L_?3~1YJytacc0#LCoPp_tRSw$*OmZ>GM5 zV&8Kf$n`h2@84G`+I=_0c{jVj>ri~wqWEsENxe43cXM6p%+IZjUcM{0p{eIxxgD(L zyOKTn4RF>*d&c`sur~W7PM?+sPoVg$M@gU72ivEK)SeHcsqdsLLrEXM1=g?ZN3NKv=1^7PfY-H_t50VRE%1h%gmQQOyX)b~@$ zzTN}Zuk7o+aP{={dtkNnbu9Qkin@I@raXVY_kHkc6m|P5PhXb?Z$E4bR>6mvaB{cDQ3PNmpxS?b?V)O{bjsDImN z$^AQcavRg!YWkITZs&a`iqG~G=Y3o19U8nH^^O$heP`;-`(ki8@4tuJrksyIz|}MF ze*~*#-WP+_G8caWt7YCF2dkCy{si3qlzILPSI^l00?yct;n>ymORguuiz&(V6j-e_ z?_KOmes4oDm-Alc`&(m6zQ4nh&p75&)32Os*J*c(&#n~b+B~~aoZq)lr+@!w^!$F| WpK!IODe3bw;29KczE5)Br~d_$M=(MF diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index d951b247c1f2e6116832ab6fbd677a0e3ef937c0..b58a3274efdbbcf459803bf752365656e4568355 100644 GIT binary patch literal 49240 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 diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 65bbe5c..5f0f6be 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -160,7 +160,7 @@ impl Renderer { let state_buf = device.create_buffer(1 * 1024 * 1024, dev)?; let anno_buf = device.create_buffer(64 * 1024 * 1024, dev)?; - let bin_buf = device.create_buffer(64 * 1024 * 1024, dev)?; + let bin_buf = device.create_buffer(64 * 1024 * 1024, host)?; let ptcl_buf = device.create_buffer(48 * 1024 * 1024, dev)?; let image_dev = device.create_image2d(WIDTH as u32, HEIGHT as u32, dev)?; @@ -176,12 +176,12 @@ impl Renderer { let bin_alloc_buf_dev = device.create_buffer(12, dev)?; // TODO: constants - let bin_alloc_start = 256 * 64 * N_WG; + let bin_alloc_start = ((n_elements + 255) & !255) * 8; device .write_buffer(&bin_alloc_buf_host, &[ n_elements as u32, 0, - bin_alloc_start, + bin_alloc_start as u32, ]) ?; let bin_code = include_bytes!("../shader/binning.spv"); @@ -268,18 +268,22 @@ impl Renderer { ); cmd_buf.write_timestamp(&query_pool, 2); cmd_buf.memory_barrier(); + /* cmd_buf.dispatch( &self.coarse_pipeline, &self.coarse_ds, (WIDTH as u32 / 256, HEIGHT as u32 / 256, 1), ); + */ cmd_buf.write_timestamp(&query_pool, 3); cmd_buf.memory_barrier(); + /* cmd_buf.dispatch( &self.k4_pipeline, &self.k4_ds, ((WIDTH / TILE_W) as u32, (HEIGHT / TILE_H) as u32, 1), ); + */ cmd_buf.write_timestamp(&query_pool, 4); cmd_buf.memory_barrier(); cmd_buf.image_barrier(&self.image_dev, ImageLayout::General, ImageLayout::BlitSrc); From 121f29fef68fec89079132411586a0bbfe3ae00a Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Sat, 30 May 2020 08:35:26 -0700 Subject: [PATCH 29/32] Merge one segment at a time No parallelism yet, but seems to improve performance. --- piet-gpu-hal/src/vulkan.rs | 2 +- piet-gpu/bin/cli.rs | 4 +- piet-gpu/shader/binning.comp | 256 +++++++++++++++++------------------ piet-gpu/shader/binning.spv | Bin 16024 -> 15652 bytes piet-gpu/shader/coarse.comp | 73 ++-------- piet-gpu/shader/coarse.spv | Bin 49240 -> 46048 bytes piet-gpu/src/lib.rs | 13 +- 7 files changed, 144 insertions(+), 204 deletions(-) diff --git a/piet-gpu-hal/src/vulkan.rs b/piet-gpu-hal/src/vulkan.rs index 402b13d..2fac015 100644 --- a/piet-gpu-hal/src/vulkan.rs +++ b/piet-gpu-hal/src/vulkan.rs @@ -1016,7 +1016,7 @@ unsafe fn choose_compute_device( devices: &[vk::PhysicalDevice], surface: Option<&VkSurface>, ) -> Option<(vk::PhysicalDevice, u32)> { - for pdevice in &devices[1..] { + for pdevice in devices { let props = instance.get_physical_device_queue_family_properties(*pdevice); for (ix, info) in props.iter().enumerate() { // Check for surface presentation support diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index cc4cb44..5d7c09e 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -181,10 +181,12 @@ fn main() -> Result<(), Error> { println!("Coarse kernel time: {:.3}ms", (ts[2] - ts[1]) * 1e3); println!("Render kernel time: {:.3}ms", (ts[3] - ts[2]) * 1e3); + /* let mut data: Vec = Default::default(); - device.read_buffer(&renderer.bin_buf, &mut data).unwrap(); + device.read_buffer(&renderer.ptcl_buf, &mut data).unwrap(); piet_gpu::dump_k1_data(&data); //trace_ptcl(&data); + */ let mut img_data: Vec = Default::default(); // Note: because png can use a `&[u8]` slice, we could avoid an extra copy diff --git a/piet-gpu/shader/binning.comp b/piet-gpu/shader/binning.comp index d193dd2..d35c2d9 100644 --- a/piet-gpu/shader/binning.comp +++ b/piet-gpu/shader/binning.comp @@ -43,7 +43,6 @@ layout(set = 0, binding = 3) buffer BinsBuf { // Note: cudaraster has N_TILE + 1 to cut down on bank conflicts. shared uint bitmaps[N_SLICE][N_TILE]; shared uint count[N_SLICE][N_TILE]; -shared uint sh_my_tile; shared uint sh_chunk_start[N_TILE]; shared float sh_right_edge[N_TILE]; @@ -57,145 +56,138 @@ uint state_right_edge_index(uint partition_ix) { void main() { uint chunk_n = 0; uint my_n_elements = n_elements; - while (true) { - if (gl_LocalInvocationID.x == 0) { - sh_my_tile = atomicAdd(tile_ix, 1); + uint my_partition = gl_WorkGroupID.x; + + for (uint i = 0; i < N_SLICE; i++) { + bitmaps[i][gl_LocalInvocationID.x] = 0; + } + barrier(); + + // Read inputs and determine coverage of bins + uint element_ix = my_partition * N_TILE + gl_LocalInvocationID.x; + AnnotatedRef ref = AnnotatedRef(element_ix * Annotated_size); + uint tag = Annotated_Nop; + if (element_ix < my_n_elements) { + tag = Annotated_tag(ref); + } + int x0 = 0, y0 = 0, x1 = 0, y1 = 0; + float my_right_edge = INFINITY; + bool crosses_edge = false; + switch (tag) { + case Annotated_FillLine: + case Annotated_StrokeLine: + AnnoStrokeLineSeg line = Annotated_StrokeLine_read(ref); + x0 = int(floor((min(line.p0.x, line.p1.x) - line.stroke.x) * SX)); + y0 = int(floor((min(line.p0.y, line.p1.y) - line.stroke.y) * SY)); + x1 = int(ceil((max(line.p0.x, line.p1.x) + line.stroke.x) * SX)); + y1 = int(ceil((max(line.p0.y, line.p1.y) + line.stroke.y) * SY)); + crosses_edge = tag == Annotated_FillLine && ceil(line.p0.y * TSY) != ceil(line.p1.y * TSY); + break; + case Annotated_Fill: + case Annotated_Stroke: + // Note: we take advantage of the fact that fills and strokes + // have compatible layout. + AnnoFill fill = Annotated_Fill_read(ref); + x0 = int(floor(fill.bbox.x * SX)); + y0 = int(floor(fill.bbox.y * SY)); + x1 = int(ceil(fill.bbox.z * SX)); + y1 = int(ceil(fill.bbox.w * SY)); + // It probably makes more sense to track x1, to avoid having to redo + // the rounding to tile coords. + my_right_edge = fill.bbox.z; + break; + } + + // If the last element in this partition is a fill edge, then we need to do a + // look-forward to find the right edge of its corresponding fill. That data is + // recorded in aggregates computed in the element processing pass. + if (gl_LocalInvocationID.x == N_TILE - 1 && tag == Annotated_FillLine) { + uint aggregate_ix = (my_partition + 1) * ELEMENT_BINNING_RATIO; + // This is sequential but the expectation is that the amount of + // look-forward is small (performance may degrade in the case + // of massively complex paths). + do { + my_right_edge = uintBitsToFloat(state[state_right_edge_index(aggregate_ix)]); + aggregate_ix++; + } while (isinf(my_right_edge)); + } + + // Now propagate right_edge backward, from fill to segment. + for (uint i = 0; i < LG_N_TILE; i++) { + // Note: we could try to cut down on write bandwidth here if the value hasn't + // changed, but not sure it's worth the complexity to track. + sh_right_edge[gl_LocalInvocationID.x] = my_right_edge; + barrier(); + if (gl_LocalInvocationID.x + (1 << i) < N_TILE && isinf(my_right_edge)) { + my_right_edge = sh_right_edge[gl_LocalInvocationID.x + (1 << i)]; } barrier(); - uint my_tile = sh_my_tile; - if (my_tile * N_TILE >= my_n_elements) { - break; - } + } + if (crosses_edge) { + x1 = int(ceil(my_right_edge * SX)); + } - for (uint i = 0; i < N_SLICE; i++) { - bitmaps[i][gl_LocalInvocationID.x] = 0; + // At this point, we run an iterator over the coverage area, + // trying to keep divergence low. + // Right now, it's just a bbox, but we'll get finer with + // segments. + x0 = clamp(x0, 0, N_TILE_X); + x1 = clamp(x1, x0, N_TILE_X); + y0 = clamp(y0, 0, N_TILE_Y); + y1 = clamp(y1, y0, N_TILE_Y); + if (x0 == x1) y1 = y0; + int x = x0, y = y0; + uint my_slice = gl_LocalInvocationID.x / 32; + uint my_mask = 1 << (gl_LocalInvocationID.x & 31); + while (y < y1) { + atomicOr(bitmaps[my_slice][y * N_TILE_X + x], my_mask); + x++; + if (x == x1) { + x = x0; + y++; } - barrier(); + } - // Read inputs and determine coverage of bins - uint element_ix = my_tile * N_TILE + gl_LocalInvocationID.x; - AnnotatedRef ref = AnnotatedRef(element_ix * Annotated_size); - uint tag = Annotated_Nop; - if (element_ix < my_n_elements) { - tag = Annotated_tag(ref); - } - int x0 = 0, y0 = 0, x1 = 0, y1 = 0; - float my_right_edge = INFINITY; - bool crosses_edge = false; - switch (tag) { - case Annotated_FillLine: - case Annotated_StrokeLine: - AnnoStrokeLineSeg line = Annotated_StrokeLine_read(ref); - x0 = int(floor((min(line.p0.x, line.p1.x) - line.stroke.x) * SX)); - y0 = int(floor((min(line.p0.y, line.p1.y) - line.stroke.y) * SY)); - x1 = int(ceil((max(line.p0.x, line.p1.x) + line.stroke.x) * SX)); - y1 = int(ceil((max(line.p0.y, line.p1.y) + line.stroke.y) * SY)); - crosses_edge = tag == Annotated_FillLine && ceil(line.p0.y * TSY) != ceil(line.p1.y * TSY); - break; - case Annotated_Fill: - case Annotated_Stroke: - // Note: we take advantage of the fact that fills and strokes - // have compatible layout. - AnnoFill fill = Annotated_Fill_read(ref); - x0 = int(floor(fill.bbox.x * SX)); - y0 = int(floor(fill.bbox.y * SY)); - x1 = int(ceil(fill.bbox.z * SX)); - y1 = int(ceil(fill.bbox.w * SY)); - // It probably makes more sense to track x1, to avoid having to redo - // the rounding to tile coords. - my_right_edge = fill.bbox.z; - break; - } + barrier(); + // Allocate output segments. + uint element_count = 0; + for (uint i = 0; i < N_SLICE; i++) { + element_count += bitCount(bitmaps[i][gl_LocalInvocationID.x]); + count[i][gl_LocalInvocationID.x] = element_count; + } + // element_count is number of elements covering bin for this invocation. + uint chunk_start = 0; + if (element_count != 0) { + // TODO: aggregate atomic adds (subgroup is probably fastest) + chunk_start = atomicAdd(alloc, element_count * BinInstance_size); + sh_chunk_start[gl_LocalInvocationID.x] = chunk_start; + } + // Note: it might be more efficient for reading to do this in the + // other order (each bin is a contiguous sequence of partitions) + uint out_ix = (my_partition * N_TILE + gl_LocalInvocationID.x) * 2; + bins[out_ix] = element_count; + bins[out_ix + 1] = chunk_start; - // If the last element in this partition is a fill edge, then we need to do a - // look-forward to find the right edge of its corresponding fill. That data is - // recorded in aggregates computed in the element processing pass. - if (gl_LocalInvocationID.x == N_TILE - 1 && tag == Annotated_FillLine) { - uint aggregate_ix = (my_tile + 1) * ELEMENT_BINNING_RATIO; - // This is sequential but the expectation is that the amount of - // look-forward is small (performance may degrade in the case - // of massively complex paths). - do { - my_right_edge = uintBitsToFloat(state[state_right_edge_index(aggregate_ix)]); - aggregate_ix++; - } while (isinf(my_right_edge)); - } - - // Now propagate right_edge backward, from fill to segment. - for (uint i = 0; i < LG_N_TILE; i++) { - // Note: we could try to cut down on write bandwidth here if the value hasn't - // changed, but not sure it's worth the complexity to track. - sh_right_edge[gl_LocalInvocationID.x] = my_right_edge; - barrier(); - if (gl_LocalInvocationID.x + (1 << i) < N_TILE && isinf(my_right_edge)) { - my_right_edge = sh_right_edge[gl_LocalInvocationID.x + (1 << i)]; + barrier(); + // Use similar strategy as Laine & Karras paper; loop over bbox of bins + // touched by this element + x = x0; + y = y0; + while (y < y1) { + uint bin_ix = y * N_TILE_X + x; + uint out_mask = bitmaps[my_slice][bin_ix]; + if ((out_mask & my_mask) != 0) { + uint idx = bitCount(out_mask & (my_mask - 1)); + if (my_slice > 0) { + idx += count[my_slice - 1][bin_ix]; } - barrier(); + uint out_offset = sh_chunk_start[bin_ix] + idx * BinInstance_size; + BinInstance_write(BinInstanceRef(out_offset), BinInstance(element_ix, my_right_edge)); } - if (crosses_edge) { - x1 = int(ceil(my_right_edge * SX)); - } - - // At this point, we run an iterator over the coverage area, - // trying to keep divergence low. - // Right now, it's just a bbox, but we'll get finer with - // segments. - x0 = clamp(x0, 0, N_TILE_X); - x1 = clamp(x1, x0, N_TILE_X); - y0 = clamp(y0, 0, N_TILE_Y); - y1 = clamp(y1, y0, N_TILE_Y); - if (x0 == x1) y1 = y0; - int x = x0, y = y0; - uint my_slice = gl_LocalInvocationID.x / 32; - uint my_mask = 1 << (gl_LocalInvocationID.x & 31); - while (y < y1) { - atomicOr(bitmaps[my_slice][y * N_TILE_X + x], my_mask); - x++; - if (x == x1) { - x = x0; - y++; - } - } - - barrier(); - // Allocate output segments. - uint element_count = 0; - for (uint i = 0; i < N_SLICE; i++) { - element_count += bitCount(bitmaps[i][gl_LocalInvocationID.x]); - count[i][gl_LocalInvocationID.x] = element_count; - } - // element_count is number of elements covering bin for this invocation. - uint chunk_start = 0; - if (element_count != 0) { - // TODO: aggregate atomic adds (subgroup is probably fastest) - chunk_start = atomicAdd(alloc, element_count * BinInstance_size); - sh_chunk_start[gl_LocalInvocationID.x] = chunk_start; - } - uint out_ix = (my_tile * N_TILE + gl_LocalInvocationID.x) * 2; - bins[out_ix] = element_count; - bins[out_ix + 1] = chunk_start; - - barrier(); - // Use similar strategy as Laine & Karras paper; loop over bbox of bins - // touched by this element - x = x0; - y = y0; - while (y < y1) { - uint bin_ix = y * N_TILE_X + x; - uint out_mask = bitmaps[my_slice][bin_ix]; - if ((out_mask & my_mask) != 0) { - uint idx = bitCount(out_mask & (my_mask - 1)); - if (my_slice > 0) { - idx += count[my_slice - 1][bin_ix]; - } - uint out_offset = sh_chunk_start[bin_ix] + idx * BinInstance_size; - BinInstance_write(BinInstanceRef(out_offset), BinInstance(element_ix, my_right_edge)); - } - x++; - if (x == x1) { - x = x0; - y++; - } + x++; + if (x == x1) { + x = x0; + y++; } } } diff --git a/piet-gpu/shader/binning.spv b/piet-gpu/shader/binning.spv index 9f22a33b7a8812ba864f15fbee6d4b490ce04a31..6ea087746cc4641eb1746f46597823f20f338685 100644 GIT binary patch literal 15652 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 bcB|+2ukXUuzC+1eeGhyNMW1Udca8rKw{Co` literal 16024 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 0 ? - BinInstance_read(BinInstanceRef(start_chunk + BinChunk_size)).element_ix : ~0; - } if (th_ix < N_SLICE) { sh_bd_sign[th_ix] = 0; } @@ -138,47 +122,11 @@ void main() { sh_is_segment[th_ix] = 0; } - while (wr_ix - rd_ix <= N_TILE) { - // Choose segment with least element. - uint my_min; - if (th_ix < N_WG) { - if (th_ix == 0) { - sh_selected_n = 0; - sh_min_buf = ~0; - } - } - barrier(); - // Tempting to do this with subgroups, but atomic should be good enough. - if (th_ix < N_WG) { - my_min = sh_first_el[th_ix]; - atomicMin(sh_min_buf, my_min); - } - barrier(); - if (th_ix < N_WG) { - if (my_min == sh_min_buf && my_min != ~0) { - sh_elements_ref = sh_chunk[th_ix] + BinChunk_size; - uint selected_n = sh_chunk_n[th_ix]; - sh_selected_n = selected_n; - uint next_chunk = sh_chunk_next[th_ix]; - if (next_chunk == 0) { - sh_first_el[th_ix] = ~0; - } else { - sh_chunk[th_ix] = next_chunk; - BinChunk chunk = BinChunk_read(BinChunkRef(next_chunk)); - sh_chunk_n[th_ix] = chunk.n; - sh_chunk_next[th_ix] = chunk.next.offset; - sh_first_el[th_ix] = BinInstance_read( - BinInstanceRef(next_chunk + BinChunk_size)).element_ix; - } - } - } - barrier(); - uint chunk_n = sh_selected_n; - if (chunk_n == 0) { - // All chunks consumed - break; - } - BinInstanceRef inst_ref = BinInstanceRef(sh_elements_ref); + while (wr_ix - rd_ix <= N_TILE && partition_ix * N_TILE < my_n_elements) { + uint in_ix = (partition_ix * N_TILE + bin_ix) * 2; + uint chunk_n = bins[in_ix]; + uint elements_ref = bins[in_ix + 1]; + BinInstanceRef inst_ref = BinInstanceRef(elements_ref); if (th_ix < chunk_n) { BinInstance inst = BinInstance_read(BinInstance_index(inst_ref, th_ix)); uint wr_el_ix = (wr_ix + th_ix) % N_RINGBUF; @@ -186,6 +134,7 @@ void main() { sh_right_edge[wr_el_ix] = inst.right_edge; } wr_ix += chunk_n; + partition_ix++; } barrier(); diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index b58a3274efdbbcf459803bf752365656e4568355..a3dd59ec043b0771053ede1211b7504eb3bf7e93 100644 GIT binary patch literal 46048 zcmb822b^71^|nu#nI!byTS7_bz4snUfY3XWHc1AOOh^Gjk={j`^eT!VC{hFg5knCX zMMdn20wM@Dq<6mOx%a%2JvaLM`(54c_kGveYp-3-KIfj9jBRtRJfN!jRRj6YG?1T# ztGQ6BYTl}UqaHSVzv1gln?81(jW%9S`vO&)Rl~MmHK6JMw^JsK>Yl9rt+}e|$CQO= zufDJ%0gY*C<3E_I#~|(1Jk_w_ox}Ftd)V-uJI73!(m8GV%!yr{6T2sOb&l6Ll=W@5AoDRtyUwXyqsFc_ZTi$1W2Ub?XAW!6SbzO3 zY1>xKPur;^)xr1{s^jc8eQM9qU1i42oVELzo%4Wd5xDXDv_UgZZGMusJb60@a@T=0 z>Cdq&R4oplBiC+o?I(s~e(Y`sJj%vv|mh~nW6q|)pGbZbL?NOShsDC$Sj-v zsQW4N91Nd5H_bfN{Iuq&UgqhT)SPek_Ho@~r%wQPume`1o*j3N5%i{3j$vimWDG;< z_R~jA?;6`2LSwGg{mdT2eAOCo`)c@hP9HVC+@uLmcjR2Hj(@YC1*>)Xw9QpDN4_ml znsdH(5od!wac1wO|0|ODaLLvAEKqIoAL9HkF*Yt@Y|$r%WBR`$G{>`fZ;bxcR^T1F zC+{|S+VoMA$8Yfu;n(pZHV^*ON}?V9V*UTqI< zw4?6eKRZzuO&iybnxC$TU6Z;dPw(vJYH<&$yDz79kDoBTvuo`5#&th`H4LqY(Pt70 zJ!#QvTeXXE8fRDkYB%tXlg9S$RcK=ms{5He2OZTOaL(c#C(M|9GyxlaYJN(;;qcy= zVCPiL&$O=b$G39-yDaH@u>NkJ?{4EKsX)h*YJ`1 zDaWklm`9HqbM)A$JyWzhX7xG8ykvD4dL46)5!VSd1~7ZPs{d`Y-Sudj*TNyh&Ug<6 zXS_!=``oW*Vt3Tsv;7bw5i~-cW7l z%Kz7f|DPMgu^ijmM_Y9qxH*<3tCQ>g_G>(U{0}`g9sk>=%q7@Z;}eTMPwMS+q3ZPX z>1O+1y40`OJEs@{z% zE`Zm5jXf#%!!2nlkGE(|AJ2@g$zywcJS%4Q-Uk+`E{4}}`VM1u66Lh0ZkwDmps3i`{Kt^}J%2j5?=ryezNV$Yb)F_U^9`niyDtu^oA z?bWsT;ZdIv9o2O;pV&RA+kyJ6dVxl$99hHo<3=mHw3>EeWj^Q>mEND-d5cT_gO&OH^5Uz9k(78ynppg>fUF=9cDC+ zKF?xxKcn*82sYN;4Ye8T0Wh(AJZr>$q{)5MYw#zUe8VPht9}G%&R<*gJUBVM*z~J& zYOh{t^4_?AYx3T>Z!~!wx4n9&$v2{E^!rhh`^eYGc`i0q`dtc~ewPE!o{RQsaFh4; zJEX~b`(3lid;49l$?JaGs!ibOXBasB?9ue=9p8v1@9pP+ChzU%&?fKgr?bg>$2Ydg zd&hSayxDJibsl)isHvm87rEY-wD7B2_?P)uhYWUZQ)zA@GblBwrU%AGnck%WG`;7P5}GNz?s0fI;s=l+k1`_ z*Pfc6$({9+Jd@^pt2+&ivx3^WIJx$9zE1-m#l6V3@-yM(9Gu;U53DYR*Jqe#4ri2a zbGj#wZ?qp!UEX3Z?>*OGEBW1U_Ce!2hv$m#gT_znJg8^t(Zik zPtEcFRR3;XEY)YK%+{{f{V@QoyU`c#e!#YA+$-2qwppUq@|D6h8r0O77}D`6q@I@zja4t2 zIc^Sb4%7Qg|7vS^c5HLx%|3Q$v|~=Zk=C(xueId;ml9h#O9PV z#&Rec<4X#SoJKXZ@~ba;TQv!tHzAF7lfk8f^KzWp*uL>kLMy*p8|MuD8s970t8>A# z$Iww-0B286o7g?3E91PVsZAO+?Pz*wudadnZYcI^`^0FkZh#*@srimo|63b;RPSEB z3qG^3KiJ^0w^fh9$2RtVTlJK*ZPj4-@!aX0o7Kgf{^Iq;Q=52mFqd>g-(Fn%90+diwYKUA z^}7FV@bLsS&a?(zzkyE{*LmC|o;u1V4ci^PwvOui;Ko(zb5w1A60Lb%^sk=RKB0Lb z*Dj41FBU#+Twb+Lj$eHGS97CJ=gr#0qPhELe*Jlxi?g{eh?e`)(zf$#LeRF1wt6fp zfu~IE8QoR$jo{O|j%n<4V{a#SQx$uEm)eJmyS^89RC}XMpTK3@cz|?X+N&ec@Tp_9 zRh{tkI{`eq-;U~Jc+ZUKowLvRj_M4w*6ZQ|w7%Cxdvz&VxvwvS&)z5P)fMpB-$B}| zD_iW>!<%RK%*On@0%txOV|cU0_pLs>qk3Q8KJ(LFeSnsSWasb`dCPNizy1xaer{&A z;N>A`y8&pfYrbQ-2bMyc+%s7@T{Q9>j9!j;tp`=XP$ia(~R*!WV%z;@0QD(k=F7TlgS2-(~Bc>+pT1kG-Q>1@7FI zKI`=HX|L96;TyN`P5bbUYIk_+*!S<_!@JEsyrViC-n!3v3ZG$|Q+G0ErVvzByRuvJ zeRO5GpJAMtYTTy4?TzoHD{0^pKDbF6{9YcM%Xsz9~5`i%ACuWrYuS;+YKhV@x_bN#xV|}_*nr9O=``b&Q=Yswgk4hsRvP(q}@E!a^tK3 zw;wgjVEBC0YQ`|`Le$N^+8aMRHm2GN%*`UyX`A+oqNy+3@M&xOj4|z(LbJU;cQhBr zXD-g6xi}yCs+)`Rp=LXC@#lbvH3)1hHFI%()7F@_U#U?y$8P%}wa*}IL&3IJ)7KX} zwpW+0MeRI<^H|gvSGZsL?K6Bcu(`_Z%bzzH&%U;%9zao(Z&P#agUNl{+NPg2=VB*n z?HS9?U^RV=zZ-Sp?+!M;nzlV^oAWXpEH_U4_XO*&&HneIc0U+Ln=$sIj{p8({ngxi z2T%{9^rP5^Kj*Ly^|Uz%Y#VL%aWJ)g7)zUd96=rbBfQc+tiG86t%HzqfP%F>cpA?HkO+E;uvcE)onkc;Eqe}ghF#Z z)lMn2k+pVap{4Kh(9-w$VEa~co-d%Dr!I3-yO%yst&cYQxsclYY^zP5OR4p7jf`^{ zwSCBqt=pL!J9$3`F5|ri_v}i%Pry9BHsbZe(RiMP#^1Q+o)PjLYVKJl@2>gan$N7c>zw$v zp$&vPZ@F(eZ{8o}#<`E$F{(Lla_^C9+Gl~y%d^BdxnCN``=VUGpHbUJ%{X%JjfwLQ zuyI`9Sm--BwNu^k9^F^zvP*#6Ya?U346-FR}} z4H-}WPO$qu{v&Ify8d$C8|iPnt{Ruu$V9Y({hD*F-UIiO{LX@}RqzYpg9?5Le8rmE z{#AJAl=IfJz5MU+)sCLLSNMByKHG@)@;u*VgwPrM*9VvD)AM=7H;<{#SvYTJUjj{@bvd&oh$h)JFdH*Ubg| z8K1T2-D|;TQTK~ysbv#dLmN3zvBRqBeH6o(qvyWwoIMA^A8z5kQ;ywpc2>b{@B8G~ z<-RX2x$lch?)&0!?@hiZ4!6DUiNn30`Hr~cz8enL-*>{{-bZ~G9IoAWz$Nz`aJc89 zlM=4K?|Mt_``VKGzP9APuMPKk&-b+9vxx6I+Hmc@qYbw|-_eF^_Z@AxcHhyK+;^_w z-gkWO8m_I-gmCyjq|DC+I{aD zyZQUxwdB5YExGSpOWuzgLCJmJT5{jDmVBNT?t9nLKDdRi+`@g|TKfB*wdB5IE%}%h z?mO1lhcaH@v4(rz`;Il-?=rq$4R?NgzZ$N;?^eV0zo~`$ZZ&rOeYYB}zwcJVJr8`h z8a@=e?^eV8&gi?HThuOB2_f8VEu>+k#2lKVb2Tz}uEhU@S9)NsEi4J!Ch z?1KyL_if*&^54euhvV^GYPk9NE;U@c?^45U@4M7+^YLA3$$gI+ZhpQ;4L82;QNxYz zJ5>4hJSF*k;eLwWA$*?Nnr9}auljbapNWtB0l42&)YI-ku~olH8}X}eO^?b!q|Mq z{61Jcah?RXj{gT}>WT9d*f{Em^Fy%u2*&F7@~6SZRySXtBh}K+Ghn}amGkuzxVrv6 zXUgOMGjQwpo<&nnUOxxBF6wFb9JsZgU!bXi`8&f^)eh+TV=MQM=XV>{W4^}Jl`6JwT+RVr2Y_;r-7r<)i`z5g2i2wYJR^GXU;wW8^<~Ll=?Hu#}sw*k=ySFv`c$y zZT{D}($)`bZ0mRFvyHl);zxT&ZMQZj-y_UL(a&~bWBUCj{SB;b*2bKNdR|Im&R5&5 zGoJau#>l)Z09JEe>{mWF#aQ+$PTNJnwhdnl+?<>Kjk#GI?mG%NR?+ z%NR?;eea>4He;w|%#P2ztdqwwU}I(vE(=zBj)};esX5P?GslTs`vTz6~0^-;g4z7Ey|t9#yi9jp!ZVpF(& z>KVgkVD(vy-}Uibfm-_60<5+@PYJ&B-V*HlU9V|v@!JZl%^1F4kjJ(S*ci!UTdC`oO!g}-ryamZKF*e-vpUFSS}?9tKv+{qAtE{g(H; zBjD<}9~=ojlww z7!P(m!zX~9o6JQwTundYX;X{;QDCp9_#X{d%lWBI&Hjf_yAQ18E7#oX^aaK` zPMqVx_7y%8Uhbb0;QFX%|C|U`&;B_Mtdfc3XFla=eBZJTWf^o2UEYYHB|p@l!|jt0-KK)fLoq+o+9qC7f3@joLk< zpRwRK)au5%hFZ;gf#<`GU^UMiuNz~! z?l+-ni{F=OKj)+LyBS^EeB57c_hqo-T(yp&Ju$ulwl9B1T*kNsP1_mdY>cmhjiYYP zH&Cl3&TZiTl=IinwZ-rD+OIX|JJ7Wy=dXj!c{PeLv?s9+OKurKZ&j_bMynSZPo4XF>1BM`5`#{m2>?xy0-ZJsP?nJ z((lLU+S1=MVB4zO-&53TIb(hTo54{+nF$LD#l@t>;C=s$wh-l+Tg6WDg?PkY&){mL^>)`|TB zxa|K$_#ldUd|m>Vw6&^~4_lPW*EG1JSjm?c88B=OBH~1Gmqz-Mnb(X*VC(w(7}iez4=r{44<1 zPd)7x1e>q6_$>rh&pi4wR5ka})x>jOUP)~~Lzsufu$=*SUj2Eh+~=2Nuq_VWf?8d> zKYvxryet7$Yd!CmMAK%Q8;C7Wo29^MGmF?6@6zbnY~%e~o;J%AZOVCE7G0Zd?xKx6 zZI&zAJX!R!Ji0d9JV+b4*m>%Ki=Fpi_hGK<%%64gUWs;&G3&iDymgPQf~KBzTNP|O z^{m?vu)6D(>v1UD>v14O`()P4XXDkt+RSw@wLJN(0e1e9&zkVoeAYr!Pd;mdZKs}m z)&Z-R`K${!AJ0zh$!9&VHuLdk?(*cb0eEte&xY{Ud^SQ;Pd*!iZKs}mHUX=b`D_YL zKH8JdW?*gR^@9BJHlJ@*$GWO`Roj~oqF=w1*~4?GYp>j(Vl#E1#2^(?WyJFc&Qn`i9qXtvc)d)f{MyZ+&Og3DNY!4peg?TKYO?Pig;8VB>0Y4GyGMOUxs|#>{u-POzH3M^MYX7mTF# zUSRDz=+V?;D1Nk$t?kyv_SvxuY>f0Z4(yoI*Lbj6-q%b3+eY1IC;2Ffec89zSkBc^ zV8;@EG}yB@dwwEZAN8D%lfbTp=cD%Q@5x|o$+HJMxZqR3?zilZW58Kc+v=}R#x)hJ zk8{{fEl-})!DZev;O3q6Iu@>vdh$LFY=4ehd-6UWtSx!Z1ebZA059`C5uUtltG_cFVrwXHgYh&%cIF0&riXZJ~)OPE{IumS+@Uy`7mwj+H zSRZxwf&5g8vF%How&#F-<_|v??w-qguJgcZS94~^=X|*R=Uw6jVExp69`I**p9e2O z+(vwG+Fl602tD7Az5v(P`+?7i+8wuZV6N6_dl9(o_hPu3&qJA4wQ^n^bJ|}5F27G+ z3Rkmlzju5Q?8iCNb{R#@JjBLv4_yu}?`c=SXSLkZu0&H$-&cXldA%C0=Dg61vF-sILvBv@g4d@c zk8gq1a{b*0wvD=TEx(guU-m6d?EAsx_4jRfxlbN|tCiQ^gK+c7_4g21KlNOH-vKYd zb!L3+>F;51dHsDCu5YeC?aqU9Vy@O{`v|z~_j_=)Tz}fs%6WFo^6WEf<9Qv;0_XlL ze-!S%%l>~1tfrsqs7=jyW2nu~I{7^gcJ4ASPk_~upEfnykEFK!XzCZLIe)hDeRR(w z`+c&;w);Wd&f5L+L+Ym~ezgCnwp%B!AA^mN{qYP~Ex#8ae~Mx(a}uZRPr$Yf|0&pa z`B~?m!98d7(-yyH!P>%q4mQsAoFksk&w&TgPCY)q0ITQc1iu6uNBtSb>fZkqSUr3H z*I>28_zl>(%NqO^tfrrHsZGtfa^4)Tb=v<9Y@TWVd$3y8P@7u07S3DR{}Jr`g#QU{ zpBeWH;O8m&nU7o_@r%^1xv^gYo13-k{4(`lD1NlRQroT5*Q;P-WS##CR?9le|4cEK zvBj->FR@<3R*vI!xUuu|n!kaa6ZNdc-@)p4*7u5kfQ_T>`Ts9!Kb}e2{z*|Y7jgQ2 z16=m~Z+O}Fn{fM9Pv38W)${uiZ-b4ap1FSq?A&WhU+;p|!`}m!`{#YQnts_oYOXbC;FGD^N z1HrbaMxd3 z;;ao;&-t?s*x2gs?e(brIPcolrKmY(apJ5Gw*Rby+_{afwcL5Pt##UN0Cs*d_6@;m zxjwb2m22s|r~SrY=fb}I_ZafnHUYa9UuW)p7T*-C=H768n}hv0K5d&()XY(wI9q^? zllk2etX9tNR&eK6TiR_6RuA6>Y>tVuEm%#zYXTl zY`b%9w{~p(d2ZMNte@?~$!Qqa*x|c^9c!Lfb_4soqMx?-?GDx!z6aPi*>A(agJ`E7 zpFP3qd0yEIY#epRxC^yf{6~P*@+`9tSS{DhzF;-m?oBOE+x@_5;roM~`|O1S!1}1W z7Y?HK<9um5kfP>%iJjNXpL*gR0xtVG6ke|JVQ@A5vc_s@e>m8A%l*KkFD+2=H)rn1vbXrRrA^z4^|sT zagOApD8@3jIAiJtn@eIH1vXaBz@x#tbFHXnEhmE2GY^x%#!+`X6R6eF$7Hbc9Nq(V zuY^wl+a}Ln$AFzv_0t)XW1I?(o;;?Zsb`N)2OC>G`)~$WJ#CH!tK}R$4s3qv?yZ^B zew;UL$5YgtH*s=40c_4$OS$V4Uu(H*XIpFAI+hc`&av|+KMAaseWXpT+z+l@`u-ev z{(_$ZUXYS!lT*R{De76H)4;aV7VUJf`gQg5!O38?^m8V-{9UuN;M-Bs&)HzL3pqOy z^BnCIZHaR(*gY1X^T7J4=Xcc37gG)-mUDUrSS>L=4_3>Wbs_jreAVOg1+cp9FQAsk zb`e-DxnB%cJBt|k9kWZ|>W%)<<+dNf8hsI*pDkVnmix0s{}#mM;B~0gZF?!Tn)nK8 zfBu#@SHTnKO0Yb?t9CWmcQNXDhP?)??)}UCc^%k~`%~Ms6gBsl*na$XFE@Z^v91~a zjqs%@neUsxYQFo(oO}tcUcNWG8LplLmt$MP_`w)}3^ zcft0(D#aMuGvAMZTj%?GXzKZWm04h8sJq`EqxR!|*Y+qy&HXOUx;+l|T1#I~fbFYX z|L?=qA7RaW#(WZNJ9W?6#I?@*-XDPN*K13E`!uf~QYSBco}#FkmpFMn4KDNg5xh08 zAET*z9k_l!0joVj$-Mj&Y%Fc&n!c=)>(9XEV*dJ@hq?ZoI=SleEafj0a}{Tep9BA= zz3>ZkZSnhM?bo^&eub{>p?ZFQ4YsYidHj|-d1(6$hBr#eVHS{k{Nh?e|4A_4NA^xa{}OaDCL%@5|uK zqy5VDEBm#7xocw{u1BuV{EkL!*7{yo`&y^3SHbDa96ekAil!c)*J>Zee%j3K6>4?c_`c&G;Q0&wPw;}2+)w@m?oUzAz5flc?X-E{J&!p5 z2CLi0o78gqxB%PRVEfhf7PZ`ekHO_zy#v0O+BVvpqs6G5PwV9QF4(-1!+T&g|J{vo zwW-;DVQSkiO0DgE>U-%Uu|9y1j8CZLuBrXb1@`wBZ4>`CGiV}+%VQe|F5jii z4Oeq6yq4z!`|(`XHZNs6_l(g`TiR_6)@Drihdj1zz{bqAxGmf>M?F5rqw=1~3 zmUe@y1TkeBPY@apC{|{z7wpUx-k!-M0Rol3Myc6|ClS&6!|7AMrCi z^)o0+r}00eT%Ats^{<}xI}G4==i#}=c7&_tzM@Ud^TPPHH(uJG1@`YoEkl6eR$UR_c^`@zm(#$tp0L+wEMSpF0bvG zPkEX1mGJU=$W?IXJbUSCu$tdRvPRdyZKusXub@^-j@N?K@*U+mu$noYPfpi^&DnpK zVtcthws-tDf}LA!H&Dy9U54#TVB2cDiCUiTG&h5*V)kaJgr`0$0mA zeHHA-wbyowHj3*cPMq7o#>rZI4Sp*nYa!Ps zZ{L9HpLvt(llb2R`}b6wn>)dBZO+@>VB2cDi&~y}y9eyN*+!f5<{UU)$DR1!0%u+P z8!qzF*EaeNA=bCSIaBTj%k%F^9sqBFkGlEXN3E8A9s(Ob^Yk6~gOtpxTp#_t&pZq! zOvB&5FZW%z{+U;~K3S(n!1h^w|N9iaD^|4R?X3k?^|1OUCKMIy>qpF_Z zfBbtl+8zhXGuPh-UyqM%v?bn?U^UwqSDtu30PCyGxbn37Avk$F1(s{`?}Gmb>=Fdv6we^3xz1T1>vG*)|Mc;9u=ld?f4~P&(&sjKJes z!~R@tmzO!u1uvfw+TgB5o)OyNYUMLR2i$hr?9;zlsFoc2gVpkkFaWG(PCg?H1e!fWV$_f0pNwm9u;a?wECJU)Ya`c3f1eSS1eebU zOTqQeyvg-R{AIv-M)2=I%C$Lf%Ykj{y!kgF<(aqT!Ooj)v^j6if#Y@DiN7K^>*76J zUi#Wb-yy_W37qrVzcDHIydI2gW$-4{>gF?uS}pyo3ND`!hQR&1l$lq#KF*^*uN(?C zhVx>Kxv5u!tLNWYtq#7K>qb58)&Ltf^Doy=yU*Wif{kmx#%1ZMwczUUSsQGOtchG7 z{e6~L7o79kzojWpto6aU_xN`;0^1gkl3#+4`DMqqul8CRZmn}Cyt zf3s7rZ8&{w26ha#^Y3@clgH-Zlku^Qwv2lVaPAN3e@is=obg+M)yn(I*6_5mKe>M5 z@>wEht##sWgP&(#__px!p0XWWO+VvlQxn_H>$|*cK#i`TRF2pI% z_F-@}V|upl2KM8bp>0=+nsLQB+xGz9gr7O>4wk2n;o$OI+7qspKK2Itu@7x~Q4Xfq zhd6OYfQ=*fJlzLwUvkfteZih9BiPZG!S{pPMn7$7cK|r&$^Kxuc^^n^o9vN;z@8ua zXmbwD)x69v@eTo-hcUdq<)yD};(KV}YYuX4EJ<}3*tIZ@YwnIcQcCr9|`lKEkN-z zA7xYW8%RAr#r_wh&i_Bctzeq>@^7^82U_?eE&PdsKMnp-&2!Fu4Q{{XJ>Yh@`eWqk ze*8K`E%yL(Qp>^}vo<#!~02DXj5>;5eD z&nfN&*TX*L`X%Nsz>Y65e+gEL{a0Y~jQ!VOwesrWT`!~3Z`zBniyq?~IC$9FieH*OJxUQ)@ws*m0-1p&X?@^NP2jIljp0*!?wHepF zA&>22a2fYgxY{R_#QhAMxZ2Z}TSMdjo5#5BF?nolVAnF&T02}V_71RX5_^BRTCT+b zVB4tYS{w+Ti<0YKu3uu#4R$>ea~`-_?DK-1*VyNStEJER!M0IPp9_HP^Eui%PPu-G zxggj%P0WShYOya2c5Y%{1g@4o7X{l!J$)_)w$EH^a{Y|iI9F=BXU^i_oH@(T&+-&M z%ThdZmZDyc;+eAobXj*8o2ydiS{z2Z^4i>^g^y_A2ej}* zTX<&+A6xKl@KH6-`s@lfuku>m4NX1Q>h55*T&w1+mNgg-R?D@zCs?h#R`-HuFKJKP zy}{btOO9P0+dkkj?!Iuf@><;wp19i6c7L!o<2r}(*bW4jaSwv4&0-#MtsV?dTzc}A8woDsj)JR|*Xn3^;%ZOZF<@=R zb#KUH>jIZ?$HCRg=YjF?#MPd*6TsSx>mHNGb~MHW1kLI%NZ~OY@f!pt@fM&$AZm0XMkM)#6Aw} z_!Ikhu$t{>QlCJveXbq3KDHmsemfDY-7`d-Go(MZH7I^oqj-i4rCy!l8L}pI=J#{7 zQ!lUKQ_$3N4W9~DGmq@?)8JWK?HSYQU~T3kPEPaEb{&eJwJ6DHZLm45OYONfkor8@ zd9EeLv*7xbIi8KCo^#_Iu$ueYcRjX07q0F;Gp5}47v^$4cmZm4bCoAopPe?K_*svV zT-OJi>xR_kx-j)6v@3JH5UyXD>le_}lj}ubwdA@G{ujg5&DEH4b2XPs!G1?nH&=Ob z9gMyS#m`2R%YRPp){I7zmo2xP9`T4*# z;8p6r%vGLTz3**7@v|8vxo!?N*Da~d)o0N!(XPz(`bHmpbG-pgJ-OZpR!go+66Yqk zy15!tZm#BXGuY=~b#s*`*R9ZZp!nIAVy;_LZ&%}OsJEw>_l^zR{JFy|#MlmA&goat z)N_s83f>-{{G9G9a`ujDw?C&>lwdUG3=k>R6`*U7xH#ha~(9~TE?~A_&tEKNh zfYY}z?ORR1^!+@z?E8;!wOPc=p7;~ozMZeMdjU;7eZ2@yU&gR6HT}wW=&ti&6h8-3 zT<86%52^7%)Q3`B=fkP9&My(CT<1T-ZBx$u%V_FZ=f8l}vd+d(%NSk(t7V;E1*?_o z{8za7w661O=-M*=*TEUTaU8#ze(CRT;IhBJ!_~6R{{a7!Vt-lZ1Hk{P^{n$7aJBUP zZ*clHrhTjFm%iTwmwmqlSDQurtn=G&`!3h{9W?dy^)5Jl8N--^DE$eIywT%5Euv*snW3XCtotyWz zPvGWL_VX#4ddB`4IAb@4V^?eVRkMFiwT!(3tX7V_KiquEeg>eaXY2#P8M`qYyPAHj>pqQG zevY9y_9@g;DUNqKb@tobv{f(rod-=lTpDhSa{iVPlljwU%bI758^DcG&fkV;>Y2Zdz-s0E zZ4A%+X?Hwoez!^NP2jDuH$_v={@V^_o1Y6Q&fodeU#RitsV|~9f0s}@e?Hr8LmcNXW7-yOn{vLkLsQRuZ4Xv+ zzRc4YYL1~l+74i~%)yRewdQ@O`S(J0f}4;1sM~gK>YdTG<@sk9uv+d1!@yY=3?6aTKeA)oc@h#|7!ZB|NYe|>Hh$*+LJ~92g1{TIsSvt zwWa@q!D{LM5ODf8uKla&m;Mh`r=(UJI8}Us2;PQeR2&TDY1z*TP8Rl-I&2xNXX7VKkb0u7xpRwOk9v zP|IA51*>H)y1;79YoYl(Iu33=Wk2K5)HC)8;EdfEj$KW^^wkY6`#K7)mVNK_el*;^ z+;?Sv6VbJ$ze(WqXB_)e(=Yu^2ABQyz}03E+c@?&1#W-NPubrw=-SfXRB-w;j{T|W z*SfaX6U)yvl-wt-t+9PxM{#X$pw8N!K%DYEaazHBW<9gujeAYab1qJU+ebNu>1gVi zj~QV5&c6dZ4z7>-dyHo$wI6@KP22GlHFFi)*L~PO2R@nN8s+boo(flYE!{h(fSn`9 zr7by}4p#G7aRPp4fYtPM%yNBuu$={VOxhg3T$}CA0qd{rY-)M#L+8rzv5mI$eI7V{ z8~=Q`nlbE09^VUU-&_};hpRcR)0qeFBVPckUq~^AbF1d>`DNa$%l)wjJ~vVP+(>b5 zzeJsVa1H+MgXD1u{9;OePw`T)nse-$eG%-(HPdz(Ma{V4tl1Ud)-}5dO+9ONCD?IX zPOVLB-&a!{Q{q}DziYwz>uc`v*siZ_nZq04YS&THk6Qe11lwQc_a?BK`z*Pr#s5oS z`*bfkuQ!9$8t<5?^FGh_7PnCR+)VMB^g8@tNfcC+0W6>duQ}xf5(Z>W)?JSpOewC4u7r literal 49240 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 vp9EG Renderer { let state_buf = device.create_buffer(1 * 1024 * 1024, dev)?; let anno_buf = device.create_buffer(64 * 1024 * 1024, dev)?; - let bin_buf = device.create_buffer(64 * 1024 * 1024, host)?; + let bin_buf = device.create_buffer(64 * 1024 * 1024, dev)?; let ptcl_buf = device.create_buffer(48 * 1024 * 1024, dev)?; let image_dev = device.create_image2d(WIDTH as u32, HEIGHT as u32, dev)?; @@ -192,12 +192,13 @@ impl Renderer { &[], )?; - let coarse_alloc_buf_host = device.create_buffer(4, host)?; - let coarse_alloc_buf_dev = device.create_buffer(4, dev)?; + let coarse_alloc_buf_host = device.create_buffer(8, host)?; + let coarse_alloc_buf_dev = device.create_buffer(8, dev)?; let coarse_alloc_start = WIDTH_IN_TILES * HEIGHT_IN_TILES * PTCL_INITIAL_ALLOC; device .write_buffer(&coarse_alloc_buf_host, &[ + n_elements as u32, coarse_alloc_start as u32, ]) ?; @@ -264,26 +265,22 @@ impl Renderer { cmd_buf.dispatch( &self.bin_pipeline, &self.bin_ds, - (N_WG, 1, 1), + (((self.n_elements + 255) / 256) as u32, 1, 1), ); cmd_buf.write_timestamp(&query_pool, 2); cmd_buf.memory_barrier(); - /* cmd_buf.dispatch( &self.coarse_pipeline, &self.coarse_ds, (WIDTH as u32 / 256, HEIGHT as u32 / 256, 1), ); - */ cmd_buf.write_timestamp(&query_pool, 3); cmd_buf.memory_barrier(); - /* cmd_buf.dispatch( &self.k4_pipeline, &self.k4_ds, ((WIDTH / TILE_W) as u32, (HEIGHT / TILE_H) as u32, 1), ); - */ cmd_buf.write_timestamp(&query_pool, 4); cmd_buf.memory_barrier(); cmd_buf.image_barrier(&self.image_dev, ImageLayout::General, ImageLayout::BlitSrc); From 192ddc5eabea7742c8e2b5da8e1ef5fe408c8878 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Sat, 30 May 2020 15:37:34 -0700 Subject: [PATCH 30/32] Parallel merge The fancy stuff :) --- piet-gpu/shader/coarse.comp | 82 ++++++++++++++++++++++++++++-------- piet-gpu/shader/coarse.spv | Bin 46048 -> 49012 bytes 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 082d902..78c758b 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -30,9 +30,16 @@ layout(set = 0, binding = 3) buffer PtclBuf { #define N_RINGBUF 512 +#define LG_N_PART_READ 8 +#define N_PART_READ (1 << LG_N_PART_READ) + shared uint sh_elements[N_RINGBUF]; shared float sh_right_edge[N_RINGBUF]; +// Number of elements in the partition; prefix sum. +shared uint sh_part_count[N_PART_READ]; +shared uint sh_part_elements[N_PART_READ]; + shared uint sh_bitmaps[N_SLICE][N_TILE]; shared uint sh_backdrop[N_SLICE][N_TILE]; shared uint sh_bd_sign[N_SLICE]; @@ -89,7 +96,7 @@ void main() { // invocations within the workgroup. We'll use variables to abstract. uint bin_ix = N_TILE_X * gl_WorkGroupID.y + gl_WorkGroupID.x; uint partition_ix = 0; - uint my_n_elements = n_elements; + uint n_partitions = (n_elements + N_TILE - 1) / N_TILE; // Top left coordinates of this bin. vec2 xy0 = vec2(N_TILE_X * TILE_WIDTH_PX * gl_WorkGroupID.x, N_TILE_Y * TILE_HEIGHT_PX * gl_WorkGroupID.y); uint th_ix = gl_LocalInvocationID.x; @@ -107,8 +114,14 @@ void main() { SegmentRef last_chunk_segs = SegmentRef(0); alloc_chunk_remaining = 0; - uint wr_ix = 0; + // I'm sure we can figure out how to do this with at least one fewer register... + // Items up to rd_ix have been read from sh_elements uint rd_ix = 0; + // Items up to wr_ix have been written into sh_elements + uint wr_ix = 0; + // Items between part_start_ix and ready_ix are ready to be transferred from sh_part_elements + uint part_start_ix = 0; + uint ready_ix = 0; if (th_ix < N_SLICE) { sh_bd_sign[th_ix] = 0; } @@ -122,21 +135,58 @@ void main() { sh_is_segment[th_ix] = 0; } - while (wr_ix - rd_ix <= N_TILE && partition_ix * N_TILE < my_n_elements) { - uint in_ix = (partition_ix * N_TILE + bin_ix) * 2; - uint chunk_n = bins[in_ix]; - uint elements_ref = bins[in_ix + 1]; - BinInstanceRef inst_ref = BinInstanceRef(elements_ref); - if (th_ix < chunk_n) { - BinInstance inst = BinInstance_read(BinInstance_index(inst_ref, th_ix)); - uint wr_el_ix = (wr_ix + th_ix) % N_RINGBUF; + // parallel read of input partitions + do { + if (ready_ix == wr_ix && partition_ix < n_partitions) { + part_start_ix = ready_ix; + uint count = 0; + if (th_ix < N_PART_READ && partition_ix + th_ix < n_partitions) { + uint in_ix = ((partition_ix + th_ix) * N_TILE + bin_ix) * 2; + count = bins[in_ix]; + sh_part_elements[th_ix] = bins[in_ix + 1]; + } + // prefix sum of counts + for (uint i = 0; i < LG_N_PART_READ; i++) { + if (th_ix < N_PART_READ) { + sh_part_count[th_ix] = count; + } + barrier(); + if (th_ix < N_PART_READ) { + if (th_ix >= (1 << i)) { + count += sh_part_count[th_ix - (1 << i)]; + } + } + barrier(); + } + if (th_ix < N_PART_READ) { + sh_part_count[th_ix] = part_start_ix + count; + } + barrier(); + ready_ix = sh_part_count[N_PART_READ - 1]; + partition_ix += N_PART_READ; + } + // use binary search to find element to read + uint ix = rd_ix + th_ix; + if (ix >= wr_ix && ix < ready_ix) { + uint part_ix = 0; + for (uint i = 0; i < LG_N_PART_READ; i++) { + uint probe = part_ix + ((N_PART_READ / 2) >> i); + if (ix >= sh_part_count[probe - 1]) { + part_ix = probe; + } + } + ix -= part_ix > 0 ? sh_part_count[part_ix - 1] : part_start_ix; + BinInstanceRef inst_ref = BinInstanceRef(sh_part_elements[part_ix]); + BinInstance inst = BinInstance_read(BinInstance_index(inst_ref, ix)); + uint wr_el_ix = (rd_ix + th_ix) % N_RINGBUF; sh_elements[wr_el_ix] = inst.element_ix; sh_right_edge[wr_el_ix] = inst.right_edge; } - wr_ix += chunk_n; - partition_ix++; - } - barrier(); + barrier(); + + wr_ix = min(rd_ix + N_TILE, ready_ix); + } while (wr_ix - rd_ix < N_TILE && (wr_ix < ready_ix || partition_ix < n_partitions)); + // We've done the merge and filled the buffer. @@ -475,9 +525,7 @@ void main() { barrier(); rd_ix += N_TILE; - // The second disjunct is there as a strange workaround on Nvidia. If it is - // removed, then the kernel fails with ERROR_DEVICE_LOST. - if (rd_ix >= wr_ix || bin_ix == ~0) break; + if (rd_ix >= ready_ix && partition_ix >= n_partitions) break; } Cmd_End_write(cmd_ref); } diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index a3dd59ec043b0771053ede1211b7504eb3bf7e93..252ff10d8b2b61be2b6d1649e52ed678ea539a04 100644 GIT binary patch literal 49012 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 laAV}(t-b}WmY6>V+eSSxe*#u_UL4D literal 46048 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 From 2c185c3718d8b830cfbd54d8e21673d2c99b796f Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Sat, 30 May 2020 21:12:55 -0700 Subject: [PATCH 31/32] Simplify ringbuf We don't really need a ring buffer, as we only read what we're actually going to process. --- piet-gpu/shader/coarse.comp | 21 ++++++++------------- piet-gpu/shader/coarse.spv | Bin 49012 -> 48612 bytes 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 78c758b..3656f77 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -28,13 +28,11 @@ layout(set = 0, binding = 3) buffer PtclBuf { #include "bins.h" #include "ptcl.h" -#define N_RINGBUF 512 - #define LG_N_PART_READ 8 #define N_PART_READ (1 << LG_N_PART_READ) -shared uint sh_elements[N_RINGBUF]; -shared float sh_right_edge[N_RINGBUF]; +shared uint sh_elements[N_TILE]; +shared float sh_right_edge[N_TILE]; // Number of elements in the partition; prefix sum. shared uint sh_part_count[N_PART_READ]; @@ -178,16 +176,14 @@ void main() { ix -= part_ix > 0 ? sh_part_count[part_ix - 1] : part_start_ix; BinInstanceRef inst_ref = BinInstanceRef(sh_part_elements[part_ix]); BinInstance inst = BinInstance_read(BinInstance_index(inst_ref, ix)); - uint wr_el_ix = (rd_ix + th_ix) % N_RINGBUF; - sh_elements[wr_el_ix] = inst.element_ix; - sh_right_edge[wr_el_ix] = inst.right_edge; + sh_elements[th_ix] = inst.element_ix; + sh_right_edge[th_ix] = inst.right_edge; } barrier(); wr_ix = min(rd_ix + N_TILE, ready_ix); } while (wr_ix - rd_ix < N_TILE && (wr_ix < ready_ix || partition_ix < n_partitions)); - // We've done the merge and filled the buffer. // Read one element, compute coverage. @@ -195,9 +191,8 @@ void main() { AnnotatedRef ref; float right_edge = 0.0; if (th_ix + rd_ix < wr_ix) { - uint rd_el_ix = (rd_ix + th_ix) % N_RINGBUF; - uint element_ix = sh_elements[rd_el_ix]; - right_edge = sh_right_edge[rd_el_ix]; + uint element_ix = sh_elements[th_ix]; + right_edge = sh_right_edge[th_ix]; ref = AnnotatedRef(element_ix * Annotated_size); tag = Annotated_tag(ref); } @@ -355,7 +350,7 @@ void main() { } } uint out_offset = seg_alloc + Segment_size * ix + SegChunk_size; - uint rd_el_ix = (rd_ix + slice_ix * 32 + bit_ix) % N_RINGBUF; + uint rd_el_ix = slice_ix * 32 + bit_ix; uint element_ix = sh_elements[rd_el_ix]; ref = AnnotatedRef(element_ix * Annotated_size); AnnoFillLineSeg line = Annotated_FillLine_read(ref); @@ -407,7 +402,7 @@ void main() { } } uint element_ref_ix = slice_ix * 32 + findLSB(nonseg_bitmap); - uint element_ix = sh_elements[(rd_ix + element_ref_ix) % N_RINGBUF]; + uint element_ix = sh_elements[element_ref_ix]; // Bits up to and including the lsb uint bd_mask = (nonseg_bitmap - 1) ^ nonseg_bitmap; diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index 252ff10d8b2b61be2b6d1649e52ed678ea539a04..5a43f4abf13e1d65d900a91bcbeda31a384f3e65 100644 GIT binary patch literal 48612 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%0Ar-qVlu2fV%bEZGul9*$Lib1=`XsgtKZTT#@^ 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 P%WYu$QFp9z$NK*Ok!Hg$ literal 49012 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 laAV}(t-b}WmY6>V+eSSxe*#u_UL4D From f3cb904f866a9469dae606dde112e1872a553386 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Sun, 31 May 2020 09:55:20 -0700 Subject: [PATCH 32/32] Add command line args for loading svg --- Cargo.lock | 72 ++++++++++++++++++++++++++++++++++++++++ piet-gpu/Cargo.toml | 1 + piet-gpu/bin/cli.rs | 27 +++++++++++++-- piet-gpu/src/lib.rs | 40 +++++++++++----------- piet-gpu/src/pico_svg.rs | 29 +++++++++++----- 5 files changed, 139 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4a0a133..2755d5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,15 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi 0.3.8", +] + [[package]] name = "approx" version = "0.3.2" @@ -59,6 +68,17 @@ dependencies = [ "raw-window-handle", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi 0.3.8", +] + [[package]] name = "autocfg" version = "1.0.0" @@ -106,6 +126,21 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "clap" +version = "2.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + [[package]] name = "cloudabi" version = "0.0.3" @@ -259,6 +294,15 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36b5f248235f45773d4944f555f83ea61fe07b18b561ccf99d7483d7381e54d" +[[package]] +name = "hermit-abi" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" +dependencies = [ + "libc", +] + [[package]] name = "inflate" version = "0.4.5" @@ -525,6 +569,7 @@ dependencies = [ name = "piet-gpu" version = "0.1.0" dependencies = [ + "clap", "piet", "piet-gpu-hal", "piet-gpu-types", @@ -758,6 +803,12 @@ dependencies = [ "byteorder", ] +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + [[package]] name = "syn" version = "1.0.17" @@ -769,6 +820,21 @@ dependencies = [ "unicode-xid 0.2.0", ] +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "unicode-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" + [[package]] name = "unicode-xid" version = "0.1.0" @@ -781,6 +847,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "void" version = "1.0.2" diff --git a/piet-gpu/Cargo.toml b/piet-gpu/Cargo.toml index 6bdf178..a338d76 100644 --- a/piet-gpu/Cargo.toml +++ b/piet-gpu/Cargo.toml @@ -26,3 +26,4 @@ png = "0.16.2" rand = "0.7.3" roxmltree = "0.11" winit = "0.22" +clap = "2.33" diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index 5d7c09e..31024aa 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -2,10 +2,12 @@ use std::fs::File; use std::io::BufWriter; use std::path::Path; +use clap::{Arg, App}; + use piet_gpu_hal::vulkan::VkInstance; use piet_gpu_hal::{CmdBuf, Device, Error, MemFlags}; -use piet_gpu::{render_scene, PietGpuRenderContext, Renderer, HEIGHT, WIDTH}; +use piet_gpu::{render_scene, render_svg, PietGpuRenderContext, Renderer, HEIGHT, WIDTH}; #[allow(unused)] fn dump_scene(buf: &[u8]) { @@ -152,6 +154,17 @@ fn trace_ptcl(buf: &[u32]) { 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, _) = VkInstance::new(None)?; unsafe { let device = instance.device(None)?; @@ -161,7 +174,17 @@ fn main() -> Result<(), Error> { let query_pool = device.create_query_pool(5)?; let mut ctx = PietGpuRenderContext::new(); - render_scene(&mut ctx); + 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; + } + render_svg(&mut ctx, input, scale); + } else { + render_scene(&mut ctx); + } let scene = ctx.get_scene_buf(); //dump_scene(&scene); diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 4652c5a..3ec7e1d 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -34,6 +34,17 @@ const N_CIRCLES: usize = 0; const N_WG: u32 = 16; +pub fn render_svg(rc: &mut impl RenderContext, filename: &str, scale: f64) { + let xml_str = std::fs::read_to_string(filename).unwrap(); + let start = std::time::Instant::now(); + let svg = PicoSvg::load(&xml_str, scale).unwrap(); + println!("parsing time: {:?}", start.elapsed()); + + let start = std::time::Instant::now(); + svg.render(rc); + println!("flattening and encoding time: {:?}", start.elapsed()); +} + pub fn render_scene(rc: &mut impl RenderContext) { let mut rng = rand::thread_rng(); for _ in 0..N_CIRCLES { @@ -138,7 +149,7 @@ pub struct Renderer { k4_pipeline: D::Pipeline, k4_ds: D::DescriptorSet, - + n_elements: usize, } @@ -177,13 +188,10 @@ impl Renderer { // TODO: constants let bin_alloc_start = ((n_elements + 255) & !255) * 8; - device - .write_buffer(&bin_alloc_buf_host, &[ - n_elements as u32, - 0, - bin_alloc_start as u32, - ]) - ?; + device.write_buffer( + &bin_alloc_buf_host, + &[n_elements as u32, 0, bin_alloc_start as u32], + )?; let bin_code = include_bytes!("../shader/binning.spv"); let bin_pipeline = device.create_simple_compute_pipeline(bin_code, 4, 0)?; let bin_ds = device.create_descriptor_set( @@ -196,12 +204,10 @@ impl Renderer { let coarse_alloc_buf_dev = device.create_buffer(8, dev)?; let coarse_alloc_start = WIDTH_IN_TILES * HEIGHT_IN_TILES * PTCL_INITIAL_ALLOC; - device - .write_buffer(&coarse_alloc_buf_host, &[ - n_elements as u32, - coarse_alloc_start as u32, - ]) - ?; + device.write_buffer( + &coarse_alloc_buf_host, + &[n_elements as u32, coarse_alloc_start as u32], + )?; let coarse_code = include_bytes!("../shader/coarse.spv"); let coarse_pipeline = device.create_simple_compute_pipeline(coarse_code, 4, 0)?; let coarse_ds = device.create_descriptor_set( @@ -212,11 +218,7 @@ impl Renderer { let k4_code = include_bytes!("../shader/kernel4.spv"); let k4_pipeline = device.create_simple_compute_pipeline(k4_code, 1, 1)?; - let k4_ds = device.create_descriptor_set( - &k4_pipeline, - &[&ptcl_buf], - &[&image_dev], - )?; + let k4_ds = device.create_descriptor_set(&k4_pipeline, &[&ptcl_buf], &[&image_dev])?; Ok(Renderer { scene_buf, diff --git a/piet-gpu/src/pico_svg.rs b/piet-gpu/src/pico_svg.rs index 5317f5e..140c42d 100644 --- a/piet-gpu/src/pico_svg.rs +++ b/piet-gpu/src/pico_svg.rs @@ -29,6 +29,7 @@ pub struct FillItem { } struct Parser<'a> { + scale: f64, items: &'a mut Vec, } @@ -37,7 +38,7 @@ impl PicoSvg { let doc = Document::parse(xml_string)?; let root = doc.root_element(); let mut items = Vec::new(); - let mut parser = Parser::new(&mut items); + let mut parser = Parser::new(&mut items, scale); for node in root.children() { parser.rec_parse(node)?; } @@ -60,13 +61,16 @@ impl PicoSvg { } impl<'a> Parser<'a> { - fn new(items: &'a mut Vec) -> Parser<'a> { - Parser { - items - } + fn new(items: &'a mut Vec, scale: f64) -> Parser<'a> { + Parser { scale, items } } fn rec_parse(&mut self, node: Node) -> Result<(), Box> { + let transform = if self.scale >= 0.0 { + Affine::scale(self.scale) + } else { + Affine::new([-self.scale, 0.0, 0.0, self.scale, 0.0, 1536.0]) + }; if node.is_element() { match node.tag_name().name() { "g" => { @@ -77,21 +81,28 @@ impl<'a> Parser<'a> { "path" => { let d = node.attribute("d").ok_or("missing 'd' attribute")?; let bp = BezPath::from_svg(d)?; - let path = Affine::new([1.5, 0.0, 0.0, -1.5, 0.0, 1500.0]) * bp; + let path = transform * bp; // TODO: default fill color is black, but this is overridden in tiger to this logic. if let Some(fill_color) = node.attribute("fill") { if fill_color != "none" { let color = parse_color(fill_color); let color = modify_opacity(color, "fill-opacity", node); - self.items.push(Item::Fill(FillItem { color, path: path.clone() })); + self.items.push(Item::Fill(FillItem { + color, + path: path.clone(), + })); } } if let Some(stroke_color) = node.attribute("stroke") { if stroke_color != "none" { - let width = 1.5 * f64::from_str(node.attribute("stroke-width").ok_or("missing width")?)?; + let width = self.scale.abs() + * f64::from_str( + node.attribute("stroke-width").ok_or("missing width")?, + )?; let color = parse_color(stroke_color); let color = modify_opacity(color, "stroke-opacity", node); - self.items.push(Item::Stroke(StrokeItem { width, color, path })); + self.items + .push(Item::Stroke(StrokeItem { width, color, path })); } } }

3dIb`ZoUe;A+OOA9;NDs(rIh_lB!E zuA`U-$Gk6CeIJT3oLe>jhA8u9-MT+6!q4kmiu=PoabAs2qCUUC7t~mv3n{L}PpGqR z4kv#0jcrdTxcBu*HP5*AgWFgBrm7RJb`6?)U?|wjJ)muW%4CZDinHH_fz7krZ^O~l zGiM{fYR*sk?*coH?7@+6{nYJ$G_{xgYa2yTvwv~+U^lpR4~{`o&mKIW_IK{af|IXn zCD%XW8CTeo>v%NvjAsH^&A8bE2ZGfnQjF`^)Z7Ef+uE2XVw(gu4}D!*xxUW3V|1>Q zQ%~(%&hbHL>dEO~u$u2e$@>tvYn<=vQ{d*Oo_15gw$+w)-v^g#^8>hk>aNXU)LyQQ zwnHgu<}J?J{1Du_Hb7TRaWU$XxeYB;|Q^59_wLBGE?mxLc V#%$h?&3%yhv@Z9B`y=Pb{{z)BoU8x< literal 33272 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 diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 70b02f5..65bbe5c 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -46,8 +46,8 @@ pub fn render_scene(rc: &mut impl RenderContext) { let circle = Circle::new(center, radius); rc.fill(circle, &color); } - let mut path = BezPath::new(); /* + let mut path = BezPath::new(); path.move_to((100.0, 1150.0)); path.line_to((200.0, 1200.0)); path.line_to((150.0, 1250.0)); From a616b4d010a346d7cbfc026a0bb255d0f0626292 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Wed, 20 May 2020 16:36:09 -0700 Subject: [PATCH 18/32] Rework right_edge computation in elements Trying to fit it into the fancy monad doesn't really work, so use a more straightforward approach to compute it from the aggregate. Also add yEdge logic (basically copying piet-metal). With a fix to ELEMENT_BINNING_RATIO (which I had simply gotten wrong), the example renders almost correctly, with small bounding box artifacts. --- piet-gpu-types/src/state.rs | 1 - piet-gpu/shader/binning.comp | 9 ++++---- piet-gpu/shader/binning.spv | Bin 22592 -> 19600 bytes piet-gpu/shader/build.ninja | 2 +- piet-gpu/shader/coarse.comp | 24 +++++++++++++++++++++ piet-gpu/shader/coarse.spv | Bin 38016 -> 43040 bytes piet-gpu/shader/elements.comp | 38 +++++++++++++++++++++++----------- piet-gpu/shader/elements.spv | Bin 45312 -> 45536 bytes piet-gpu/shader/setup.h | 2 +- piet-gpu/shader/state.h | 10 +++------ piet-gpu/src/lib.rs | 2 +- piet-gpu/src/render_ctx.rs | 26 ++++++++++++++--------- 12 files changed, 76 insertions(+), 38 deletions(-) diff --git a/piet-gpu-types/src/state.rs b/piet-gpu-types/src/state.rs index b93e9f3..35076f0 100644 --- a/piet-gpu-types/src/state.rs +++ b/piet-gpu-types/src/state.rs @@ -8,7 +8,6 @@ piet_gpu! { translate: [f32; 2], bbox: [f32; 4], linewidth: f32, - right_edge: f32, flags: u32, } } diff --git a/piet-gpu/shader/binning.comp b/piet-gpu/shader/binning.comp index 138621e..713a654 100644 --- a/piet-gpu/shader/binning.comp +++ b/piet-gpu/shader/binning.comp @@ -50,10 +50,10 @@ shared uint sh_chunk_jump[N_TILE]; shared float sh_right_edge[N_TILE]; -#define StateBuf_stride (4 + 2 * State_size) +#define StateBuf_stride (8 + 2 * State_size) -StateRef state_aggregate_ref(uint partition_ix) { - return StateRef(8 + partition_ix * StateBuf_stride); +uint state_right_edge_index(uint partition_ix) { + return 2 + partition_ix * (StateBuf_stride / 4); } void main() { @@ -120,8 +120,7 @@ void main() { // look-forward is small (performance may degrade in the case // of massively complex paths). do { - StateRef agg_ref = state_aggregate_ref(aggregate_ix); - my_right_edge = State_read(agg_ref).right_edge; + my_right_edge = uintBitsToFloat(state[state_right_edge_index(aggregate_ix)]); aggregate_ix++; } while (isinf(my_right_edge)); } diff --git a/piet-gpu/shader/binning.spv b/piet-gpu/shader/binning.spv index dc1713b5505929835d055744fc3124cd12d0d6e1..e932e4d3b944d2352e9ea514e2b4b4250c3e9a46 100644 GIT binary patch literal 19600 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 WpK!IODe3bw;29KczE5)Br~d_$M=(MF literal 22592 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 diff --git a/piet-gpu/shader/build.ninja b/piet-gpu/shader/build.ninja index 3b6b963..14c72aa 100644 --- a/piet-gpu/shader/build.ninja +++ b/piet-gpu/shader/build.ninja @@ -12,7 +12,7 @@ build image.spv: glsl image.comp | scene.h build elements.spv: glsl elements.comp | scene.h state.h annotated.h -build binning.spv: glsl binning.comp | annotated.h bins.h setup.h +build binning.spv: glsl binning.comp | annotated.h state.h bins.h setup.h build coarse.spv: glsl coarse.comp | annotated.h bins.h ptcl.h setup.h diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 2389e27..03c4535 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -310,6 +310,30 @@ void main() { switch (tag) { case Annotated_FillLine: + AnnoFillLineSeg fill_line = Annotated_FillLine_read(ref); + // This is basically the same logic as piet-metal, but should be made numerically robust. + vec2 tile_xy = vec2(tile_x * TILE_WIDTH_PX, tile_y * TILE_HEIGHT_PX); + float yEdge = mix(fill_line.p0.y, fill_line.p1.y, (tile_xy.x - fill_line.p0.x) / (fill_line.p1.x - fill_line.p0.x)); + if (min(fill_line.p0.x, fill_line.p1.x) < tile_xy.x && yEdge >= tile_xy.y && yEdge < tile_xy.y + TILE_HEIGHT_PX) { + Segment edge_seg; + if (fill_line.p0.x > fill_line.p1.x) { + fill_line.p1 = vec2(tile_xy.x, yEdge); + edge_seg.start = fill_line.p1; + edge_seg.end = vec2(tile_xy.x, tile_xy.y + TILE_HEIGHT_PX); + } else { + fill_line.p0 = vec2(tile_xy.x, yEdge); + edge_seg.start = vec2(tile_xy.x, tile_xy.y + TILE_HEIGHT_PX); + edge_seg.end = fill_line.p0; + } + alloc_chunk(chunk_n_segs, seg_chunk_ref, first_seg_chunk, seg_limit); + Segment_write(SegmentRef(seg_chunk_ref.offset + SegChunk_size + Segment_size * chunk_n_segs), edge_seg); + chunk_n_segs++; + } + Segment fill_seg = Segment(fill_line.p0, fill_line.p1); + alloc_chunk(chunk_n_segs, seg_chunk_ref, first_seg_chunk, seg_limit); + Segment_write(SegmentRef(seg_chunk_ref.offset + SegChunk_size + Segment_size * chunk_n_segs), fill_seg); + chunk_n_segs++; + break; case Annotated_StrokeLine: AnnoStrokeLineSeg line = Annotated_StrokeLine_read(ref); Segment seg = Segment(line.p0, line.p1); diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index bc097e44388cf90bb53859a8801d1fe1b4f343e3..3d3f3ff34e9f936435e516410382d85c0fb9e92a 100644 GIT binary patch literal 43040 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 D@a*Ai literal 38016 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