diff --git a/piet-gpu-types/src/scene.rs b/piet-gpu-types/src/scene.rs index 9480ea3..c4a9b9a 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, FillColor, LineSeg, QuadSeg, SetLineWidth, Transform, + Clip, CubicSeg, Element, FillColor, LineSeg, QuadSeg, SetFillMode, SetLineWidth, Transform, }; piet_gpu! { @@ -40,19 +40,23 @@ piet_gpu! { bbox: [f32; 4], // TODO: add alpha? } + struct SetFillMode { + fill_mode: u32, + } enum Element { Nop, - Line(TagFlags, LineSeg), - Quad(TagFlags, QuadSeg), - Cubic(TagFlags, CubicSeg), + Line(LineSeg), + Quad(QuadSeg), + Cubic(CubicSeg), - FillColor(TagFlags, FillColor), + FillColor(FillColor), SetLineWidth(SetLineWidth), Transform(Transform), BeginClip(Clip), EndClip(Clip), FillImage(FillImage), + SetFillMode(SetFillMode), } } } diff --git a/piet-gpu/shader/elements.comp b/piet-gpu/shader/elements.comp index 80a456d..7fb02fc 100644 --- a/piet-gpu/shader/elements.comp +++ b/piet-gpu/shader/elements.comp @@ -63,6 +63,11 @@ uint state_flag_index(uint partition_ix) { #define FLAG_SET_LINEWIDTH 1 #define FLAG_SET_BBOX 2 #define FLAG_RESET_BBOX 4 +#define FLAG_SET_FILL_MODE 8 +// Fill modes take up the next bit. Non-zero fill is 0, stroke is 1. +#define LG_FILL_MODE 4 +#define FILL_MODE_BITS 1 +#define FILL_MODE_MASK (FILL_MODE_BITS << LG_FILL_MODE) // This is almost like a monoid (the interaction between transformation and // bounding boxes is approximate) @@ -88,8 +93,11 @@ State combine_state(State a, State b) { c.translate.x = a.mat.x * b.translate.x + a.mat.z * b.translate.y + a.translate.x; c.translate.y = a.mat.y * b.translate.x + a.mat.w * b.translate.y + a.translate.y; c.linewidth = (b.flags & FLAG_SET_LINEWIDTH) == 0 ? a.linewidth : b.linewidth; - c.flags = (a.flags & (FLAG_SET_LINEWIDTH | FLAG_SET_BBOX)) | b.flags; + c.flags = (a.flags & (FLAG_SET_LINEWIDTH | FLAG_SET_BBOX | FLAG_SET_FILL_MODE)) | b.flags; c.flags |= (a.flags & FLAG_RESET_BBOX) >> 1; + uint fill_mode = (b.flags & FLAG_SET_FILL_MODE) == 0 ? a.flags : b.flags; + fill_mode &= FILL_MODE_MASK; + c.flags = (c.flags & ~FILL_MODE_MASK) | fill_mode; c.path_count = a.path_count + b.path_count; c.pathseg_count = a.pathseg_count + b.pathseg_count; c.trans_count = a.trans_count + b.trans_count; @@ -148,6 +156,10 @@ State map_element(ElementRef ref) { c.translate = t.translate; c.trans_count = 1; break; + case Element_SetFillMode: + SetFillMode fm = Element_SetFillMode_read(ref); + c.flags = FLAG_SET_FILL_MODE | (fm.fill_mode << LG_FILL_MODE); + break; } return c; } @@ -288,7 +300,7 @@ void main() { // registers (though register pressure is an issue). ElementRef this_ref = Element_index(ref, i); ElementTag tag = Element_tag(this_ref); - uint fill_mode = fill_mode_from_flags(tag.flags); + uint fill_mode = fill_mode_from_flags(st.flags >> LG_FILL_MODE); bool is_stroke = fill_mode == MODE_STROKE; switch (tag.tag) { case Element_Line: diff --git a/piet-gpu/shader/elements.spv b/piet-gpu/shader/elements.spv index c7bade5..d1ca8dd 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 5dd38b2..38c2549 100644 --- a/piet-gpu/shader/scene.h +++ b/piet-gpu/shader/scene.h @@ -34,6 +34,10 @@ struct ClipRef { uint offset; }; +struct SetFillModeRef { + uint offset; +}; + struct ElementRef { uint offset; }; @@ -126,6 +130,16 @@ ClipRef Clip_index(ClipRef ref, uint index) { return ClipRef(ref.offset + index * Clip_size); } +struct SetFillMode { + uint fill_mode; +}; + +#define SetFillMode_size 4 + +SetFillModeRef SetFillMode_index(SetFillModeRef ref, uint index) { + return SetFillModeRef(ref.offset + index * SetFillMode_size); +} + #define Element_Nop 0 #define Element_Line 1 #define Element_Quad 2 @@ -136,6 +150,7 @@ ClipRef Clip_index(ClipRef ref, uint index) { #define Element_BeginClip 7 #define Element_EndClip 8 #define Element_FillImage 9 +#define Element_SetFillMode 10 #define Element_size 36 ElementRef Element_index(ElementRef ref, uint index) { @@ -243,6 +258,14 @@ Clip Clip_read(ClipRef ref) { return s; } +SetFillMode SetFillMode_read(SetFillModeRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + SetFillMode s; + s.fill_mode = raw0; + return s; +} + ElementTag Element_tag(ElementRef ref) { uint tag_and_flags = scene[ref.offset >> 2]; return ElementTag(tag_and_flags & 0xffff, tag_and_flags >> 16); @@ -284,3 +307,7 @@ FillImage Element_FillImage_read(ElementRef ref) { return FillImage_read(FillImageRef(ref.offset + 4)); } +SetFillMode Element_SetFillMode_read(ElementRef ref) { + return SetFillMode_read(SetFillModeRef(ref.offset + 4)); +} + diff --git a/piet-gpu/src/render_ctx.rs b/piet-gpu/src/render_ctx.rs index 06db5a4..e80d67d 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, FillColor, LineSeg, QuadSeg, SetLineWidth, Transform, + Clip, CubicSeg, Element, FillColor, SetFillMode, LineSeg, QuadSeg, SetLineWidth, Transform, }; use piet::{ @@ -32,6 +32,7 @@ pub struct PietGpuRenderContext { // Will probably need direct accesss to hal Device to create images etc. inner_text: PietGpuText, stroke_width: f32, + fill_mode: FillMode, // We're tallying these cpu-side for expedience, but will probably // move this to some kind of readback from element processing. /// The count of elements that make it through to coarse rasterization. @@ -69,6 +70,7 @@ struct ClipElement { bbox: Option, } +#[derive(Clone,Copy,PartialEq)] enum FillMode { // Fill path according to the non-zero winding rule. Nonzero = 0, @@ -89,6 +91,7 @@ impl PietGpuRenderContext { elements, inner_text, stroke_width, + fill_mode: FillMode::Nonzero, path_count: 0, pathseg_count: 0, trans_count: 0, @@ -116,6 +119,14 @@ impl PietGpuRenderContext { } } +fn set_fill_mode(ctx: &mut PietGpuRenderContext, fill_mode: FillMode) { + if ctx.fill_mode != fill_mode { + ctx.elements + .push(Element::SetFillMode(SetFillMode { fill_mode: fill_mode as u32 })); + ctx.fill_mode = fill_mode; + } +} + impl RenderContext for PietGpuRenderContext { type Brush = PietGpuBrush; type Image = PietGpuImage; @@ -143,6 +154,7 @@ impl RenderContext for PietGpuRenderContext { .push(Element::SetLineWidth(SetLineWidth { width: width_f32 })); self.stroke_width = width_f32; } + set_fill_mode(self, FillMode::Stroke); let brush = brush.make_brush(self, || shape.bounding_box()).into_owned(); match brush { PietGpuBrush::Solid(rgba_color) => { @@ -151,7 +163,7 @@ impl RenderContext for PietGpuRenderContext { let path = shape.path_elements(TOLERANCE); self.encode_path(path, false); let stroke = FillColor { rgba_color }; - self.elements.push(Element::FillColor(FillMode::Stroke as u16, stroke)); + self.elements.push(Element::FillColor(stroke)); self.path_count += 1; } _ => (), @@ -174,9 +186,10 @@ impl RenderContext for PietGpuRenderContext { // Perhaps that should be added to kurbo. self.accumulate_bbox(|| shape.bounding_box()); let path = shape.path_elements(TOLERANCE); + set_fill_mode(self, FillMode::Nonzero); self.encode_path(path, true); let fill = FillColor { rgba_color }; - self.elements.push(Element::FillColor(FillMode::Nonzero as u16, fill)); + self.elements.push(Element::FillColor(fill)); self.path_count += 1; } } @@ -184,6 +197,7 @@ impl RenderContext for PietGpuRenderContext { fn fill_even_odd(&mut self, _shape: impl Shape, _brush: &impl IntoBrush) {} fn clip(&mut self, shape: impl Shape) { + set_fill_mode(self, FillMode::Nonzero); let path = shape.path_elements(TOLERANCE); self.encode_path(path, true); let begin_ix = self.elements.len(); @@ -291,30 +305,18 @@ impl RenderContext for PietGpuRenderContext { } impl PietGpuRenderContext { - fn encode_line_seg(&mut self, seg: LineSeg, is_fill: bool) { - if is_fill { - self.elements.push(Element::Line(FillMode::Nonzero as u16, seg)); - } else { - self.elements.push(Element::Line(FillMode::Stroke as u16, seg)); - } + fn encode_line_seg(&mut self, seg: LineSeg) { + self.elements.push(Element::Line(seg)); self.pathseg_count += 1; } - fn encode_quad_seg(&mut self, seg: QuadSeg, is_fill: bool) { - if is_fill { - self.elements.push(Element::Quad(FillMode::Nonzero as u16, seg)); - } else { - self.elements.push(Element::Quad(FillMode::Stroke as u16, seg)); - } + fn encode_quad_seg(&mut self, seg: QuadSeg) { + self.elements.push(Element::Quad(seg)); self.pathseg_count += 1; } - fn encode_cubic_seg(&mut self, seg: CubicSeg, is_fill: bool) { - if is_fill { - self.elements.push(Element::Cubic(FillMode::Nonzero as u16, seg)); - } else { - self.elements.push(Element::Cubic(FillMode::Stroke as u16, seg)); - } + fn encode_cubic_seg(&mut self, seg: CubicSeg) { + self.elements.push(Element::Cubic(seg)); self.pathseg_count += 1; } @@ -327,13 +329,13 @@ impl PietGpuRenderContext { } _ => None }.into_iter().chain(Some(el)) - }).chain(Some(PathEl::ClosePath)), is_fill) + }).chain(Some(PathEl::ClosePath))) } else { - self.encode_path_inner(path, is_fill) + self.encode_path_inner(path) } } - fn encode_path_inner(&mut self, path: impl Iterator, is_fill: bool) { + fn encode_path_inner(&mut self, path: impl Iterator) { let flatten = false; if flatten { let mut start_pt = None; @@ -351,7 +353,7 @@ impl PietGpuRenderContext { p0: last_pt.unwrap(), p1: scene_pt, }; - self.encode_line_seg(seg, is_fill); + self.encode_line_seg(seg); last_pt = Some(scene_pt); } PathEl::ClosePath => { @@ -361,7 +363,7 @@ impl PietGpuRenderContext { p0: last, p1: start, }; - self.encode_line_seg(seg, is_fill); + self.encode_line_seg(seg); } } } @@ -385,7 +387,7 @@ impl PietGpuRenderContext { p0: last_pt.unwrap(), p1: scene_pt, }; - self.encode_line_seg(seg, is_fill); + self.encode_line_seg(seg); last_pt = Some(scene_pt); } PathEl::QuadTo(p1, p2) => { @@ -396,7 +398,7 @@ impl PietGpuRenderContext { p1: scene_p1, p2: scene_p2, }; - self.encode_quad_seg(seg, is_fill); + self.encode_quad_seg(seg); last_pt = Some(scene_p2); } PathEl::CurveTo(p1, p2, p3) => { @@ -409,7 +411,7 @@ impl PietGpuRenderContext { p2: scene_p2, p3: scene_p3, }; - self.encode_cubic_seg(seg, is_fill); + self.encode_cubic_seg(seg); last_pt = Some(scene_p3); } PathEl::ClosePath => { @@ -419,7 +421,7 @@ impl PietGpuRenderContext { p0: last, p1: start, }; - self.encode_line_seg(seg, is_fill); + self.encode_line_seg(seg); } } }