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 18f4dc5..55a43f2 100644 Binary files a/piet-gpu/shader/elements.spv and b/piet-gpu/shader/elements.spv differ diff --git a/piet-gpu/shader/path_coarse.spv b/piet-gpu/shader/path_coarse.spv index 8c61a4b..7098a63 100644 Binary files a/piet-gpu/shader/path_coarse.spv and b/piet-gpu/shader/path_coarse.spv differ 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 => {