From 736f883f66dbe1458fba63c9b8fb20ee0d9f3005 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Tue, 12 May 2020 10:53:54 -0700 Subject: [PATCH] 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;