diff --git a/piet-gpu-types/src/scene.rs b/piet-gpu-types/src/scene.rs index 935ea4b..9480ea3 100644 --- a/piet-gpu-types/src/scene.rs +++ b/piet-gpu-types/src/scene.rs @@ -1,7 +1,7 @@ use piet_gpu_derive::piet_gpu; pub use self::scene::{ - Clip, CubicSeg, Element, Fill, LineSeg, QuadSeg, SetLineWidth, Stroke, Transform, + Clip, CubicSeg, Element, FillColor, LineSeg, QuadSeg, SetLineWidth, Transform, }; piet_gpu! { @@ -22,16 +22,13 @@ piet_gpu! { p2: [f32; 2], p3: [f32; 2], } - struct Fill { + struct FillColor { rgba_color: u32, } struct FillImage { index: u32, offset: [i16; 2], } - struct Stroke { - rgba_color: u32, - } struct SetLineWidth { width: f32, } @@ -45,19 +42,12 @@ piet_gpu! { } enum Element { Nop, - // 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), - StrokeQuad(QuadSeg), - FillQuad(QuadSeg), - StrokeCubic(CubicSeg), - FillCubic(CubicSeg), - Stroke(Stroke), - Fill(Fill), + Line(TagFlags, LineSeg), + Quad(TagFlags, QuadSeg), + Cubic(TagFlags, CubicSeg), + + FillColor(TagFlags, FillColor), SetLineWidth(SetLineWidth), Transform(Transform), BeginClip(Clip), diff --git a/piet-gpu/shader/elements.comp b/piet-gpu/shader/elements.comp index 9dc714e..1c8accb 100644 --- a/piet-gpu/shader/elements.comp +++ b/piet-gpu/shader/elements.comp @@ -110,30 +110,26 @@ State map_element(ElementRef ref) { c.pathseg_count = 0; c.trans_count = 0; switch (tag) { - case Element_FillLine: - case Element_StrokeLine: - LineSeg line = Element_FillLine_read(ref); + 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); c.pathseg_count = 1; break; - case Element_FillQuad: - case Element_StrokeQuad: - QuadSeg quad = Element_FillQuad_read(ref); + 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); c.pathseg_count = 1; break; - case Element_FillCubic: - case Element_StrokeCubic: - CubicSeg cubic = Element_FillCubic_read(ref); + 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)); c.pathseg_count = 1; break; - case Element_Fill: + case Element_FillColor: case Element_FillImage: - case Element_Stroke: case Element_BeginClip: c.flags = FLAG_RESET_BBOX; c.path_count = 1; @@ -291,11 +287,11 @@ void main() { // 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); - uint tag = Element_tag(this_ref).tag; - switch (tag) { - case Element_FillLine: - case Element_StrokeLine: - LineSeg line = Element_StrokeLine_read(this_ref); + ElementTag tag = Element_tag(this_ref); + bool is_stroke = fill_mode_from_flags(tag.flags) == MODE_STROKE; + switch (tag.tag) { + case Element_Line: + LineSeg line = Element_Line_read(this_ref); PathStrokeCubic path_cubic; path_cubic.p0 = line.p0; path_cubic.p1 = mix(line.p0, line.p1, 1.0 / 3.0); @@ -303,7 +299,7 @@ void main() { path_cubic.p3 = line.p1; path_cubic.path_ix = st.path_count; path_cubic.trans_ix = st.trans_count; - if (tag == Element_StrokeLine) { + if (is_stroke) { path_cubic.stroke = get_linewidth(st); } else { path_cubic.stroke = vec2(0.0); @@ -311,20 +307,19 @@ void main() { // We do encoding a bit by hand to minimize divergence. Another approach // would be to have a fill/stroke bool. PathSegRef path_out_ref = PathSegRef(conf.pathseg_alloc.offset + (st.pathseg_count - 1) * PathSeg_size); - uint out_tag = tag == Element_FillLine ? PathSeg_FillCubic : PathSeg_StrokeCubic; + uint out_tag = !is_stroke ? PathSeg_FillCubic : PathSeg_StrokeCubic; write_mem(conf.pathseg_alloc, path_out_ref.offset >> 2, out_tag); PathStrokeCubic_write(conf.pathseg_alloc, PathStrokeCubicRef(path_out_ref.offset + 4), path_cubic); break; - case Element_FillQuad: - case Element_StrokeQuad: - QuadSeg quad = Element_StrokeQuad_read(this_ref); + case Element_Quad: + QuadSeg quad = Element_Quad_read(this_ref); path_cubic.p0 = quad.p0; path_cubic.p1 = mix(quad.p1, quad.p0, 1.0 / 3.0); path_cubic.p2 = mix(quad.p1, quad.p2, 1.0 / 3.0); path_cubic.p3 = quad.p2; path_cubic.path_ix = st.path_count; path_cubic.trans_ix = st.trans_count; - if (tag == Element_StrokeQuad) { + if (is_stroke) { path_cubic.stroke = get_linewidth(st); } else { path_cubic.stroke = vec2(0.0); @@ -332,20 +327,19 @@ void main() { // We do encoding a bit by hand to minimize divergence. Another approach // would be to have a fill/stroke bool. path_out_ref = PathSegRef(conf.pathseg_alloc.offset + (st.pathseg_count - 1) * PathSeg_size); - out_tag = tag == Element_FillQuad ? PathSeg_FillCubic : PathSeg_StrokeCubic; + out_tag = !is_stroke ? PathSeg_FillCubic : PathSeg_StrokeCubic; write_mem(conf.pathseg_alloc, path_out_ref.offset >> 2, out_tag); PathStrokeCubic_write(conf.pathseg_alloc, PathStrokeCubicRef(path_out_ref.offset + 4), path_cubic); break; - case Element_FillCubic: - case Element_StrokeCubic: - CubicSeg cubic = Element_StrokeCubic_read(this_ref); + case Element_Cubic: + CubicSeg cubic = Element_Cubic_read(this_ref); path_cubic.p0 = cubic.p0; path_cubic.p1 = cubic.p1; path_cubic.p2 = cubic.p2; path_cubic.p3 = cubic.p3; path_cubic.path_ix = st.path_count; path_cubic.trans_ix = st.trans_count; - if (tag == Element_StrokeCubic) { + if (is_stroke) { path_cubic.stroke = get_linewidth(st); } else { path_cubic.stroke = vec2(0.0); @@ -353,27 +347,28 @@ void main() { // We do encoding a bit by hand to minimize divergence. Another approach // would be to have a fill/stroke bool. path_out_ref = PathSegRef(conf.pathseg_alloc.offset + (st.pathseg_count - 1) * PathSeg_size); - out_tag = tag == Element_FillCubic ? PathSeg_FillCubic : PathSeg_StrokeCubic; + out_tag = !is_stroke ? PathSeg_FillCubic : PathSeg_StrokeCubic; write_mem(conf.pathseg_alloc, path_out_ref.offset >> 2, out_tag); PathStrokeCubic_write(conf.pathseg_alloc, PathStrokeCubicRef(path_out_ref.offset + 4), path_cubic); 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(abs(st.mat.x * st.mat.w - st.mat.y * st.mat.z)); - AnnotatedRef out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size); - Annotated_Stroke_write(conf.anno_alloc, 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; - out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size); - Annotated_Fill_write(conf.anno_alloc, out_ref, anno_fill); + case Element_FillColor: + FillColor fill = Element_FillColor_read(this_ref); + // TODO: merge paths when annotations use tag flags. + if (is_stroke) { + AnnoStroke anno_stroke; + anno_stroke.rgba_color = fill.rgba_color; + vec2 lw = get_linewidth(st); + anno_stroke.bbox = st.bbox + vec4(-lw, lw); + anno_stroke.linewidth = st.linewidth * sqrt(abs(st.mat.x * st.mat.w - st.mat.y * st.mat.z)); + AnnotatedRef out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size); + Annotated_Stroke_write(conf.anno_alloc, out_ref, anno_stroke); + } else { + AnnoFill anno_fill; + anno_fill.rgba_color = fill.rgba_color; + anno_fill.bbox = st.bbox; + AnnotatedRef out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size); + Annotated_Fill_write(conf.anno_alloc, out_ref, anno_fill); + } break; case Element_FillImage: FillImage fill_img = Element_FillImage_read(this_ref); @@ -381,7 +376,7 @@ void main() { anno_fill_img.index = fill_img.index; anno_fill_img.offset = fill_img.offset; anno_fill_img.bbox = st.bbox; - out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size); + AnnotatedRef out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size); Annotated_FillImage_write(conf.anno_alloc, out_ref, anno_fill_img); break; case Element_BeginClip: diff --git a/piet-gpu/shader/elements.spv b/piet-gpu/shader/elements.spv index 51d3e4c..6ed690d 100644 Binary files a/piet-gpu/shader/elements.spv and b/piet-gpu/shader/elements.spv differ diff --git a/piet-gpu/shader/scene.h b/piet-gpu/shader/scene.h index 56c1a86..5dd38b2 100644 --- a/piet-gpu/shader/scene.h +++ b/piet-gpu/shader/scene.h @@ -14,7 +14,7 @@ struct CubicSegRef { uint offset; }; -struct FillRef { +struct FillColorRef { uint offset; }; @@ -22,10 +22,6 @@ struct FillImageRef { uint offset; }; -struct StrokeRef { - uint offset; -}; - struct SetLineWidthRef { uint offset; }; @@ -78,14 +74,14 @@ CubicSegRef CubicSeg_index(CubicSegRef ref, uint index) { return CubicSegRef(ref.offset + index * CubicSeg_size); } -struct Fill { +struct FillColor { uint rgba_color; }; -#define Fill_size 4 +#define FillColor_size 4 -FillRef Fill_index(FillRef ref, uint index) { - return FillRef(ref.offset + index * Fill_size); +FillColorRef FillColor_index(FillColorRef ref, uint index) { + return FillColorRef(ref.offset + index * FillColor_size); } struct FillImage { @@ -99,16 +95,6 @@ FillImageRef FillImage_index(FillImageRef ref, uint index) { return FillImageRef(ref.offset + index * FillImage_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; }; @@ -141,19 +127,15 @@ ClipRef Clip_index(ClipRef ref, uint index) { } #define Element_Nop 0 -#define Element_StrokeLine 1 -#define Element_FillLine 2 -#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_BeginClip 11 -#define Element_EndClip 12 -#define Element_FillImage 13 +#define Element_Line 1 +#define Element_Quad 2 +#define Element_Cubic 3 +#define Element_FillColor 4 +#define Element_SetLineWidth 5 +#define Element_Transform 6 +#define Element_BeginClip 7 +#define Element_EndClip 8 +#define Element_FillImage 9 #define Element_size 36 ElementRef Element_index(ElementRef ref, uint index) { @@ -210,10 +192,10 @@ CubicSeg CubicSeg_read(CubicSegRef ref) { return s; } -Fill Fill_read(FillRef ref) { +FillColor FillColor_read(FillColorRef ref) { uint ix = ref.offset >> 2; uint raw0 = scene[ix + 0]; - Fill s; + FillColor s; s.rgba_color = raw0; return s; } @@ -228,14 +210,6 @@ FillImage FillImage_read(FillImageRef ref) { 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]; @@ -274,36 +248,20 @@ ElementTag Element_tag(ElementRef ref) { return ElementTag(tag_and_flags & 0xffff, tag_and_flags >> 16); } -LineSeg Element_StrokeLine_read(ElementRef ref) { +LineSeg Element_Line_read(ElementRef ref) { return LineSeg_read(LineSegRef(ref.offset + 4)); } -LineSeg Element_FillLine_read(ElementRef ref) { - return LineSeg_read(LineSegRef(ref.offset + 4)); -} - -QuadSeg Element_StrokeQuad_read(ElementRef ref) { +QuadSeg Element_Quad_read(ElementRef ref) { return QuadSeg_read(QuadSegRef(ref.offset + 4)); } -QuadSeg Element_FillQuad_read(ElementRef ref) { - return QuadSeg_read(QuadSegRef(ref.offset + 4)); -} - -CubicSeg Element_StrokeCubic_read(ElementRef ref) { +CubicSeg Element_Cubic_read(ElementRef ref) { return CubicSeg_read(CubicSegRef(ref.offset + 4)); } -CubicSeg Element_FillCubic_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)); +FillColor Element_FillColor_read(ElementRef ref) { + return FillColor_read(FillColorRef(ref.offset + 4)); } SetLineWidth Element_SetLineWidth_read(ElementRef ref) { diff --git a/piet-gpu/shader/setup.h b/piet-gpu/shader/setup.h index 7d4a3e8..d26d842 100644 --- a/piet-gpu/shader/setup.h +++ b/piet-gpu/shader/setup.h @@ -39,3 +39,12 @@ struct Config { Alloc anno_alloc; Alloc trans_alloc; }; + +// Fill modes. +#define MODE_NONZERO 0 +#define MODE_STROKE 1 + +// fill_mode_from_flags extracts the fill mode from tag flags. +uint fill_mode_from_flags(uint flags) { + return flags & 0x1; +} diff --git a/piet-gpu/src/render_ctx.rs b/piet-gpu/src/render_ctx.rs index d05b712..06db5a4 100644 --- a/piet-gpu/src/render_ctx.rs +++ b/piet-gpu/src/render_ctx.rs @@ -3,7 +3,7 @@ use std::{borrow::Cow, ops::RangeBounds}; use piet_gpu_types::encoder::{Encode, Encoder}; use piet_gpu_types::scene::{ - Clip, CubicSeg, Element, Fill, LineSeg, QuadSeg, SetLineWidth, Stroke, Transform, + Clip, CubicSeg, Element, FillColor, LineSeg, QuadSeg, SetLineWidth, Transform, }; use piet::{ @@ -69,6 +69,13 @@ struct ClipElement { bbox: Option, } +enum FillMode { + // Fill path according to the non-zero winding rule. + Nonzero = 0, + // Fill stroked path. + Stroke = 1, +} + const TOLERANCE: f64 = 0.25; impl PietGpuRenderContext { @@ -143,8 +150,8 @@ impl RenderContext for PietGpuRenderContext { self.accumulate_bbox(|| shape.bounding_box() + Insets::uniform(width * 0.5)); let path = shape.path_elements(TOLERANCE); self.encode_path(path, false); - let stroke = Stroke { rgba_color }; - self.elements.push(Element::Stroke(stroke)); + let stroke = FillColor { rgba_color }; + self.elements.push(Element::FillColor(FillMode::Stroke as u16, stroke)); self.path_count += 1; } _ => (), @@ -168,8 +175,8 @@ impl RenderContext for PietGpuRenderContext { self.accumulate_bbox(|| shape.bounding_box()); let path = shape.path_elements(TOLERANCE); self.encode_path(path, true); - let fill = Fill { rgba_color }; - self.elements.push(Element::Fill(fill)); + let fill = FillColor { rgba_color }; + self.elements.push(Element::FillColor(FillMode::Nonzero as u16, fill)); self.path_count += 1; } } @@ -286,27 +293,27 @@ impl RenderContext for PietGpuRenderContext { impl PietGpuRenderContext { fn encode_line_seg(&mut self, seg: LineSeg, is_fill: bool) { if is_fill { - self.elements.push(Element::FillLine(seg)); + self.elements.push(Element::Line(FillMode::Nonzero as u16, seg)); } else { - self.elements.push(Element::StrokeLine(seg)); + self.elements.push(Element::Line(FillMode::Stroke as u16, seg)); } self.pathseg_count += 1; } fn encode_quad_seg(&mut self, seg: QuadSeg, is_fill: bool) { if is_fill { - self.elements.push(Element::FillQuad(seg)); + self.elements.push(Element::Quad(FillMode::Nonzero as u16, seg)); } else { - self.elements.push(Element::StrokeQuad(seg)); + self.elements.push(Element::Quad(FillMode::Stroke as u16, 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)); + self.elements.push(Element::Cubic(FillMode::Nonzero as u16, seg)); } else { - self.elements.push(Element::StrokeCubic(seg)); + self.elements.push(Element::Cubic(FillMode::Stroke as u16, seg)); } self.pathseg_count += 1; }