From 0f44bc8b784b47ca25db7f4efe81a18a711d6e6a Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Tue, 9 Jun 2020 16:01:47 -0700 Subject: [PATCH] Start GPU-side flattening This starts the work on GPU-side flattening by plumbing curves through. --- piet-gpu-types/src/pathseg.rs | 21 ++++++ piet-gpu-types/src/scene.rs | 8 +- piet-gpu/shader/build.ninja | 2 +- piet-gpu/shader/elements.comp | 31 +++++++- piet-gpu/shader/elements.spv | Bin 47936 -> 52612 bytes piet-gpu/shader/path_coarse.spv | Bin 15920 -> 15936 bytes piet-gpu/shader/pathseg.h | 130 +++++++++++++++++++++++++++++++- piet-gpu/shader/scene.h | 26 +++++-- piet-gpu/src/render_ctx.rs | 22 +++++- 9 files changed, 220 insertions(+), 20 deletions(-) diff --git a/piet-gpu-types/src/pathseg.rs b/piet-gpu-types/src/pathseg.rs index 5ad382b..ab3fcd1 100644 --- a/piet-gpu-types/src/pathseg.rs +++ b/piet-gpu-types/src/pathseg.rs @@ -18,6 +18,25 @@ piet_gpu! { // halfwidth in both x and y for binning stroke: [f32; 2], } + struct PathFillCubic { + p0: [f32; 2], + p1: [f32; 2], + p2: [f32; 2], + p3: [f32; 2], + path_ix: u32, + // A note: the layout of this struct is shared with + // PathStrokeCubic. In that case, we actually write + // [0.0, 0.0] as the stroke field, to minimize divergence. + } + struct PathStrokeCubic { + p0: [f32; 2], + p1: [f32; 2], + p2: [f32; 2], + p3: [f32; 2], + path_ix: u32, + // halfwidth in both x and y for binning + stroke: [f32; 2], + } /* struct PathQuad { p0: [f32; 2], @@ -37,6 +56,8 @@ piet_gpu! { Nop, FillLine(PathFillLine), StrokeLine(PathStrokeLine), + FillCubic(PathFillCubic), + StrokeCubic(PathStrokeCubic), /* Quad(AnnoQuadSeg), Cubic(AnnoCubicSeg), diff --git a/piet-gpu-types/src/scene.rs b/piet-gpu-types/src/scene.rs index 5792c94..5e4899f 100644 --- a/piet-gpu-types/src/scene.rs +++ b/piet-gpu-types/src/scene.rs @@ -92,10 +92,10 @@ piet_gpu! { 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), + StrokeQuad(QuadSeg), + FillQuad(QuadSeg), + StrokeCubic(CubicSeg), + FillCubic(CubicSeg), Stroke(Stroke), Fill(Fill), SetLineWidth(SetLineWidth), diff --git a/piet-gpu/shader/build.ninja b/piet-gpu/shader/build.ninja index 4f6e07f..13fb1b2 100644 --- a/piet-gpu/shader/build.ninja +++ b/piet-gpu/shader/build.ninja @@ -16,7 +16,7 @@ build binning.spv: glsl binning.comp | annotated.h state.h bins.h setup.h build tile_alloc.spv: glsl tile_alloc.comp | annotated.h tile.h setup.h -build path_coarse.spv: glsl path_coarse.comp | annotated.h tile.h setup.h +build path_coarse.spv: glsl path_coarse.comp | annotated.h pathseg.h tile.h setup.h build backdrop.spv: glsl backdrop.comp | annotated.h tile.h setup.h diff --git a/piet-gpu/shader/elements.comp b/piet-gpu/shader/elements.comp index 230b710..0855219 100644 --- a/piet-gpu/shader/elements.comp +++ b/piet-gpu/shader/elements.comp @@ -115,14 +115,16 @@ State map_element(ElementRef ref, inout bool is_fill) { c.bbox.zw = max(line.p0, line.p1); c.pathseg_count = 1; break; - case Element_Quad: - QuadSeg quad = Element_Quad_read(ref); + case Element_FillQuad: + case Element_StrokeQuad: + QuadSeg quad = Element_FillQuad_read(ref); c.bbox.xy = min(min(quad.p0, quad.p1), quad.p2); c.bbox.zw = max(max(quad.p0, quad.p1), quad.p2); c.pathseg_count = 1; break; - case Element_Cubic: - CubicSeg cubic = Element_Cubic_read(ref); + case Element_FillCubic: + case Element_StrokeCubic: + CubicSeg cubic = Element_FillCubic_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)); c.pathseg_count = 1; @@ -331,6 +333,27 @@ void main() { pathseg[path_out_ref.offset >> 2] = out_tag; PathStrokeLine_write(PathStrokeLineRef(path_out_ref.offset + 4), path_line); break; + case Element_FillCubic: + case Element_StrokeCubic: + CubicSeg cubic = Element_StrokeCubic_read(this_ref); + PathStrokeCubic path_cubic; + path_cubic.p0 = st.mat.xy * cubic.p0.x + st.mat.zw * cubic.p0.y + st.translate; + path_cubic.p1 = st.mat.xy * cubic.p1.x + st.mat.zw * cubic.p1.y + st.translate; + path_cubic.p1 = st.mat.xy * cubic.p2.x + st.mat.zw * cubic.p2.y + st.translate; + path_cubic.p1 = st.mat.xy * cubic.p3.x + st.mat.zw * cubic.p3.y + st.translate; + path_cubic.path_ix = st.path_count; + if (tag == Element_StrokeCubic) { + path_cubic.stroke = get_linewidth(st); + } else { + path_cubic.stroke = vec2(0.0); + } + // We do encoding a bit by hand to minimize divergence. Another approach + // would be to have a fill/stroke bool. + path_out_ref = PathSegRef((st.pathseg_count - 1) * PathSeg_size); + out_tag = tag == Element_FillLine ? PathSeg_FillCubic : PathSeg_StrokeCubic; + pathseg[path_out_ref.offset >> 2] = out_tag; + PathStrokeCubic_write(PathStrokeCubicRef(path_out_ref.offset + 4), path_cubic); + break; case Element_Stroke: Stroke stroke = Element_Stroke_read(this_ref); AnnoStroke anno_stroke; diff --git a/piet-gpu/shader/elements.spv b/piet-gpu/shader/elements.spv index 18f4dc546895238d4235612eacdf8bb1809d1341..55a43f2579fa9e0941bc29175631138ea17b2a0a 100644 GIT binary patch literal 52612 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 literal 47936 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 diff --git a/piet-gpu/shader/path_coarse.spv b/piet-gpu/shader/path_coarse.spv index 8c61a4bb6f791b50e9567561def3cd6431197839..7098a630e9193c4f1362f158e1627dc0b19dd1c5 100644 GIT binary patch delta 72 zcmdl`bD)NonMs+Qfq{{M0|*yv3uo WfV2*f_X$YP2lD4JZRX=w(gy$zCJi6} delta 56 zcmX?5v!RBUnMs+Qfq{{M0|@7D{EU1$3~UUa7#JAlF)=Xw LV%RLlucQwEXpanX diff --git a/piet-gpu/shader/pathseg.h b/piet-gpu/shader/pathseg.h index dc36d7e..de4ed28 100644 --- a/piet-gpu/shader/pathseg.h +++ b/piet-gpu/shader/pathseg.h @@ -8,6 +8,14 @@ struct PathStrokeLineRef { uint offset; }; +struct PathFillCubicRef { + uint offset; +}; + +struct PathStrokeCubicRef { + uint offset; +}; + struct PathSegRef { uint offset; }; @@ -37,10 +45,41 @@ PathStrokeLineRef PathStrokeLine_index(PathStrokeLineRef ref, uint index) { return PathStrokeLineRef(ref.offset + index * PathStrokeLine_size); } +struct PathFillCubic { + vec2 p0; + vec2 p1; + vec2 p2; + vec2 p3; + uint path_ix; +}; + +#define PathFillCubic_size 36 + +PathFillCubicRef PathFillCubic_index(PathFillCubicRef ref, uint index) { + return PathFillCubicRef(ref.offset + index * PathFillCubic_size); +} + +struct PathStrokeCubic { + vec2 p0; + vec2 p1; + vec2 p2; + vec2 p3; + uint path_ix; + vec2 stroke; +}; + +#define PathStrokeCubic_size 44 + +PathStrokeCubicRef PathStrokeCubic_index(PathStrokeCubicRef ref, uint index) { + return PathStrokeCubicRef(ref.offset + index * PathStrokeCubic_size); +} + #define PathSeg_Nop 0 #define PathSeg_FillLine 1 #define PathSeg_StrokeLine 2 -#define PathSeg_size 32 +#define PathSeg_FillCubic 3 +#define PathSeg_StrokeCubic 4 +#define PathSeg_size 48 PathSegRef PathSeg_index(PathSegRef ref, uint index) { return PathSegRef(ref.offset + index * PathSeg_size); @@ -97,6 +136,77 @@ void PathStrokeLine_write(PathStrokeLineRef ref, PathStrokeLine s) { pathseg[ix + 6] = floatBitsToUint(s.stroke.y); } +PathFillCubic PathFillCubic_read(PathFillCubicRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = pathseg[ix + 0]; + uint raw1 = pathseg[ix + 1]; + uint raw2 = pathseg[ix + 2]; + uint raw3 = pathseg[ix + 3]; + uint raw4 = pathseg[ix + 4]; + uint raw5 = pathseg[ix + 5]; + uint raw6 = pathseg[ix + 6]; + uint raw7 = pathseg[ix + 7]; + uint raw8 = pathseg[ix + 8]; + PathFillCubic 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.path_ix = raw8; + return s; +} + +void PathFillCubic_write(PathFillCubicRef ref, PathFillCubic s) { + uint ix = ref.offset >> 2; + pathseg[ix + 0] = floatBitsToUint(s.p0.x); + pathseg[ix + 1] = floatBitsToUint(s.p0.y); + pathseg[ix + 2] = floatBitsToUint(s.p1.x); + pathseg[ix + 3] = floatBitsToUint(s.p1.y); + pathseg[ix + 4] = floatBitsToUint(s.p2.x); + pathseg[ix + 5] = floatBitsToUint(s.p2.y); + pathseg[ix + 6] = floatBitsToUint(s.p3.x); + pathseg[ix + 7] = floatBitsToUint(s.p3.y); + pathseg[ix + 8] = s.path_ix; +} + +PathStrokeCubic PathStrokeCubic_read(PathStrokeCubicRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = pathseg[ix + 0]; + uint raw1 = pathseg[ix + 1]; + uint raw2 = pathseg[ix + 2]; + uint raw3 = pathseg[ix + 3]; + uint raw4 = pathseg[ix + 4]; + uint raw5 = pathseg[ix + 5]; + uint raw6 = pathseg[ix + 6]; + uint raw7 = pathseg[ix + 7]; + uint raw8 = pathseg[ix + 8]; + uint raw9 = pathseg[ix + 9]; + uint raw10 = pathseg[ix + 10]; + PathStrokeCubic 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.path_ix = raw8; + s.stroke = vec2(uintBitsToFloat(raw9), uintBitsToFloat(raw10)); + return s; +} + +void PathStrokeCubic_write(PathStrokeCubicRef ref, PathStrokeCubic s) { + uint ix = ref.offset >> 2; + pathseg[ix + 0] = floatBitsToUint(s.p0.x); + pathseg[ix + 1] = floatBitsToUint(s.p0.y); + pathseg[ix + 2] = floatBitsToUint(s.p1.x); + pathseg[ix + 3] = floatBitsToUint(s.p1.y); + pathseg[ix + 4] = floatBitsToUint(s.p2.x); + pathseg[ix + 5] = floatBitsToUint(s.p2.y); + pathseg[ix + 6] = floatBitsToUint(s.p3.x); + pathseg[ix + 7] = floatBitsToUint(s.p3.y); + pathseg[ix + 8] = s.path_ix; + pathseg[ix + 9] = floatBitsToUint(s.stroke.x); + pathseg[ix + 10] = floatBitsToUint(s.stroke.y); +} + uint PathSeg_tag(PathSegRef ref) { return pathseg[ref.offset >> 2]; } @@ -109,6 +219,14 @@ PathStrokeLine PathSeg_StrokeLine_read(PathSegRef ref) { return PathStrokeLine_read(PathStrokeLineRef(ref.offset + 4)); } +PathFillCubic PathSeg_FillCubic_read(PathSegRef ref) { + return PathFillCubic_read(PathFillCubicRef(ref.offset + 4)); +} + +PathStrokeCubic PathSeg_StrokeCubic_read(PathSegRef ref) { + return PathStrokeCubic_read(PathStrokeCubicRef(ref.offset + 4)); +} + void PathSeg_Nop_write(PathSegRef ref) { pathseg[ref.offset >> 2] = PathSeg_Nop; } @@ -123,3 +241,13 @@ void PathSeg_StrokeLine_write(PathSegRef ref, PathStrokeLine s) { PathStrokeLine_write(PathStrokeLineRef(ref.offset + 4), s); } +void PathSeg_FillCubic_write(PathSegRef ref, PathFillCubic s) { + pathseg[ref.offset >> 2] = PathSeg_FillCubic; + PathFillCubic_write(PathFillCubicRef(ref.offset + 4), s); +} + +void PathSeg_StrokeCubic_write(PathSegRef ref, PathStrokeCubic s) { + pathseg[ref.offset >> 2] = PathSeg_StrokeCubic; + PathStrokeCubic_write(PathStrokeCubicRef(ref.offset + 4), s); +} + diff --git a/piet-gpu/shader/scene.h b/piet-gpu/shader/scene.h index 5bb879b..3e57303 100644 --- a/piet-gpu/shader/scene.h +++ b/piet-gpu/shader/scene.h @@ -240,12 +240,14 @@ TransformRef Transform_index(TransformRef ref, uint index) { #define Element_Nop 0 #define Element_StrokeLine 1 #define Element_FillLine 2 -#define Element_Quad 3 -#define Element_Cubic 4 -#define Element_Stroke 5 -#define Element_Fill 6 -#define Element_SetLineWidth 7 -#define Element_Transform 8 +#define Element_StrokeQuad 3 +#define Element_FillQuad 4 +#define Element_StrokeCubic 5 +#define Element_FillCubic 6 +#define Element_Stroke 7 +#define Element_Fill 8 +#define Element_SetLineWidth 9 +#define Element_Transform 10 #define Element_size 36 ElementRef Element_index(ElementRef ref, uint index) { @@ -455,11 +457,19 @@ LineSeg Element_FillLine_read(ElementRef ref) { return LineSeg_read(LineSegRef(ref.offset + 4)); } -QuadSeg Element_Quad_read(ElementRef ref) { +QuadSeg Element_StrokeQuad_read(ElementRef ref) { return QuadSeg_read(QuadSegRef(ref.offset + 4)); } -CubicSeg Element_Cubic_read(ElementRef ref) { +QuadSeg Element_FillQuad_read(ElementRef ref) { + return QuadSeg_read(QuadSegRef(ref.offset + 4)); +} + +CubicSeg Element_StrokeCubic_read(ElementRef ref) { + return CubicSeg_read(CubicSegRef(ref.offset + 4)); +} + +CubicSeg Element_FillCubic_read(ElementRef ref) { return CubicSeg_read(CubicSegRef(ref.offset + 4)); } diff --git a/piet-gpu/src/render_ctx.rs b/piet-gpu/src/render_ctx.rs index 7908ff2..221b737 100644 --- a/piet-gpu/src/render_ctx.rs +++ b/piet-gpu/src/render_ctx.rs @@ -223,6 +223,24 @@ impl PietGpuRenderContext { self.pathseg_count += 1; } + fn encode_quad_seg(&mut self, seg: QuadSeg, is_fill: bool) { + if is_fill { + self.elements.push(Element::FillQuad(seg)); + } else { + self.elements.push(Element::StrokeQuad(seg)); + } + self.pathseg_count += 1; + } + + fn encode_cubic_seg(&mut self, seg: CubicSeg, is_fill: bool) { + if is_fill { + self.elements.push(Element::FillCubic(seg)); + } else { + self.elements.push(Element::StrokeCubic(seg)); + } + self.pathseg_count += 1; + } + fn encode_path(&mut self, path: impl Iterator, is_fill: bool) { let flatten = true; if flatten { @@ -286,7 +304,7 @@ impl PietGpuRenderContext { p1: scene_p1, p2: scene_p2, }; - self.elements.push(Element::Quad(seg)); + self.encode_quad_seg(seg, is_fill); last_pt = Some(scene_p2); } PathEl::CurveTo(p1, p2, p3) => { @@ -299,7 +317,7 @@ impl PietGpuRenderContext { p2: scene_p2, p3: scene_p3, }; - self.elements.push(Element::Cubic(seg)); + self.encode_cubic_seg(seg, is_fill); last_pt = Some(scene_p3); } PathEl::ClosePath => {