From 24b3def0a1d7fb99d0ad9b34ea7acc75e2206def Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Fri, 22 May 2020 14:18:39 -0700 Subject: [PATCH] Start work on parallel segment output Output of segments is in parallel. Getting closer, some problems with chaining but mostly correct. --- piet-gpu/bin/cli.rs | 4 +- piet-gpu/shader/coarse.comp | 196 +++++++++++++++++++++++++----------- piet-gpu/shader/coarse.spv | Bin 43620 -> 44012 bytes piet-gpu/src/lib.rs | 4 +- piet-gpu/src/pico_svg.rs | 6 +- 5 files changed, 142 insertions(+), 68 deletions(-) diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index c5c0b6b..3fdc5f8 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -107,8 +107,8 @@ fn main() -> Result<(), Error> { /* let mut data: Vec = Default::default(); - device.read_buffer(&renderer.bin_buf, &mut data).unwrap(); - //piet_gpu::dump_k1_data(&data); + device.read_buffer(&renderer.ptcl_buf, &mut data).unwrap(); + piet_gpu::dump_k1_data(&data); //trace_merge(&data); */ diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 0519b2e..8130e39 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -44,6 +44,14 @@ shared uint sh_elements_ref; shared uint sh_bitmaps[N_SLICE][N_TILE]; shared uint sh_backdrop[N_SLICE][N_TILE]; shared uint sh_bd_sign[N_SLICE]; +shared uint sh_is_segment[N_SLICE]; + +// Shared state for parallel segment output stage + +// Count of total number of segments in each tile, then +// inclusive prefix sum of same. +shared uint sh_seg_count[N_TILE]; +shared uint sh_seg_alloc; // scale factors useful for converting coordinates to tiles #define SX (1.0 / float(TILE_WIDTH_PX)) @@ -60,26 +68,6 @@ void alloc_cmd(inout CmdRef cmd_ref, inout uint cmd_limit) { } } -// Ensure that there is space to encode a segment. -void alloc_chunk(inout uint chunk_n_segs, inout SegChunkRef seg_chunk_ref, - inout SegChunkRef first_seg_chunk, inout uint seg_limit) -{ - // TODO: Reduce divergence of atomic alloc? - if (chunk_n_segs == 0) { - if (seg_chunk_ref.offset + 40 > seg_limit) { - seg_chunk_ref.offset = atomicAdd(alloc, SEG_CHUNK_ALLOC); - seg_limit = seg_chunk_ref.offset + SEG_CHUNK_ALLOC - Segment_size; - } - first_seg_chunk = seg_chunk_ref; - } else if (seg_chunk_ref.offset + SegChunk_size + Segment_size * chunk_n_segs > seg_limit) { - uint new_chunk_ref = atomicAdd(alloc, SEG_CHUNK_ALLOC); - seg_limit = new_chunk_ref + SEG_CHUNK_ALLOC - Segment_size; - SegChunk_write(seg_chunk_ref, SegChunk(chunk_n_segs, SegChunkRef(new_chunk_ref))); - seg_chunk_ref.offset = new_chunk_ref; - chunk_n_segs = 0; - } -} - // Accumulate delta to backdrop. // // Each bit for which bd_bitmap is 1 and bd_sign is 1 counts as +1, and each @@ -128,6 +116,7 @@ void main() { for (uint i = 0; i < N_SLICE; i++) { sh_bitmaps[i][th_ix] = 0; sh_backdrop[i][th_ix] = 0; + sh_is_segment[th_ix] = 0; } while (wr_ix - rd_ix <= N_TILE) { @@ -219,6 +208,7 @@ void main() { atomicAnd(sh_bd_sign[my_slice], ~my_mask); } } + atomicOr(sh_is_segment[my_slice], my_mask); // Set up for per-scanline coverage formula, below. float invslope = abs(dy) < 1e-9 ? 1e9 : dx / dy; c = (line.stroke.x + abs(invslope) * (0.5 * float(TILE_HEIGHT_PX) + line.stroke.y)) * SX; @@ -279,14 +269,102 @@ void main() { } barrier(); - // Output elements for this tile, based on bitmaps. + // We've computed coverage and other info for each element in the input, now for + // the output stage. We'll do segments first using a more parallel algorithm. + + uint seg_count = 0; + for (uint i = 0; i < N_SLICE; i++) { + // Count each segment as 1 and each non-segment element as 1. A finer + // approach would be to count bytes accurately (non-segment elements that + // are not strokes and fills wouldn't count). + seg_count += bitCount(sh_bitmaps[i][th_ix]); + } + sh_seg_count[th_ix] = seg_count; + // Prefix sum of sh_seg_count + for (uint i = 0; i < LG_N_TILE; i++) { + barrier(); + if (th_ix >= (1 << i)) { + seg_count += sh_seg_count[th_ix - (1 << i)]; + } + barrier(); + sh_seg_count[th_ix] = seg_count; + } + if (th_ix == N_TILE - 1) { + sh_seg_alloc = atomicAdd(alloc, seg_count * Segment_size + SegChunk_size); + } + barrier(); + uint total_seg_count = sh_seg_count[N_TILE - 1]; + uint seg_alloc = sh_seg_alloc; + + // Output buffer is allocated as segments for each tile laid end-to-end, + // but with gaps for non-segment elements (to fit the linked list headers). + + for (uint ix = th_ix; ix < total_seg_count; ix += N_TILE) { + // Find the work item; this thread is now not bound to an element or tile. + // First find the tile (by binary search) + uint tile_ix = 0; + for (uint i = 0; i < LG_N_TILE; i++) { + uint probe = tile_ix + ((N_TILE / 2) >> i); + if (ix >= sh_seg_count[probe - 1]) { + tile_ix = probe; + } + } + // Now, sh_seg_count[tile_ix - 1] <= ix < sh_seg_count[tile_ix]. + // (considering sh_seg_count[-1] == 0) + + // Index of segment within tile's segments + uint seq_ix = ix; + // Maybe consider a sentinel value to avoid the conditional? + if (tile_ix > 0) { + seq_ix -= sh_seg_count[tile_ix - 1]; + } + // Find the segment. This is done by linear scan through the bitmaps of the + // tile, accelerated by bit counting. Binary search might help, maybe not. + uint slice_ix = 0; + uint seq_bits; + while (true) { + seq_bits = sh_bitmaps[slice_ix][tile_ix]; + uint this_count = bitCount(seq_bits); + if (this_count > seq_ix) { + break; + } + seq_ix -= this_count; + slice_ix++; + } + // Now find position of nth bit set (n = seq_ix) in seq_bits; binary search + uint bit_ix = 0; + for (int i = 0; i < 5; i++) { + uint probe = bit_ix + (16 >> i); + if (seq_ix >= bitCount(seq_bits & ((1 << probe) - 1))) { + bit_ix = probe; + } + } + if ((sh_is_segment[slice_ix] & (1 << bit_ix)) != 0) { + uint out_offset = seg_alloc + Segment_size * ix + SegChunk_size; + uint rd_el_ix = (rd_ix + slice_ix * 32 + bit_ix) % N_RINGBUF; + uint element_ix = sh_elements[rd_el_ix]; + ref = AnnotatedRef(element_ix * Annotated_size); + AnnoStrokeLineSeg line = Annotated_StrokeLine_read(ref); + Segment seg = Segment(line.p0, line.p1); + Segment_write(SegmentRef(seg_alloc + Segment_size * ix + SegChunk_size), seg); + } + } + + // 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. uint slice_ix = 0; uint bitmap = sh_bitmaps[0][th_ix]; uint bd_bitmap = sh_backdrop[0][th_ix]; uint bd_sign = sh_bd_sign[0]; + uint is_segment = sh_is_segment[0]; + uint seg_start = th_ix == 0 ? 0 : sh_seg_count[th_ix - 1]; + seg_count = 0; while (true) { - if (bitmap == 0) { + uint nonseg_bitmap = bitmap & ~is_segment; + if (nonseg_bitmap == 0) { backdrop += count_backdrop(bd_bitmap, bd_sign); + seg_count += bitCount(bitmap & is_segment); slice_ix++; if (slice_ix == N_SLICE) { break; @@ -294,16 +372,19 @@ void main() { bitmap = sh_bitmaps[slice_ix][th_ix]; bd_bitmap = sh_backdrop[slice_ix][th_ix]; bd_sign = sh_bd_sign[slice_ix]; - if (bitmap == 0) { + is_segment = sh_is_segment[slice_ix]; + nonseg_bitmap = bitmap & ~is_segment; + if (nonseg_bitmap == 0) { continue; } } - uint element_ref_ix = slice_ix * 32 + findLSB(bitmap); + uint element_ref_ix = slice_ix * 32 + findLSB(nonseg_bitmap); uint element_ix = sh_elements[(rd_ix + element_ref_ix) % N_RINGBUF]; // Bits up to and including the lsb - uint bd_mask = (bitmap - 1) ^ bitmap; + uint bd_mask = (nonseg_bitmap - 1) ^ nonseg_bitmap; backdrop += count_backdrop(bd_bitmap & bd_mask, bd_sign); + seg_count += bitCount(bitmap & bd_mask & is_segment); // Clear bits that have been consumed. bd_bitmap &= ~bd_mask; bitmap &= ~bd_mask; @@ -315,40 +396,8 @@ void main() { tag = Annotated_tag(ref); switch (tag) { - case Annotated_FillLine: - AnnoFillLineSeg fill_line = Annotated_FillLine_read(ref); - // This is basically the same logic as piet-metal, but should be made numerically robust. - vec2 tile_xy = vec2(tile_x * TILE_WIDTH_PX, tile_y * TILE_HEIGHT_PX); - float yEdge = mix(fill_line.p0.y, fill_line.p1.y, (tile_xy.x - fill_line.p0.x) / (fill_line.p1.x - fill_line.p0.x)); - if (min(fill_line.p0.x, fill_line.p1.x) < tile_xy.x && yEdge >= tile_xy.y && yEdge < tile_xy.y + TILE_HEIGHT_PX) { - Segment edge_seg; - if (fill_line.p0.x > fill_line.p1.x) { - fill_line.p1 = vec2(tile_xy.x, yEdge); - edge_seg.start = fill_line.p1; - edge_seg.end = vec2(tile_xy.x, tile_xy.y + TILE_HEIGHT_PX); - } else { - fill_line.p0 = vec2(tile_xy.x, yEdge); - edge_seg.start = vec2(tile_xy.x, tile_xy.y + TILE_HEIGHT_PX); - edge_seg.end = fill_line.p0; - } - alloc_chunk(chunk_n_segs, seg_chunk_ref, first_seg_chunk, seg_limit); - Segment_write(SegmentRef(seg_chunk_ref.offset + SegChunk_size + Segment_size * chunk_n_segs), edge_seg); - chunk_n_segs++; - } - Segment fill_seg = Segment(fill_line.p0, fill_line.p1); - alloc_chunk(chunk_n_segs, seg_chunk_ref, first_seg_chunk, seg_limit); - Segment_write(SegmentRef(seg_chunk_ref.offset + SegChunk_size + Segment_size * chunk_n_segs), fill_seg); - chunk_n_segs++; - break; - case Annotated_StrokeLine: - AnnoStrokeLineSeg line = Annotated_StrokeLine_read(ref); - Segment seg = Segment(line.p0, line.p1); - alloc_chunk(chunk_n_segs, seg_chunk_ref, first_seg_chunk, seg_limit); - Segment_write(SegmentRef(seg_chunk_ref.offset + SegChunk_size + Segment_size * chunk_n_segs), seg); - chunk_n_segs++; - break; case Annotated_Fill: - if (chunk_n_segs > 0) { + if (seg_count > 0) { AnnoFill fill = Annotated_Fill_read(ref); SegChunk_write(seg_chunk_ref, SegChunk(chunk_n_segs, SegChunkRef(0))); seg_chunk_ref.offset += SegChunk_size + Segment_size * chunk_n_segs; @@ -367,12 +416,21 @@ void main() { cmd_ref.offset += Cmd_size; } backdrop = 0; + seg_count = 0; break; case Annotated_Stroke: - if (chunk_n_segs > 0) { + if (chunk_n_segs > 0 || seg_count > 0) { + uint chunk_offset = seg_count > 0 ? seg_alloc + seg_start * Segment_size : 0; + SegChunkRef chunk_start = SegChunkRef(chunk_offset); + if (chunk_n_segs > 0) { + SegChunk_write(seg_chunk_ref, SegChunk(chunk_n_segs, chunk_start)); + } else { + first_seg_chunk = chunk_start; + } + if (seg_count > 0) { + SegChunk_write(chunk_start, SegChunk(seg_count, SegChunkRef(0))); + } AnnoStroke stroke = Annotated_Stroke_read(ref); - SegChunk_write(seg_chunk_ref, SegChunk(chunk_n_segs, SegChunkRef(0))); - seg_chunk_ref.offset += SegChunk_size + Segment_size * chunk_n_segs; CmdStroke cmd_stroke; cmd_stroke.seg_ref = first_seg_chunk.offset; cmd_stroke.half_width = 0.5 * stroke.linewidth; @@ -382,9 +440,25 @@ void main() { cmd_ref.offset += Cmd_size; chunk_n_segs = 0; } + seg_start += seg_count + 1; + seg_count = 0; + break; + default: + // This shouldn't happen, but just in case. + seg_start++; break; } } + if (seg_count > 0) { + SegChunkRef chunk_start = SegChunkRef(seg_alloc + seg_start * Segment_size); + if (chunk_n_segs > 0) { + SegChunk_write(seg_chunk_ref, SegChunk(chunk_n_segs, chunk_start)); + } else { + first_seg_chunk = chunk_start; + } + seg_chunk_ref = chunk_start; + chunk_n_segs = seg_count; + } barrier(); rd_ix += N_TILE; diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index 1329bb6f475f7cc836cc7715a5792d8410f48850..bd07113e408eafba66282984f99011952cd47a95 100644 GIT binary patch literal 44012 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

c+g4TArA9g3Z%?aSyfE*Zd}q`rQ<6 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 literal 43620 zcma)_2e@5TwS_mi_Xec*UJ^P;@4a_I4MplDlPExU(|9os#A!Vx2Tx<`%}-P1jE#JHXz6Gj}` zqu&gL--yveMot`BI&|St=Fml64rkNRqsMGDV&upH#2eT%eAk{~6jDXshTF=MwIrP zuUN!cZK^m^_R{|q$$U6+bw0CJYkfnU|4)oHix}%o6~i(8UlH2lS+_Sv-)aN!W+O&# zJ9_+tL8FKCv{t~quI@Eu{%5T=gBQNtBSsJHIXr7n7zol@g^BfjViVf6*P*l86x?b@ z-Nk>lpe~xW&X1Z`&&ZxpJ)o=FySl1v+wC_$aN_8Lacb>9HLoe-@2a+k6R+i`&HI#^mmeO^o7@~n_Z-fh z*)h&s?MR(=&E(Q)c|LZ)znxp(YG?4~qlWgLBWSB1b+0nNUE#gHVDnM)8s9UVt{q$7 zmS&Cz4jMVEyFSd(J>%v5psSjz+7qoeZuep1Moj3*s`tvwTWbj8+aDXfI^v4MR&9^Y zdEedmt$FXL_5hEkxDWPf_mSIeD|a=oDP!!c_JtE%zm|{Os~n%2;~P9^$iYL$jTx)m z@u^QczB#Kw=yl9#Mpq})7@Mji8&Qv}eXhERld&BD&e(>u;}0A&azyJE=enqQO&MEP z#ddDZvwqslScbcTrQzPq^++GSY=%~DhC*Kj^zFjMh1JRukKC zd-IcfP04Sj>IgV}cW*g*+9{%8JrnJ<^8>lv&m7gUbvsvinxxZk%0{1NGnn_$z41D# zW5DgXowGWzj@>@c|BvWxM{?~wMy<7*RK!1_H~#F^sfquM8)Mq!n|tDvBJOvKxM$XJ zU7i0&)>9UdcFxE5inwR=#_g)U59Zm{`#j0D_nMm5l(p=v&Vko{t-U7q;$v%^H(Kw- zYM%R^(L?ET&guu?y3N!nPwBASd$qdap~F_Q@ZrT}?&njt_eMu`0XXO6LNJYT3vcD6 z<~1evuIgfV{Ir>?npZwI!{(~ZJQ%%>(^5c8!>MD1ijl< z{JW|fYX2#D&QRTgHe}31o;31_VXE!UZSynigNJqx9x-9mAU{m}zIJn48$V+BXn03; zE8OQ4ZFhjj4LWQUDtO=OF6!Rr*3BlGB5GR~56Uww*jV={avAF(YGSP#{&<_O7XECT zuioYz)k}c(T6R=#gOk&HZNECF&g#Q9?~VIOoA<{3vd!zbovqDD9%~@C^6JZGg|FG> zd>4eL-zC85cbT?dZ@(+Fd2hd~ws~*AYqfcAzw5Vo-ET*=DLnn`3Qj+JwEcSf*{98W z`#GS^d;95W^WJ_AYV+RljcN1V@lAxc`|YgG2ag>zZqTTDE-r2Gs~h~Lsdz_qCp>xI z3!bu9I;&s9c~%_nHt{o6?llME z4ZdrG@7Lh_PsKZ`LGa{wOfT-N&ISAI!hMZ#byerVH}ziFduH33<8Iv}>+xTJpO@=# z5%?e+?a`Dx8=){>+uit!;e^{mD}gw2?w^Cz-MT^=c3JkF+&E8+;;S#R1-#w8Ewn< zSiaJK1TVDe`&w5=&3UkLth0nOnew^d$HZ?!1;H{Gun ztNqIu{fs-!XF+GxAN{a#8DD3$IDFjD@UChZWBI&Ld>*W#c3>^k<6aZ36~E@|z^ANX zSG8Wzrkf9N^WS(Xt^MWCJ+wtzZx5|!WNV&wYQ)>O?ZZb*>*EBUuBsa?pGNKV=%|LF z`JAr(kh)FlGl>JQHnG<7wa2{9Xlvv7EQ-@4(q3JtN!OqSem_XzYQE6RmIcX!0IZyseEI zH2z?E>8w_S`z|i?u+mg9I;&OThqqR;y|(MM_#jSln{N(3qOcEW@!0vE4jXvyj6QVy$T4HNsBVED z&Zm=ebBCBuH1Yl7ac%qQ-r^EehE@PZGl}~4NL4#l1;FmV|_3$#@4GsHG8~o-5|5bzE z+29Yu`NCNLzSCJvZrGn_@TaEYUDbK#PpIF)*pU=czWqY->_gT1XFSk8aD<7XzX96!+bAM}|dsnTuAo>rZs!i|1hu9Zx@s*P>WxQfvOwTD7>^yx6`)-H$RK?PjEw8)s3t{is>` z!)K;eGlp?zr*8Mv+4>#Wm}-kM4|7tdZQ9R;rani@r=#_IVcO4+W_y1_G#AHbF76w1 zaX$1_Hy7ta&35MEZ+eN<4{R(ob8&vt)|j?myj8cyZu_NcpMKbu0oz_p-(_o?x_kv{ z=OLVr+}60l{gP~-;p>3SRc>D!P$$<7!P8UJ?0ch{Ywu6)8`n1dv^f`BP;1Xvwgju` zWBhHY6Ms9f@zrd*eQlFFmI2gi`q_48>a^VjY+E(wao5^rzwsFe)<>K9?ndqYv8^^^ z?nNE{y}|mcxmWk0?njx9Vjufb>#LqN`+;qv%|7<0whv=zvyUOv@gEA-Urk$2VH;N4 z)QmNp+E}*HroRtiiRC*CW2tHL`$24DYMYv|#!?&0Hrn*}y?gu*1shAvxj&3re|6g* zRdC0pc6^~ZpK2!;nmMbTUTEq2`)KL=Y_NT+xev~v_FP)rcjr=TS2x~y)aGV8ZI0za z>cqPUtiPJ^F0O5I<6T0nUEO$>QX9{9+SIPBHOKGX?oW%K)S6shOWW2}{S@rJkXyb@ zKl0lsUg5Wc?Q0QozLVOV&HpZHx%u5qt&iIJHNU6UjANVoC|>&CPp!Xt?32OM75quC zKCwR!_P%WU7pa{O?;G-0YVQ0e?^nTPygrc5+HSuyz`LkzKQq{PbJl#5nlD)M9csQn z%_r2nf6Y&-xo0`~-iGGA-FeIBtn=pQsoZ|=r*@2L&YS#!TGRd{*u1u&$X~Cy_ZZ{m z^VYb2uFCcMH??uqj4SuERZaWs0OPve$#?r&n;WmQz{d2m*159H`C$Ir(u^th^Hwb} zekfs*!>@sRzYo7nJ0<*1@a#x_M!f;HudMN#a8hpR_9yrAT+Mvms&To8{)whT{QbPw zKmE-{@Z;et(B8AP2;6-c`wH+);0t4432bb0w7-?%?jLpI%YC1trhWAq+a`A3-DnT@ zy-mq|Un5Wd1L0qD+5g^PW4VuvzfbL}Zalf~a%{gGv*w!hz->F4+P2Pzd<d2f8Xb5xBq{DTVtu`@}IR$U4QupwWj~a zV9%2Kr62s0+NQ3*+;=9%*ZyUVbN;ORXyi(M^TU1C2=52)SMVj_KI3S&{h{#gv8SyP zeiVG!gGcWeehQpx)9^D|{P+#!o~xJP?(NuD^=4C58;8H$;>WkzJ2zMGKXX&Bz)X1- zJfNlP$ULTubgd*OL3rHQdjY zxe9JP-?_%_XN&J!OYVEtaQ%J1TJnAk?mN}e?mN|z`%X37d*PY|-;VgcJB{7`wk^2N zb2}B>d!X+2N7izW9R zWB4ZI>wCuVWf;#@1$Te>o-ua0?;1;fe}ntZv9$Z%G2Hp_-D9}(?fb`)`wp_?zK1Ni z?;^vE=ljTTcvq`tC8@`S9IixX3?;OMJ@3n%PkMA2xyYCyrZSVWWaNGO7 zQNAfp6+U0TP4Ss~GTMfGcwnyHsr8fak-rP~8CgB;{tmX?eMP(X;OfWM?cN95R(&I$ zUww8_ONaXtmB?@Yda zulx*bY<2VX*-9<_ddC{yjx9Vv(9a^)WK4|Kv z)pOecR&!l4-<@#dX)_<6{nQe-FSt2R)1#>;pBcc$R8PBaft&fvh^Br@ozF~QwKAWX z;l|TuK0bS@WuMIoR!iTrgVkoE#Agn$Lce|=ToSJ4ynjlZrNLfbFjj3#QPlh%AeOpcR-x!;d$~UG zUkj|iYqu@qS{tmUuipja>k`ZNCF_EBq*mANcL%k^S|4mKS?dkp`jl(EAzVHAZ3JGA zVq0U%^-0W4!0niQvC|H2%vZr>+|AImCGO^6-xb*2xN?0GcS~@Y>sIh)u3Mw2-&F5~ zZNO^o7x%*UU@!NAw(TgpQrrvT^fdtN9A}+(0IT^f!Fk_+7(2q%&DHN%a_4;uY&(Ou zrdHnt&CiTo!0KMM*;X6nbNV(fb1;|K1~zQ^B){Fjw$I${4&H*|9NAv3kFm4Yt~Dw0(!7W{%>-*$gyN-rXl=JPrylA-6#Z-`Hs&zu^f$b=SsQZ%^+A-xJh--7XFMap#>l*k z0;@SM_A5V-Vl4X=r|mefZNtZda}J!F36w)9`e`#?HS0->Y@8eF98QC)IhV%y9@xt`+D@ma8At5gok6X?_)PFXit`dbYkB&&evNp(=eL{% zPd?w*w>Xn$!__jbbHQHbqwO4un)!$m=X|hna&Nc*z6C`+K0g4foA-It^4NX|R(qeP zV((iQf*qUB!;V$EV@luF|J#@@(zh7X#c;KZ=~A$lW72jBMa?mZlhb8ja|*v4Y))Cb zE5Q1w$LB|2+uT^M@0DP+fs|`&?JBTswYlb3Qzsv7Kc=Xek2tY^0xswHTKFmy_4r%| zR?oS+9<1iQ#Cg94tQP+p!DY;w;OkM;8!#Ad= z$LChCy6tbKmdAD*SS|DY3$R+w%I)CHo3>w4?xZ+x;>7tCxXj}YxO-FGe&w!@`^%WN zH%{8$rH%a*emC5_ve)kcyAJvpPp(h=?*r?fXQy9-)%3leTApX82f#i%scXNNS}n02 z1ZON8;r9?+pYj>!VYqton+$eMZEH-qK8g7VxE-_o?DQzyn6DDQjQbdxw#0oL?0VYX zxN?0G_X%)0Z%@LVhwRO#z-s05&C_so<2^$y&-2Z*V4rW)^L+ChSlugmJr8$%?cX?Z z{mS)qo#j_kyWZCF+y|`xxBJU)u({Wb{Q|W-WBx5zEqnGwuv+%)@4#N(d$heoQ8Ndz zIUG}S^~C)H*t47Gz(0c3%;9Bfd3;|1m-GH7xH;wd;#IId>fV1|qfSoR{!CFbC$VvT z_Iw>|U-tP|YI*v66KtQ_-T=$j+`G+&7-3e}V0{-1Gm2t7mV01pa_xTVu-g zNzBi{iRpXJ&*8@OEa~HWlP}=v#{81{Ka@`?wl$`_<-`2Hre4?S<0~}RGyK0`=O%OU zHCRnQ<7rcKZyif*JZt$;H9v;Bx!-L|%DvAT{)XXied)IYT<)_@_~JB49$j#K)YGOf z*nF}Vr-$pO?q2*BwU_5Z+YA&na~3DgOkn#8pBY~6hgsnIsAoUS3Rcg4@HZH>#F!nd zmbIP(>{_eGXHKxXbK`F|^4R7Ar@iYsH{5kq*T=P=2dr*;=Tn~c^MQ?-@AM0R)%-ox zbH6n07X+)@-rtDiw%4{WSWTP17s)+eK2I$IUY=TAJ55@edm!^`om>_JJD+*4)E}0%+Ue$8cbla+pw>+Gm5?Zx;Mn7Y3u3wD@)Y!2bS3l!q9#;lu9{t@+?mW&( z+*QDE8~YCSukVA0fz|p@jB_A$;%FOA8AUOUIQfhKFG|U0&_UpR zxm~Nr=V0*Sln=Q_c+VRN*3X>pqiFZuW}n8lpR^qfF5{1Z8(%#>W5LFMj4>Pk5U|<@ zb${c)wo8B7%l_-{8+HLoLb*=jsxqXo_K0yJmaSQ@nHMZcRaN``||{_ z`?z^;J`qh@Vw?oFZRX=-aOOi>;(ZsKc;$XS6H^_dEU;u;a`;UIx}rJ?$)0lJf00!b04ih?Y>-|+J2U19)1j-m*Tu$MJ@Mv!#@*yHF!O0b?sMDt7TrU z0jo9d&p$!aW}8*1m?d%@~u zKKH@R$9t#t#-dGoRm}sVAS`f^Dasd|m{rm-)N|&-`dlKEDHNGoRuqw`aRe^H8ZRg@VCI;TN3+i zu(8L~{_lX*vZn8XZKFP^{+#?f*tptUgEy(w67zkqG4mbwA7C|o-=miMS@2J4KMSn= zo$EvDe^I=&|GTzZ8{22GkHE%AUmt@VbNc!Otd{pHpMq_p?z5Bp1B!jwx7b+D)#qTx z68;5v5+!^7ORzrbxj+5~>{@t#)SmtQ6VoT|p1k{l&D(KnPu|mmwI%Nvz-8Xwf|q&E2+v-$t^WEX z@0q~*n74mMRBmk7au%>-(dM5QmAme3g zg}{eU8((|cE(~6r`1wAw2wdOE#PK;%yW@5a%+)$=7X_F7E(TZgc_{O$R?e$qPWygf zrla@!Uw^pTWMsd0ETNstIn%Z{Ma?|K#&HiV2`)d=mV!4wLzYHUPv6Uc%XwWEuI9Xs zt>Z2SSI=Hv9&8--dzkC9@clN}{q^LPQ zamKd_xIAO4!M%^>KDj#FzjvXY`^6ey+i7zx{4>yMiMb|NEoX8qaL%N*oXNGp+I;Wf z=ixfFpYJoZ`B{?lwk}+|{dud29|>%lX>^Y#VjwTD~F0zU*6^*js|j^S2ee+$USZ)yng?4cvTk{mmY^D+>wmi)A-**>4gem1^F zKINa6u&>?lb3PJ#58H9JGQWGmy}!91wZ-o{U~R_Oomw8--rzFEK5*B_7~0~uFIbx~ z_M(<=%K7)cy1%?`6aQ{Bb^Z6FmOI7+sGTEg_xfP!Arvp|LuX*H&W3^2oTJt|FR+(!v<;^mNHLB$aSj4ICUYG@Est#^*!;B}OfC0!MbF$Q@GaD~ z(PkU<(co_KcFpBu;A;9CN1Iyu7z=i7vqugAt7VU9Q;YvNuv*4F9;}u>$2Jk{<+!y? zpr|=+vGbhyR8QQ)z{U+fT;F1U9RYWL>8CAzM}oCEHrGQQ+fm>$=F#vn<}q;hn||8T z?pUxkW4bo-*p34mGyCs&u=`IvK9j)ews-yHv7G=m&&=P6V72U449bmb+%!L14;FYM=&E;3rYRTmuuz!yr^KdU*?M}2j^WFznPn%zZ zyBJp5+z(eP+dKeQ-eybMtd{GR3}p-k6N0Eq;%H_4Bh} za^d$Vnl{hrNlf5lVEgbMmROI&)f4LpaQZC$oTgB>En5@ZPoMp#|vQl*5>ze=i@hE_lIlh+&FIioYRM>wWsgjg1>2eFQRLU z-%GXMtBo9ghpsKZZ~h)^-%C-9p*{2cGPpV4e?U{uZ%BUx8$;c_|0ilMKbN(=LQ!*n zi?eR8f*o7>`ZL(RCet=Puff&tWBvVp^%t=1)crh4Tf-Tl_w#{hE8>Lv(F-)${u=ux-`N<0Ic;W!uYL}8Jm%@%m+Ke%mxbN9-h=*wrXHWK zz+X^2o5qmqlU)A`Zsz(mntFV^IqNf-7|DM+Z`hPc^_uqqtGVVGQwQ8w+LBKv*nBc3 zxqj{m=hwM(PSbZ^a5KN@(bSX64B&DuzXjJvJ^juI&fahy<@|go%u8Um1*yjW%->mIiXzKBq8(hw%T%U3-om1N* zSM%~e{rF6C{Qg~Mxi5{|>Y~F&73eR@?mh(DHS;kNdg4 z2>3>7`!c3&^-0@B!P?BnyyWR~F|cj4nU_4aeqdv1GcS2!_6OUCHuI7@UmKCf;$UNW z7UI7IntGm*mjtW%y(&IS!Obtv0!zbv4p2|KWx%%8=ChUmcGR+9b#qycT5e4L-KcMa zT{~^dgXKP-I&Uk0e?V;;ZOLOruz94wYYlG#ntq0CNTNf-(%niWV zXZ}5SdG^_cVE37Q8Pm4@zwEQE;O;Z^wA&hNTW#5A+kn;0Wm{^wdAe5HgIynO z+fmE2&jx_qXSUInvF!jhkMy}CT&>(^YVNb!*#S!OA~KbYWgH0h z&oZi;)6Udt*?+r()pADNe|x~y)s(gZI^o@jDi-wg{R&?#1Kb*^9@4<;I#st$*g}1n?F3>!U4woCvm$tk+3! z*GoNfcQV*D)|UNo3b?sHzKf=wcBg{XY@c?gfz!@B<@&|`J+S*U=Nnp`!T+eo=S=YF zlsvP_^>IwTr#TDUO$`10cXz%I*FS47*C+AL20Qmc6 z=Y!{>Pi^Oc<(cCjfaj}i7l7qimmh+!roC;nCEkT#HRBmqo_H65_0?uvdD>k9P97J7 z<=Xb8kITT0!FHEY%ah0D;A8Qzjkb*YM_}WXb8{t{de-?Wu-X-rwEHnQ?d(skpSU0K zJV%~A&yi=wvtypF-!)*@FMnV0CvY`$@a$a&_VVm$yOyG69IXYU5EJZJAF zuxC%*oUW%<%RK%JoO%2ySgwt#x`qGAdfp6{XC8kJc0FyQE%9yztJ#-v<%xG2SYK_% zm1iD*0XDaC{(lKq_uknziEf0-s8IeYB;|yTMtz8p<@5kD*X3v9jUrnqR(A3lJH()i} zr`>PCX=k2t{bGL!+`J$E4oy8izX!iaas7-T*GIeWAzubNm)<+g&38b5fUD=X`agnK zWxdtY?iH|cbKjQhmwx{QHg0(~UWKd2=g(kc z_(Ki;c!NJ%@E5@^)jWIb7jXM6&)+ZM>JO4DsaLMVQ?Au5xCl9O5#2WPF(G2`xsc8ah)T1Y)^p8xKF{= zo}?u1)8NF_p0>|`wHepBmdExSxQzP(Tg6E-l%|-FN%t1Xj#q%;RbcpSetQOQ+aID zv1Vo54!Bw$O3rB~IB~V7Z5LRZaorp8*ro@UalZvu^WW4?+!?`%t37RJ0&6p_drThN ztYFtNXKgmPTI{ofU6a`7fUD&!&Iz`Sdd}ipVE3;3)IR0C?>UQ7=bp0!?Q&1p2;Kh=(~_^y;Hx&c z{~xBMzyBYm;hTUrt$FS-OTx{gyvHnsrk;Du(qOfW+Z@$0m&=0Ha*tUKtacx9vsagg zXRm5c+i!!lxo;e+Jhm0VW!#nEYUMpna09?l15U$pTlKI^ToVeQ4 zc4M$Mu+q&-%hpNvnkHm?2m0pir3;4 z&t^aBB`BWFrKoc@ccopg6cjL%vO&`8|zx>g5?e9ZfxF_ za=jR=mRx7Y{}Q;mxf)Y$uI6$X*zbty<|M?MHnz?aEw# z1lO<3^-47L$+fbU7y-q zeHOimc4e;DwfdMk*Xz;Llj{v&wd6VtKCM)Sbhn%oi_Kg z`P>dxH&=0T-2&Uz6t68Q$#pBR`E5h(yqfc!v~ym=?``l$3jQScsha03-UT;qIljBm z)N{YM2dtL+mVK&a{11ZFGS-K{YUOAC!|(?v_M<&*Cxf-Qp5`u(?NM+U_c6Fy`I-MX zJaM(B?Gs>a#&r(lu{{m8ugt+SVCTi$^q1=s|7XF*$bJ1eu$t{pqJExY`+P3Q^|8Hc z`9f`XoyA$_?Xm4h@ftvJowuXjf#N#vM4fegk#^-ezue%j7W{Sa8#T`uUxFLAT<71R zsb`&k4_3=M+oxLQ;SXT7tn(kiYUMh=0?&GAPuo9%wK>1$E|2Zc;4d0ur}j52lCk71lw2U;BR2(CF?BLC;o4Njp6y+mbrQxtY&-H5YxoIRt-Xfrd)}vT^C|oJ3{5@H3!j5OqUd7`=U+{~^z{X}?CVRoT66@vXs_*Gs1j>_C*$;i-_U&Ay zT?d+a`sxIyFJsu3nttW)B+zOQ#cO|xYrHpgca8U>K7isH52nr2Rvu3XN~8AtEKO`!RgzW_N}I0`kn_|_B}6Ltz6^z;Pzdv@%(7& z>1zRS`Z9)nsp;2T1#bP+a4Ksk6ol5~p0_h2XX+*LYzx^{nwC zV708VG1M~lMZs!Wcn@`zKe>C-seQ|KcZVbn+reAaHwjNKS&8T&F|wTyjPuv$6x<>2O1_Om>i zddB{3aK>&7$F8PdbL>YD%j+()GZuL3tlnfIz_>dAXG zuv(e->hR>P-MrPFCstyw0dK}$6HPs9xE5Hg5BG3CfA^yO+HiGan!nsVurIdtz>ZU! zW0B|2D69|mUThm}&G|bKo7eG_%-^IM52QYU;{2UN?R@!Jy#;YHfBI}$^Neu=xG~E4 z+Yn7X^S2RLt(?D&;h8_}jz`UTNbF7E&Dfiwsb~H+1FL2Jb|#n2;p)aTf4TFwE4Hn` zCs1p1Eb`3X)?lAkY@@9?f2U&eI+>FBJEg|n1HVgg{!XKI{)STTLY&N>KD*XDW84OA zjB@_AMN`lGZ3k8>=WhT!^QYbMsBKS4>>a?(*gK-BXa05qt7ZO%kju_+bz_>p-1+Ol zHW2JMwK*1f=5IH!pA)vx)||gHv3Z?N$^3n<#@&wyK7?a1#iaQ8%;g)w+~n?^EZ-Q_Jyk()BNSm z-)L;zV8^M=vB)!j2Y|<8vyHaq{QUr%*Lf7@?`-PxYkV&C1r+D+ht$sBM%06ddgf~=Sk3t|Ph+S#hQ4S$V71J_FtA$tbEy4qArFU}kNv3I)@R%U(Y59I zX9QR+p9cqlvo6N9e>MHm|H0t0|B-OD_On;J|55Ptub%!#qiak5W58$+niLNdE9|cxR|3`z6~GH1+Jq&7$F8Pd`Z@(%_Vr!3TK3*X^l>WOzFuwgcN)63^mjTq{Tavp)bwl4_0`1k zx{{K6=T$Yf&mU8q>uabp*XI(aymwwy@I&#twBX)bFRyv#?0fL5(91bH15G{iaVFTl z^XG-X57$TiQ^s=+wU>XUPutlPHFFi)*ZtUk0KR}?U+v$nt-MPJw@9w&%OCaU_ZZXqb+@32~OX} zzY4BqjH_xM-yhe$xo=(#S94q^)O+_QVD)P##&B-c{IiXjH|ys9xDh|EpHSQ%?ulz_ z?76(Iz}MGUpBpHy#ZA=NH;)oO`^L7<7u<9Go0?}_*TU^9f7bIlImI<|58MFuat~;` zo^lVxe#O~uH-gQx+;2Cbsb|i93RZJ|(*MuEjw5^UX1IRp_WyHgFZ!0!5Uf7fCuh7&po;$#5#?2nM3#@)8#kh`5%{`F3 zt&Mphw!6XRp|5Kz*VlP>jLvm(y0`W%=lDJ}_2l$xu$s?8$@_k|Yn*re55Ub&J?$O@ z+g4lJJp?Y-=3%&g>aNWr)LyQQw#gJV^A=}q9s@Vm=5aLja&4Rg_pi3(@B~;bc|HkN zE9d+vxOzF~YKieQ*zt!y12?Ak9M|Aku$q6)>07RSe zZ^3@H>Z2`vz6iF@tmRAKa{tNoF{ZhC{@e$dPwR4DxIfDKi0`Lvp?Lk2lKaTd3Vbuz zwfQ-9^7 { - rc.fill(&fill_item.path, &fill_item.color); - //rc.stroke(&fill_item.path, &fill_item.color, 1.0); + //rc.fill(&fill_item.path, &fill_item.color); + rc.stroke(&fill_item.path, &fill_item.color, 1.0); } Item::Stroke(stroke_item) => { rc.stroke(&stroke_item.path, &stroke_item.color, stroke_item.width);