From d14895b107b6ad8e7551ff3d63c4e671d01f0fcd Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Fri, 20 Nov 2020 09:26:02 -0800 Subject: [PATCH] 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~ zi1G4+5fd4&RPkSL^j3^&D%a0%s9&(4e*SR{O)FM3bQagNwluUB+FBZ#3mq*@#lp%K zFuI4nLhKRI>8oFX)*{rs<_Tvk!t*5purkgScowYAs+}TphU}~7>jyQd_ z{3qwrv1CzWLsNTedxv~E);TO%)V^AOO*WAA*fGALqouKVzs_PuS5t9fQhZ|9*KYY_YM}(rlAF){*zw(dWQyD7Gm}u?@w>C25^VkQb_9jfW6luCsGC z;(cRXk^k$oB7<|Q%7(v}qc+egQ6eRlL(la0hqVwqEIi8HRH zwPkrrTd|?Q3I^|*b`+K@EjF|?FJZrHnC;m2=V(~fQ7E>=oRj1WK$>;^NZ6 zJC`xmD6M-{RtN4x72Vi6S3c0^Vwbb{*-xKS!3)6*jxum@|bHX znp~5^KC)~d687w}eQ4No%l4GAU6m~WlzXhsTEQzCI~tdxt+N6hJ$Hebbf@jPAKE^P z;MB&i67V>u_2}hm8rmB2jVRZs$*zH))!N$Ll+07kCk?iSynEJYJlYx?&_34Kh^7WD z7YoHgd+EI(@4I11Yr|RX9n0!F+PhZFn^PXU3BFJoTb(_KUQ{S9Z(PwyuD+#r31jBB zH#N4-Yg>sjqaz(#lRbh?&y>A<3NH5gDKNK%$m;A%;B+0I#$L^CJLVbinjH7YvvcxW zh=y@@7YXKSH}Q(qptz056d z>|92CZ8nC5j`=mUceU}_D&?tzk2RYN-r=jsrfVva56dj{9Go-2%5`{?*y7iCs^P1Ibg1 zR3mfA;XU&iS5{d)>m=uw#(3}5UhnzJ_SnRBP11aE2hx0UkJ`Z^VA$+{gIf zm`15b`?g2pBB$p+&8clp$2bSizTXw!m)hOAy=QLUPoVzYks)+qFJsAWe(`RMagKF} z&uBb-&}GgqWLC1}hT-dnu0w`1u0LAazDM(a>o`t+EcQUOoH@)p2pxF`gUu;t?5?>_ zUV9kYvD%Jxelga$IabcPei!tWd#lbyf%VIeW!*=kovY*Y)iK6BFvfAN!9FFvysi^+ zdpw*^*!zRcGm3uVtl67qO541X@))@px$QY~oO!IrwJ$x!`=Zt-AO30JUWq*uoR0S; z<^Dzd31IiYd?%xwmunY&p9(gg{`1lL-6Q7_XWV&smbHyriMFPk^U(G@%jsW>HjiuW z9(ZP*gQEXhw6XGz*WQqGo}C-O#ySt(yk-C)v{;vdd^X&)bl>q|iN^(3}u&-`CS zyH1`j{jS%uNS3Wz8}w7x(e|C$azo)|9lGvWjt%>cO*p}GVdp@zP_iBah_cZD`f4`}zy}iQrdz$*|Dr~=< zso(EoYF|`g`+ZFPej8KU?_=1$*ZekyJqp`zqxLLz(Ra*s$OdFQauj>&`Mn;IpUIB9 zzuLxo*KR?)f0L1jzcuHLcmK7;o6yF+$(gwg?b%VkMx8s*cOpLe??UTWN8Y=^=6HhI z-s5`_Iq&FO&_W25g6fx8j=iI@+ARfZxO1$&ZAM^SKIOgU1 zM%#G}Kz|b%fymqYww&{f`u_SBBIo&vJ?IDiHlptu@@Ri2=lq5YWUSnG5qs*3=x<}4 z_d$OT(HCo_9reBsj(XF;&cR$iK=ehuALd+I??-S^Pk+?=F<77V+z;)j_Y-i`^L|9V zpMv#8y`O>Q(t1C~wx0dzk9xlV>$6@DT083f5*+pBfTP~8!1|)zufcL@z29I*J^fMd zw_ttN+k)1PdM|>bUOKYR3pmE=OZzZRe~kSrcuQjc4V>os zJ9gwUR)6&2IQ^09A7JMY_CLWr$fMkad(f{S#>jhz|Bd$X{iE++h@9&zjy(SX+f(eL zwtEt>>WF;}Y!Al1iq;O_8{o+EI#@g2wJ7b&M@0WAg1zXND5;23Y z-3z~o#tg|W`QI=g|>I>WH#T-!Jg zauGi~j}LnUb`P;}UU$bfM&9$fC)&p~(zgd9=NgG4&q#232DQUC3ha8tT8ze)iyrmK zxtEb!t?fQrOP!tt_o?!CjPvz3uWKZ(%dz9eg3Y7$42?tYjri!_C-^7DW33}+ zoa^gayGH8BaX8qz(dRUwamvk+Uz_y>q^7}mlEN6`8_$aWC`=;+m zM9!YX?$e?8XM#7N?IHR+8qVDDd-WKwoaf->{Ep7Tc5K{HW1>%eaSrByyBU}EejJ>9 z)H@#R{2b$Zq8@xKVw^d(jd5LK4(b>`54@3a(eDY^a`(YSA17jWSM)I-PCoYIB(P)U zBiG4b`I(Hhm$_iMsJ#Fz7xqH1Yv(y}eNO?eLXSs`HHVyWG3HdTIiio#z;do_#GH;@ zUZZl}Gq5AC@#d8?uJS!$4)4*K_|kWlcKFT$KSPXpd_QTs-e;pdGiu)>=c3O;eDt56 z`_+-_0mGVZUf859bN(UagFq~BXX{hIC6J@(^{R_*1C%OJfEJjcxUDm+O|_OV?ogUXqWPYr*niZva~} z-XGV2>gs`ch_Cm#>o4<+Kl$`yy&|dk@LKWBhNkH^c|)hzI(yGD_m!Lz7Jb2dekT993!_{ z`#iL@)Y@m~_POYHvtFFXUZsDd9YBtq5ubs`;q>m`_&Xtv-vu52KD#%#PhwA~u%}ko zM^@OgE9|+6eKL4KZu^ZJ2i_O4kMW3W;@#g5k>5&;_5nG!KiczPk8%-zFjy|m>LFk` z?{$3BrhpGZtfN219tPHDUgxSEzQe(3-s#wK-t)+N1UT~QkFhhr`poN^X@~D9aGLk% zlF!&3d5-}{-m<^EHnZUMnb)<~j=aZ$)4a!F%gsTe-{ZlNSAUGH2kSGhd#D}0dEhkf ziP&-{Adz=IIP&U`u_uA`nb)(R9lnKN*F4U`Dc}W&dt|(}F%f?n*tPUs?>pplL@xYi zfL)XDp9z)=|5;$?7XGura^XJ*Y>(kT7c94s8_^$q9%4=NI#$2EpO3x(vG=&Q+QyrE z0Qy2i|90-1IPPXGcqrntE8^W8j2?n`H+MtF`ZVUe>k~aM0?Wr;X#&f6S7L9P!Lc{` zV{8jppFN4AC;J_N_zXj$r{Q3G+8yotqA$7?>4QX%OTosaJr=O#<9=QYmh=4hy>R?8 zMBaTjr*{0C=W?*`FL`^_j$V6%$09zX5qsSeJtoJa&~=D??}d){+zN2o_a)elN&D`= zmXEdV1k1U$_Gu2eSkKj9xtQk~u-s%M*5^`i9g^0$3|l_->vHg_JjNW>k~1!9T>(yO ueE?f-8e*-r=ygaP;+b?@KlGKzI>a+xi@pkxi`rL%qqaG%EoWSOQ~n2)i6(mh 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 zcmb821)yEkwY4|gdxN_>L4&&$cXulqLJ~qCMg+Iu?(R~YA_a=OyGx-h6k3XvQd|oB z-*?U)H|y^5Uf+M(l{LngbIm!|+H1?%_a=0VKlcPxHC{DwHDPu9L{;lERW&|JRZUX$ zYW3@GwB<&Njv75+(WRGJT!+c4j+UQ3Q{dA@-$@(VZ}2b;v-hg1DbdxYqRmOu*Ve)R z#;1P>(n;2JH|o3Yrkk$2(b|3cj~LN+)ad<&4D35(@UVe>2MiuLu>a`6dk@ra!oqLx zu>M2F3@9DC@F??lQ4y1>)*do&=)hs4w-~rv)v8j>&+y%L8#Qorh1aCjWboRr@8Dqr z2JYKu)aa38`j1{@+~7sVEV0CjW0qJE{|?rqszwsfoRflEW9ZE}1^q_UzU9C{-8t3# zjLX?sO#`p}TB{`YGlJE1)#R?#h$W0WNi{vV_U}7#V7~z=aa^ueiTHL@)8X5$udA97 zy!n`Z1Il{T{FL>~46prK`Q?66k398QpWI_OV##)Wvw`dJJ?a~mdt809;+y*B0IxY_ z*TMbk0^Lb!e#X_;RrQ9)Pn$KU`ALoPvPOAojK8~f#8U0!J`XrCdlVg!BiOE5|Bh;I z{M&VRR`Y?^89ZbNF4WT1;z&i!omHRON4*#SllvJtXxDyy`wt&7e57$Ft`=9oyP>UDYD>n2r05+A}q%`6<_JF}R`h(`Kz|eumbX=KzPX+wVW>sFRenur0f5 zSsJ|M=#j(s9B3u&9BO{X)zVol3vc_i*F)~7=Xy+1t$^0{>rqK}in5O79p74$Ue$_@ z8#vliV%xz3M(>_=QTH>hmR{A$@X}YiYoqRG9|O>Laiv?0zVz)`RnLTFZL2aawXFf( zYGl7*qjnoUa%g**TKUxdjH|7yS{vT&3wH0S`5D@8G`v@fyVpk>b4b6@Tq3D;(rP{W z?y)@z?N-U&uF^fdF8=KrC#g09=Q=pL-=JJ#8dt&gxv*jPxXG){igE3OiAKuVb4c#`Z-F>-g^x+I4JK#Mrrr(XOL)LHS<` z@mTCs#OPPVu#W#8p5embwO0|Rdl&zgVw}r}BF2~^hI8?f_FqD@)`LfcdOnj^`~E_V z^@r{KzlO1neY#_GRr`at9L@P!Tm#hnjJpPORtLgce)OLIaz8o$<<0Zo>zBIsyj^!4 zZn>>%g_`%hA)J-DuEp19rrn108#Jo$?E;VJH+pyOQDcU&0KIsUG6s)yEq&C$K{1hW z;_49eR;@i&BC{NKtvMLK_THGbIk-c|Ynzm%C-)#wk zw*Su~S?f3*-W|K6Is)u`d>%WRe)VC)hI`Cia_6n)XWV*sRmZ~Pr_F1unxFFiVC$} zrB`(tJmK}#Zr$pBhBmaGbD6F>8=rFgFVCm0;+N*6X%P-mF|RX;nR%TB&b-d8bG5He z9-(%v>VC$}tE)O6Ze4BP_AyfP(>zA%<-9zeYMvYAdC*l&TU~-SF7AKMw!60GT-%2F z2r}XN`@u!T%Umx8XRepkc|83Gw5QrSF6w^9&2{?fD!4VZeEYT&{byx~^nWOfcwURH zpigaAg4?xCTV3BHU+ZfBAG`Q}%BFX{2yWw6o@ zd^Z;PZtBiALv`CP=JTrk|24hFj&9HKmLlh^-8mI$OHF`F^9Hd)n`WvAduB4i=v8H|Emc>wD{3K6u~OUQ_e>KC1P+E}o^j z58lY%b9EXhPA#*d!=r-)&*|K`rNpOT-ks3S_iF*@AeOg-C%wD4H`6Z;2;rx zecIo-dsPo&AJK2*=)t204?H)G*_rd#i4{F(aRgcjB(zqwP$9eawzxJD~ z9{0pAj+?1^4!!^Ip}YEk+p4$6oy#j;J@2L6q2umStha9z{!jO^XUNg7RYynlEd8!6 zJW2Hu{m_0R`VMTr(@5t3c!Kgge-YpII!{@>1|BqUbnBf;>;9jS|FxR-K7W<*qj(N5 z?t5TY-*+D$n4{l5i_^ios*mWm>_2eWz%|Br8E@@3H9w=&>5cJ8OKHdW0!$2cben(I z=IrP;|GCYVYV(e&7bie_&vaBXf>X<^;BmF^ZlKM(HJrX=_pYtpWZ=J@)Ij82Qb$z1u zoIAGGoc~Gf7~N}qdYgBz^*L?cz1A1DdG}gh*5=)N^r|-R-lNyIdA&zFtDD<=d22HN z?QOn7_`Pkugqv0S!!@@+`I9xbK>71Ew?OCiD!hHHJF1_-?X~Ktw&G^k#=WZTz}?SN z-D}m>2~+jUvsN8*2XCY`-mg1OM|FNT?yQ~ym;37J27j%=UvKbt8~nWn|FpsX*@Jgf zpTpbp>#Dv1w{FA(2eoGB{a?+`2%bgw&emG1?`q9;+l+^U{@BGw@KC@zA8==NBzWAs zI;vyfZNJXyLa@)z#?|+t9=@H`E%3c~>k|8|4g2%({=V<6$G_0S-cfxCAF@w7_E!zt zkMPm;xSx91CT#t@VbGAi8x8N@ZwOC!bUrAozgD{r-p}&ob=2;CeH3UtJy`pKXc1h4 zh2hydOM~0@&#vlMV7?9KbZX6e#rD|xn$l6N>e#xr4SLu*tBo3b(+1zX!MEzcJF4yA zJQcO__60K!Ujx8-lxs;^&h3HI<&3j>px1b>ZrEnmMOmD zPf(pOPA|XyGp3_D4O@He9o1#x`uX4*@bJ;Q4;)#q%VY3y`>L~gq6hD)K7$Y3m!Is6 zYW>KE*qzl+J$yQ=@h0dQWBdm1f;*x1aqp}qY1k)k@F^O6>K?pTH4A*m@Zo#*-L+r; zJ-eSfyQecI2ascEquex22l4Zd`PFWZB6Rjb0=x!o1*b7i$2 zKAqK?4Ze1RuiJxnR2#$F`!jq?TfSL-mEPx&rMy`T=4oH zvxU`qk5TheexIEuv4)QsUCwJEbfwh3Ld!4lJF8zc_$u)7Ub1Qrdq=gaoQDEyJ*J1P zv$~|gFKzHE;N^Yj%7*>R2A^~y*JoUB3wV~#`K{itui4=1!gu36jK@O%^?KO5stw_-{I!!l+xGD3thVdHyQ-bw zx^uT_y+m}(&OLm3RsG=Iacf8YhW7C5tcExEs0JU~;D;A{UE+PnytoyBDt^Y*+4`;t zZ2f$DJ!YKmnR6@hS(o)y>uBN5)_2aiH1IK=-$8q~SeqZkb7L7l&(PE1W4`biTD<+- z?eH?6KFR0vc9~CZKE9^3^8XsgarwGh&!^3JKC>pC&!c5Lx$y|wiuX0VjF)G>xedz4 z^-$ygEq|W_%Xo6*#qRHKOah;!_CJo}A)kph4Oz|G8*lx^)3HY8pOfZib}U?8YR%u? zs9IcYPJ9X1ZST$To4DrsX>iRl<4*HCr6YO}k*->4?a(rUA*4pNz8Dl>B z#F!s!3^i?iYMZq$0G8{cZIRmM8ZHWUoSL@9=v@cL`CUq`k2b%&C7)m19H*wuFK*f# z=a)9QKH7XCmT@bA9jB(vpOq)yDqy)j+SZ`YxHZ9!Q}cNFQ0_R-Wsld|^lDkFb--$l zv!)H`jpg2uZ$j_!4Br~;@d@7%>{^HS2RoS?gY+GA^T z%nP-aJ@*RSoZ7v%zCk}HZ9LjSHU9(HkKFiwqRmrt{okiq+ia}mhxGcmt{>6MozKVg z`lzi~bMK96iTf$okN%(0Ycp2tUx8DX`VX{n{CF5U&>Y_dcKxQQ`J^>>u9?^5X#BrB z{*sW+f-eu}>wjgumB7ZExv=kpR@Qe=?W6zL8uzaKU8ghAQs33Jy$`znchubTUw%)` zJwN16)!gfg{LPwsJ&|{^5Z34QL_S5$Jr_JKdG7GI`1~N(?-+Xi-_krTa-Sj8vhKG) z+%MsG!rf!x_roVH_@nSi3jQ>F(t^JTpRDGt%NOvhyZmdo>z?EE9o%(x9(kT|9zM6o zoyYd{xV1FrA@|uu&2f2dah%U5a>spJ`=~ii?lX$xGQX85Zr_5h4PU@H5>dsN_bC;THFh`B)T6|sr(`d!eUp`;0Wla`)-{2YcP| zd@}wDaQCKq{C5P;S^MtibrQLXAz#hNE+q<@@>o4~`SK=L5W5+oDU^xHp9)Cz}S9iSp&{_*Ws=<8^ zX8f$z_i&&6!h5lx>%kY`D7)w8fzQc^1Lz&+`WsvB`;5f0#)&zZueFTz6vM)tTi{*e(*kf4%;OAc7J&GtTx-iWSEsCKHIydUdL_AvY$QA ztj&-6^;;CScdK^XTklS-$+^fEW_&oMRlYwBcc1wVHC(&zPs1I*YQeSp4z;vzRB-LS zLydhb+;^xY_Z@18`bTz}tDhHLjdWw>_VQ zY4<&4$$d{*a^Fps+;@}V&d>Ld;m*(Zkm1&ISi$x8J!EP3J!H6c-$jNy-uICu_kCo^ zeIFU_c;82cJKlGaCHK8#$$c*w?t1!OvgE##3^%^-Bunl)$#BQ}UNYQgKHp1*Yk#NU zJ_Gt*GIqJ|Cc}NM_WfkI^YPtexYtMDIfh%G?;6AJfcvg7+<3lg4EGt*ca7n|%6E<7 z*0XED&F}lh*o}Wl!Hw_x#@K!4^nGJEu=0Ilxbb}77;Ze@H-;O}_l@B`bNaq9+<3lk z48H*G`^IqVpQ+&H^L=CNm&1MESn|~iZam*N#_lt+?;FF-@B7AZzkB(IR>fF`l;95N=ZFZX9 zbynt{2CRD5`nC9YFP{sprq4Ir73a~3&-eIio13QQy;8h7QT)C%KiFq?zpvRoA~RCe zhd;D=&o!p~D#V=+P1|?GNj&>^$nyg}HlOoS=R#n|IM$zO%41sutab!*psN<+4>g|) zGxx>8&ABgurk-(2g4KL>JhYCr6x?{)%M~R!fX!!D@a_-Hf@o{>#DD9q%)P z-0|900;_5Bc|pDcxqXIO8T=Bxy7O>PuLAZt!!>q{TtBhT7p|{sx*FJHWdAj})}UXL z=12QlwcS2*SsQGOo5 zy_0Wyu=)HxpE>LRS9iS6fO7rkpx=eo-1q%zT_3-z?+SOEd&-z=(f5a|>ob6UAgwRW zvF4HMlRSgL%{+Ucsq5qQc~7u;oU3{K4m1R=uFp{VVYJ<8jx~>5pX3<&ZIw!=12S4wcS3s&H)=E*ST}SYQA&xIwwDaW-McioA;&6 z_dIOP`JRuao_rU8&G!h$>J93;5bQaiejxeis*CtT{RVuz_FfD&j=J~ZOX>YspSDYA zYSt%CJ(q)<_521+J@s4xF6+4xZawO$=PIyzuHRRKjic@{xQt#cF|P%yS;qkS>u6pt z)b;V2a(%7q<9Yg9_%*cL%jEhb<_+M)916aXwmVH-AFm%b)w(gg#@q}yX0E4leG>C` z;Ka1fTWDSf)KllJwVpa}gB$aG@;P_8K8g8zaAJDS-%j(KSJ%hu!X344Os^ex!j0** z!x41o`o#Y}a2fM{xaYEZd>#O+C+34- zV|q<6rd*%+KLjpgJ`DG~RgcdjVD-d&6l_e-e`Ct^(cgRRRt_rGJU zL#~hZIp|-YHS@e!>-xBtUxJ&bykEWySJ%gL;uV_bgk#Mk*C%;i1vm4&hNiBM^LZU? zp7&Wh*V%LR4Y;~K&gTy_&lAU*N3Kuuya{gRc?(TlANTwp!R9INmw$q*>vJIepJ|>u zjx~>5pX7NP+|2V1nz}xo2Y&&Zr@UXj3s=|2^XWa>p)|*uN3Kuu{1x2H^EWj0dEsDu;-m)%_G;xeSa3c z_X+!)lb?c5qvbjDpJ26IH?^si_t-Px=5x>d3;Y@Feb(E2a(~7U`{%Vi*LJx-SJ3X> z{-U-gpWL42iC?H=Q=YJ0AAa-Rue|GKv4x+O2|-_-V8gXFP$ett`9 z9+&TGJ>QjofUA8^^B(;py&vz<+Wt*bbM3`h`=7xJ(pUI^^ZeRb%KwicZ@cDeCO(SuD^ioeRrQaCIDMU<})E&&2?}-6NCLY zA8ixS)SQnvaV7<4KEBhHXFijGA0>vm_4tlgE%TWYocZ{^SDyJy1$I7;(U$r6Zoqw+ zTI^HHG~m?Y`(nAZFwJUO@RRiF@t+Rt8s$C4^x(Y5(BGVD`en=vU~^nYT%XlugsWxU zW(NCl-L%a_Q*+(Ki8CwMIC;jJ4X);KGOzEq)x^HrHoo(o18gjNpLu)J&qed2eeT+B zZw;-V&w%xF+?@2OVLq^V%rP&$JnJ<-*!5D+`t$*3eY7Xe0$^>%aXsa+EeKZ2K3fQ^ z=5chNEdusqt=bl*{fcI-;>1}ToLUzH%d^jx0K3oBt-*KsYTA9KSPFayy}EXP7NBNq z?aP4Gv@K09k8L@y*E(&>g5`PUSRVWw;~k?-AAQw57Fl!WlXx zQR>>aqE|~jzXn@R&X4WDYPp{50QTd4(6&8oCz^8?8^=AoBiPvH@Yu;+C&%mSnE3Vu z=lJdnmdDl)oH)CH$5-D^>K_gYZ*hYp795P6Z=50Jaaq*oO|@aV0o@vhl0J1 zs~dMLy;|ZP4mOvz!|3JK<@3)GV4oKpqpf`A(Z37Ldrs;&3Z6QS1k0_1LtPyW_THx+ z|6{<$$i47baPEcrn?p@M*L-Gr*W6y7XF>bs_s`@w4&USimhWOgY_Y`oB)yZJF&(S^yp9*e12cL$fetrG?b2?ZpHJ%AhjoQwj zsac~qvCjrKW1oYjp8bC=SS`;)XMxqSUgv|=jOq8(3&4Hp)%Eea?1f-;ef;in5%@e> zzMIJPNz9AEiRpLjOW=OTR@cY(PM3n!jp_H9%fQCWc`DZ@F)s%vrgi=X?)Pc+)OiJ1 zJ#}6QHs<@}^Ek@&NzALjiRpLetKoikR@cY(PS=3djp_HQYr)3M_bRzQiFqA3G5wx= zJ^WC5_4xc2tZq!di`@V=X1~z^275hpj5d9Y zqwYDEy>GAW9(vCeZTEsbFYJAmzn}gAnjh^C)^_{M>mjf)^8Wf^uv&gjC%=zoEMto^ z_EE57Gq1Y33znz07r@r0?Rk2+vFWN8`NN<2IYyg4&(NzU_A6jDZ7YwO6F2+mke*vfVcffLM_jicj1#d>LZtZW=t9hKfp8XZAM={VhjPW7ZIO@s$5xANAV>I>T{sgRMa~)l? z-iqN zTITc>xH+e<(bO}iZ@_9cKjnIUi{_lHplxo4Nmurk>nCg4Jxz z_52Afxy$wZ8C_d)yD^QUo^=@y&6>1XkLx);xVfGkXzCf)33jZy>-i-y)Uy7)z^-v( zP5}3JopS$`>l6P8!TN_!1UKep^>LpB?(dP>CZ$-9@uXmn!J?KAc8q?>IT_fTxj)Mt zlN^(S%@IBY+!~T|N_aEpRA|ZRc*p3MoKu6%`93wdE^@~t$24Gb#(!G4{<$~H_0jHq zb~>=fqI};xJ(_xaW&j%__hPv|=JXmhBiQR)zPHQ-SIfJHS-^gLzSK4|ZBClkE3t9B zSI!2u7U$*fsmZ-wc`RlJzecZ~xy=E#uH4V%`gxBtr}51b|K8xzU+(+i(%*5J_q<^1 zDc_OLho+uq$@#%*&Nbij7l5n#yT<;$j=z(8!O$~D|$wFF%KA=XvCBv{S;lzNte8!O*QmInKG z2#lk@xzzMau4TZ@T+5=VA6n;H4y@)gh3n$q9a`lSeQ{DqNLsQSV&B2aUcRjtdC!5Sk2a4&mGW` zyIjv5(X}P_PGIAxXI*v%Taz~HaXtHjo9np?ntI0d13OmT_4K-~mO1qYH|I0}O+9lO z2v)N-*K;>C=j6Fx?$1GJ+A^oz!NyTf?!n+@?mf`dlY39FnytB>L(r1DT+gBC+LC)1 z*f{E0m*HS*(q=ub=Lm3fJ@-OW&$y9b$Ev%YyV9$9t#IFu0+-Jeqv2gN&tLuJ`ow<> zxV*pb4KMHS`@kozTb?QQg}bMHrf|Gt^vgW<1Di9?6mrKT=lhU=oY>Yfp z$n`O&_oXAic+f;UTz(C z;(r;~8HE(iNe;TUb@Glk#7jX_skg)Pq%SAym4sovPG2JcL-ZjLMH)tr;h z6xV<~{#ietDXv9R&vo-Uu$s+}=Wd=Uu17PbuyNFr`vGt> z_k(Ea$^8&m&DLDwhtZO|T;oU3wI%nXVB@G~tsVngleVnYJ4ynjsJkAo^fx29jl&kZ-HH7 z*T8)&*H61^{6}zeo_|79&piJOR0je}Ii!KC66+rXHVvf{l@96}djSw|xdK@3sGetNFX%KC65I_T#gPw$Ewb z+E_KQaeP+!3T!Rzg)hPKJga;S_E|+ebNdEtU3pfK>*syToW?g#{J#U2{_;Gl#NR&i z{vK>S<+I8UXzF=Z`8Qb2W1RONKf=}f(DJPE6Ik83%|kEG`uz+p*Utl^=D$agXO;0h zShVJ|%J^v7jJptH{Y_8mEeO~B9Q_r(X7g)`6F!l6;8!OK$6TtJVqQAM+^h>S@ z!OdI~p{XCrUNiT^V72mDWfHi$G5x!Xa_hJg|H;7~FKzxkMtPo9rU3h_;uvk^vx+|> zFa}*U4YoY1_;(%U?kWG>jcLIH>DA5Q-;GquoTdk>mFqVHTs=NBg8e&_xfjdzF~56y z7I5~oe|J)zJv}SfJ*{qj|E{H)vAzDy26k+Yjn7K6qp2I;=ZZPNjyt60bAr`k?_JwB zC#FAtm`b(A=gK{=jW#2=J~l9 zntFUT2OA^%U9L}ZYyoZ_i!IUAb1b$3tCh!MYq)xOEVhBGTdV6X&$0M5ILBgJuw2`* ztkL#hYjoUp^zs~w9l+P);}~t_vG8ZFITn4fNWu7T(L zu3)ts3(tf8aP{~M0QakXj3L)2>pT$LJQlm5smEs!*cdq$a(%SZRlD;?^H>ZOlTz zuJb`?>RIQ5!D{6?9|Bh|*ZEMmde&K`kf5A*1y5E z9Q%Mrpw{m?h#B7ceFt+-4$j_vv-Zh3WIb=u)KlLd!Tzp9o>Tt}R`d69Q}f$k$7%B# zZ$0nO)PEwUIJNqF6w}lEOiQ!ADe0%9S>Fuw`FDQa0~;&+g9iVi!M|(pp9}843*vvz z!+4qhU%}=r=l?gj`a`U%>+yG*TJDS1q?Yyh2Usm@@lUXt&lukC{Wlr@Mf-$i9_<?7?NJ11D1$JDvYW19sAaaVw=El*3_Ux5=>d&aH^)@EGym^`*sz#hw-Ypa6QVqXpHF^PS3 zuv*T=HNcKh&$+lJ*u9(cU#?$bt_AjZB<9*+wb<7IyRNaX3sy_c^}vo%PtNth=FIso z*DoX9C5BWy|K+h z^D`ICYtEeXbJM)$%uAnZ&X$bJHNoeMZ5w>Y2JhG4yEXWp4L+j5$29l>1s@AOtme5U zZ3VaP@|v_YntHBD+kn-wPR>a!YyWGoTCPdkfz`@u()RG|ZS5Jm16Z4T+qIF$wiCFF zyE9y^ye9R9C$9F4-36@8xUR80wq3zx-2QO2@|rXNp19gGb|6@raXnV@*am^ixVyvE z#-rsN9}G@h?HRiVSetP@=JMEvfXlc;;cDeIX&5|lwP);bur}kmcjU3{1uo-`gsYX; zq*3t1)t<4V!P<=L9+bznH@J+uFI;UOTGnAdaN=su*!{uUjO$*O$95pNjC(L#?I2p> z9s*8W?HPM0SetP@C*-jm4tDS5+HnL}E%qb9?xEO^0;}cPax~a6>bbTY13rwFYp7hm z#5@-4{z=T^z-qA{4|X5Kegar6IZp&TMm;%C0-JMkn)8(FmzXDm-2;hv3Ro@nQ^6j~ z*iQqiCFkj2$EYXg8DMkf+9202G0y^f924_wuv+ZrfIS|up9@w?&hx;IQBThE!R9=Z z<~-&48M8OnvJ1f4y>5$h-R^^JA)22BXkNGHr(clfb$em@T(>V~TzTETyx>=Zuc^5; zd0oE*Zk+NudMTQEuA`TM)v{LRP|I5U2CSCr=oMhK@;Z7YJo{UF#$E;1=6YDGJhp4W zYN_=)u=Qte%JtFTHM+jGyME%VpYNTPr1@E#=K3v4zXZ+oTZ%sGcO&D<^}D6ucYyD# zd1|@|Zk%%cZbnnj`uz^9mi04-TIP8xSS{;!8(6Jezu&{Ne%dqkcCa?r&sycN-33<5 z9=#i^X6^UT-%E2oSx>n>j(5%OtL?6*IP1AAwiRf8mTR?+)_?0^d9dsGEBdVGgN!TJ z^U;Do1%A5bSy#wpkHVKnuu=ObXXtfx8DGVjO0YFW?6!D{7tJ^{~qYR}jw!P;C; zYn8|L3|K9-J`1+~te;#T{avHyYP;(v&ibv4Z8e&oRcP+VmFQQcxqhqDXZ>DeT)BR) z7W_@{TQyHjFTssduHVaO>RG>6z-n1PbEsvWuYuLFey@Yo%Jq8#p7qn7v3~$-bN#GU z9@`(mYT1u}0;^>|{u%6ivYv8%9PgUFUE5txan^HfZ0phdtV44>*Q8&U=6bGApY?o~ zapiix-{2oN_-76NRfB)u;6E3q!+f0Xnr=Jc^#RMenXnqk&Wqd9?#4;_3}KP1x-EY@vLAq&*L25+2A?8+B47D z!P=}zoSJ-x=-*BBvk5IVZ3?!g&FQ_i`L2Cl#+BEa-f;cO8s|b&&$VN2u$p_@cl=Y} zKM!2pwKJyNpBY)peBkNpT-GWtYuyr?pR(31;MTeoeQI5Vab>Lw!1XI@T@Xz@wJro! zD{EaCu5PWyl&983!Pcs7t@70Bdy;KwezvA%UAF;S>#ympbtd{{7+2Q11YEze)+N!@ zQ|nS-wbVKz{!7Evt<{)vYqgeT!M+bxw^n&s>-O0El(lXLx7Ho#Q|ro%D{EZ=u3uT} zuh7&}>xy8tveuR0>egyZd1_q+Y_00nDo?Gw(RZf#*^!oY-3e^1ed(=rPWrVOSJt{Z zT)(o`HPF;k>zZJ-)H(`JF zZ_T)}*3IGim9=hxrk+~21goXirSabiu5PWylv}H{Y}3N+T-GX2t$r69O!G5{mRffQ zTk9V5);d4^PK+yS-43o_S?l&_>Zx@Huv%)J5C0wE>egyZxwTr$&S1Ykt6Qr)wXTdl zl;&qoT525vw$@?v*6Q>5K*p7|_JiwJ*19X2dTQ+tR!glb;y(bcZmq_YTdTF~240O` z-CE_T)qB@oG(W>>sdWU{T1V1btIxhe8CTXi7_MJg>mF$8sdZ1VT54Sb{~>U7Yc;0a zTCHUmc)dE8waQcLV(4RNen!z!>u9jG?oDs4i_(u_Tv_X0aQ(_!N1~~x)=^-!)Vc`% zqv7h-YD~GcTFc&G-vOvwtGujrUu=HLTK9ol>wfg9^&rNTweAPkudH={H1*Va09dW8 z^+33~wHi~NS`P+WtGczyQ|p1~htT{SL~~sapg)*qt%uTEtM7KkGR|7Vk8JRh8vOJI zKc~SjZ1Bq({Hg}OzQJ#9@Y@^w-UffT!JlmK=L)Oj>I~iQYJq51zRT1}8 zc;af$*wet;jO#I!$94v|jC&?rt^BTa7CdpaXYAQvZN_yk$YVPfT*f^Qu2z26Iv<|6 z+B5b7ur}kmr{u9+1TN!V3|I49$@*RbPh9O8dns6(aowBp*e(Z`aeo6>E5B=90Z&}* z8G9vIn{nOa^4P8hmvOIwtL;uvxVOU9ZlPs;Zv!W;_Kf{KSetP@cjd9&0WRa-1y{S1mbiC=6IXl2 z-UHTVT(1H0*zNfwdX;NqTu~FM!LqFTvGbq$Tdl;KbFQ zv9Ex&8TWa5d2FwNy%y#B^y^@?*xvx3RM`IjR*U^ju-Ar+e+#S@`yavQ6!t%X)$*?3 z&tP*J*Rk63uHbF(t2D2p)-KmSvEKoEo+tNTz-qC-3-)}C{XMW+-WB{6>=^aDEBG7O zoOxFu*Do>O2Yaq2=HJ0;v3~&eoQwTKuv+XNfjxI({}`+m`zK(}f!O~6R?B*Q3O1*4 z9jiU-^-r+1=iVjPKe0apdmbeAzrbpazlZC}=U~U533k2Z`egi4`^ZDo~`aBQ)%)s(lj+Fd9e$VHH@zB)syf8jkEo*KJwalpltd{46 zPPkg07hLx)xb?Wt)g3z(eJ^xv9d8MNy0+w>39OdWFZpKym-%Of ztL1sd{IkK$?=`19{ zTk_8fR!jc*z{zi1^Q-BX{PTm${C(hRc|J1#0&w$t4wds?5M5jHF9cRg{)NHGZ(Q@M z>6iSAfXn=g!qxH|W&XwB$zRTYadd6TzXVt<`IiJIzj4j4reE?e1upY14OhE`Yl3mi zzYIM2%l*GBy0+wB4y=~^%Y&2OxaL>WFZovhm-&AMSIcvm`B#J|f4ToxLf4l3D}&XN ze-&`@8`u15`X&FW;4=SeaJBne^KajGR)?G4`;Ozh_N{@Yp4@AKTU!0Q)@#9&+nDB7 z(=WN#2A8?lfvdHj@!I#Wb>Zgr9+q+Ip{Zw{>w}Za80J#bFJm?Um$^2CtF@o|+POA@ zo6GxO#%+wIo?M%Nlgk+9QqwPEHU*ctHiN6Rp9R~wHiw(bduPUNfu^2ZTY{6z80J#b zul#!lgy8Qaw?D_zc|Sdd{)8GIM}H#C`{~K_xu0%DobrCUHQX`f_wQ}c)N?=G7Oa;0 zsWH?thaJIcxu5O?R`a@%=Z2l()>G!`i>98r?*iU|rjIe4yPAH<)el_e+7+%=-aGrl z&DFei4?x$J{W}n>mi)Vcli#@JSJN-~2Z77{yTjGWd*@(y@|V}HJ7O~2$H3NG^xgR7PI&f)OnFRxuA(6uH1USPH49|=x=Cde3>GWsO-23OyXYcPv zoO18)4|hztCl5eV&)z=}td_lR47IGsL14A){e!`3+52A04}n`x@^~#h6iq#AF&36iQ`gUkGK1=-QJ1 zbg)|Tp8-yO{(IqS?e9?SXW0AT=6An3&g<^|XzI!R09Y-#9|R}2 zG0m-}U-P`XigmAW5=r*7@mt=2vt+RvYl!JDxkM^n!^`vh3c~H_J`27Cn`5*!*YA34ey*Wq{jROC&#%|fT)*GayIwxuzDAs^pFXeG zJnQ!y+!*EhJ&&fI^?L!VR<7TR@T{M9=cCp>pWEyA61*AvWi<7y-z#9Xtl!bp@+w^2 znAR_M{f@=<2H1IOb1w3%-ygs}<2y!MbNz0{=H~`l*6+p|`wVmw&Gq{oz3b;Q(!0dT z`swpt&9i=Q!i`a`-&<(vS-(Gm)ynnz6Flpu-TA1suSf0m`!l>5`)xG!tlv9ewXEMM z)bba&x-qR^?)sgE?XO_xsm-~_vwnXA`<&<)ZO!$&4V#~G{ceG~mafgMG}rI<^jW`8 ziBqoMKWm=#dmnC$a{c~}rk?ft0Nm2>%lZ2uTGmgy^HFPGPuuJF5xg1uV>I=w-zQ+T za{c}RS2w2h%d>u;ft{x|=OWMg{R`~+IYwJ^{qDo&=Wd#7b_e}EHNK1fUYhH7KfUYc z{r_{~xPDo$FW`IrKvN9;}u%_yMfee(q?$&-pjpdd#El zSnmfvqHD|j;3u$Jj?2&B?`V!QuKCsUOMXK2$Uh!jt$Y?4AD;Z>vp@&Bw&d>wt0jLI zIQflhel`7)zZbZi{{(Qg_O+`${|VvdZ$1l6gsv_5CkCq}|0Lk#H?H~B^h^Fp!Dar* z;A-Ww$mH60DZ|Q-PD;xaL>WFZrhim-(lGtF_Oa_WY-Xo8LWD9{=gk zwI%=bV726*0i68CHNTpE$v-2w%s&%ct$d$5Gd%gr`Oku`E%|2!t0n(z;N&;1`PKAG z{@KB0{yE@k<@?+@;pX>!UOE5X=-QHhF0fki&kas~T;`t_u2#O!oe!S; z<^1PI*OvT!z-r0A066)LYkoETl7B&PnSUX;TKj!&``WoM-2CM`#YNE6lWS3Mav8&1 zYWgMDV&F2@;&8R{-R%-^bNNoOTjrz*ZgYwCI2$uGXJu0wf4K) z_Fi8Op8VxnFORM*`Bwm|CI7F$$!}cqtLax>`}b#V57GQQK=az~wf#Yw*ZYU*Q}2q5 zRnNa+vNBw4C0ef6tAKqctIcOl{2Wo+qclGc(31NRu(=v@Vk^=!a6_0+QwTx~;I za&HXwm}xVYJh=`;e~#woX_~pl(mzw+XTj!up5A+|_RWZqXBOW_?bwxjaC^UOj@=l| zd*c@9+T0(G+Y)U2_i5$6-U?k?{*AM(!H!kW*lobSlw(_TZSngxI61t3xpv;Cw?o&K z9NU8(t3D~S(`N@TWw!o~yfx^PxI2QAzl^&Ry0-Z33{L*?-06$1E%|o=J61ho`+Y3wi;LOn)^cl;~{+<6Iuxp@Ca_tVbrsjQrFuJyk z-2?1+^^DyU?B2=o_IjYEZ+wP;Gv{)>hN5eW-!O3IT<)FW=-M*p5n#uvXY5|!U&=8O zU0eJ{fs>;=#-q`-CC3=BW7V^+dxNvC)}T+~?gLK#@_6lwt}TB1fs?;ni~Z5HCI10n z$Es)Sf#6@taS*z;_#F&RjchvNa&t>4uxm>Ty(Y3|zH{i^<+&fpGYs;Lk z1UpteW3K}LQjV+9wZ-omaB`H#_*!&r$#EUnvFcgZ>%m!9YtScge+y3j@_5~Vt}T8y zf|I{oi<{83CI8J}$Es)S@4&y5;}&#n@w*kA9Az!Hp=(Qy--8{io;ltQ&K#{lpB(=? zz^;Kl$#o~#nwr<0yU?{|?A>6;t7q&z;OrgibnmF?yL$aQa`%CaUw#+3A5Hyne0-*R z0IcSB$ozN09)zpAkG>)9!(c!DJK5SEqN(}(B~DF`fU|bx{&*B!Tl^j?{K~!hIJ&lb zsO?_*CuojU_unQ=PW!KT7SiWQnlrMSN?#ep1txWSgqVEZ^6~GSN;U{ z1)JOX>2F@w_pkJR ztWBTyXlm9b&awU*IO|*PL1h8tV^7H{{W}1a<6`ht}TB5Ed0v7`Wd>m)b%g0W7S>D>t6+*gv7EZ^~PNwHWg=Sj{;6pQGRUEA^~E z`}(Y}K8dMAbH3v_h?aWC2dmjQ^;@wrz5}gUQzx2w#&?0$Y#E>V_Cj;MKF^s)-^88( z+>AXTntJ9t5m?PQevFlQd(F{TANNa+x4k^qAp7jwiSczkn%~bSLDv?)Nx@z_ntqd^ zYs)=)axhsKb;nAZf%J-9oY3u z&90A{zVVqJoZ8BqGoWjW-;ChYR$g~zLf4ksW(GS}J!5A9Cr5b=nH61I{AL3uM_J44 z=-QHF4zOd@Gygfk=GK;Yy}_Bk@tnV!zVVq0oZ8BH&5f=te)E7+TX_uUMc0vwiB%%&F37k`Fy|J7wmk>-#Om}P2Ku-q?dc2G^XQ? zlkvNPt+yQCA5A^u<>h$eusITMAlNlBp5x{EWPNr6TW`5OgV5BoKD&d}+!q-) z7~WjhJKQi#Y(3_)9=U$84+S^t8HT2wdWM75{H~IpuaAJMdmbg$ zUhw2{eXMCDntEy+1y*x>#*GGNoHfezi+v2(8q43e-y2OmKKp=OpVDVvH1+(PWIwRu z)H7~>u(=&)Zn=K39{@IY`TO?=qN&H{Ah5YhpM%lVllu^`Qw9|9u7b z+R@;R=+(75cQx1B@A}7sov+{Zwabqy>?hTBpDVS?Pps|!`%|aXcK5t?`N_52YtgB- zJ?Fmsgxc=UI!~+ZITz(#@3J3GukG23^3r}rZO?v@=f30o&xAW?ub&st`}u)?g#i=Q);Vc>s4pN{Z3_XjC1Juf2()>&xK4<_mkGW$3fjWoku@0{rU9nJ>%zCT@22# zav#Y(R@2~r33y9-b=T5;r{-F?)|Z0SJQl|DJLP3?_4r&~`*=L!^BXkvd@s8K?0n~~ z$7|1ixDuTG;P|!hxe84^-^;EBs~N}jqHC>VzK80oPindb+^qXrH1+sgSNoK8Uyr7q zx_=9{?mjffYfs%bfSYySh^C&pZvv|s$B(g6zw529KF0Jo-V8Qo_LkiHv-dssj>pzL zl4E-tILFp&fZSs{Gh==a-kx6FTwXKOGXLAbYUQ!L1FjyQJ8PeEjqXBI&l=qg&arj8 z_Kd#=Y!1)$?2UWj>KT6@IOC0_-SJ*4?guy5_yIKatnq_jHREJ$J;$;)^wlRdJp^vf z`(ZTo_&iekl=FTRO+9r#2F~7ay!MQL9Ne7u6KLug|0Fo$jio*FehS>2_tR+VnfEhb zHRJd(R_5>V)>j{6y0@MM8#8-L-rQSWce1x$1ZQu(Af~yudK2piYme6&*0`UdK*nW$LJlfnsIV$%Dt(tKB@OF;O4r$i>4l*_iCSV z-TsQEp1S`Ac1~Wm9Irj&-v>9>?eA#n@%f_*B;x)h0R*@HMZ;a z3Anj#|3FjEx_t^(GmangWc}Q4`s!m$_x3-*#?0Q9H~03rh5x^5|2zZ8+rM+zj`w+O z&oh9$8Sgx_oGV|0bFO>^mV2(OOl{wQhtjKC`4LxYN>ZE%;Te} z$EO2qz4{nKu8($~D>}i(DEDv|ntFVCfsLV$G35G`_reRXJ13vhC#wB@P8a7n-E;mm znx9u_KBteRf0gF_^mTf3dMqYmoO<@X$6|6c^&|0-PXV5krk?pu33gn$CR3rQ=e_mR zU^SZ`uLJpSPELbH`f3fDeu+CRxEXgkH1))t9;{|FhWEY1ouSr@t6%=DmKnk3)Smx7 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 zcmai+2YjAY*~Z_Jq!e1&t3XQ&W$(SAlu-(0gKR^Zq$M8M=XKCULYo*QV=$WnI zp~Y&oXK2sSmLZ3>WogUMkm@;G$RWO)B(01-d20LQ0}hxxbWw zZ~FAP-2*k0Hs)LSR(@(8o!zqsy89ckYR zU$8Z)c?|Thm?q_jZvDr1^iFS|-_td4{E+^i=CC5ZgSngMFj(Et99F=;F^9I=%8l5) zdU|{7tMA&Yc?{LxT3Z#)82Z&~lzTMSu5RtqIyz@|_4m!u$2qD0@7hP!#-LYun=7ws z8mfDBe6#jz48`r&F?Z%*?dl#wwYSyA!>cj$t=FUG(Okd!|E|B0!}@2LfTN9_FoC|v z`=9EsY8#rvTEt`y>onpgbrM3Zase9u_jy-30rD|(wtEqbo)!kOx6wY{+Z^KW`qoa4u z@g4N8{!HRgStlQ9z!(^uk8%aIQ0z**H_(RXwNNGn+Pv`|6^T??7GJJ z+m*Pi)ucv_edf)oZ@KzdY92#%wbu54GnRgJAGycu?)eNmoeD?RCc`VA=2EKsLwkQO zd>e7CwJD9bgT3oHSGmW~_)FCGgOj^`pkqdHQzoQZbob>{d{WCnm4D-5(p=7>aR(N0 zhgEUjaQ=H#V;v72j2l%uvWRoZ|G(HHir8bT*um@V{|I)T#|*}fta;72F4a}>Un7k> zu889iU5{%#68>u#XQQRoRmAlq&Wr26ht=bFR8{9=RBcun_x~)AWA+Zlj;x(n#kz_9 zk0I-0`jBbtfwmft``V#APY>yy!3l5dEj5p!z13Qq2e16<`%msMcc7zxfZn$vHSeL_ zv%3a;d09jBv-ytmc=f2ya{=~~PZ77(&IXsyShaJ|28wqH*YrHJ9t!f^>tb+! z$NbIc>ii@02M%=hPMX(v?#MmnpxGOLnSjIiE9r^%wp-^{H@LU%I={BTw`}m1+E)Rt zy?O4SPaSuG%R269@WH%4Yw#*>OYK2;@;nAko+lfAgY$W|!3Xoa(BOl4UWF&mo8aVm zx8XOKXBa2FoagWcAIvifo;<67lV|mY-(a5c4PMpLQd_r(+pLJ&YLU2>+75-^ZiU~T zO@8|peg_qPhc)>fUHDBa{H8bg^$z;A*3Jaa>FDq98ur|u-^4F%;#W5DYnu3%oA^yl z{I({3*CM>7{vDEaz871$e|`q{+R8qbdw4C_yZ`R5H0SbABYtq7Jlx=e`}na2-?9#q~WKiA+}C-=+n?1y38Abdg8ZzQ;b3%TLP`#!vWea`Kk!Ht2tQ=_)g=t%XL zRl!5+-clO_PrkLmvrcLE7Et%y)VS(?P`H6Mj;VrE0J0Cu+!l!~eD|`mnXN36{fSsiHJK)s&3vhkDoH)Oi{O+95 z+kSXo|IEq#ee>o_;f2C6A4TuvdDgfOw$`48pL|ODyq?(u*hkdARf(J2+c&MFcgpOO z`Z_xXdirJ?Ubp)li+w73vl)9!?Ui~yecNh(1yAc`mD=adA-ryX4b5kqzVCo}4+OW? zZ|p*H()&zM_+yXyD# zmfE)9{(3E~wLQT@@5rsSOXkRaIH1jpKszXF2Y-CufZEN^0mV$bgcI3Bzj(!Mk~i#u8FVE#8+N~x71dH zXAL%A#MWBds)=vY#J69Bx72oqr`AKk)BF%@@8nCcaefb5#J{z6L=!)%i66TNZ>e>_ zlfM^S?fU^f%{gbhFQ9oPG)8QUxv(k6g@ZX-Yaa#IH*j_Cu7}U{ov`9xfe)>1OYLTO z#=kGV)yeQ&+~4H?z@UF??TIGai}1$v(pr06JgdW3Fup%pYnRXury6FE`Iv*3}xL89yGMQS|EOG50d`$-OMtylTcTSJ}*&_~pUs z`Wo-go)W(b*myPP;T*@&hSALDdg`m5e6FFovF7vVLCH4`Y(6#P$I~a?nw-y?^}aEV z@#|DR;4CBY}?*zMV!e@Y8i}0CX=NR4x zwsyI-`{2r)eQsI1+E`S->6WaTYo_iTPNLVQzjHp7-Z*{br&YYMPg`rJSKM(n#BP5E z&1+?K_?h(PR~wJ@EP6Hbn#*|O%y&M`Bm5$;*X0=IlKY?Y@;)cGo=YnqwH+&dS*4|( zkHJ0kzntEjWxZFyhtcAHP304Q9o&13@n4~LJv`5;^A@o2+Ha-T?wsWhRNV6@f2HE? z3;D=l4gc{KU#H^k3+>xi+jAIN7`+izpMA_2f&`YoR0_L3|rT&Tkf++&GCO(;qrX{25kX$^Q%7&2iBhl zPlDByjDHGX{p!j84A}hPKHKEU_b0f|1M7Vg?3}IF>)|c9n%9H*-v*mseIrKJ_8!eS zr|I`TTtDsWlg;&AlBA9?f!;AZ$NH`f*H?Wb&Y0KkuHfX$y3R&hZ|3a%t*eJ%=WSi` z6X0s@9dphEn^QgWd=9(<+Gs|94Qvg#FLF>O(TyB`6L9G3+R1(HdpM8xz}^RpaVx# zjw`t1`ArqO`TeE}cRqesh5H=v+bZ1a-fyaK?S5BxcU9Y z3OB#sSmEaP8>{4gV}*Mk@H;Eq=ab)9;g09`Rk->5z6y7L`;8Uu^UQCoL5{!QSmDO| zjTP?m&hM;n$MYL2Tz|i@!nOOI6|UXyEctHSHGDq&ism!J`^(PUGrSM}y3)_ZNB%He z&1XmAehW6v_lv|m0#{#9#r+O!tokn8D7-hR#s4v|TE_W3Sj}hBfz&dE+)sejjrU$A zPYr(n`#dUZcowc6pFe_qMwRn>9ULc z|5@4Xv(Enlo8uO0^x63aP0i=s+vJeHPBWLe#fg0vY^-bLbNJshp8@LDCij^g|6%O$ z_{aAYIF<;hXO>ov^JXr3U=)V^DAbNGzWE{Pk z`^Njux?r`7z?si_aP?7CG=YA7nsb;)({=!rsc%r(wDUiz8hY1qEZ-UQ5pPJJYh@#_ zbFg&&BHa`tg_purp>|T$hEQsSS{Czd}Erq%q>prR$yau&)6EQ=KGu1yX(9S zT-{oImyx?hUMt&yy$4voIrZNjO+7w4R6e<%>9Zr6dSZ40oBM3?YTp^GkGl6M&+{%| zb8FL2Z6a9R_hrZPdAS>S9PNDbP?LU>w9^(=al3Yzr6s#V87}(m+tLFVdus-U^b2!*KuB*l}&k=C-#2pDXR{cO$zPMql zyFa^f&dy8Utj#fC*Tyw;ZRN&zeR-{Ujo3Q}*ZNqn^UnSKLtwSs-?gb3pX=9aP3}F; zd@IqLZwmRQfgfdj$L;{jy(jrIhfZ)ey}I_}=+(qs^tn%TgUxI2ePRav@iY&|?Wyec zna2rWbL2iT6Rei|gnT;9T;>)hb{5#!oU=Y~FHPOrmt_^jcHV708F zHZ|ijH|uab@5OV$u0^hC&&vQ@-S@7!@OfZ0>*=SLo9`rg>#=vOPNqMF=AnIlWw*Cp zbDa)0hq+FpmwS%<`NlI zBVh0OXER^dx;HdbQN}aj-S!*>pKrt$a3J0atfj zeI9)RY@YI2b0u8edcC)N60Bxy;;sTG&RXR9WzL@lyZ+&y0qcK7mGf$_KI-xLEVzuh zrs9eD99$pu3#&Yz2b)Vs_sayN?aL2in8a>}%0qdiln6H9ekDS{Z!1}4@`FbPRc}${dAI008`M*}_YlE#t zztsD6u=P4G&x>4t_qBW6y=kBM-UN33xzF4TR?B@xo0{>tZrtOJ=g&rN0lRPg*{C_a z7kmS*?)csljsaeRB@y-$4;tWWm6_o-Xq>Nn!!x6HS|YF@v-Ps_bGd0#T0ee!)9 zd>ri}d^5+};Ocn?y&dejPNZqeJLq@7+HCxf%nd)f`drpQ)W>;!mp=ECJHXaq@BQTa z^mo!cocCRo-99z_0BnxjPksni%l$M!D_x& zWz3(z9W#0E1-n<&^)c?JVD-Fr{S0g_^;}=~fpdLnFR!m3!L?`1`@xPG{_~1kgYR9x zfctKtu8;2{4}jI>6X+kLx%PSQdZ^OWJ*rt?JTHd=JL$h|@yPUK0(l=}KIM}su4c&8c zW872jFZYGLbMU(PJ=l5YT7CkomTOs?n(^7Q?l1Wo^yXWc-h5NY_Y64iT~CAMKGRpl z_6P7`^y=336up|U-t+zl_L-;cIk4P$`SZ9xf%VV+^=INQz}5ACo?foM_lTFk`j_{J zm*MLAzeq3Np8KP}hxs#jA-&_8OS^v0(rZiJSHR}I2;Z#Xt8n$)EB*q`y+T{=6@LY5 zv++MxuHi>lAIp8BkK?{ZpL@mMz^=2s_lm#M|AXe?dH82#w@*#~0-GcEiZ{S&xmU5lQHa7gNif50#4R?>J>*MwJ4p?38_4h8#`Q-Y0uhP^#ny2= zel&G+di~MW_0%;CUdAj2_k5`*rUk5?YpoS*9`%gX2DaAgFePp{ntI|E2OF!NYjgxy zJuTPh5@@+bT^HxBZ`N-~uficrHD!o)7!Xb0jwBnQL?uTrJnAHZ|k3S3Ixf zHM&Zb&uerUd~=QZb9=eh=y+_)f{&zEw>E#yuVyV?qsxPHjrucwx%2WGT@kE*d5x|F zS2vfx1CZW5Xv@Jo{{II3?E9^>I(H16GgE zx?tBN_ZNNEgR3X+`e4UN%m!fBKs|Xk1gq!yyAe2h(lO1cPjYMwwl?SBI?9cy?;(2U z>e{7-O~BTWJ-I1ZEqhX%n(;Xgu35P!M>C%J+>={i%bxUiKl1F!Ey3<-P)%ExHO!Dl>oxlr;b6j(2 z*U$5zP3-TYvL|-|yO#Fu$%*v4(L7w6NtN9`YrVUemOZ%#SS@=}zAMdK<`yS*PjSKb zs(8-(WH=?(>N@qf-}XjRkIxjaYf|pXecR#fJqE6BuA}JX z`nyj*1lGUYr|occ{g0)WXP+JiUPzqdnoGNWo+EAI4*KlVX<*mV-hJ9d-%ayyZKhXt z`>gd0usO0%j|Zz|pUOLF<}$Z9v9rMbZYz8?++5*(6?guA1I&T@3{}_1caRgo>WS$G zn*HP@0ITQwc^=sPM_0FHU!GLy{@hrfe7_t-J?5E@=J+1bPOdcbMLVU^ zD3|w$#p~}%r=l6Jzjnv6p5geM1~%Tm`O|MVRL}qE;9i>R=306F)b#iKc&=Py`>e+q zVAmtp!kJ*TTnpONjL-S^TqXW2u;(c83*c&ruiN0|wX<4r?VO7(*Umz)+-t|b<2n!A zL9gyy&Y@S!*yn@Ie-8ex%LQYwd)_C~v^lSj(yJ%t zO0c=ZKUwiyBUiz_M%4B38u=7hT|R+x^J%bo^Pc`0u$s-o8VDP_hOS2U+~mIF-v2CI zJ=fVaU~^uF&2jYk99%u`)1L<$r=IueFM!q4@;-ennsxZiYyH-!Z|3<$u=8|1Tqn6P zo?p+k=g2#t(VzWxeW?!NZ#5pMwZ(5qY9 z_4I1i?sa@4SS{=6-u@a~J$vEn;Oqr$`JTK9tj$I__4A~khhzCoqmTGz`dkyYfL#-N zuZeHc-%9gvj^C>6_F1!UgUyj^;x@2at_k@!Xy!7vII*{bjSc@!#dH3?3-|n~>*GGX z1FSB0pMDQqeouZMu4ePdKFt_+qB~~xn z&%V6}te%#A`(w21TjyiV`exog0XuKk$8#h%#&h8La2@S4r+dN9Df{-PV72U9ZED8n z9D6>>eLIFVFy9pB|8s2FxA%kP?%SQQ{Q^9TUftU6qgN9@K%c$zAlN$X-Aliu{}s){ zaerOe?K6+xfX$J;^e|X0drAHf&0OXdC-%2sV{^_P0b5(HAJ6OW;Og;t6zsV-*81i8 zSf8~zhJD6(4D1+L=f}ZnS!Zo(#%KQ4TCVfhVx6DFmUVstEO(vvpsc6BC(^6?j^OqG zG+5mufA91RT)kdDdq(bfBN)f}{aKvz^`7!aY>t;TG3WDW>Y3A@z-rIY688c)agHO` zuN=qmsrfW`y#l^`z5&Ab+qLDGB|m)C-!=Kd(98a!~{v+vcr)htm{V&e7vK)9tn#b}qua#x#SD<;VtVEw{k{TN*4{Rv#Hyf*HIC$ILz{uHduysn8nw)?@UFD%rH&0{s1`)?Kc)oJd(vGm!0kAchm_hb`)wu!$`@K?aE zRy;L74tJb#|NS1Wp8fX(SS|Ci2DPliQ((31zo)@!<^FpHp7W_av3~$-^L#oNd2G*t z%e;SttCjojd3f?_Pwbz-+RW>k$YXmET;_cVu67MA_t2NY$*VoFe+FwauWKuh?Jr>G zkhT3Q*!9l-lj{@z*TB~1{u@pIH=3I9r_sMoGd}xIu216s4z`Aj_YbiCIZtwZ^mmW_ zlcwGMC(iyG51v5tSd-@d8%Mtu&HcAFefHm*;Kd96ZWA9iY*G90g8SdK_;0)zpPJtS zJ5IU(-iE7Z|GfiN%e<^XE$i?eSS|bSeXv@&|Nafn`P80RKOnVvKAnp^w#C3@-WIsp zRk&rJw8E2Ddt%$b+RW>k$YWa^T;?4CS1b4567b~Jp4cV9+RW?P%47Qg*g0fvmx8e+v*fz>h($5hKYi~*}<|BVH!mHTfEc+RKx#Et`N^L$#nJhnB#W!|;mYUTc$08d`+ ziCr74&AhIGJht_~j+Hf7AMCnhpUCxz{{~=lWS?vZR?9xw2yA@ziCiD!J(n9-cF(go z=Xq0XThKfC_8r+~G&p4KXlZ7NtTweAPD{;Z!| zAN^gU{VTicC(ioqjBO&#V;7qHaVPp+X|CUH^jW`yh%49cu!0{2KDy$m>0r2d%Jn-0 zO+D*(C|E7)=NM|4=Lf-RS--=$g9B#^@(byC43(@0_uqIn!ZOHBuYt?6KT&*=#IGl}z@rp8m?`js`Fil&}(a5`A+G@3t~ zHU12+x@&Dtxj)~vmb1W1(W_glJhl4Hdl=2*5L#+I6l|>@q_@^p=+7svto0nYer2r- z(bQAxxnQ-_x-$Of!PTwRoN{ZmmJ7gRssQu#IhuNEy#lP3TGzq<6L58FHK*KKt>u$o-xt-bRi0WmMem?_ zw9``SabRnmMsKaYcU(hUS?j0a`jxeQ22DM+UJX`DtsCS2S-85jnp1AA*77;9?>y?( zDo?G;p?A|fI%%o33v8{^>8*8H`Y#h#)_N^mzp~aZqN%6W>%eNMbs7A>1Xs6KbIPsN zTCNALSdC?^^3>{cw1?&~gO*y42V3h2^wzpH{Y}J`wcY^NudMY(H1*W_HLzN0-3tG& z!_}?ToN{ZmmYeIiF_yK;Q>)LYSu~HCwA9)Qw$|D7*6OqVHsZ=!zX{i`to2qj_0;+; zuv%)}5&v())veW>a%;7g+rbm*)vZ;YTK7Odk>=4yORaOj*4j^Rtv*NZBCf3U4!C}0 zt=~gaPp#hvtEJXS_}>Xvw^nn?t<_q70G?cpWv%kmIuHG1n#V~rYaO7UPqWrj=&kj1 z`n!p<*6@3q_(M(n;U@lA6Mw3SKUeUVz%N%k&z^hW)>W>}kI~fgeEbPmE$<7~td?iu z&%tV0%U^)i%J+o_;P=xUM|)x)1Z#7Rox41?UxLfLzk;h>MUFhDe+^Gw?TP&jSetoW zLwRh!1($grfvc52XZRgFd9^3@QLr}idM@O#Jq|AO{vNJ&T~Y57@Z{B=*eAi-%*#81n%kND73N}XFeR?YE_Zs+Rn)}Bw<@zP(-@u-ao++ zYnT7V^#>>w&e=$ePwjPwXa%m#~4#^YgxOAZ`#DSYU0~B zasPec)MLIQ3hvq;-^BfQhU2e&eiJ{niJ#HL7Zlw2e5c}`7uU*roxgWp4D4Fk`!@)! z^ldZ`?ZYd(efGiPU~}BUdH25|T!N-{1~L9Ogz^@exy&t2>?p9Y^3nKz0PMN#Gmd^K zxLWq)(qPY}an>%^$J(sZ{PxMe4A}g+w=WA;%Qc}*t(?1cCVqMFInrPxElxH7mP)=Cl^r964VTz-l>P@-=AYGPgMEzYcf-xgFnpo}+cq)Z?>W<>PZM zKI@~Yr-lu{){wsm*$}MeGi*+kcO$s^*{q4JS?^>t_1mj^#ol1!)Zbv7eds;BZ)lrBQ*$4Rv%XWo zj-Rpi12?bV{%Go%^8sKrTh8@?U~_3pu7kkM>wGYpdVCH6H;;EHntJ{_jl;mksi%ey zg4J#QclECO;o521Tx*{VY8mTDuw#{LeH2_hK1YMCx%4>(t{$Ib!R9V~J_J|K-|M%7 z-2;9%JBB&*Nsi;d&9!u(smEs;xVe^2H1+s&fvu(78{KgA)G{5MS{%b1`eZFlGb^96j$SnN{2KtXz}DgKOO4ka+w8)o-$Yu@{~WOPKAQC% zPp_7-`@zli%|%m>&p_o<);AALJvE#JwmyGmV!U?8JCR!Y53bK_%Rb7^yp7tpJjV>svZ{t&(Fiv<+^BgVl^r+%4e5c|PR&c|OL|dp_)QKE46=eB?d*n_#uP zXKPb4ek{H5> 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 {