From f53d00e6bcbeb08efa2f1c58074a1d782cd6ce67 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Thu, 19 Nov 2020 11:53:59 -0800 Subject: [PATCH 1/4] Add transforms and state stack Actually handle transforms in RenderCtx (was implemented in renderer but not actually plumbed through). This also requires maintaining a state stack, which will also be required for clipping. This PR also starts work on encoding clipping, including tracking bounding boxes. WIP, none of this is tested yet. --- piet-gpu-types/src/annotated.rs | 5 + piet-gpu-types/src/ptcl.rs | 10 ++ piet-gpu-types/src/scene.rs | 15 ++- piet-gpu/shader/annotated.h | 53 +++++++++++ piet-gpu/shader/backdrop.spv | Bin 7256 -> 7256 bytes piet-gpu/shader/binning.spv | Bin 10152 -> 10152 bytes piet-gpu/shader/coarse.spv | Bin 37332 -> 37332 bytes piet-gpu/shader/kernel4.comp | 20 ++++ piet-gpu/shader/kernel4.spv | Bin 26192 -> 30140 bytes piet-gpu/shader/path_coarse.spv | Bin 28876 -> 28876 bytes piet-gpu/shader/ptcl.h | 86 ++++++++++++++++- piet-gpu/shader/scene.h | 57 ++++++++++++ piet-gpu/shader/tile_alloc.spv | Bin 8816 -> 8816 bytes piet-gpu/src/render_ctx.rs | 158 +++++++++++++++++++++++++++----- 14 files changed, 376 insertions(+), 28 deletions(-) diff --git a/piet-gpu-types/src/annotated.rs b/piet-gpu-types/src/annotated.rs index 681a7ec..04f2111 100644 --- a/piet-gpu-types/src/annotated.rs +++ b/piet-gpu-types/src/annotated.rs @@ -18,12 +18,17 @@ piet_gpu! { // That's expected to be uncommon, so we could special-case it. linewidth: f32, } + struct AnnoClip { + bbox: [f32; 4], + } enum Annotated { Nop, Stroke(AnnoStroke), Fill(AnnoFill), FillMask(AnnoFillMask), FillMaskInv(AnnoFillMask), + BeginClip(AnnoClip), + EndClip(AnnoClip), } } } diff --git a/piet-gpu-types/src/ptcl.rs b/piet-gpu-types/src/ptcl.rs index 95dcdc6..d05218b 100644 --- a/piet-gpu-types/src/ptcl.rs +++ b/piet-gpu-types/src/ptcl.rs @@ -30,6 +30,14 @@ piet_gpu! { backdrop: i32, mask: f32, } + struct CmdBeginClip { + tile_ref: u32, + backdrop: i32, + } + struct CmdEndClip { + // This will be 1.0 for clips, but we can imagine blend groups. + alpha: f32, + } struct CmdSolid { rgba_color: u32, } @@ -46,6 +54,8 @@ piet_gpu! { Fill(CmdFill), FillMask(CmdFillMask), FillMaskInv(CmdFillMask), + BeginClip(CmdBeginClip), + EndClip(CmdEndClip), Stroke(CmdStroke), Solid(CmdSolid), SolidMask(CmdSolidMask), diff --git a/piet-gpu-types/src/scene.rs b/piet-gpu-types/src/scene.rs index 1359c1b..7e2fb43 100644 --- a/piet-gpu-types/src/scene.rs +++ b/piet-gpu-types/src/scene.rs @@ -1,6 +1,8 @@ use piet_gpu_derive::piet_gpu; -pub use self::scene::{CubicSeg, Element, Fill, LineSeg, QuadSeg, SetLineWidth, Stroke, Transform}; +pub use self::scene::{ + BeginClip, CubicSeg, Element, EndClip, Fill, LineSeg, QuadSeg, SetLineWidth, Stroke, Transform, +}; piet_gpu! { #[rust_encode] @@ -36,6 +38,15 @@ piet_gpu! { mat: [f32; 4], translate: [f32; 2], } + struct BeginClip { + bbox: [f32; 4], + // TODO: add alpha? + } + struct EndClip { + // The delta between the BeginClip and EndClip element indices. + // It is stored as a delta to facilitate binary string concatenation. + delta: u32, + } enum Element { Nop, // Another approach to encoding would be to use a single @@ -55,6 +66,8 @@ piet_gpu! { Transform(Transform), FillMask(FillMask), FillMaskInv(FillMask), + BeginClip(BeginClip), + EndClip(EndClip), } } } diff --git a/piet-gpu/shader/annotated.h b/piet-gpu/shader/annotated.h index 847ca06..986eff2 100644 --- a/piet-gpu/shader/annotated.h +++ b/piet-gpu/shader/annotated.h @@ -12,6 +12,10 @@ struct AnnoStrokeRef { uint offset; }; +struct AnnoClipRef { + uint offset; +}; + struct AnnotatedRef { uint offset; }; @@ -50,11 +54,23 @@ AnnoStrokeRef AnnoStroke_index(AnnoStrokeRef ref, uint index) { return AnnoStrokeRef(ref.offset + index * AnnoStroke_size); } +struct AnnoClip { + vec4 bbox; +}; + +#define AnnoClip_size 16 + +AnnoClipRef AnnoClip_index(AnnoClipRef ref, uint index) { + return AnnoClipRef(ref.offset + index * AnnoClip_size); +} + #define Annotated_Nop 0 #define Annotated_Stroke 1 #define Annotated_Fill 2 #define Annotated_FillMask 3 #define Annotated_FillMaskInv 4 +#define Annotated_BeginClip 5 +#define Annotated_EndClip 6 #define Annotated_size 28 AnnotatedRef Annotated_index(AnnotatedRef ref, uint index) { @@ -130,6 +146,25 @@ void AnnoStroke_write(AnnoStrokeRef ref, AnnoStroke s) { annotated[ix + 5] = floatBitsToUint(s.linewidth); } +AnnoClip AnnoClip_read(AnnoClipRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = annotated[ix + 0]; + uint raw1 = annotated[ix + 1]; + uint raw2 = annotated[ix + 2]; + uint raw3 = annotated[ix + 3]; + AnnoClip s; + s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + return s; +} + +void AnnoClip_write(AnnoClipRef ref, AnnoClip s) { + uint ix = ref.offset >> 2; + annotated[ix + 0] = floatBitsToUint(s.bbox.x); + annotated[ix + 1] = floatBitsToUint(s.bbox.y); + annotated[ix + 2] = floatBitsToUint(s.bbox.z); + annotated[ix + 3] = floatBitsToUint(s.bbox.w); +} + uint Annotated_tag(AnnotatedRef ref) { return annotated[ref.offset >> 2]; } @@ -150,6 +185,14 @@ AnnoFillMask Annotated_FillMaskInv_read(AnnotatedRef ref) { return AnnoFillMask_read(AnnoFillMaskRef(ref.offset + 4)); } +AnnoClip Annotated_BeginClip_read(AnnotatedRef ref) { + return AnnoClip_read(AnnoClipRef(ref.offset + 4)); +} + +AnnoClip Annotated_EndClip_read(AnnotatedRef ref) { + return AnnoClip_read(AnnoClipRef(ref.offset + 4)); +} + void Annotated_Nop_write(AnnotatedRef ref) { annotated[ref.offset >> 2] = Annotated_Nop; } @@ -174,3 +217,13 @@ void Annotated_FillMaskInv_write(AnnotatedRef ref, AnnoFillMask s) { AnnoFillMask_write(AnnoFillMaskRef(ref.offset + 4), s); } +void Annotated_BeginClip_write(AnnotatedRef ref, AnnoClip s) { + annotated[ref.offset >> 2] = Annotated_BeginClip; + AnnoClip_write(AnnoClipRef(ref.offset + 4), s); +} + +void Annotated_EndClip_write(AnnotatedRef ref, AnnoClip s) { + annotated[ref.offset >> 2] = Annotated_EndClip; + AnnoClip_write(AnnoClipRef(ref.offset + 4), s); +} + diff --git a/piet-gpu/shader/backdrop.spv b/piet-gpu/shader/backdrop.spv index e2093d9a02e92895c45174a57d2701d2e434dd20..7f0852dadb86d116244ff9b7433f8e417daef0f5 100644 GIT binary patch delta 18 Zcmca%al?X>nMs+Qfq{{MYa^$>3;-$^1Bn0t delta 18 Zcmca%al?X>nMs+Qfq{{MV55yj8J(_!DuSqDU4#L zZx_!^5lcjtWkRNaJ?Q*DokGuyyv5Io9fBfC*70f?7%3y!E*`@DHbjPSD&Sj^6M7AJ zzD&vr{Hn4OHwfTXae8oh)e$(oFYppFcY!r%`b^<{)SIZ4P&)>H03c3+8S#;SFFl?cjpvtjPo&`>kORUD>{7SzXLCz^_Hx2273H_ zI<3yMpaG|jqqn>Sr>D0Jkah)4pDCP?np@UTy9R#-vc`F-^_Dl_F>35-{cX9C?ip?M in7Xqacx@cLMHjrI-qQIwGN#=E`Fr)eckCNJWVv6JXHXyj delta 554 zcmX|8IZne+5FOiW?yxNiM1dlRDCiIkViBnj4T>c=K$?I_FbRuU3`szuGDbqbgMuO_ zAQ1%>5*MJcI0C1j;JwJ7;`=T0=Xu7?x@OijZOfb)6)|M&)t+(KO>!uL_HqE<%j5c~o&V{m$J=qMro19%Fpw`83&(BtRQ zX%W+cGdK~B-trurp58J*+66Ryrf^1TZdpU^3j77g8t0|fTV8`VP-9O#*m5P^Hd^X2 i^(NZzS~z-(4tQI=r&E(;OuGR__)~h`TlV!HGV2es&rm!7 diff --git a/piet-gpu/shader/kernel4.comp b/piet-gpu/shader/kernel4.comp index d21aa87..919d120 100644 --- a/piet-gpu/shader/kernel4.comp +++ b/piet-gpu/shader/kernel4.comp @@ -27,6 +27,8 @@ layout(rgba8, set = 0, binding = 2) uniform writeonly image2D image; #include "ptcl.h" #include "tile.h" +#define BLEND_STACK_SIZE 4 + // Calculate coverage based on backdrop + coverage of each line segment float[CHUNK] computeArea(vec2 xy, int backdrop, uint tile_ref) { // Probably better to store as float, but conversion is no doubt cheap. @@ -69,6 +71,8 @@ void main() { vec2 xy = vec2(xy_uint); vec3 rgb[CHUNK]; float mask[CHUNK]; + uint blend_stack[BLEND_STACK_SIZE][CHUNK]; + uint blend_sp = 0; for (uint i = 0; i < CHUNK; i++) { rgb[i] = vec3(0.5); mask[i] = 1.0; @@ -137,6 +141,22 @@ void main() { mask[k] = mix(mask[k], fill_mask.mask, 1.0 - area[k]); } break; + case Cmd_BeginClip: + CmdBeginClip begin_clip = Cmd_BeginClip_read(cmd_ref); + area = computeArea(xy, begin_clip.backdrop, begin_clip.tile_ref); + for (uint k = 0; k < CHUNK; k++) { + blend_stack[blend_sp][k] = packUnorm4x8(vec4(rgb[k], clamp(abs(area[k]), 0.0, 1.0))); + } + blend_sp++; + break; + case Cmd_EndClip: + CmdEndClip end_clip = Cmd_EndClip_read(cmd_ref); + blend_sp--; + for (uint k = 0; k < CHUNK; k++) { + vec4 rgba = unpackUnorm4x8(blend_stack[blend_sp][k]); + rgb[k] = mix(rgb[k], rgba.rgb, end_clip.alpha * rgba.a); + } + break; case Cmd_Solid: CmdSolid solid = Cmd_Solid_read(cmd_ref); fg_rgba = unpackUnorm4x8(solid.rgba_color).wzyx; diff --git a/piet-gpu/shader/kernel4.spv b/piet-gpu/shader/kernel4.spv index d159fe54ca5caf49dd92dbdf5f5f6c3317aed27e..0d3858159e8435dce93ded58eaa62665d5a970c1 100644 GIT binary patch literal 30140 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 diff --git a/piet-gpu/shader/path_coarse.spv b/piet-gpu/shader/path_coarse.spv index db5bc57af80015b065c9a69d0e04de0844737f37..f82a031295caaf30b5e4075547981235bfe9d90c 100644 GIT binary patch delta 20 bcmX@}knzkzMowlXWp)MzMh32poQDelL> 2; + uint raw0 = ptcl[ix + 0]; + uint raw1 = ptcl[ix + 1]; + CmdBeginClip s; + s.tile_ref = raw0; + s.backdrop = int(raw1); + return s; +} + +void CmdBeginClip_write(CmdBeginClipRef ref, CmdBeginClip s) { + uint ix = ref.offset >> 2; + ptcl[ix + 0] = s.tile_ref; + ptcl[ix + 1] = uint(s.backdrop); +} + +CmdEndClip CmdEndClip_read(CmdEndClipRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = ptcl[ix + 0]; + CmdEndClip s; + s.alpha = uintBitsToFloat(raw0); + return s; +} + +void CmdEndClip_write(CmdEndClipRef ref, CmdEndClip s) { + uint ix = ref.offset >> 2; + ptcl[ix + 0] = floatBitsToUint(s.alpha); +} + CmdSolid CmdSolid_read(CmdSolidRef ref) { uint ix = ref.offset >> 2; uint raw0 = ptcl[ix + 0]; @@ -334,6 +394,14 @@ CmdFillMask Cmd_FillMaskInv_read(CmdRef ref) { return CmdFillMask_read(CmdFillMaskRef(ref.offset + 4)); } +CmdBeginClip Cmd_BeginClip_read(CmdRef ref) { + return CmdBeginClip_read(CmdBeginClipRef(ref.offset + 4)); +} + +CmdEndClip Cmd_EndClip_read(CmdRef ref) { + return CmdEndClip_read(CmdEndClipRef(ref.offset + 4)); +} + CmdStroke Cmd_Stroke_read(CmdRef ref) { return CmdStroke_read(CmdStrokeRef(ref.offset + 4)); } @@ -379,6 +447,16 @@ void Cmd_FillMaskInv_write(CmdRef ref, CmdFillMask s) { CmdFillMask_write(CmdFillMaskRef(ref.offset + 4), s); } +void Cmd_BeginClip_write(CmdRef ref, CmdBeginClip s) { + ptcl[ref.offset >> 2] = Cmd_BeginClip; + CmdBeginClip_write(CmdBeginClipRef(ref.offset + 4), s); +} + +void Cmd_EndClip_write(CmdRef ref, CmdEndClip s) { + ptcl[ref.offset >> 2] = Cmd_EndClip; + CmdEndClip_write(CmdEndClipRef(ref.offset + 4), s); +} + void Cmd_Stroke_write(CmdRef ref, CmdStroke s) { ptcl[ref.offset >> 2] = Cmd_Stroke; CmdStroke_write(CmdStrokeRef(ref.offset + 4), s); diff --git a/piet-gpu/shader/scene.h b/piet-gpu/shader/scene.h index 6823fe6..0a4a2ce 100644 --- a/piet-gpu/shader/scene.h +++ b/piet-gpu/shader/scene.h @@ -32,6 +32,14 @@ struct TransformRef { uint offset; }; +struct BeginClipRef { + uint offset; +}; + +struct EndClipRef { + uint offset; +}; + struct ElementRef { uint offset; }; @@ -123,6 +131,26 @@ TransformRef Transform_index(TransformRef ref, uint index) { return TransformRef(ref.offset + index * Transform_size); } +struct BeginClip { + vec4 bbox; +}; + +#define BeginClip_size 16 + +BeginClipRef BeginClip_index(BeginClipRef ref, uint index) { + return BeginClipRef(ref.offset + index * BeginClip_size); +} + +struct EndClip { + uint clip_size; +}; + +#define EndClip_size 4 + +EndClipRef EndClip_index(EndClipRef ref, uint index) { + return EndClipRef(ref.offset + index * EndClip_size); +} + #define Element_Nop 0 #define Element_StrokeLine 1 #define Element_FillLine 2 @@ -136,6 +164,8 @@ TransformRef Transform_index(TransformRef ref, uint index) { #define Element_Transform 10 #define Element_FillMask 11 #define Element_FillMaskInv 12 +#define Element_BeginClip 13 +#define Element_EndClip 14 #define Element_size 36 ElementRef Element_index(ElementRef ref, uint index) { @@ -233,6 +263,25 @@ Transform Transform_read(TransformRef ref) { return s; } +BeginClip BeginClip_read(BeginClipRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + uint raw1 = scene[ix + 1]; + uint raw2 = scene[ix + 2]; + uint raw3 = scene[ix + 3]; + BeginClip s; + s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + return s; +} + +EndClip EndClip_read(EndClipRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + EndClip s; + s.clip_size = raw0; + return s; +} + uint Element_tag(ElementRef ref) { return scene[ref.offset >> 2]; } @@ -285,3 +334,11 @@ FillMask Element_FillMaskInv_read(ElementRef ref) { return FillMask_read(FillMaskRef(ref.offset + 4)); } +BeginClip Element_BeginClip_read(ElementRef ref) { + return BeginClip_read(BeginClipRef(ref.offset + 4)); +} + +EndClip Element_EndClip_read(ElementRef ref) { + return EndClip_read(EndClipRef(ref.offset + 4)); +} + diff --git a/piet-gpu/shader/tile_alloc.spv b/piet-gpu/shader/tile_alloc.spv index af52665184d64b41d3f1e8b3f9174a3ea38b1c49..449f4d6874ff4de6782271dbd4f09d1d23ddadf4 100644 GIT binary patch delta 18 Zcmez1^1+3ZnMs+Qfq{{MYa?g65&$m#1MdI; delta 18 Zcmez1^1+3ZnMs+Qfq{{MV, + clip_stack: Vec, } #[derive(Clone)] @@ -43,6 +50,21 @@ pub enum PietGpuBrush { Gradient, } +#[derive(Default)] +struct State { + /// The transform relative to the parent state. + transform: Affine, + n_clip: usize, +} + +struct ClipElement { + /// Index of BeginClip element in element vec, for bbox fixup. + begin_ix: usize, + bbox: Option, + /// The transform relative to the next clip element on the stack. + transform: Affine, +} + const TOLERANCE: f64 = 0.25; impl PietGpuRenderContext { @@ -58,6 +80,9 @@ impl PietGpuRenderContext { stroke_width, path_count: 0, pathseg_count: 0, + cur_transform: Affine::default(), + state_stack: Vec::new(), + clip_stack: Vec::new(), } } @@ -96,17 +121,19 @@ impl RenderContext for PietGpuRenderContext { fn clear(&mut self, _color: Color) {} fn stroke(&mut self, shape: impl Shape, brush: &impl IntoBrush, width: f64) { - let width = width as f32; - if self.stroke_width != width { + let width_f32 = width as f32; + if self.stroke_width != width_f32 { self.elements - .push(Element::SetLineWidth(SetLineWidth { width })); - self.stroke_width = width; + .push(Element::SetLineWidth(SetLineWidth { width: width_f32 })); + self.stroke_width = width_f32; } let brush = brush.make_brush(self, || shape.bounding_box()).into_owned(); - let path = shape.path_elements(TOLERANCE); - self.encode_path(path, false); match brush { PietGpuBrush::Solid(rgba_color) => { + // Note: the bbox contribution of stroke becomes more complicated with miter joins. + 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)); self.path_count += 1; @@ -126,21 +153,34 @@ impl RenderContext for PietGpuRenderContext { fn fill(&mut self, shape: impl Shape, brush: &impl IntoBrush) { let brush = brush.make_brush(self, || shape.bounding_box()).into_owned(); - let path = shape.path_elements(TOLERANCE); - self.encode_path(path, true); - match brush { - PietGpuBrush::Solid(rgba_color) => { - let fill = Fill { rgba_color }; - self.elements.push(Element::Fill(fill)); - self.path_count += 1; - } - _ => (), + if let PietGpuBrush::Solid(rgba_color) = brush { + // Note: we might get a good speedup from using an approximate bounding box. + // Perhaps that should be added to kurbo. + 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)); + self.path_count += 1; } } fn fill_even_odd(&mut self, _shape: impl Shape, _brush: &impl IntoBrush) {} - fn clip(&mut self, _shape: impl Shape) {} + fn clip(&mut self, shape: impl Shape) { + let begin_ix = self.elements.len(); + let path = shape.path_elements(TOLERANCE); + self.encode_path(path, true); + self.elements.push(Element::BeginClip(BeginClip { + bbox: Default::default(), + })); + self.clip_stack.push(ClipElement { + bbox: None, + begin_ix, + transform: Affine::default(), + }); + self.path_count += 1; + } fn text(&mut self) -> &mut Self::Text { &mut self.inner_text @@ -149,15 +189,42 @@ impl RenderContext for PietGpuRenderContext { fn draw_text(&mut self, _layout: &Self::TextLayout, _pos: impl Into) {} fn save(&mut self) -> Result<(), Error> { + self.state_stack.push(Default::default()); Ok(()) } + fn restore(&mut self) -> Result<(), Error> { - Ok(()) + if let Some(state) = self.state_stack.pop() { + if state.transform != Affine::default() { + let a_inv = state.transform.inverse(); + self.elements + .push(Element::Transform(to_scene_transform(a_inv))); + self.cur_transform *= a_inv; + } + for _ in 0..state.n_clip { + self.pop_clip(); + } + Ok(()) + } else { + Err(Error::StackUnbalance) + } } + fn finish(&mut self) -> Result<(), Error> { Ok(()) } - fn transform(&mut self, _transform: Affine) {} + + fn transform(&mut self, transform: Affine) { + self.elements + .push(Element::Transform(to_scene_transform(transform))); + if let Some(tos) = self.state_stack.last_mut() { + tos.transform *= transform; + } + if let Some(tos) = self.clip_stack.last_mut() { + tos.transform *= transform; + } + self.cur_transform *= transform; + } fn make_image( &mut self, @@ -189,7 +256,13 @@ impl RenderContext for PietGpuRenderContext { fn blurred_rect(&mut self, _rect: Rect, _blur_radius: f64, _brush: &impl IntoBrush) {} fn current_transform(&self) -> Affine { - Default::default() + self.cur_transform + } + + fn with_save(&mut self, f: impl FnOnce(&mut Self) -> Result<(), Error>) -> Result<(), Error> { + self.save()?; + // Always try to restore the stack, even if `f` errored. + f(self).and(self.restore()) } } @@ -316,6 +389,33 @@ impl PietGpuRenderContext { } } } + + fn pop_clip(&mut self) { + let tos = self.clip_stack.pop().unwrap(); + let delta = (self.elements.len() - tos.begin_ix).try_into().unwrap(); + self.elements.push(Element::EndClip(EndClip { delta })); + self.path_count += 1; + if let Some(bbox) = tos.bbox { + if let Element::BeginClip(begin_clip) = &mut self.elements[tos.begin_ix] { + begin_clip.bbox = rect_to_f32_4(bbox); + } else { + unreachable!("expected BeginClip, not found"); + } + self.accumulate_bbox(|| bbox); + } + } + + fn accumulate_bbox(&mut self, f: impl FnOnce() -> Rect) { + if let Some(tos) = self.clip_stack.last_mut() { + let bbox = f(); + let bbox = tos.transform.transform_rect_bbox(bbox); + tos.bbox = if let Some(old_bbox) = tos.bbox { + Some(old_bbox.union(bbox)) + } else { + Some(bbox) + }; + } + } } impl Text for PietGpuText { @@ -410,3 +510,15 @@ impl IntoBrush for PietGpuBrush { fn to_f32_2(point: Point) -> [f32; 2] { [point.x as f32, point.y as f32] } + +fn rect_to_f32_4(rect: Rect) -> [f32; 4] { + [rect.x0 as f32, rect.y0 as f32, rect.x1 as f32, rect.y1 as f32] +} + +fn to_scene_transform(transform: Affine) -> Transform { + let c = transform.as_coeffs(); + Transform { + mat: [c[0] as f32, c[1] as f32, c[2] as f32, c[3] as f32], + translate: [c[4] as f32, c[5] as f32], + } +} From d14895b107b6ad8e7551ff3d63c4e671d01f0fcd Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Fri, 20 Nov 2020 09:26:02 -0800 Subject: [PATCH 2/4] Continuing work on clips I realized there's a problem with encoding clip bboxes relative to the current transform (see #36 for a more detailed explanation), so this is changing it to absolute bboxes. This more or less gets clips working. There are optimization opportunities (all-clear and all-opaque mask tiles), and it doesn't deal with overflow of the blend stack, but it seems to basically work. --- piet-gpu-types/src/annotated.rs | 8 ++-- piet-gpu-types/src/bins.rs | 3 -- piet-gpu-types/src/ptcl.rs | 6 +++ piet-gpu-types/src/scene.rs | 13 ++---- piet-gpu/bin/cli.rs | 36 +++++++++++++- piet-gpu/shader/annotated.h | 48 +++++++++---------- piet-gpu/shader/backdrop.comp | 1 + piet-gpu/shader/backdrop.spv | Bin 7256 -> 7264 bytes piet-gpu/shader/binning.comp | 12 ++--- piet-gpu/shader/binning.spv | Bin 10152 -> 9872 bytes piet-gpu/shader/bins.h | 6 +-- piet-gpu/shader/coarse.comp | 64 ++++++++++++------------- piet-gpu/shader/coarse.spv | Bin 37332 -> 36028 bytes piet-gpu/shader/elements.comp | 19 ++++++++ piet-gpu/shader/elements.spv | Bin 57980 -> 62548 bytes piet-gpu/shader/kernel4.comp | 10 +++- piet-gpu/shader/kernel4.spv | Bin 30140 -> 31800 bytes piet-gpu/shader/ptcl.h | 47 +++++++++++++++++-- piet-gpu/shader/scene.h | 44 +++++------------- piet-gpu/shader/tile_alloc.comp | 11 ++++- piet-gpu/shader/tile_alloc.spv | Bin 8816 -> 8948 bytes piet-gpu/src/lib.rs | 21 ++++++--- piet-gpu/src/render_ctx.rs | 80 +++++++++++++++++++++----------- 23 files changed, 268 insertions(+), 161 deletions(-) diff --git a/piet-gpu-types/src/annotated.rs b/piet-gpu-types/src/annotated.rs index 04f2111..cedbd3f 100644 --- a/piet-gpu-types/src/annotated.rs +++ b/piet-gpu-types/src/annotated.rs @@ -4,16 +4,18 @@ piet_gpu! { #[gpu_write] mod annotated { struct AnnoFill { - rgba_color: u32, + // The bbox is always first, as we take advantage of common + // layout when binning. bbox: [f32; 4], + rgba_color: u32, } struct AnnoFillMask { - mask: f32, bbox: [f32; 4], + mask: f32, } struct AnnoStroke { - rgba_color: u32, bbox: [f32; 4], + rgba_color: u32, // For the nonuniform scale case, this needs to be a 2x2 matrix. // That's expected to be uncommon, so we could special-case it. linewidth: f32, diff --git a/piet-gpu-types/src/bins.rs b/piet-gpu-types/src/bins.rs index 1ac2413..88f16f1 100644 --- a/piet-gpu-types/src/bins.rs +++ b/piet-gpu-types/src/bins.rs @@ -7,9 +7,6 @@ piet_gpu! { mod bins { struct BinInstance { element_ix: u32, - // Right edge of the bounding box of the associated fill - // element; used in backdrop computation. - right_edge: f32, } struct BinChunk { diff --git a/piet-gpu-types/src/ptcl.rs b/piet-gpu-types/src/ptcl.rs index d05218b..86e4572 100644 --- a/piet-gpu-types/src/ptcl.rs +++ b/piet-gpu-types/src/ptcl.rs @@ -34,6 +34,11 @@ piet_gpu! { tile_ref: u32, backdrop: i32, } + // This is mostly here for expedience and can always be optimized + // out for pure clips, but will be useful for blend groups. + struct CmdBeginSolidClip { + alpha: f32, + } struct CmdEndClip { // This will be 1.0 for clips, but we can imagine blend groups. alpha: f32, @@ -55,6 +60,7 @@ piet_gpu! { FillMask(CmdFillMask), FillMaskInv(CmdFillMask), BeginClip(CmdBeginClip), + BeginSolidClip(CmdBeginSolidClip), EndClip(CmdEndClip), Stroke(CmdStroke), Solid(CmdSolid), diff --git a/piet-gpu-types/src/scene.rs b/piet-gpu-types/src/scene.rs index 7e2fb43..855b500 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::{ - BeginClip, CubicSeg, Element, EndClip, Fill, LineSeg, QuadSeg, SetLineWidth, Stroke, Transform, + Clip, CubicSeg, Element, Fill, LineSeg, QuadSeg, SetLineWidth, Stroke, Transform, }; piet_gpu! { @@ -38,15 +38,10 @@ piet_gpu! { mat: [f32; 4], translate: [f32; 2], } - struct BeginClip { + struct Clip { bbox: [f32; 4], // TODO: add alpha? } - struct EndClip { - // The delta between the BeginClip and EndClip element indices. - // It is stored as a delta to facilitate binary string concatenation. - delta: u32, - } enum Element { Nop, // Another approach to encoding would be to use a single @@ -66,8 +61,8 @@ piet_gpu! { Transform(Transform), FillMask(FillMask), FillMaskInv(FillMask), - BeginClip(BeginClip), - EndClip(EndClip), + BeginClip(Clip), + EndClip(Clip), } } } diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index 7876f9e..dfed520 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -165,6 +165,38 @@ fn trace_ptcl(buf: &[u32]) { } } } + 6 => { + let backdrop = buf[tile_offset / 4 + 2]; + println!(" {:x}: begin_clip {}", tile_offset, backdrop); + let mut seg_chunk = buf[tile_offset / 4 + 1] as usize; + let n = buf[seg_chunk / 4] as usize; + let segs = buf[seg_chunk / 4 + 2] as usize; + println!(" chunk @{:x}: n={}, segs @{:x}", seg_chunk, n, segs); + for i in 0..n { + let x0 = f32::from_bits(buf[segs / 4 + i * 5]); + let y0 = f32::from_bits(buf[segs / 4 + i * 5 + 1]); + let x1 = f32::from_bits(buf[segs / 4 + i * 5 + 2]); + let y1 = f32::from_bits(buf[segs / 4 + i * 5 + 3]); + let y_edge = f32::from_bits(buf[segs / 4 + i * 5 + 4]); + println!( + " ({:.3}, {:.3}) - ({:.3}, {:.3}) | {:.3}", + x0, y0, x1, y1, y_edge + ); + } + loop { + seg_chunk = buf[seg_chunk / 4 + 1] as usize; + if seg_chunk == 0 { + break; + } + } + } + 7 => { + let backdrop = buf[tile_offset / 4 + 1]; + println!("{:x}: solid_clip {:x}", tile_offset, backdrop); + } + 8 => { + println!("{:x}: end_clip", tile_offset); + } _ => { println!("{:x}: {}", tile_offset, tag); } @@ -246,9 +278,9 @@ fn main() -> Result<(), Error> { /* let mut data: Vec = Default::default(); - device.read_buffer(&renderer.tile_buf, &mut data).unwrap(); + renderer.tile_buf.read(&mut data).unwrap(); piet_gpu::dump_k1_data(&data); - //trace_ptcl(&data); + trace_ptcl(&data); */ let mut img_data: Vec = Default::default(); diff --git a/piet-gpu/shader/annotated.h b/piet-gpu/shader/annotated.h index 986eff2..677e473 100644 --- a/piet-gpu/shader/annotated.h +++ b/piet-gpu/shader/annotated.h @@ -21,8 +21,8 @@ struct AnnotatedRef { }; struct AnnoFill { - uint rgba_color; vec4 bbox; + uint rgba_color; }; #define AnnoFill_size 20 @@ -32,8 +32,8 @@ AnnoFillRef AnnoFill_index(AnnoFillRef ref, uint index) { } struct AnnoFillMask { - float mask; vec4 bbox; + float mask; }; #define AnnoFillMask_size 20 @@ -43,8 +43,8 @@ AnnoFillMaskRef AnnoFillMask_index(AnnoFillMaskRef ref, uint index) { } struct AnnoStroke { - uint rgba_color; vec4 bbox; + uint rgba_color; float linewidth; }; @@ -85,18 +85,18 @@ AnnoFill AnnoFill_read(AnnoFillRef ref) { uint raw3 = annotated[ix + 3]; uint raw4 = annotated[ix + 4]; AnnoFill s; - s.rgba_color = raw0; - s.bbox = vec4(uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3), uintBitsToFloat(raw4)); + s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.rgba_color = raw4; return s; } void AnnoFill_write(AnnoFillRef ref, AnnoFill s) { uint ix = ref.offset >> 2; - annotated[ix + 0] = s.rgba_color; - annotated[ix + 1] = floatBitsToUint(s.bbox.x); - annotated[ix + 2] = floatBitsToUint(s.bbox.y); - annotated[ix + 3] = floatBitsToUint(s.bbox.z); - annotated[ix + 4] = floatBitsToUint(s.bbox.w); + annotated[ix + 0] = floatBitsToUint(s.bbox.x); + annotated[ix + 1] = floatBitsToUint(s.bbox.y); + annotated[ix + 2] = floatBitsToUint(s.bbox.z); + annotated[ix + 3] = floatBitsToUint(s.bbox.w); + annotated[ix + 4] = s.rgba_color; } AnnoFillMask AnnoFillMask_read(AnnoFillMaskRef ref) { @@ -107,18 +107,18 @@ AnnoFillMask AnnoFillMask_read(AnnoFillMaskRef ref) { uint raw3 = annotated[ix + 3]; uint raw4 = annotated[ix + 4]; AnnoFillMask s; - s.mask = uintBitsToFloat(raw0); - s.bbox = vec4(uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3), uintBitsToFloat(raw4)); + s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.mask = uintBitsToFloat(raw4); return s; } void AnnoFillMask_write(AnnoFillMaskRef ref, AnnoFillMask s) { uint ix = ref.offset >> 2; - annotated[ix + 0] = floatBitsToUint(s.mask); - annotated[ix + 1] = floatBitsToUint(s.bbox.x); - annotated[ix + 2] = floatBitsToUint(s.bbox.y); - annotated[ix + 3] = floatBitsToUint(s.bbox.z); - annotated[ix + 4] = floatBitsToUint(s.bbox.w); + annotated[ix + 0] = floatBitsToUint(s.bbox.x); + annotated[ix + 1] = floatBitsToUint(s.bbox.y); + annotated[ix + 2] = floatBitsToUint(s.bbox.z); + annotated[ix + 3] = floatBitsToUint(s.bbox.w); + annotated[ix + 4] = floatBitsToUint(s.mask); } AnnoStroke AnnoStroke_read(AnnoStrokeRef ref) { @@ -130,19 +130,19 @@ AnnoStroke AnnoStroke_read(AnnoStrokeRef ref) { uint raw4 = annotated[ix + 4]; uint raw5 = annotated[ix + 5]; AnnoStroke s; - s.rgba_color = raw0; - s.bbox = vec4(uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3), uintBitsToFloat(raw4)); + s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.rgba_color = raw4; s.linewidth = uintBitsToFloat(raw5); return s; } void AnnoStroke_write(AnnoStrokeRef ref, AnnoStroke s) { uint ix = ref.offset >> 2; - annotated[ix + 0] = s.rgba_color; - annotated[ix + 1] = floatBitsToUint(s.bbox.x); - annotated[ix + 2] = floatBitsToUint(s.bbox.y); - annotated[ix + 3] = floatBitsToUint(s.bbox.z); - annotated[ix + 4] = floatBitsToUint(s.bbox.w); + annotated[ix + 0] = floatBitsToUint(s.bbox.x); + annotated[ix + 1] = floatBitsToUint(s.bbox.y); + annotated[ix + 2] = floatBitsToUint(s.bbox.z); + annotated[ix + 3] = floatBitsToUint(s.bbox.w); + annotated[ix + 4] = s.rgba_color; annotated[ix + 5] = floatBitsToUint(s.linewidth); } diff --git a/piet-gpu/shader/backdrop.comp b/piet-gpu/shader/backdrop.comp index 20c6ce9..6fa9f9f 100644 --- a/piet-gpu/shader/backdrop.comp +++ b/piet-gpu/shader/backdrop.comp @@ -57,6 +57,7 @@ void main() { case Annotated_Fill: case Annotated_FillMask: case Annotated_FillMaskInv: + case Annotated_BeginClip: PathRef path_ref = PathRef(element_ix * Path_size); Path path = Path_read(path_ref); sh_row_width[th_ix] = path.bbox.z - path.bbox.x; diff --git a/piet-gpu/shader/backdrop.spv b/piet-gpu/shader/backdrop.spv index 7f0852dadb86d116244ff9b7433f8e417daef0f5..ee4dda8b01420e16d40b3ec7f84eb3bcdddcfc4d 100644 GIT binary patch delta 20 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 hX3%1NE&thPnPw{`e%K! z2YO|tXK>aVCd&q7wIy9Y|Frp&I*ZMdrW|�Xt_^r8r}D$@*k9=xStnW1&sQO-MH~ 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 diff --git a/piet-gpu/shader/bins.h b/piet-gpu/shader/bins.h index 85f7536..3ce06e0 100644 --- a/piet-gpu/shader/bins.h +++ b/piet-gpu/shader/bins.h @@ -10,10 +10,9 @@ struct BinChunkRef { struct BinInstance { uint element_ix; - float right_edge; }; -#define BinInstance_size 8 +#define BinInstance_size 4 BinInstanceRef BinInstance_index(BinInstanceRef ref, uint index) { return BinInstanceRef(ref.offset + index * BinInstance_size); @@ -33,17 +32,14 @@ BinChunkRef BinChunk_index(BinChunkRef ref, uint index) { BinInstance BinInstance_read(BinInstanceRef ref) { uint ix = ref.offset >> 2; uint raw0 = bins[ix + 0]; - uint raw1 = bins[ix + 1]; BinInstance s; s.element_ix = raw0; - s.right_edge = uintBitsToFloat(raw1); return s; } void BinInstance_write(BinInstanceRef ref, BinInstance s) { uint ix = ref.offset >> 2; bins[ix + 0] = s.element_ix; - bins[ix + 1] = floatBitsToUint(s.right_edge); } BinChunk BinChunk_read(BinChunkRef ref) { diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 16573e8..12ec348 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -45,7 +45,6 @@ layout(set = 0, binding = 4) buffer PtclBuf { #define N_PART_READ (1 << LG_N_PART_READ) shared uint sh_elements[N_TILE]; -shared float sh_right_edge[N_TILE]; // Number of elements in the partition; prefix sum. shared uint sh_part_count[N_PART_READ]; @@ -148,7 +147,6 @@ void main() { BinInstanceRef inst_ref = BinInstanceRef(sh_part_elements[part_ix]); BinInstance inst = BinInstance_read(BinInstance_index(inst_ref, ix)); sh_elements[th_ix] = inst.element_ix; - sh_right_edge[th_ix] = inst.right_edge; } barrier(); @@ -161,10 +159,8 @@ void main() { uint tag = Annotated_Nop; uint element_ix; AnnotatedRef ref; - float right_edge = 0.0; if (th_ix + rd_ix < wr_ix) { element_ix = sh_elements[th_ix]; - right_edge = sh_right_edge[th_ix]; ref = AnnotatedRef(element_ix * Annotated_size); tag = Annotated_tag(ref); } @@ -173,13 +169,11 @@ void main() { uint tile_count; switch (tag) { case Annotated_Fill: - case Annotated_FillMask: - case Annotated_FillMaskInv: case Annotated_Stroke: - // Because the only elements we're processing right now are - // paths, we can just use the element index as the path index. - // In future, when we're doing a bunch of stuff, the path index - // should probably be stored in the annotated element. + case Annotated_BeginClip: + case Annotated_EndClip: + // We have one "path" for each element, even if the element isn't + // actually a path (currently EndClip, but images etc in the future). uint path_ix = element_ix; Path path = Path_read(PathRef(path_ix * Path_size)); uint stride = path.bbox.z - path.bbox.x; @@ -224,20 +218,23 @@ void main() { el_ix = probe; } } - AnnotatedRef ref = AnnotatedRef(el_ix * Annotated_size); + AnnotatedRef ref = AnnotatedRef(sh_elements[el_ix] * Annotated_size); uint tag = Annotated_tag(ref); uint seq_ix = ix - (el_ix > 0 ? sh_tile_count[el_ix - 1] : 0); uint width = sh_tile_width[el_ix]; uint x = sh_tile_x0[el_ix] + seq_ix % width; uint y = sh_tile_y0[el_ix] + seq_ix / width; - Tile tile = Tile_read(TileRef(sh_tile_base[el_ix] + (sh_tile_stride[el_ix] * y + x) * Tile_size)); - // Include the path in the tile if - // - the tile contains at least a segment (tile offset non-zero) - // - the tile is completely covered (backdrop non-zero) - // - the tile is not covered and we're filling everything outside the path (backdrop zero, inverse fills). - bool inside = tile.backdrop != 0; - bool fill = tag != Annotated_FillMaskInv; - if (tile.tile.offset != 0 || inside == fill) { + bool include_tile; + if (tag == Annotated_BeginClip || tag == Annotated_EndClip) { + include_tile = true; + } else { + Tile tile = Tile_read(TileRef(sh_tile_base[el_ix] + (sh_tile_stride[el_ix] * y + x) * Tile_size)); + // Include the path in the tile if + // - the tile contains at least a segment (tile offset non-zero) + // - the tile is completely covered (backdrop non-zero) + include_tile = tile.backdrop != 0 || tile.tile.offset != 0; + } + if (include_tile) { uint el_slice = el_ix / 32; uint el_mask = 1 << (el_ix & 31); atomicOr(sh_bitmaps[el_slice][y * N_TILE_X + x], el_mask); @@ -247,8 +244,7 @@ void main() { barrier(); // Output non-segment elements for this tile. The thread does a sequential walk - // through the non-segment elements, and for segments, count and backdrop are - // aggregated using bit counting. + // through the non-segment elements. uint slice_ix = 0; uint bitmap = sh_bitmaps[0][th_ix]; while (true) { @@ -291,27 +287,27 @@ void main() { } cmd_ref.offset += Cmd_size; break; - case Annotated_FillMask: - case Annotated_FillMaskInv: + case Annotated_BeginClip: tile = Tile_read(TileRef(sh_tile_base[element_ref_ix] + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); - AnnoFillMask fill_mask = Annotated_FillMask_read(ref); alloc_cmd(cmd_ref, cmd_limit); if (tile.tile.offset != 0) { - CmdFillMask cmd_fill; - cmd_fill.tile_ref = tile.tile.offset; - cmd_fill.backdrop = tile.backdrop; - cmd_fill.mask = fill_mask.mask; - if (tag == Annotated_FillMask) { - Cmd_FillMask_write(cmd_ref, cmd_fill); - } else { - Cmd_FillMaskInv_write(cmd_ref, cmd_fill); - } + CmdBeginClip cmd_begin_clip; + cmd_begin_clip.tile_ref = tile.tile.offset; + cmd_begin_clip.backdrop = tile.backdrop; + Cmd_BeginClip_write(cmd_ref, cmd_begin_clip); } else { - Cmd_SolidMask_write(cmd_ref, CmdSolidMask(fill_mask.mask)); + // TODO: here is where a bunch of optimization magic should happen + float alpha = tile.backdrop == 0 ? 0.0 : 1.0; + Cmd_BeginSolidClip_write(cmd_ref, CmdBeginSolidClip(alpha)); } cmd_ref.offset += Cmd_size; break; + case Annotated_EndClip: + alloc_cmd(cmd_ref, cmd_limit); + Cmd_EndClip_write(cmd_ref, CmdEndClip(1.0)); + cmd_ref.offset += Cmd_size; + break; case Annotated_Stroke: tile = Tile_read(TileRef(sh_tile_base[element_ref_ix] + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index 17bae646ea923caa058b081b9f19ef0943d5794a..436ac1a0542e4deaf176301bd29aaf4cfc57f54f 100644 GIT binary patch literal 36028 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̇WwX>T39e;XiBybzFDCd*-OEx))COja(iAvjpJw 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 LXXed1`{Vxsjw7or diff --git a/piet-gpu/shader/elements.comp b/piet-gpu/shader/elements.comp index 8bb9a4d..7606554 100644 --- a/piet-gpu/shader/elements.comp +++ b/piet-gpu/shader/elements.comp @@ -132,9 +132,13 @@ State map_element(ElementRef ref) { case Element_FillMask: case Element_FillMaskInv: case Element_Stroke: + case Element_BeginClip: c.flags = FLAG_RESET_BBOX; c.path_count = 1; break; + case Element_EndClip: + c.path_count = 1; + break; case Element_SetLineWidth: SetLineWidth lw = Element_SetLineWidth_read(ref); c.linewidth = lw.width; @@ -421,6 +425,21 @@ void main() { out_ref = AnnotatedRef((st.path_count - 1) * Annotated_size); Annotated_FillMaskInv_write(out_ref, anno_fill_mask); break; + case Element_BeginClip: + Clip begin_clip = Element_BeginClip_read(this_ref); + AnnoClip anno_begin_clip = AnnoClip(begin_clip.bbox); + // This is the absolute bbox, it's been transformed during encoding. + anno_begin_clip.bbox = begin_clip.bbox; + out_ref = AnnotatedRef((st.path_count - 1) * Annotated_size); + Annotated_BeginClip_write(out_ref, anno_begin_clip); + break; + case Element_EndClip: + Clip end_clip = Element_EndClip_read(this_ref); + // This bbox is expected to be the same as the begin one. + AnnoClip anno_end_clip = AnnoClip(end_clip.bbox); + out_ref = AnnotatedRef((st.path_count - 1) * Annotated_size); + Annotated_EndClip_write(out_ref, anno_end_clip); + break; } } } diff --git a/piet-gpu/shader/elements.spv b/piet-gpu/shader/elements.spv index fed60fbd9d3ef3f21f09eb3ce043034ac95b0675..73ff3f4b5b3b827ec48d85e8beee9758203ea656 100644 GIT binary patch literal 62548 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 literal 57980 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 P@=RcL=jR&AUBmweobIbX diff --git a/piet-gpu/shader/kernel4.comp b/piet-gpu/shader/kernel4.comp index 919d120..a7d5e92 100644 --- a/piet-gpu/shader/kernel4.comp +++ b/piet-gpu/shader/kernel4.comp @@ -149,12 +149,20 @@ void main() { } blend_sp++; break; + case Cmd_BeginSolidClip: + CmdBeginSolidClip begin_solid_clip = Cmd_BeginSolidClip_read(cmd_ref); + float solid_alpha = begin_solid_clip.alpha; + for (uint k = 0; k < CHUNK; k++) { + blend_stack[blend_sp][k] = packUnorm4x8(vec4(rgb[k], solid_alpha)); + } + blend_sp++; + break; case Cmd_EndClip: CmdEndClip end_clip = Cmd_EndClip_read(cmd_ref); blend_sp--; for (uint k = 0; k < CHUNK; k++) { vec4 rgba = unpackUnorm4x8(blend_stack[blend_sp][k]); - rgb[k] = mix(rgb[k], rgba.rgb, end_clip.alpha * rgba.a); + rgb[k] = mix(rgba.rgb, rgb[k], end_clip.alpha * rgba.a); } break; case Cmd_Solid: diff --git a/piet-gpu/shader/kernel4.spv b/piet-gpu/shader/kernel4.spv index 0d3858159e8435dce93ded58eaa62665d5a970c1..a02387a44179da376d0a018757f2cfac88fd55e6 100644 GIT binary patch literal 31800 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 literal 30140 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 2; + uint raw0 = ptcl[ix + 0]; + CmdBeginSolidClip s; + s.alpha = uintBitsToFloat(raw0); + return s; +} + +void CmdBeginSolidClip_write(CmdBeginSolidClipRef ref, CmdBeginSolidClip s) { + uint ix = ref.offset >> 2; + ptcl[ix + 0] = floatBitsToUint(s.alpha); +} + CmdEndClip CmdEndClip_read(CmdEndClipRef ref) { uint ix = ref.offset >> 2; uint raw0 = ptcl[ix + 0]; @@ -398,6 +426,10 @@ CmdBeginClip Cmd_BeginClip_read(CmdRef ref) { return CmdBeginClip_read(CmdBeginClipRef(ref.offset + 4)); } +CmdBeginSolidClip Cmd_BeginSolidClip_read(CmdRef ref) { + return CmdBeginSolidClip_read(CmdBeginSolidClipRef(ref.offset + 4)); +} + CmdEndClip Cmd_EndClip_read(CmdRef ref) { return CmdEndClip_read(CmdEndClipRef(ref.offset + 4)); } @@ -452,6 +484,11 @@ void Cmd_BeginClip_write(CmdRef ref, CmdBeginClip s) { CmdBeginClip_write(CmdBeginClipRef(ref.offset + 4), s); } +void Cmd_BeginSolidClip_write(CmdRef ref, CmdBeginSolidClip s) { + ptcl[ref.offset >> 2] = Cmd_BeginSolidClip; + CmdBeginSolidClip_write(CmdBeginSolidClipRef(ref.offset + 4), s); +} + void Cmd_EndClip_write(CmdRef ref, CmdEndClip s) { ptcl[ref.offset >> 2] = Cmd_EndClip; CmdEndClip_write(CmdEndClipRef(ref.offset + 4), s); diff --git a/piet-gpu/shader/scene.h b/piet-gpu/shader/scene.h index 0a4a2ce..6558ad3 100644 --- a/piet-gpu/shader/scene.h +++ b/piet-gpu/shader/scene.h @@ -32,11 +32,7 @@ struct TransformRef { uint offset; }; -struct BeginClipRef { - uint offset; -}; - -struct EndClipRef { +struct ClipRef { uint offset; }; @@ -131,24 +127,14 @@ TransformRef Transform_index(TransformRef ref, uint index) { return TransformRef(ref.offset + index * Transform_size); } -struct BeginClip { +struct Clip { vec4 bbox; }; -#define BeginClip_size 16 +#define Clip_size 16 -BeginClipRef BeginClip_index(BeginClipRef ref, uint index) { - return BeginClipRef(ref.offset + index * BeginClip_size); -} - -struct EndClip { - uint clip_size; -}; - -#define EndClip_size 4 - -EndClipRef EndClip_index(EndClipRef ref, uint index) { - return EndClipRef(ref.offset + index * EndClip_size); +ClipRef Clip_index(ClipRef ref, uint index) { + return ClipRef(ref.offset + index * Clip_size); } #define Element_Nop 0 @@ -263,25 +249,17 @@ Transform Transform_read(TransformRef ref) { return s; } -BeginClip BeginClip_read(BeginClipRef ref) { +Clip Clip_read(ClipRef ref) { uint ix = ref.offset >> 2; uint raw0 = scene[ix + 0]; uint raw1 = scene[ix + 1]; uint raw2 = scene[ix + 2]; uint raw3 = scene[ix + 3]; - BeginClip s; + Clip s; s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); return s; } -EndClip EndClip_read(EndClipRef ref) { - uint ix = ref.offset >> 2; - uint raw0 = scene[ix + 0]; - EndClip s; - s.clip_size = raw0; - return s; -} - uint Element_tag(ElementRef ref) { return scene[ref.offset >> 2]; } @@ -334,11 +312,11 @@ FillMask Element_FillMaskInv_read(ElementRef ref) { return FillMask_read(FillMaskRef(ref.offset + 4)); } -BeginClip Element_BeginClip_read(ElementRef ref) { - return BeginClip_read(BeginClipRef(ref.offset + 4)); +Clip Element_BeginClip_read(ElementRef ref) { + return Clip_read(ClipRef(ref.offset + 4)); } -EndClip Element_EndClip_read(ElementRef ref) { - return EndClip_read(EndClipRef(ref.offset + 4)); +Clip Element_EndClip_read(ElementRef ref) { + return Clip_read(ClipRef(ref.offset + 4)); } diff --git a/piet-gpu/shader/tile_alloc.comp b/piet-gpu/shader/tile_alloc.comp index 3e1e52f..de6e827 100644 --- a/piet-gpu/shader/tile_alloc.comp +++ b/piet-gpu/shader/tile_alloc.comp @@ -50,8 +50,10 @@ void main() { case Annotated_FillMask: case Annotated_FillMaskInv: case Annotated_Stroke: - // Note: we take advantage of the fact that fills and strokes - // have compatible layout. + case Annotated_BeginClip: + case Annotated_EndClip: + // Note: we take advantage of the fact that fills, strokes, and + // clips have compatible layout. AnnoFill fill = Annotated_Fill_read(ref); x0 = int(floor(fill.bbox.x * SX)); y0 = int(floor(fill.bbox.y * SY)); @@ -67,6 +69,11 @@ void main() { Path path; path.bbox = uvec4(x0, y0, x1, y1); uint tile_count = (x1 - x0) * (y1 - y0); + if (tag == Annotated_EndClip) { + // Don't actually allocate tiles for an end clip, but we do want + // the path structure (especially bbox) allocated for it. + tile_count = 0; + } sh_tile_count[th_ix] = tile_count; // Prefix sum of sh_tile_count diff --git a/piet-gpu/shader/tile_alloc.spv b/piet-gpu/shader/tile_alloc.spv index 449f4d6874ff4de6782271dbd4f09d1d23ddadf4..93cdea6258378fa92ac0a87e231aa21e644e6d66 100644 GIT binary patch delta 2838 zcmZ{m&2Lp@5QpE>_TEyUmWqmeBUouIs0b(@T0sg3m5)*=P$>1b0+Lj(DJ9Xw2(BJxh-`n2Y zw5~O!EX_~z(#3Dv+p_LbT9D?R@$u>#qB8&FGGyxFcx7y&I$51+^kq9*x+E{ZGB$l_ z@;vmF*QSXnrRG$Ezch=>si%Imv}E)#n9Ag2bzJ=#ca?@L`6CN`l7dXPxCA8hX{^91&OB#+-fx;pj+?Awh`7Ib8Tj^1!n zb)g-6(RS!XdwaoNPP_*Y{?*t6_0IOyokIoItiz2>?K4@{QSa^OX)8foN6Pg>9cQxc z`sW>0ewQxt_dS0bGmEY_)ov2<8S({3b`5t3g`uwn9H(|SGcPiE9jt0WxGSGxAXoJL#E|QnQ7FPd3a*wMG@v->) z+^>$}3&7_1Cf{H?SkB|Wm1-?Xq7^Zj$;CgCd>2U-??oi*02g<%2;1b}lYBj~onWu9 z{9F0%?*+?W%Ntk>Hjn%b_Mh$t_&8#n_aSmNBaY$^fQ!YKU>A!&h;4ECD83Xd{|?1H zie+H)$TR*_^I|VY9KX&ewj$@lUWx6Qy`3jog)L`X+yOa{A(~g;ZIY|;v66Bqj_2FPJ)MVI^K8_9>JFD zLVOdt(LNrNzV(QljffrRR{W2GzeM*U(OfT_n|n8(-Ue*Bg*ASD;nntRHiBKe1MH1A z$j*Dv`s_Rks_!=1$MLzwwSH){qmd`TACWtn9oz(Ax7|ntJqaftugqq!3+0`}Rj<3z`-H)h*bF;8TEaqJF#aAVK>PWO3Y>csfoVCOEJlMI0y$5Ve9B#k2 z%Fm0AxQ=_lF6>8+vU~sI*awz(NBhw)AV0&$mDM)JLd)O}g0G@&>Ht_<-zvBl!LHRe z;H{F_&b?}aJOo~ccF_RhMB>2IG18-8M|uQ_-3@}}V|T~Ea=Qq%*kQ0-#2*LO9M5<8 zT|WVq^nGx_5Lhl2oCJH;`i8;UHh2o{k*WRIkD$*WKKjq*es$y;1(V~>SLGap9mZ82 z0~;joCDcBRnA~FGNcIxgX2Y(4Z7_~>9BhpIZYJ%^KLLI-P0YPd2DmH9xNPpgLh4BT zGPs!dJht3z62?^Iic=YBv@QR-spfaSjj$o)7!oOugSR2!KLM5t|1h|(;6DkL3;!u_ zf5CqmEO(S|m4B~|Ak-X5CU&s_UL*ggJBth=epBpU+Xa#Q9QYt&ynnZhA;!nO*FM+y PVAaRX*=T*^+CBdQ><$&= delta 2711 zcmZ{m%WqXx6o>a|dv9rLEwy6hQOc_V7M}>xR%#0c1r>R;yi2_$Mw?WwDJ9Xw@RPDyLGD&v4DpSKmpgF`kXK4gGe+8yOdfjnHHM3oVL`jmHKD5!&DX*s~6`kFISe9i7KV1;F_tIugot{vy|G> z6x`dDrOI2)?6!39KXxe{GN-y+S@h-r4xR{nKn< zn5tVbrxB9V2J8!HHcrUZj=8LM3#e}$uG>JfHj;&=GaQzPOTLM zyF(iZ{|4|s_^n6&?z9CW3ho2Dwt>8WYd@SP?4W!dyMleIakHx@tGI5%tu=&p&_&xJ z7wu;X_FCc{c=!jg9eCK*J5=xO9%Q(a-M6!>t3KAVy}sNtolV!j>#6cPveMt{{x(+k ze%(_0lbnx`z4nO`R_r#!M}H~z ztD{#rOSNW^uV{9IK!*<5JdisS(QZL3W^wTk@Ds!;9#jAxfpRY?*$i|KY(3q-iK{-`Dp$ixUa^~ z2Q>E@t^!*~M~wd=u;bSk&HBOeVLuG^$llNEtj3lz&STOi=P|_M)lRZDfm11tat+Sn zZq{NK@5DN67iF9cw2iTWXYO>=vA_Ya(})GG2g@;$OZP64Wbq<-=22h}?1Dbak7*;e z+y=yB+Kl$`nDlKz=if0 zt!_c#L4kySJ@jh>s~A%iDqdxy_N-`eShN@rpbSHc#HIKZ*8n z>-wHRO2j0gkPlE-FIy#grvK(&~C7gh4k%0#t;jMqtLTp3&pM7gDvNz;?|B} zdqMO?-Y8gpyhby>Y7&mo#x}8;W6~F2mGG&f&~sp8jP+}&9lpI_#~k)J*qAu#=fO44 zSw~#Q39yMekFtBe~%vpG180pUVB}sC61_jlDGze8tXSxWc&E zXTb)^dkM9t5sTYQ9L3IoU2NFr!4{7rodp{sKS?)V{tMuD(G_1W1KgElTsC)LBXty> z0~ZUwhArp68!;8R;#6iDSJ!^oQuE~ei+ln(hS=5=?c+$am;vuW{JYSBK8eVM{}gx} z3IA!ZT=>s`rwabFV7RmQ0?t7kE(Dwh%N^xg*NL7*Y-(YX^}F~B=sCp2$Gz8{ZLAvn Mq?LaoZf@HDFPl0J%m4rY diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index dc3c2c8..6eff190 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -7,7 +7,7 @@ pub use render_ctx::PietGpuRenderContext; use rand::{Rng, RngCore}; -use piet::kurbo::{BezPath, Circle, Line, Point, Vec2}; +use piet::kurbo::{BezPath, Circle, Point, Vec2}; use piet::{Color, ImageFormat, RenderContext}; use piet_gpu_types::encoder::Encode; @@ -52,16 +52,23 @@ pub fn render_scene(rc: &mut impl RenderContext) { let circle = Circle::new(center, radius); rc.fill(circle, &color); } - /* + let _ = rc.save(); let mut path = BezPath::new(); - path.move_to((100.0, 1150.0)); - path.line_to((200.0, 1200.0)); - path.line_to((150.0, 1250.0)); + path.move_to((200.0, 150.0)); + path.line_to((100.0, 200.0)); + path.line_to((150.0, 250.0)); + path.close_path(); + rc.clip(path); + + let mut path = BezPath::new(); + path.move_to((100.0, 150.0)); + path.line_to((200.0, 200.0)); + path.line_to((150.0, 250.0)); path.close_path(); rc.fill(path, &Color::rgb8(128, 0, 128)); - */ + let _ = rc.restore(); rc.stroke( - Line::new((100.0, 100.0), (200.0, 150.0)), + piet::kurbo::Line::new((100.0, 100.0), (200.0, 150.0)), &Color::WHITE, 5.0, ); diff --git a/piet-gpu/src/render_ctx.rs b/piet-gpu/src/render_ctx.rs index b3aa895..73d1f27 100644 --- a/piet-gpu/src/render_ctx.rs +++ b/piet-gpu/src/render_ctx.rs @@ -1,9 +1,9 @@ -use std::{borrow::Cow, convert::TryInto, ops::RangeBounds}; +use std::{borrow::Cow, ops::RangeBounds}; use piet_gpu_types::encoder::{Encode, Encoder}; use piet_gpu_types::scene::{ - BeginClip, CubicSeg, Element, EndClip, Fill, LineSeg, QuadSeg, SetLineWidth, Stroke, Transform, + Clip, CubicSeg, Element, Fill, LineSeg, QuadSeg, SetLineWidth, Stroke, Transform, }; use piet::{ @@ -53,6 +53,10 @@ pub enum PietGpuBrush { #[derive(Default)] struct State { /// The transform relative to the parent state. + rel_transform: Affine, + /// The transform at the parent state. + /// + /// This invariant should hold: transform * rel_transform = cur_transform transform: Affine, n_clip: usize, } @@ -61,8 +65,6 @@ struct ClipElement { /// Index of BeginClip element in element vec, for bbox fixup. begin_ix: usize, bbox: Option, - /// The transform relative to the next clip element on the stack. - transform: Affine, } const TOLERANCE: f64 = 0.25; @@ -168,18 +170,20 @@ impl RenderContext for PietGpuRenderContext { fn fill_even_odd(&mut self, _shape: impl Shape, _brush: &impl IntoBrush) {} fn clip(&mut self, shape: impl Shape) { - let begin_ix = self.elements.len(); let path = shape.path_elements(TOLERANCE); self.encode_path(path, true); - self.elements.push(Element::BeginClip(BeginClip { + let begin_ix = self.elements.len(); + self.elements.push(Element::BeginClip(Clip { bbox: Default::default(), })); self.clip_stack.push(ClipElement { bbox: None, begin_ix, - transform: Affine::default(), }); self.path_count += 1; + if let Some(tos) = self.state_stack.last_mut() { + tos.n_clip += 1; + } } fn text(&mut self) -> &mut Self::Text { @@ -189,18 +193,22 @@ impl RenderContext for PietGpuRenderContext { fn draw_text(&mut self, _layout: &Self::TextLayout, _pos: impl Into) {} fn save(&mut self) -> Result<(), Error> { - self.state_stack.push(Default::default()); + self.state_stack.push(State { + rel_transform: Affine::default(), + transform: self.cur_transform, + n_clip: 0, + }); Ok(()) } fn restore(&mut self) -> Result<(), Error> { if let Some(state) = self.state_stack.pop() { - if state.transform != Affine::default() { - let a_inv = state.transform.inverse(); + if state.rel_transform != Affine::default() { + let a_inv = state.rel_transform.inverse(); self.elements .push(Element::Transform(to_scene_transform(a_inv))); - self.cur_transform *= a_inv; } + self.cur_transform = state.transform; for _ in 0..state.n_clip { self.pop_clip(); } @@ -211,6 +219,9 @@ impl RenderContext for PietGpuRenderContext { } fn finish(&mut self) -> Result<(), Error> { + for _ in 0..self.clip_stack.len() { + self.pop_clip(); + } Ok(()) } @@ -218,10 +229,7 @@ impl RenderContext for PietGpuRenderContext { self.elements .push(Element::Transform(to_scene_transform(transform))); if let Some(tos) = self.state_stack.last_mut() { - tos.transform *= transform; - } - if let Some(tos) = self.clip_stack.last_mut() { - tos.transform *= transform; + tos.rel_transform *= transform; } self.cur_transform *= transform; } @@ -392,23 +400,38 @@ impl PietGpuRenderContext { fn pop_clip(&mut self) { let tos = self.clip_stack.pop().unwrap(); - let delta = (self.elements.len() - tos.begin_ix).try_into().unwrap(); - self.elements.push(Element::EndClip(EndClip { delta })); + let bbox = tos.bbox.unwrap_or_default(); + let bbox_f32_4 = rect_to_f32_4(bbox); + self.elements + .push(Element::EndClip(Clip { bbox: bbox_f32_4 })); self.path_count += 1; + if let Element::BeginClip(begin_clip) = &mut self.elements[tos.begin_ix] { + begin_clip.bbox = bbox_f32_4; + } else { + unreachable!("expected BeginClip, not found"); + } if let Some(bbox) = tos.bbox { - if let Element::BeginClip(begin_clip) = &mut self.elements[tos.begin_ix] { - begin_clip.bbox = rect_to_f32_4(bbox); - } else { - unreachable!("expected BeginClip, not found"); - } - self.accumulate_bbox(|| bbox); + self.union_bbox(bbox); } } + /// Accumulate a bbox. + /// + /// The bbox is given lazily as a closure, relative to the current transform. + /// It's lazy because we don't need to compute it unless we're inside a clip. fn accumulate_bbox(&mut self, f: impl FnOnce() -> Rect) { - if let Some(tos) = self.clip_stack.last_mut() { + if !self.clip_stack.is_empty() { let bbox = f(); - let bbox = tos.transform.transform_rect_bbox(bbox); + let bbox = self.cur_transform.transform_rect_bbox(bbox); + self.union_bbox(bbox); + } + } + + /// Accumulate an absolute bbox. + /// + /// The bbox is given already transformed into surface coordinates. + fn union_bbox(&mut self, bbox: Rect) { + if let Some(tos) = self.clip_stack.last_mut() { tos.bbox = if let Some(old_bbox) = tos.bbox { Some(old_bbox.union(bbox)) } else { @@ -512,7 +535,12 @@ fn to_f32_2(point: Point) -> [f32; 2] { } fn rect_to_f32_4(rect: Rect) -> [f32; 4] { - [rect.x0 as f32, rect.y0 as f32, rect.x1 as f32, rect.y1 as f32] + [ + rect.x0 as f32, + rect.y0 as f32, + rect.x1 as f32, + rect.y1 as f32, + ] } fn to_scene_transform(transform: Affine) -> Transform { From 13134e7cb328b385d4ac6e8a4c3239801de33172 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Sat, 21 Nov 2020 07:00:03 -0800 Subject: [PATCH 3/4] Restore FillMask logic Per discussion, don't remove FillMask until we get unbounded clip stacks. --- piet-gpu/shader/coarse.comp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 12ec348..e540614 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -169,6 +169,8 @@ void main() { uint tile_count; switch (tag) { case Annotated_Fill: + case Annotated_FillMask: + case Annotated_FillMaskInv: case Annotated_Stroke: case Annotated_BeginClip: case Annotated_EndClip: @@ -308,6 +310,27 @@ void main() { Cmd_EndClip_write(cmd_ref, CmdEndClip(1.0)); cmd_ref.offset += Cmd_size; break; + case Annotated_FillMask: + case Annotated_FillMaskInv: + tile = Tile_read(TileRef(sh_tile_base[element_ref_ix] + + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); + AnnoFillMask fill_mask = Annotated_FillMask_read(ref); + alloc_cmd(cmd_ref, cmd_limit); + if (tile.tile.offset != 0) { + CmdFillMask cmd_fill; + cmd_fill.tile_ref = tile.tile.offset; + cmd_fill.backdrop = tile.backdrop; + cmd_fill.mask = fill_mask.mask; + if (tag == Annotated_FillMask) { + Cmd_FillMask_write(cmd_ref, cmd_fill); + } else { + Cmd_FillMaskInv_write(cmd_ref, cmd_fill); + } + } else { + Cmd_SolidMask_write(cmd_ref, CmdSolidMask(fill_mask.mask)); + } + cmd_ref.offset += Cmd_size; + break; case Annotated_Stroke: tile = Tile_read(TileRef(sh_tile_base[element_ref_ix] + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); From b928c7a3edf47f83cc85fcc2ad89a58853c6d293 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Sat, 21 Nov 2020 10:47:28 -0800 Subject: [PATCH 4/4] Restore FillMaskInv logic --- piet-gpu/shader/coarse.comp | 4 +++- piet-gpu/shader/coarse.spv | Bin 36028 -> 42212 bytes 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index e540614..12ebb98 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -234,7 +234,9 @@ void main() { // Include the path in the tile if // - the tile contains at least a segment (tile offset non-zero) // - the tile is completely covered (backdrop non-zero) - include_tile = tile.backdrop != 0 || tile.tile.offset != 0; + bool inside = tile.backdrop != 0; + bool fill = tag != Annotated_FillMaskInv; + include_tile = tile.tile.offset != 0 || inside == fill; } if (include_tile) { uint el_slice = el_ix / 32; diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index 436ac1a0542e4deaf176301bd29aaf4cfc57f54f..98dde1b67218125314ac038ecdef8ad4b1dccea4 100644 GIT binary patch literal 42212 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 literal 36028 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