From e9ff509ab98876f05b32f95258bc3da445b1c557 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Wed, 17 Mar 2021 11:08:28 +0100 Subject: [PATCH] use tag flags for fill vs stroke modes in scene elements Encode stroke vs fill as tag flags, thereby reducing the number of scene elements. Encoding change only, no functional changes. The previous Stroke and Fill commands are merged to one command, FillColor. The encoding to annotated element is divergent, which is fixed when annotated elements move to tag flags. Updates #70 Signed-off-by: Elias Naur --- piet-gpu-types/src/scene.rs | 24 +++------- piet-gpu/shader/elements.comp | 87 ++++++++++++++++------------------ piet-gpu/shader/elements.spv | Bin 70244 -> 68864 bytes piet-gpu/shader/scene.h | 84 ++++++++------------------------ piet-gpu/shader/setup.h | 9 ++++ piet-gpu/src/render_ctx.rs | 29 +++++++----- 6 files changed, 96 insertions(+), 137 deletions(-) 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 51d3e4c2a215a472e0f1e53f9d6159aaff69d0c3..6ed690dd4b2dc9582c79d46b42a527e8be84987a 100644 GIT binary patch literal 68864 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 b`S%dc2ivChljwtEbOBi1{yBzn$MF9HW>Rmu literal 70244 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 AoB#j- 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; }