From 294f6fd1db5deaa3d21e719feb5c1aaaa2967400 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Tue, 2 Jun 2020 17:10:20 -0700 Subject: [PATCH] Experiment with new sorting scheme Path segments are unsorted, but other elements are using the same sort-middle approach as before. This is a checkpoint. At this point, there are unoptimized versions of tile init and coarse path raster, but it isn't wired up into a working pipeline. Also observing about a 3x performance regression in element processing, which needs to be investigated. --- piet-gpu-types/src/annotated.rs | 3 + piet-gpu-types/src/lib.rs | 2 + piet-gpu-types/src/main.rs | 2 + piet-gpu-types/src/pathseg.rs | 46 ++++++++++++ piet-gpu-types/src/state.rs | 2 + piet-gpu-types/src/tile.rs | 21 ++++++ piet-gpu/bin/cli.rs | 16 ++-- piet-gpu/bin/winit.rs | 4 +- piet-gpu/shader/annotated.h | 18 +++-- piet-gpu/shader/binning.spv | Bin 15652 -> 15876 bytes piet-gpu/shader/build.ninja | 4 + piet-gpu/shader/coarse.spv | Bin 48612 -> 49028 bytes piet-gpu/shader/elements.comp | 50 ++++++++++--- piet-gpu/shader/elements.spv | Bin 45536 -> 47936 bytes piet-gpu/shader/path_coarse.comp | 107 ++++++++++++++++++++++++++ piet-gpu/shader/path_coarse.spv | Bin 0 -> 13268 bytes piet-gpu/shader/pathseg.h | 125 +++++++++++++++++++++++++++++++ piet-gpu/shader/setup.h | 1 + piet-gpu/shader/state.h | 10 ++- piet-gpu/shader/tile.h | 105 ++++++++++++++++++++++++++ piet-gpu/shader/tile_alloc.comp | 73 ++++++++++++++++++ piet-gpu/shader/tile_alloc.spv | Bin 0 -> 8248 bytes piet-gpu/src/lib.rs | 84 +++++++++++++++++++-- piet-gpu/src/render_ctx.rs | 17 +++++ 24 files changed, 657 insertions(+), 33 deletions(-) create mode 100644 piet-gpu-types/src/pathseg.rs create mode 100644 piet-gpu-types/src/tile.rs create mode 100644 piet-gpu/shader/path_coarse.comp create mode 100644 piet-gpu/shader/path_coarse.spv create mode 100644 piet-gpu/shader/pathseg.h create mode 100644 piet-gpu/shader/tile.h create mode 100644 piet-gpu/shader/tile_alloc.comp create mode 100644 piet-gpu/shader/tile_alloc.spv diff --git a/piet-gpu-types/src/annotated.rs b/piet-gpu-types/src/annotated.rs index f7a6ad6..d53d870 100644 --- a/piet-gpu-types/src/annotated.rs +++ b/piet-gpu-types/src/annotated.rs @@ -3,9 +3,11 @@ use piet_gpu_derive::piet_gpu; piet_gpu! { #[gpu_write] mod annotated { + // Note: path segments have moved to pathseg, delete these. struct AnnoFillLineSeg { p0: [f32; 2], p1: [f32; 2], + path_ix: u32, // A note: the layout of this struct is shared with // AnnoStrokeLineSeg. In that case, we actually write // [0.0, 0.0] as the stroke field, to minimize divergence. @@ -13,6 +15,7 @@ piet_gpu! { struct AnnoStrokeLineSeg { p0: [f32; 2], p1: [f32; 2], + path_ix: u32, // halfwidth in both x and y for binning stroke: [f32; 2], } diff --git a/piet-gpu-types/src/lib.rs b/piet-gpu-types/src/lib.rs index 75a7731..62450d2 100644 --- a/piet-gpu-types/src/lib.rs +++ b/piet-gpu-types/src/lib.rs @@ -3,8 +3,10 @@ pub mod annotated; pub mod bins; pub mod encoder; +pub mod pathseg; pub mod ptcl; pub mod scene; pub mod state; pub mod test; +pub mod tile; pub mod tilegroup; diff --git a/piet-gpu-types/src/main.rs b/piet-gpu-types/src/main.rs index 9c40051..7913c5f 100644 --- a/piet-gpu-types/src/main.rs +++ b/piet-gpu-types/src/main.rs @@ -7,7 +7,9 @@ fn main() { "scene" => print!("{}", piet_gpu_types::scene::gen_gpu_scene()), "state" => print!("{}", piet_gpu_types::state::gen_gpu_state()), "annotated" => print!("{}", piet_gpu_types::annotated::gen_gpu_annotated()), + "pathseg" => print!("{}", piet_gpu_types::pathseg::gen_gpu_pathseg()), "bins" => print!("{}", piet_gpu_types::bins::gen_gpu_bins()), + "tile" => print!("{}", piet_gpu_types::tile::gen_gpu_tile()), "tilegroup" => print!("{}", piet_gpu_types::tilegroup::gen_gpu_tilegroup()), "ptcl" => print!("{}", piet_gpu_types::ptcl::gen_gpu_ptcl()), "test" => print!("{}", piet_gpu_types::test::gen_gpu_test()), diff --git a/piet-gpu-types/src/pathseg.rs b/piet-gpu-types/src/pathseg.rs new file mode 100644 index 0000000..5ad382b --- /dev/null +++ b/piet-gpu-types/src/pathseg.rs @@ -0,0 +1,46 @@ +use piet_gpu_derive::piet_gpu; + +piet_gpu! { + #[gpu_write] + mod pathseg { + struct PathFillLine { + p0: [f32; 2], + p1: [f32; 2], + path_ix: u32, + // A note: the layout of this struct is shared with + // PathStrokeLine. In that case, we actually write + // [0.0, 0.0] as the stroke field, to minimize divergence. + } + struct PathStrokeLine { + p0: [f32; 2], + p1: [f32; 2], + path_ix: u32, + // halfwidth in both x and y for binning + stroke: [f32; 2], + } + /* + struct PathQuad { + p0: [f32; 2], + p1: [f32; 2], + p2: [f32; 2], + stroke: [f32; 2], + } + struct PathCubic { + p0: [f32; 2], + p1: [f32; 2], + p2: [f32; 2], + p3: [f32; 2], + stroke: [f32; 2], + } + */ + enum PathSeg { + Nop, + FillLine(PathFillLine), + StrokeLine(PathStrokeLine), + /* + Quad(AnnoQuadSeg), + Cubic(AnnoCubicSeg), + */ + } + } +} diff --git a/piet-gpu-types/src/state.rs b/piet-gpu-types/src/state.rs index 35076f0..602fab9 100644 --- a/piet-gpu-types/src/state.rs +++ b/piet-gpu-types/src/state.rs @@ -9,6 +9,8 @@ piet_gpu! { bbox: [f32; 4], linewidth: f32, flags: u32, + path_count: u32, + pathseg_count: u32, } } } diff --git a/piet-gpu-types/src/tile.rs b/piet-gpu-types/src/tile.rs new file mode 100644 index 0000000..5a28037 --- /dev/null +++ b/piet-gpu-types/src/tile.rs @@ -0,0 +1,21 @@ +use piet_gpu_derive::piet_gpu; + +piet_gpu! { + #[gpu_write] + mod tile { + struct Path { + bbox: [u16; 4], + tiles: Ref, + } + struct Tile { + tile: Ref, + backdrop: i32, + } + // Segments within a tile are represented as a linked list. + struct TileSeg { + start: [f32; 2], + end: [f32; 2], + next: Ref, + } + } +} diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index 31024aa..04a20ba 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -185,10 +185,12 @@ fn main() -> Result<(), Error> { } else { render_scene(&mut ctx); } + let n_paths = ctx.path_count(); + let n_pathseg = ctx.pathseg_count(); let scene = ctx.get_scene_buf(); //dump_scene(&scene); - let renderer = Renderer::new(&device, scene)?; + let renderer = Renderer::new(&device, scene, n_paths, n_pathseg)?; let image_buf = device.create_buffer((WIDTH * HEIGHT * 4) as u64, MemFlags::host_coherent())?; @@ -200,16 +202,16 @@ fn main() -> Result<(), Error> { device.wait_and_reset(&[fence])?; let ts = device.reap_query_pool(&query_pool).unwrap(); println!("Element kernel time: {:.3}ms", ts[0] * 1e3); - println!("Binning kernel time: {:.3}ms", (ts[1] - ts[0]) * 1e3); - println!("Coarse kernel time: {:.3}ms", (ts[2] - ts[1]) * 1e3); - println!("Render kernel time: {:.3}ms", (ts[3] - ts[2]) * 1e3); - + println!("Tile allocation kernel time: {:.3}ms", (ts[1] - ts[0]) * 1e3); + println!("Coarse path kernel time: {:.3}ms", (ts[2] - ts[1]) * 1e3); /* + println!("Render kernel time: {:.3}ms", (ts[3] - ts[2]) * 1e3); + */ + let mut data: Vec = Default::default(); - device.read_buffer(&renderer.ptcl_buf, &mut data).unwrap(); + device.read_buffer(&renderer.tile_buf, &mut data).unwrap(); piet_gpu::dump_k1_data(&data); //trace_ptcl(&data); - */ let mut img_data: Vec = Default::default(); // Note: because png can use a `&[u8]` slice, we could avoid an extra copy diff --git a/piet-gpu/bin/winit.rs b/piet-gpu/bin/winit.rs index fd30fa3..3568732 100644 --- a/piet-gpu/bin/winit.rs +++ b/piet-gpu/bin/winit.rs @@ -42,9 +42,11 @@ fn main() -> Result<(), Error> { let mut ctx = PietGpuRenderContext::new(); render_scene(&mut ctx); + let n_paths = ctx.path_count(); + let n_pathseg = ctx.pathseg_count(); let scene = ctx.get_scene_buf(); - let renderer = Renderer::new(&device, scene)?; + let renderer = Renderer::new(&device, scene, n_paths, n_pathseg)?; event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Poll; // `ControlFlow::Wait` if only re-render on event diff --git a/piet-gpu/shader/annotated.h b/piet-gpu/shader/annotated.h index 9812264..f243fab 100644 --- a/piet-gpu/shader/annotated.h +++ b/piet-gpu/shader/annotated.h @@ -31,9 +31,10 @@ struct AnnotatedRef { struct AnnoFillLineSeg { vec2 p0; vec2 p1; + uint path_ix; }; -#define AnnoFillLineSeg_size 16 +#define AnnoFillLineSeg_size 20 AnnoFillLineSegRef AnnoFillLineSeg_index(AnnoFillLineSegRef ref, uint index) { return AnnoFillLineSegRef(ref.offset + index * AnnoFillLineSeg_size); @@ -42,10 +43,11 @@ AnnoFillLineSegRef AnnoFillLineSeg_index(AnnoFillLineSegRef ref, uint index) { struct AnnoStrokeLineSeg { vec2 p0; vec2 p1; + uint path_ix; vec2 stroke; }; -#define AnnoStrokeLineSeg_size 24 +#define AnnoStrokeLineSeg_size 28 AnnoStrokeLineSegRef AnnoStrokeLineSeg_index(AnnoStrokeLineSegRef ref, uint index) { return AnnoStrokeLineSegRef(ref.offset + index * AnnoStrokeLineSeg_size); @@ -120,9 +122,11 @@ AnnoFillLineSeg AnnoFillLineSeg_read(AnnoFillLineSegRef ref) { uint raw1 = annotated[ix + 1]; uint raw2 = annotated[ix + 2]; uint raw3 = annotated[ix + 3]; + uint raw4 = annotated[ix + 4]; AnnoFillLineSeg s; s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.path_ix = raw4; return s; } @@ -132,6 +136,7 @@ void AnnoFillLineSeg_write(AnnoFillLineSegRef ref, AnnoFillLineSeg s) { annotated[ix + 1] = floatBitsToUint(s.p0.y); annotated[ix + 2] = floatBitsToUint(s.p1.x); annotated[ix + 3] = floatBitsToUint(s.p1.y); + annotated[ix + 4] = s.path_ix; } AnnoStrokeLineSeg AnnoStrokeLineSeg_read(AnnoStrokeLineSegRef ref) { @@ -142,10 +147,12 @@ AnnoStrokeLineSeg AnnoStrokeLineSeg_read(AnnoStrokeLineSegRef ref) { uint raw3 = annotated[ix + 3]; uint raw4 = annotated[ix + 4]; uint raw5 = annotated[ix + 5]; + uint raw6 = annotated[ix + 6]; AnnoStrokeLineSeg s; s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); - s.stroke = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); + s.path_ix = raw4; + s.stroke = vec2(uintBitsToFloat(raw5), uintBitsToFloat(raw6)); return s; } @@ -155,8 +162,9 @@ void AnnoStrokeLineSeg_write(AnnoStrokeLineSegRef ref, AnnoStrokeLineSeg s) { annotated[ix + 1] = floatBitsToUint(s.p0.y); annotated[ix + 2] = floatBitsToUint(s.p1.x); annotated[ix + 3] = floatBitsToUint(s.p1.y); - annotated[ix + 4] = floatBitsToUint(s.stroke.x); - annotated[ix + 5] = floatBitsToUint(s.stroke.y); + annotated[ix + 4] = s.path_ix; + annotated[ix + 5] = floatBitsToUint(s.stroke.x); + annotated[ix + 6] = floatBitsToUint(s.stroke.y); } AnnoQuadSeg AnnoQuadSeg_read(AnnoQuadSegRef ref) { diff --git a/piet-gpu/shader/binning.spv b/piet-gpu/shader/binning.spv index 6ea087746cc4641eb1746f46597823f20f338685..524f9e44971fd90bfed307ae7e5a9a3ed88671a1 100644 GIT binary patch literal 15876 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 QS3d!tOVQ^V%U$FD1E8;lumAu6 literal 15652 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 bcB|+2ukXUuzC+1eeGhyNMW1Udca8rKw{Co` diff --git a/piet-gpu/shader/build.ninja b/piet-gpu/shader/build.ninja index 14c72aa..27fcfe2 100644 --- a/piet-gpu/shader/build.ninja +++ b/piet-gpu/shader/build.ninja @@ -14,6 +14,10 @@ build elements.spv: glsl elements.comp | scene.h state.h annotated.h build binning.spv: glsl binning.comp | annotated.h state.h bins.h setup.h +build tile_alloc.spv: glsl tile_alloc.comp | annotated.h tile.h setup.h + +build path_coarse.spv: glsl path_coarse.comp | annotated.h tile.h setup.h + build coarse.spv: glsl coarse.comp | annotated.h bins.h ptcl.h setup.h build kernel4.spv: glsl kernel4.comp | ptcl.h setup.h diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index 5a43f4abf13e1d65d900a91bcbeda31a384f3e65..4b7e1c4da977a05b992d3869cdf4105a5b8cbc06 100644 GIT binary patch literal 49028 zcmb8Y2b^71^}c^#W|Gi*ZwW1Q>Akm5LJdl1NY6kTDUi^6@4ZNuUZjYCB8Do`1QZYi z1eK~HRZs-}&vWm2CwngX`F;OaH~W3xwf5R;m$T2gXNI9|#-(~!)eKc1{`Wb^&s^1v zC{;B})vHkt7&vI)DibFSTV>6)R@FXx)n?VO%~AEPI>7CeF@w9tsy{koRsE4NC+*ee zQY4@;Ep7Y{bM-K!y_&fiFtBsLPCE@4xK-!S@#8xuPMSJ;c<1P@vBNuubxjyPbW+#B z!}aS^_;rmPI(qW3(xC&7GKUWGayXlf9XoF8uF<0h5^vD(k-H8bQIVC_=4af95fg_` zs!)0{-tYJ~MxgFzJfn9+a>p@#wPq~2;~77A(x}d^Da4wCIr>hl&I!W@4_j{HqzRLU zPFnf9d96HowbeGDZCf=PZ6}au2VT6FUKM;$KqV6x#FZw4$jq_)qL>p^zoe$_Q-&_pFYNUY2O@UN3}p5%XM;r>{HFp z^l^1m3&S&S+IsOnH9r$Z4jJ4zblm816Poj)?zn~w88=0HM>T7;7OHfa>NgI%#S z_4K&k89{ey9&K~=eJ9_RD9t%vxrnoRk2ur!(*G-x`EY60`OIFe{a@nzUt+9P#8|&a z49E2UiqIU-dfhR4RU3jg?;5-9*ol({j~zO^u>$UObwAVRfA(r~c;VaGHFntWDOrQU zK#;~NOs>z4O=;I$hxTeSaHAb{2mjlWx@g+Ce$@O7A3c1`@UfFTzPfey<%F)0qb79@ zA2zaa-OpAHKr3SOn1n)4TJ+jhZEc*!dDg4i7QDrnVcmNb+Sr5Yex}bsN3}hiGkc3s zlgA!Nz=ofipVDt2ygMe?IaTvBarj7bv~913=9oqe9zCM7zObTuy_MHyN3~$J3tD&F z&O;`2O&XpR=$13kiOplZTVJ^riu_%tl}D`Byl9-4o$RwQFKyM%;E5F1X;8D@9EXkZ zsQH;b?)K^ja5~hl;Uo7`j#zLmeah*_O0Mo~- z`oBioRgboLE$m6`jCU_^#=CE`&q3oxcQuYB*H6vQ^zn97op2{uKW&af%}>v@o2$|x zzTX*0dTJR-LvAh)8MIYHj9ahIP;m0~>?ihilgEr_`J4Mq&Cm3F+bhrQ+K)^-8eAVv z^~Rp`K00Q)&v(+SJ7~#LwV9+j9!UG-I2zo{vAr7CjN6@`+|TsTsv;7bwBf0-cW7l%Ky)X|35c~V>!6HkGAR%aC0mRR7ceP4Qf1p{2zL3I{vRs znM<&-#)lPs9^T#OoYk@E)6MpO=~BO9?;KskJ*J3zQXSV-{6B=9zSy*Lj!r1zp4c6? zqdEo5S>qYrz2<6urmuN>bsD_(YwSt6A8tt#cnn5s`gmpxA3Lnu$FpK;_kCdQ>TGx& zr{^%HCsFR(T3z|z5vw=Kh&=0_Nu2E8v%tB=&TGbRoc+K?UTS{)lHv6vH_x%drjjP}1aQN6u;j<&uBUr2u$(?wtt>EL_L<C5{P3vHh>q%qH6PtIrptl)?d2-!W-Lb9In-O^a!A6$cVur~z1JN>AsP3#&u7T#9f0QXry+il

NWVIO}<9> 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 Z!9@J`?$9=(>$9=$ZZLVi0 zIP19|Se|*@AMASCMqA8b$9*K z$5^oUvhZ>6@>yU!TundYYf}^3&OIe|?>Jxf(+j)%qMbT%CxFZIej;4W_VJknH)fs@ zCd1YA$uq*iaNBBgUrwR+GfI zoM(h1!B^pzKI9q8(O~7l9pD*5+cc{#hHjKKlENa0$44Mz|ENf96fDPvZX&oM(i~z;bQQ+ZABj zI&YU#%QJ6Rf}JX;J zb@RD~S}pzD04|>qZiHV?$-K(-aUT7-<4s^=I4{PSiTY-^dVZh(7VygKVfD1T6>Qwh zzg$1~|*ZaloaP|277;KEJiCiE3eU`Wrob&q*uspHu2It;$7g(M(`3cy2 zk8QLi-aTM7=gqkC#QP~&Uv0*fr`>(v_@J?F%OU^UN(zcmY2S^iSKKL zuQ|xIu_V>&VAsMpj#D1nAHn7Q>`i!iKYI(Vb~f#^PyYnBoi<~>L9LdUe+H}Ne)boz zn!e7X+;L>Rt=}ZqY2<1-7@x#?8(VL9`hEwlmYNjxK`CR^Yu(7oHtnv?PKkhYcA5zp@Q?cV+kUZq-@`Y+{ zEuX*U`k9;O;U~tRKj-dau-y9sQuQf#BDMP0yuDqQ;(z067PQ$Ze*AC0twY;B)U#3S ze-7&W_pNRK)4ZGC*23>=;SaX(M_c%l1%D3we9d#7-2^v}@*Z(BT>W8kcfa0BQOiBT z9M!U3cY)QiCw>A}^SbxvqW4h$l;VGL(SEe2?Y&@a&ZlFQ$96xsjQexATJz_D&A1Q1 z6IXlM{sOGcxXzh8wqJtFxDUbArcsjb!{Ef#p0Knv%HBf)iJJ+WrQt&A9Fbd2BC$opaws&qV!OidyWy z13P!IzX(>#?^wJ9wvD>`$=|W~J>_|d>ujHL{Sxz4u=AFfe*mk+{u-cSO?}ESI!ar=`pSJKXTlm*4e8w4i#`nK#nEw3lmd#P~jQJhfo2ToN zd3hJCp6mQQuv+HDF{s7=1F%}I^S^@C?xtiP{tbL5#eTG>?cc%L+!M})Jhp#;%eWuG z)ynJqV|e0fPuowx+KlU*%47QsT*mzzt~QO5eE$hfTsgU9Z>|f~%#^g~7H_ zPoIl`?K9_+T))Iz6zp2%8eI&of382dKJi}yJR>DJ^aZQM-Vf|t#=az2Ejca)wvBpn zTpDbj{#`8RMXsMQJ@1wQYxgV^=PXiZ7o=X4;#s&jbxOuqNnVS{R)N|IY2v*Bno1-D;X5g@><;! zO+DA@USPFctLChhIo=1XmTPriuv&So?g!6a(w?@RU~TRt$1aa;Fu0661g=(It3%<5 zt37RpfwdXeIh4mX0$j!&30IrOJmgv(1y5Ygl9W?a`)9@}_u8TTN#T6wKbfG4i@w4DgnW?c7%JhsW;GVZ}}wRTG8;Sg}*YERoK zU~R^AkI7>@4D4EFj~xzn-{lOD>l6PYz^+lQ(IdfXu^$C?u46wMtQPw*VCO9MW5H^% z9|v~qu^$gs%NcM2*glPGTkSamP6V5K&H%aoiG32-@hA4lU^UyHLVYU5_PKWC`q;i7 z`|UKacFz!T&X8W%R-pJW@v|x=xvmB_*EOikbuQ|wX;AEj-l_yv4d+SsDtV>C*>w(R6 z18Q^iS@cfYmAT&5=%Z(@x1*^i*B^t`lIsG*xdX0luEvy`tGV0-_BmMHT;<7iL-frl zem0?)>qgX@*4Ss^%_!!*MFTf~ZgUSYHiegS`cpLZTqE~_H^V2t3w1wS?LJDz@-wjQ zw7H+n=jUK`a}_7oEwK%t_}Pk*T(<_B-!|0Ft2sYNJLfh0;THZ>!Jh*^U-Mjxzl0mN z9N#oF^_&+Efz>=OoOkLh-XB#dY3}dZ!xiK)o}?HQ$vwYy2uP%4_KlaNCq?_!^pe z*6?+(TGr4QY8m67z-n2;KZDh>hVHe$z|F^g%D&!4*Oq(9JK(n{`WVMKRnss1y$de; zdk?PGT-)Znz7MxQ=hb#IQGbA@?pk3eg6%dzKv<$YWk(`zk|!ZKZL7IBVP8z zKj8N5e5Kt-XzJ5reXAg?&yc_kNHQt?iFN*8D4|Uf06XKNX z{3+Zv<=lUUrk-{F9ITdgHilZp@K3N>*7*ytTDi_&!p*03o&SZdE#v<;IO8{t<5$x! z{e1;4`}+@EE$jR>_#2A-Wu3nd{B|`QrKVr&Iu9e3pTQK@c|YnQHQt|kD8+RiPMvk`O`LL_`@n5e zuJcT2>RIQR!D?A&W2j~9vw+pI&a;Bmn(N%Wx6KAOpR%9X(bO~cIlvjaF&w*^eyw94 zLo7cBP#pV6>H}-sMLnA0*vC?5>~j*Q9Q#~w+mt!Yji#Ql&jVJ=*o~o{)XN#k{6cyAGa3%MiRH33z-oCG97Ox2;p)aTf4O_$ z2iW?99j7+OBG2y_EC=>HwvD#d`8xuepF=5`zr$+mJ^65o^LHe*^W{BzHR5Fc^jW>; z8RPPBW0do^0-Ac}Z$+?LIe#m`Gk@A0kDBw4*eke+wWfYma8 zBgkc2xVkaTU+(;k!Zr}>IJG$zdFF2iu=g6=oxNXY$+8Iqf^R)|D&G|A;^X< z`%$;;Ow`{;*Oup>AAr?zKiD0dbuq5}tLc~i_W+mu?+I6HK6^F$-wU4p)zkmp=-Sf% zK47)+8=KJKKqs9AB?Uo{SN`FrT?Me^lx1I zSJN;34+EF|4~MHgUi3c#p8m`6k3`p&{zrk;(tj5?{TtW*)$~jM2Y}1|4}`0E4YP-^R3WHT_zzg^P*n=K_k?!nxEJ*7$ttizr?Tmr& zXY9ss>}vX@uc_d&uS4N#+4o-Whr#X3eOLB(IJ&m-9%&wQK!w(tCY>q&5Z)W2Xnr%?Ox_W`w?Oi^px z)2Qw19_(j=&!D(Q`FpHq!_{3&_s&^h=g4tsOAhCP)qK}B3cvHfYWg~6xxVADT>y4W z+8n=Jo9!+F>#xnZkmo*hG592WY@;oGUjk0w#=jJ?@H`m1v;cAZSSmwd| z$Q5Aq%PGciZq@v~$jqB{xj(kY=PHVyD=E(H)zsMsH{Q_q^+0CpVLQEL<1_e~VXl(^Q(?-sEB`kK2uw%cl3 z=J0m7+O3rIqZa=kgY7T#dk0v}eU@C*;(sUDKHW>s>s?^A#ye)}ywCH!#dQ=v*HFAB zy$-J}@Q=XG+4a&tz$BK^=Z_DhwL?E^+}UQtiJAgYiggnYO`wC=BfHt9pHA#xS?I$>UYgrRX?T7OMCVC z6bWcdOB?^eTs;hFuV$|X59u7d+iru0Y}Yw_!i3IAlc$Xx**UhWdt~Q`u8AXuPwqN$ zq<;Mhzpn1#W2cNL9XjwRbLb#1hqG08_xSC*#*Q6AyuC(_-gD%rimbFYukoWsO&U45 zLg~YJzvAB*fx6cOM(>E^j$^`Fwws6f_)5FZi6e)O7&K|}#3{okulCg(R-3ZcTAR?e zt(uFr6G^Ir@y%Pu*=zE|@rRErGj8Ur-D_sfeXIH5#_!bz%{;YvC2x81b`IpO18LHq zW0|*F2>z9LUl~oW-k8&w<5`gQ&GB?pi`224e<$8P)x2hop`%(Ho;lLihySQ~O&mRJ zXy@?pW5-Wy&VjmP7&dJDRP7zroYhj)Y5$c}>&}}Q>fcr^iGMT4KGiaH+vbSOve}Qi zSDEJk_{_O!=Beh@nx}f1r(;rczTMkLb&Z%j2He38Se|-j+^>wFC$(}6%h4udSg~$D zdFbSkBbq~K%(c4L%rVSatqixXhHvNOp`*)9ngDf2&eclzH~X2VTD@1>+*M!6wn8}?ZM~rUV_j6T)(TW(oCZW)i z7QMDr+Z(6x?CMkP2;OGgh@QO)ZR|mHubFeuQSA)pS-j1dDcy$?u;HiXRr(Eq_rwG{ zr)pl4Mvf*&+xBT_j%m!$v7{Z z=&RL2k-zJ-+NiafH;wbMn|(IsrLEc>Jc;5u?bYlzkHf}z)VyYnyS>^MPKWw6eB@r` znAIHfu%W{bA2D(K1nrJl{i|bMq&f(_j`@`l*9kQSFmt@B|82Bg^=O;-!U4q2cn<_; zyazY?+-v;UuEt}@^;7ejIo^({6Yd1-r_FJwdG%hq`6?aa`;~#Drj{lwmB%D4$Ee{;X7dCknXz4EzT`;lo!gX_any|E|#933~)=PPN}9kgVr z+Dy_M52t-{91Cvd*j|lq#_h>Z?lm*NIU2s?*STHyh_Ccb!+Iu~YsbfG-Os|6AE>r- z<^OBL|IZELSdQ%Jqpdm$+#Jgy)p2!ydo|ua{)Zl$j{j{_<`!(M@i9f8$M*C&Z*^k& zbhG_0UFuito#{o~6NkLzW-Lb9IoyxPK}gv8Ro7DwA3udp^E~wj5!&e5 zJF4rbhmGhQ)-`$DP(Kj-PV}9oHmPfLH@vO75$?Nyw(o%_4n1m3DtMpj`_w(}hFec* zJoQ*gtb4%3^7X6{`@tsnRjUD5(db8yqgC<{>s?qOfP3|jSBj;J!Sm}3BaQa;mJaaDEs{u{k z)9;E+-qY_YP2SV*noVB!+g7a)Pd|gf>1XGrU(fhCk&l9)XyT<`}`JuSqs0W7jLU>gD3Ahz~%hh1?RnRlH0_4R_-+n z&D#C(!&O0WEyl7QT85U!#R@+`>2M#oMaQ;musyszZBldv!F}cLtsbjH{zM2ELWg zk>cJ{^Xl%b-{hGz=Ud%rXgn*Zor~jYU+4P-@L_xw*;alsyqtqmd+~nNdGPuf<}-(9 zl&3je-J=`r`&JjV*vrqJ%dwUGb~yW>@twoxiswP2$9C>Fe&XSSCyt*oVaILFsZVu3 zIyp`5n%p(M>D|;HK=1DCvGnYxN71KFTeC5q=fRW5G`fKIYn(UH$B^;EhmPH``$#H- z+Ea7W*>G~Tn(h9CUOuz`R`Z$9>^{}s(MEMmoHV(! z=b7CY-#^jE4n=M}#v70QhW@YKx*vND{e#{*!6u=%S0ABI9FeiMS0BTVni$@v`ULK5 zadhVYHj;NgeQG<-p{6w@l#Xf^j_xLn>EvUg@sZHbI?(co(#)l;niH+7kr!t+b(_ZL z3aeq9C2B2SKU~ihni`Wx>b|iw+P7Ehqc=XcYQ7D;-X28iTkY0t*BHXgU2Z>Z)xP>R z+V!c16@D}0wN+>7Q?Ki#;6@+>+h5kxzP-9e+r;t1_{xi|qk2diU)q~1qt;$MUTe5C z_!G7%o}o{*@MnARj_P?h^V>5F=jElQR(|Csc3brhIwvsTj_O@->7e!zTKWCmHecx1 z`0mnPwY8VI(Gz^!w9ZLmyM{YNB5=Ce)W!{+ba;a=1o!+Y_Jw=JXs;H7PaW4h$F=>k z4L-Ezepm%Qt+21#;IX$=o54pk)}pQ2M%=i+_>Kqe>OOMP*zptUzT2vCXu}(MwpB-i zht>F4@X$K{v%#bMB3$!x;4{~wt-1m}l}}UW<~w3OVZ^tJCpPh2U>+L{{eE%n^8&cJ zN874b)$9J>22UlZao%m{M;TY?);0^ikY`&MY#$NOm? z(|lOeE{zxm6+Xw!^vNS0pFY(|=#x3U8(TDYubiqsZ;Wv^_tTBnSc~@R0^9lgtM7{o zwKa0%y%aq2zG$y*g-;rJMB}Mp?0e*Hs^U5FVC}=pX8nBbsGdNZJcir9zB%1LFQeg8 z$7-uyfv4X$z%%>ps6K#?pE9{~=4VMq^%)w^zL8^#zL|!W`=Y(-i&pOIe(;(5gfn~i z%kI8om6ZhmA_ ztKZE#s%h}@;c2^L(OlPjk8=;4jn+NBTRB}c^1K+m9P_2Uc%SNFc%CObLweTtsY1_w zc@B+yd!8FF^@_#1w($3Q@s8>Xc=55<$gjO`t=0EQKe&UKx!ylCYZ=5OJP^@`D6 z^>48++rkIJ%jd_SUiOY^6}X@8rO!IOeA=t^TKL8-eA5=b1H6p4V~c%A3*WVc@7u!n z@5MW+E_mzQj*nK)?Id{X+#b^_PJ4A+3qQG649*K$>}R*|3*dZTu77saUR~JB-cel) zcWz6ct9$vhSJ$@i8(a9zy?94;KfHD9kN5Ivub%A1`Amkl&c&OB&tUHH+n6`DEU2n> zV7KNw?{aXj!8{Yx`1l34H@^EWtAQ(gK$AB3oxGfv@$&tsjHi}(epf2v$p;XRsuAyb zKp8LJi^_OviRX8uGM;<@v9vqBe6L9RXt-5s&VystFERaoQO1-Tld6%U@A-3p{TZEe z;?IEPOVs>r=3KrQWdT~*ZUE7ZFJ6c?j*gG%(Pz=&G!Cm z(_9>%xj2XB;(X|E6EX6+5(`G-gZM50P{?ztiEN%92Fm?P70qd`(&7W(;b|_eznz4pb z8_PC_QuH519ski_{nfOMDQsP}P0d(`Q5(xP+Vme!omdmV#!_=%96_zWy6vYF+;OQL zU1-jy+VO>UXsw-GXzBY5wDf%@*uK@A=d-A1ugh%I?xk-~>!Zzn&Zag$+iKJ20&0C+ zBja31Z69)D>+?-&eY6?pB5LE+AOOOSAG-4EBqF) zv6rHKK2Mx?KR4v&^Ml$)ZIhbcUTelN|2rvO`rl1$O!e6B14e;n*H)%H(Q zJN`Lp{zA>2@8ta=xQzD^+-FzfeGcX|yb*5}9F6C*(D>`s+-HP*>zezllXum8K+UJs z+;vX;o6!2fows~$I&XeH%8he3wPRFs-sFBps%f7AHZPwg#>wZUar|7A>-RHi+o%~w z?q_4-yaP6l>zn)rVN_cXuOq=e$NXG$O)SPe7S4Ygx^d-xKB^_o@e(FB{A~E3y8XG} z#IwEJGdZ!7qc)TktF3bHe>hx}$EJeQ_t8p*D2m%l-UTb3Av~xa@BR znhx>z^IiYs^EP}sd{x?e@B9PqUY2`5epvG*;a`G{?b_QP%jEwybmPlCt5Vb6R%6?& zgwb`F1I<_qQmdPvd?C16VlNCfj=Hhso@uFtd!7|;-=1Zq?_FzbO#S7aS;b%O*_1qU zF$&&~vB}*_r^3Ii?egp3)2S2h`MSONzX2M`pZ32N`9}^IQNwGduU$C zN9R=w-Vfft;B&&4sk!Zk!8<3Mv8L_iqv3-N@7^_hBAjch@Tm07NzH21pin$Dlk zO3V6qLmM%vT;l3u6l2(@&vnnOz0bovpDwxQ(&0WwJ&z8zy=T%T_sqHEo;jD?Gv{zW zdpv6nH=bwB;eK9u&RlZOm&5hUF zxM#Da-Lu(}dp28g&u2^S8Ewf&v~bUBOS@;bCHLGm-1h>{Zo>yM9?x#W-A|s~mfZ8( zl6!_*a?f!~?pbcQ^W%ALxby9qZpl5@ExBjACHH(c+<2bxh8xc_-jZ+G!ad_H?VjhB z+_T$~duCg5&udHWnQh5Evn{#jwk7x6w&b4MmfSPjl6ziTa?fhRT_4YC!yW${E!^|k z((YMp$$hYgH{L@FK9G2x(Z=q4dqx|s-80&7pSPaThWj1BGum*Ucb?ORYxkTs-26SK zExBj3CHIWB?&EoExbx$AY`FfO#fIzeS!~HYiw)Pmf5G)%w%|SwJd2J0Ksu;GWo zJ%Ne(^(! z-#L6g+l+TGwrBODT0a>d`8{yI*Qlr6kHNO{yG7dF3s;|Bx4RE)TlLL(+wgIoHtvv!)*U!FuXH-k>kAYire;iFc zV}1f`d-Vy-i|@Bjg4Gk}DRArDJ&mTGIM0BMqnlfha`uol+kN=C{*75xUO+9(N1a?i;)9z((Yd^1`sh?5L?WtJK5r`>PBt@->GO+9P<23W1k=S{frw3(0Z{A$@(zXPkK z@85&f{O%l|KY$%a#`s6De(J{bdxKj1{|r`3%)fxuj;HL-{_%|OuV8iC`#nSMSiMi* z0jrh!|6RCxa(EA{X3XsW_rdCCQ;h5P6*aNnRsK<9zo!KM3+!If$L}W});{MI`bP!+ zxWJzj_|qC^4nBjs7k|K*j->uKMa?sSkLc?Qu-B)|hqlitYJLY4C(f+A!yCuBX`DI0 zJ%>;?=XPrI|AcmFZ>`PmN6w+PK44>8e@LJGsAr>iX`j8eTbq+-8GR}G*-mUszhkAp zxoVrWG3TM4my($C)pqNQXMV6TGA|2&)tndmmCsEvmi>y;c2Tfx!xsZL=cZ3%ZWf1o zj-sD7^HnRK6-&a)7)!y+7)!%Fd(lsuG1M|<$7f#F$zvI?F|#-OgVkPSA~I)c&U5C> z@umF$aJg5Og{x(+ED!c_ezYw|Q8N#5;;aZR_sT%H=SJ%0Ar-qVlu2fV%bEZGul9*$Lib1=`XsgtKZTT#@^ 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 P%WYu$QFp9z$NK*Ok!Hg$ diff --git a/piet-gpu/shader/elements.comp b/piet-gpu/shader/elements.comp index 43bb9cc..230b710 100644 --- a/piet-gpu/shader/elements.comp +++ b/piet-gpu/shader/elements.comp @@ -30,9 +30,15 @@ layout(set = 0, binding = 2) buffer AnnotatedBuf { uint[] annotated; }; +// Path segments are stored here. +layout(set = 0, binding = 3) buffer PathSegBuf { + uint[] pathseg; +}; + #include "scene.h" #include "state.h" #include "annotated.h" +#include "pathseg.h" #define StateBuf_stride (8 + 2 * State_size) @@ -83,6 +89,8 @@ State combine_state(State a, State b) { c.linewidth = (b.flags & FLAG_SET_LINEWIDTH) == 0 ? a.linewidth : b.linewidth; c.flags = (a.flags & (FLAG_SET_LINEWIDTH | FLAG_SET_BBOX)) | b.flags; c.flags |= (a.flags & FLAG_RESET_BBOX) >> 1; + c.path_count = a.path_count + b.path_count; + c.pathseg_count = a.pathseg_count + b.pathseg_count; return c; } @@ -96,6 +104,8 @@ State map_element(ElementRef ref, inout bool is_fill) { c.translate = vec2(0.0, 0.0); c.linewidth = 1.0; // TODO should be 0.0 c.flags = 0; + c.path_count = 0; + c.pathseg_count = 0; is_fill = false; switch (tag) { case Element_FillLine: @@ -103,22 +113,26 @@ State map_element(ElementRef ref, inout bool is_fill) { LineSeg line = Element_FillLine_read(ref); c.bbox.xy = min(line.p0, line.p1); c.bbox.zw = max(line.p0, line.p1); + c.pathseg_count = 1; break; case Element_Quad: QuadSeg quad = Element_Quad_read(ref); c.bbox.xy = min(min(quad.p0, quad.p1), quad.p2); c.bbox.zw = max(max(quad.p0, quad.p1), quad.p2); + c.pathseg_count = 1; break; case Element_Cubic: CubicSeg cubic = Element_Cubic_read(ref); c.bbox.xy = min(min(cubic.p0, cubic.p1), min(cubic.p2, cubic.p3)); c.bbox.zw = max(max(cubic.p0, cubic.p1), max(cubic.p2, cubic.p3)); + c.pathseg_count = 1; break; case Element_Fill: is_fill = true; // fall-through case Element_Stroke: c.flags = FLAG_RESET_BBOX; + c.path_count = 1; break; case Element_SetLineWidth: SetLineWidth lw = Element_SetLineWidth_read(ref); @@ -148,6 +162,8 @@ shared vec2 sh_translate[WG_SIZE]; shared vec4 sh_bbox[WG_SIZE]; shared float sh_width[WG_SIZE]; shared uint sh_flags[WG_SIZE]; +shared uint sh_path_count[WG_SIZE]; +shared uint sh_pathseg_count[WG_SIZE]; shared uint sh_min_fill; @@ -187,6 +203,8 @@ void main() { sh_bbox[gl_LocalInvocationID.x] = agg.bbox; sh_width[gl_LocalInvocationID.x] = agg.linewidth; sh_flags[gl_LocalInvocationID.x] = agg.flags; + sh_path_count[gl_LocalInvocationID.x] = agg.path_count; + sh_pathseg_count[gl_LocalInvocationID.x] = agg.pathseg_count; for (uint i = 0; i < LG_WG_SIZE; i++) { barrier(); if (gl_LocalInvocationID.x >= (1 << i)) { @@ -197,6 +215,8 @@ void main() { other.bbox = sh_bbox[ix]; other.linewidth = sh_width[ix]; other.flags = sh_flags[ix]; + other.path_count = sh_path_count[ix]; + other.pathseg_count = sh_pathseg_count[ix]; agg = combine_state(other, agg); } barrier(); @@ -205,6 +225,8 @@ void main() { sh_bbox[gl_LocalInvocationID.x] = agg.bbox; sh_width[gl_LocalInvocationID.x] = agg.linewidth; sh_flags[gl_LocalInvocationID.x] = agg.flags; + sh_path_count[gl_LocalInvocationID.x] = agg.path_count; + sh_pathseg_count[gl_LocalInvocationID.x] = agg.pathseg_count; } State exclusive; @@ -213,6 +235,8 @@ void main() { exclusive.translate = vec2(0.0, 0.0); exclusive.linewidth = 1.0; //TODO should be 0.0 exclusive.flags = 0; + exclusive.path_count = 0; + exclusive.pathseg_count = 0; // Publish aggregate for this partition if (gl_LocalInvocationID.x == WG_SIZE - 1) { @@ -266,6 +290,8 @@ void main() { other.bbox = sh_bbox[ix]; other.linewidth = sh_width[ix]; other.flags = sh_flags[ix]; + other.path_count = sh_path_count[ix]; + other.pathseg_count = sh_pathseg_count[ix]; row = combine_state(row, other); } if (my_min_fill == ~0 && gl_LocalInvocationID.x == 0) { @@ -284,25 +310,26 @@ void main() { // gains to be had from stashing in shared memory or possibly // registers (though register pressure is an issue). ElementRef this_ref = Element_index(ref, i); - AnnotatedRef out_ref = AnnotatedRef((ix + i) * Annotated_size); uint tag = Element_tag(this_ref); switch (tag) { case Element_FillLine: case Element_StrokeLine: LineSeg line = Element_StrokeLine_read(this_ref); - AnnoStrokeLineSeg anno_line; - anno_line.p0 = st.mat.xy * line.p0.x + st.mat.zw * line.p0.y + st.translate; - anno_line.p1 = st.mat.xy * line.p1.x + st.mat.zw * line.p1.y + st.translate; + PathStrokeLine path_line; + path_line.p0 = st.mat.xy * line.p0.x + st.mat.zw * line.p0.y + st.translate; + path_line.p1 = st.mat.xy * line.p1.x + st.mat.zw * line.p1.y + st.translate; + path_line.path_ix = st.path_count; if (tag == Element_StrokeLine) { - anno_line.stroke = get_linewidth(st); + path_line.stroke = get_linewidth(st); } else { - anno_line.stroke = vec2(0.0); + path_line.stroke = vec2(0.0); } // We do encoding a bit by hand to minimize divergence. Another approach // would be to have a fill/stroke bool. - uint out_tag = tag == Element_FillLine ? Annotated_FillLine : Annotated_StrokeLine; - annotated[out_ref.offset >> 2] = out_tag; - AnnoStrokeLineSeg_write(AnnoStrokeLineSegRef(out_ref.offset + 4), anno_line); + PathSegRef path_out_ref = PathSegRef((st.pathseg_count - 1) * PathSeg_size); + uint out_tag = tag == Element_FillLine ? PathSeg_FillLine : PathSeg_StrokeLine; + pathseg[path_out_ref.offset >> 2] = out_tag; + PathStrokeLine_write(PathStrokeLineRef(path_out_ref.offset + 4), path_line); break; case Element_Stroke: Stroke stroke = Element_Stroke_read(this_ref); @@ -311,6 +338,7 @@ void main() { vec2 lw = get_linewidth(st); anno_stroke.bbox = st.bbox + vec4(-lw, lw); anno_stroke.linewidth = st.linewidth * sqrt(st.mat.x * st.mat.w - st.mat.y * st.mat.z); + AnnotatedRef out_ref = AnnotatedRef((st.path_count - 1) * Annotated_size); Annotated_Stroke_write(out_ref, anno_stroke); break; case Element_Fill: @@ -318,11 +346,9 @@ void main() { AnnoFill anno_fill; anno_fill.rgba_color = fill.rgba_color; anno_fill.bbox = st.bbox; + out_ref = AnnotatedRef((st.path_count - 1) * Annotated_size); Annotated_Fill_write(out_ref, anno_fill); break; - default: - Annotated_Nop_write(out_ref); - break; } } } diff --git a/piet-gpu/shader/elements.spv b/piet-gpu/shader/elements.spv index a2d439c77fc4575f6aff7e1dbe84b56bdfd68bae..18f4dc546895238d4235612eacdf8bb1809d1341 100644 GIT binary patch literal 47936 zcma)_1%O@E)wU1JOmKHkaCdjN26qjSi9n2Gf?IGX?zA|?i@UT)aCe6yg(8KPws;Hw z^W1yh$(~!j@B44N+3)+VwbxpEpMB(>nIvtKE-+bDO;SzH|2_oynW35#rK+Z?dNuk@ z`fk^E>9JjdmR@nCWwlRRwb?an)8W$rZl{gtKWwD>K?u{MtIa^`O*5XYjsHzb{}80T znzGuYZ@*2p+G>-&8}%DFdUU_BT?Y>D>^FSa$j*L)hK=bQ*fngwPW>h?{DzGjIDFio z(xC&7GJgjZIhBoucaG>B*|lxw;Hpt&J-uqw;K5@%yDGeCUOUzK&{k@HXT64=U?X z^HbI{C%pD+%B-_o#1T?uqryjc@9k54^#+0mBB?1-g^e z{7kH`qv`{XpEhey^OG9oWsUOG7=L%|=oOmBeL-+y_9!|cN3dD7{%zF)_&4iruNDSx zJZ$)IT&Sg^!I6rZ+p9%uAN5}RPwr>TkOBSs4IDLm)EMJVSuKHPtR6Mg!OQ%MJGQa! zI;y3>+jWf@wNIxNH21cepNZ?%UM&M}`Za5i`{`N3R27G@_UlnacZ#wOwq#vLuWCid zb#}Q8cN#XRYiQO(-Ot2YdQ~gKOJD7-ow}d>4M5+))nzsM(zj<-JrkC-t;)FO8uhBy z0B=90|H!d}M~xZLT&6}obw3kp>!{X-cl(0fFKT{9^zVZAYH;^gmobO;@8YsVty5R) z(RYvSQE0bH_GXptqjm9b);LwQ5xBn0^y}(BB&T)Dm8ZG?HtZfZZM9i3u6d9=1U-`f zVvJ3T7=4QvZnytCLbHxvb;p>d+PaA0x&FUb&>X*2F@F1EeB-qHKLWUZ+ZE$?F2*<4 z;eQX{_??RJyBFjC>m2;A6>QeOTle_ss=bQw|KooBAL8`ujx%jFsEE_OYyS7Cu=m=k zf!#4WDz5|Eb+Ipty{_hG;$CmBJjWV-^zL)HpX_sa>pu59Rrh*2V8EyY=o;sQn%C6f z?7p18@%4T@czFLIV+&s%3#!rmyM}W88#j{sZ!hlB#^_b+V>^e$M8+wreb5`V_E?L| za^g9)H-61?YUXO+?lJq18P?UAbE?OnvCTVtLtARU!7I|1Z}fwgBWUyg-0mA|JgPf( zTQwT&e7x7&uQzh!sQOaP9GV((vECikxWZ4H*Ag{9<#pKc>aKm(xTBh}ItZRT{d!C( zYA!GJtRg!N=9YPQ(3)%1Rvid#&ZAd#NFBd<{xmm_wW#}g8OVx0-vX+%Fx~%+*n76XX8(EW2xK9P(nGO+!7?r3UkuM&)hQc;aQQM}jlgW9mGf zCWD$&Z5%gsKNIIVYjq;rni{_Snu-3qvPAm-D2#a4c07G*I|1CRZRYCK9{CzqqyIYE z|5G--i^JW>b8?aAl5uK79i zS@Uzj%{8CAxEOG_#ogmZ z<38+w?m-QEuj(TDUmACL_c*VN_16uP)#EPv#c^{~SECOcHDZ7_`9{4xu4;`d*Dqed zSZBY<{C{2Nt|UkQMjdU{RrCWIctamrCZAyV`)pB;4QE8o4eLpr+}-|rgti$wcBD{LOeTNpo<`-XWQFg8cT?_rOC$MoO7@!iyG z^JDPs26m3@Tz_2S7U+Id^D|bR-WX3blxB?Q!Ng$qHuu9|D^D zsIB@MoLatb`qj0xS3fm*cic8k67qD%oxI8Gxb4+6P2PBfc5X8@`AURo)I29Vd6xwz z?}|;o?!2otd3WBmo4h;k22I|bche^C&bvjE*LmBkZJWIDxNcoLH~A{5s~PHFy4tE^3%`>JztdX$&MEvZDEuyI@w>9{yP@#ArH5a8b!U@zug85&-n||VHhK4Y zJlf>l>+w{RcOQor;LZ8AS6_iS-}{ef9HaK?`xZV47sAp$MGK#yh0oE#`?T=IdhoVt zDR|aZpc+8@IU5A&rW>j;Z+>%{`pwuRgshd2G&tM9-*lNndv?|b;RSJQS>)xLbci+wuy#8~as;_!jK$En9J(Zk+WZ4Dp3 ze>3(rEw)|YUG=zKd)Ov#yhj`|ykFl@1N#r(Y~+4)-iSBbuvte(btL{{hxY3l#yi8T z*)dIR#ITXM@3dFPo1=DeT~35&51avR-mf~UbHF1G>{pEKsLprn(B^G7^Z$+GyYFl5 z)oh0*Z)b5n18?ePBj*Cx+bI+Tv;>!y~kbSS;5ADfz6vH0FLSv3h?=*U}d(QXrC z+N#O0HP@o8noV5a!{-H$>KfWPre3Et;N_aG)q{6bo$$^BcoRIf@rsz(?bX;GKJC?h zEqwnLeh}OV<+vWvVn3>dAKk)_>%n_fr@@Dh8nsWq0sRN=)BU{IUY*zCcPX5y4ja?2 z`TUstm-X;#udZm}SGDkKdhm|w7I-tRJD_>K-P*&ay}F}?-_^qJ>A@$f?uR$txVz3R z^*+$Ux4n9(2k)pJg}Vr znRK$A`=`By&(MRj7vb5L^MRYsnY?H2;n!aI(~@#uF5QFkJ`>(Gl&gamm3q#r^zdn~ zR_nprsx=)`U#HjVVQa58f|u9kjeFQTs?8dl6R>t_oChsF2g2+7$hKDHeMHSq`Caee z#2PiOtE_)Kx>9O9wc$7MdFQkieinS>l=x>O8pjQ?9CwFM?hdEcT~^78~JM|ectHd(_Xz*@J+BkN-VY*sH!&Q zzmL)TnVq&NS;7}+s8zKIb5d)gIo|IY^LN{tyidWG=eeYepJ%jL@G)QbYz^Mg&C7iH zB%jZ7Wj?w2cr1|ru;bKR_qFI9=Q-)RuT8I(Ij;j&bDXg^pf{GsSiT9p$0dAAuw$yLT z!)qUH#yo=F8Xc?6n8(m3=CNR7s_B0mz5eR@tDRJ6sq1vK#5eW$qdUH8mn`{0X`oT1>3Gz$njnU*C)fHgZ-}Sc5OoVIcjei}z@#V(3h>Ap)FDKJHUSA=DvrvV9oWPK(p?7Sj$K0^>JMvqnBIDTeWsd!T65>Bzjc33E8|TE z@mx;4S>W6T8u8`;8_)AI_U+Ni`gW~-%)e)i`_%sC8-temj;Za7qU(Ql%{{;6SJZsv znqO0MuQ%GCues;9{H>b1_dO1|pL-m?Pad$U^R)eqtx3Galv-@^BS zd%p_b7d}#V>03b^a)Jo232JbWIIJC9!UxHUBA zA@`X;&2f1iaGdvjx#RY(ebgK$_ulWg)O!y6fP!BPU$x*@z~`;G$KwMh!)Hl$i0l6$ zT&--z*7u`^?-A?7_hYzPe8o$$8t&6i3Vm_r;d%Te+F~{C%c3RjP#3VO*73e{0NC}T z%*LD#giiuicg}L3Z`53qgKO*<`FJ>K8Zq>j`>sPR-1i*vtl6>fpZSCz2lw4__(||1 z@SmGe=YofVJ>SfC9^7+FJ^n9%d)NLif?e09)?U2?H@CX=$bHwKX1q6Q>=^UC1$R9& z{_WbX?s&QHCNlnm8apQOeMh0)c%OkimWlUyZBy4@?mLXc`>Mu{ar{5wu64%$tG25< zUhex2$A|l_qvVqiK%Vs)1)s0r2g5go_r~jy+SfHVp8U~T%lKJ50M!wPd%pPH40B`b zL10a4@qGZiB-)aAxu<+Kb}lcX*;Z!%dOlg7ANS#RD7ZB8doA&um~pK~?mMw?uRXpO z3)k+uv2gc`@5REsm-$XC-0{CHxOU%(#cn*`iG}NbbHTOWT5#>Y7mL5~d@mO6z4P7{ z?t8J)?t8ItrW=^Z8yWd(j-Sk;J{>_>rpX7qc3?K3hP|7dh=-UEzjzcz7wcF-pFxxr_J?}?e%_V1C? z=XsmY4ypMBu;U%;JwqPbufb|ZSN=4hs!j&0`K*z3I0f9g4yU52XWVIEHJ?Y0sAHWD zH=Z_gdw)_(?lZw^iE$QK&F@BAGZ&A=*>H8odq0zly_dNL#?tQsuy$?Ux8&M9Mi+s- zw`ubpCSQ%&4#jpc_(poiXv>&OYVH`v%3Tll>7`)rogO#sa^qy|Wnjm+AAU#g$Nivw zIZe&|Aa;!Feg)XIFrIUhdwk<-ug!a|dqUe)VB^?-L!4{quci6XeqC+1PoCd{jd2rm zac!;#t9jh4QGPYeSjHA->pFSR!Ti`h^J|D<4{x-1joR`lE za*vDSZU;|8?-*_R`23*mcx%54oa5@VggnRfZm`GIG1@Zbo|+ravGN?(`@kMo=b^v* z;}2kU{e3=>i~mS(j^w)^d^P_1XiLloz{Yg#94FT|^*ji+4!LXi5ZL3N>&pbN+Of2& z>SOjW*m2t2V~^7N@%U+bgr?^46DRg#VE0(~<6zf6=j#(-eblYhI@H`tiDxf&pBdji z@ty>i@t%UKeNJu0)23GLUH4hWKMgkL7wiDn;u-KUv|J0dXI-BIyRN&9UZ@>ytcRfLrr?iKecP?^ON=N^`7v$>3^VoO>?Yyy+IugUl71?hAMI1ucKhU-25gMn3#J9D`OeO3iF^v0v5YNlea6Uqr^D7d-|5lR zlWzvF`5xw2`HpHvu;+mKA>^a0X5v5UH{jznerB+7)V>rqcVbA#1$kDCW<9CeR@-#OJ1b3U+|b$EXD27A3w*T-|P4_I9v z&(rzAepk)&gj}D*TmYPyUKqH$Lq&JV0B}9jae9MOy^^ra(xnW5pZH!=b~`0 z1L~=BF|c~-TpVmnuP4To>ywyEfD_Ymeo46Jyt+PK7nTC68`EpY(qLnH?J%ZXpTt}S zoS2^9%fdas)#I}qSlyUjCzb~r)9ZvW<@&^b1#lU2MY!j(dVE#_t0(5lU}JhsFs59e z_^$#kW3CGKyj73SYGC!mTpeso&wpde_0iw^*_yRoo9`~tbs?f7U}&w|@8S`e5Uh_sb2?)b(*+Z3uS%JJwp|`e^sQ zyb-uH&&Fu#`nZ=j0h`CY?EKyDo1&@f<2kVz*mJ_M=8@}@Jez}C^ZW`;T_5Mu7i^yL zez^sjx<1ZlOR(pOW6dMiCwaC4x8~UzO4&DCJbQu7Q{FH8qp2s)0I=tsW6dMi$9+E)z4r!vof@*X=S+u8DGgb`krK+Ma8>+@DuycW)1^?a3$iXBOJs zE5mAgu8(qmzM=*m2+s=+(9FN3WLa-vMCvg0}s^ za<6|}N~;6G`_el`n?488`#s`)u>HaC)Nu&dIx?R_;cBjf^En*s$N6YGjHc#%#ECN= zocSC9mS;Xkf-fe9y7e4Iua@~71I~Po2Fo*_W5LeHG1@X8-}AUHQ;U6SIUbx^juX?Y zg|0e*|6ES59{&@;u2J4k{TiJ2Q~H}zO}~se32ct*i0iZ3$#Au-+o@nbuA8<~XlkyT zIB`w~8z;|LXTa4wPUbxgtma(Lq<6f%&$(yQpF{Jb{oLAaZ{EJdJqxU#1?5*E{-COF`a0R`ZcAq1z1n)zyuKiMaHDhbP8my-6DtdWr*Md81+cos^ zJZD@7_Bq2b+8nR1y2m1G?tC(j8^Gmy+z40mSi2rKgZ;Q3+HRt$xgO%gxec84xD_nV zdfX27+M#YuchIY4J?;d%9_rd}p;ybC?*^-ByNg~P+r8kd$30+q*5f{~>){w}Sr7Hp z{RgmhXRrSetoAwM^w*~5`eoksa`%esU@v#y%kAYkH(dAB`ye>=J^+@-HUXS#&O=~% z=JGJuxj06fwcSszp7D=?6Z=uHJo9`4oa@iyV0n(ulVJC&x^bVPS4-Syz~<8SG`&36 z!e_x=3mv1ayq@dt-tyc^9nZs4$8%u0bhS>;Ai7wbb|?*pD@8`x8yg8pVnI z0k}2xhiK|KKmH6>%e~-zuv*sZW3ZYreLnvS*zX(a`ZV5ofz|c#ne9{XN3=YX$v>eb z=4ar<^t;99aKBrq>oX&B`zu)8m_EmS0XAmNW4S(w`6W0pt@CeizelL2&cB1zQ|DJ; zW9HdXu1{kA1Du$CXZR=F?+oht_ywz@fD_a22j9Z|exM$o z@4)KD^cnPfurc!tD%U6eKY+`aKf?VkpdO!ptI-nkKVW0#xmB)D{C@(MF@J_nLQ{`V z#SaqH6LS){G4re|*GGTPnKrQd-*d*F4a;Ne06SKjKPQ&wTG3nquF9qXDieoC+(&mnD7(9}GK#Lmg{b1HD_IW#qzdSXulR`dL{p2V^L zng#ILZCbE-tWke+SjY7Aeyl^E>1b-!A*+HRkk=K&id??2}StL0~5 z@;PY6GPXEldxIS-ckj#(&ff9&0OTGce_psC_@S1lTy*Jl0Fm`|((7Tau>c+{MYY3^>Qy z-)WHNST76qSgR-Za$xlwYp+Ag!=01Yw%o71Uaf$pE$?4f1gm9_tOU*;F|KQ)rk}?- z_1ar6U9~Fz$$>X^tnzmEOV~nl9#!*l1t--Ciw?R`+?rp(pHrJ(%=AJP3c4*1%9!u`+(X}P_ z4q)S`kFV?55o}G`tjBx#PGIZtUT!Sw-Wjf*al3#WtDbSYf;Xe(xk9d=cGq|}aO*sG zM^n!{_W-Ne{FH0FCz|uLu5yk0p=rxJ_W~P7J-PdXTXPRUQ%~-JU^QFo8V^EC?sAPg z(X}P_V6bu2vsOdE)}$?KH56<;>*saNoW?g#{0{(^{&L?Pmi~^*ybl3ePx=1zP`G-Y zw+{oWIoJGN&f#$N7q|fVdpQS!kD#eLw}We*^*a(?uHR8`HLvs6u)_ZA?`XKX$I;`E z>-{lk+KfA%UY`1o1)D3^D38@~aP@~+SNZW^HDjip6X3?mJ>*2Nzpri_{mrGOUvm8# z+?wkoH1*uqP6n&_yx_W=PP|j#>c%{kUTz)!`IOVa9xrXD(aZC__6+cO_&7$J$8bY> zkNNua#-OXt;XfW;mDUd`BE|1JSLHpj;AW|zX%jqiDJ8Q5`$*Zgv@TI|28 z?b{I3`^yz@ef&hbvevwIM!O2lv3{amU2FPn&FWguHE6~(hGX;@PcCiOf~{Ad%;`F? z>y_(~TtDsJn|=>=&gIW~u7|7V{&hp`<8gV4=3JfIjp*9qcT?f#pVNuo&FI>Wsn_Hd zuw&IT_ExZUn8R~BV{b##7QfpIzw){44s>nFaVOZZ>POXi?*f}!+ti$m&f#vb>pLG! zd&b`bb{({5t?mW8mhKPtk6b_PUfcfwb`IsW{f}_LDTaU#nXzDo@uY%ReWAPeXy*w7L!_}?Tb(iN@ya~>+cmpigHjXuV8*Gh^ zdy8J4WAP67RD2wxtvnVR)_g8{udnvT^#1W4cs5$D@$ZAxvJTqRj5j;Ir)=vyWr}n?{2VTb8o&Etmf~` zrsn&=j?sYQ>)(%W~KR=nPz?d8RuDO);Ak{{(Q*;VCNS8NDF_yg}>Uu z-!AwE;16q_`9BCZZ#n;m;Oe=*Kit3*uMrQx)v`WMg4MDXPlMH-qIr#bhW=UF6EyQ^ z&)Da{+T2ghO&;3|;0I`l`x02qy`8u(gA-SK#=Zj9);JgG<*~g6et?#^Z-CXj_9pI| z;KbFQv2TI38P~Oz$Mz2R0b1hz39ROQBXQpYC$9F4eIKmNxE?ckY<~v3Ha@R;egBB2 z7W>Cw_d)D`0jq7zc`+mXCp5>Xd)z;z|BU8d@OYS0u3uvQ73};H^9!(A>|cVdGxooM z)spk?V8^H@=T~5J=De2cXH3tDf6%mhzKCvCPQR^Nivb3T0sR+~U` z?X6iY{y%`#vX6cQtCi=|zv0H)t<4FfwdX8aV!ek6yP%MlyJ53e3}ZLxY`qUYOprrdQ9c9 zO$#pLP7hb}J4MdZ8Ni9FJ!5ACYcsBULmu1A;4<#4a5aCoGjV4FC$9F4ogJ*rxb87| zY;%DpnH7T))KZ1NL|%=KNr_*cSl1 zuCXr&R!h!>z>ZN*&V|9|%=s_ZFEJMZyQYb`C|E7_#lWsj?2Cidl5+{LW7Ly#Nw7I{ zuF3T?rq`9FYP;7QajrRiuq{aQGe6C1PH*}JXkK#`qR%yFS;pm>Fa^4Qr)0_fJ0(l* z-zi!04O;l7Eqsd>zHJNl@03hD|NOpxr=&dBq~+k&U0#!xM^n!=X$7!a*2y`kW$jl2 ztL2)sGFYv=CanU`-qxP6tAe$;&s-aMY^#IIxNE@G%4^b^@Wj=gv1@^~8P_$I$F>f* zjJqyet-L0!2Txq>8M{7Mn{ho>^4K;6mvJ|OtCiQJjp2!_J!3ZkYcsCLTprtI;4<#! zaJBN9^ecGcYR}ldU~R^A@5p1@5?sdJ3a(aOleUH@uJ(-G2CU7v?m>BM+kwltJHXYp zr{!9=BRFxjXY5X3ZN_yk%VXOGT*loEuC^;Jad!tNuJ(-G1FX%so)hxe_5!>2a_#63 zR*QWA*gX{cK(Jb_ErY<0QO~ud6YRN`Yp7hm#2gHE|0L!Ruv+Xx!R~|Dhk@0Sb8oO? z)RS``usL%LmFt(7!@=%>#2f)ui+v>6V;TD>uv&7C20KPQIrjydGuH;Weu+62>~T!Y zF0fkcz|*!KggCFlNN$EYXg0bp~Ep?N&z`Wdqi*Rlh_+P!XzbKUm){1P-ji_yGp zFG9aK&Fl7(^to;y!nj;Fr$9fV;KzWEt+_R6KNN19@;Z7LntHCIhlAC!R_0L4T8sy) zbN#GU9^1KKwbXhZ*!r`6a((o7jn1#_uAeyTw=%ZXXns~{ zG=5e}zbei3Tb(}ZcLC$d^}D3tSAegqd1|^4Zk%%cE<#h!`dtiG%ler^E%Uq-td{k= z46IhJ-{tVEpZ1LX9ax*|XRY$st^%v&9KRZ@X6@I|UrTd7Sx>n>j(5$jtL?6*IP1AK zw)JR!)}gr{*Q8&U=6bGApY^4p5voEWA6ZK^Y}P-d2Dxq%eZ&L)yn6r zd*F$yJ!9_$YcsBED39$A;428a2fX*xY{hV#C;Z= zxY{%JIj}b4y2s?Py$JSL=Dd0dtQPyrV2?@cuYlEJe--T7#{L>uE%w*Ju2Jl7fYtJ| znK!}aG_GT{yWiiUf1BnxkaJqDe`3D_cAXOYU9ej0e*!z-*xv)I<+xeklkzUv~qWRf?=5=Ip`VDDbM>eLEZehSa=)t-5N2G(Xx;?(3jugz$FHld}aO~KZ*Ilb4m>FK{>oY%J0_yt_Q zvc@mb)N}3l8(7V~?K|n|@c%nl-L*5O+@FIq-oJrot#euHKj3ApeX;o|YyB16TDPE2 zt=}`Qto3WSer2uSpsA3}bt<{+F)cOP1TGg#po?880zctOzmb9$vR$y!0 zhTdA|pr2&Yp0)mmIQo^f{)DEUT7L$srPkT;Cv2r|t;UpFtF=rD_Wh;0waQa#AN1{M zezv8h*6qO7x&ytn_NMpWKPcC=1Fm0LYcDkQ)H)ehEw#>v|KxCWYc;0aTCHVD@Pc(N zYn7)~pL=$u`Pq?{T6Y3l>n`-x>hq%iHbYtKG;saOTBk)*Pp#8|)l%zH_)jmVS*tPS z)@m&?f_+w1w^n&-^%-|}nx9>1sdYE7weCT0t&7n6??#lh&H~r3taVm2_0&2WSS__K zjQ{L#b!#=I+*++=PVf?SE^C#i)|JusqWRgAmRkFPt+hYBwfY>{hjC@C^T72hYn>NO zJ+;mUR!glb;@=ytZmq_YTdTFq-@wgW)+$e}USkK*{0yL_)`4Ja?WDI>pV1a$Tv_Wv zaQ(_!7e-T0t&4!wQtKM{FA7(;R%6Po)mjz@uUF@?R(Wb2ioOrc&oG*`4x!(hX05~N zt#t}||1A(}4PUN>`)_-c_H|nLhAn)v7Vf|8QI6lPh40+L{kJ_zfB$Wdk`HU){@Wg< zeOwFo-}Z=o1oIqU^W0CChPy81u~`O9J@>q2!D`;~JQl8*TJCczgVk~@R{^U%g@5jm ztHSfUq}nrfHLy10y7uze)&Q4r*MzH;&#r616IXl2t}Uh+*JIe=9CM!s)`gdG*MqB- z&#vpk6IXlUZUEM1T=#-JwvE7L+>POC?-uK@2|RJNXY8h6ZN_y^$z$6bT*mzsT+MSO z>)RKexY{#z3$Qlhx;N#qZ3QmlZVgwPrHH!?JaM&W?6zQS#&wU&W7{5F#@zv~wtW$I zM|k3D&)A*7+KlVDB9Cnsa2a=3xZ18o+}+@bt36|P2WvB~=bSvYJ;7z%z2Iv7Xj$L> z;KbFQu>-)`jO)28k8KdRj5`>v)=5j;A>hQ-p0Pv0+KlTpKpxxP;4$SDZ^-t`R zz@F#HeKJ@r_EW%~ud$yBR?By<)4+~VH|MA1IUQ`y&+D9W{SxyGu;*%Go(Wcq{VcHO zThFQe%%*C+m0 zg596-zY47X6*T?j`snX-&(&bTUJ(_x+7j6KnWzCJDmO0%BR?G9kO<=V=FSzbE!>z|W>W-a({uXp? z9xw0Zw}RDjzTXDU`EFd-NKL=wza3oWzXPt8=L+-R2~U3YOZ5t&^7ge*q_dx&J>w*OvUBg4L4$GjQ@7*ZgYwCI9E(GXG!UYWvW< z&N}}uz~=Y9<2bKW zT?kBK%a{UZWE@PNWt>ITq{JmqO#=kXA*Z6l|yoYvy51@(nqd$=5 z{q$h^+)pv}xSvh}cT9OdofJ(y_tQ48+64Tpr}2Nf%wck{TJEP)fYrQiWbRYKt*6X0 z6`FeHJ~enUnm)!bmzsXbH4V7TH7#7Nymw9qH&^SmdwO(j*}pS@)slZkaPk}1{A&6o z|4iUA|IBbTuMx)a+?fTQ{N=T4R&;I2KO0yr`DX_wzj4j4reE^U0WR~;30EucopZsH zzr1$Mjjk>E=K-rF|GePjH?H~B^h^Hvz-9j4aJBN@*$1Bd<+W>mbZyDM09Y;g7X&B2 zam}x$U->)t?){@^evY8I-w&l9U*p5+kEFTxkEYMwUx+y6v*f~X$CP_=5j6Ge{YAlQ z6Y#ShW2j|476Yqg?=KEkEBF2qaO)}aEQzL`xi1CI+>K!_HT{xnX>gfq8Ms=x_m_p6 zt99=$hpsJazdTqi`BwlZzj4j4reE@}2rl!l1XnBf{>t#=FZcc`=-QHhRj^v}uLe$j zZe+5o%W13q{zt;2aMB@25j^=rHEdB8{_PjfR=6UyPde1x0%k7O{bA5KG zdF~B;;l?P>k1f#Da~^I9R-1rd>fQ>Ty0u%kTGqqw!&}2!V{e0|o^y6vu$srwpI08j zxb5KT#v<({@y$70l z)^AU+TGnqYIr_oXjcNUI*KZuQ0bu8;&AG_4egnY=VRMYO*7Z9No1e33uHPB-=hXNt z`g3Wn-}&^p?hhhPdEM`XJEmN(!D#APuOVQy3HVv3G1PJ{4hO4c4Mu>~n%Dj2bHGTr z^_WLJ=i?}JZ8;xDgVl0e_66@lbDVL_uclw}j{%qY$HLXhdqEdG`O9nZICO2vzaLmF z`S%AWzj4j4reE?O050<%2v;lb1qZ>+-+C`P7+qWP9|BfO{zJjZZ(Q@M>6iS6fy?}d z!_~@r!4dG}FXulVU0d=W306z~qrk~;T=T2xm;6VA%lyZ{)tdK$=DBk$-24;jWA8oW zI5hR-Iv$){#xR$fe#vzLxXg7TT&;Xx@N2la{JvK1wUf}bWv(ZK)sp`daPk}1{A&6o z|Eb_I|7mcw@_oVS@Z>Mo`V4e!$$utTE&0y^C%Fd0@W>YV(?J{PV%;zPl9X-KDmRX@1Jw7s1Va34L;3 zz&Q2fz7Vcf=DrASZf&XO60rKkwA6DcSlxQWsmFbEInB?dw5-KtVC(rEed_rgUJjC&`#w)ouzPX6-Txf@+u^4|k?ta`@Y z3;v}X_n~Wx-yguq;Wg6zVf}wZ*OnajgB`1$IX(c+9IZj0@w_K;{DWZEK%eA#2y9KQ z*Zm3T+A{WGu;bM;_7SjqX9D9r-d+#X^o`G>;LN#PugB1}#qV)&=3MTbC(yNJ&QF3J ztDdn>fqyB-)9Bjb_Y62W%47U2y0+wa4(wRJa{L8dTl_u&Cr7z=K1J7-9G`(5tDbfJ9PGL-O0x!i68Ep*+^fCOwcST;*OIRf*s<#VeJIIkpTAdhey}z99H_tbSo4DPeymxa1!!v4 zEKV&8fm3sNycb5-7QaObzw%fuimoj+F9vq3x-}=KeQI7DY)#HXf9tX4CFxVMK1OpEYS})+WxeUK^bCE%)&{=-T49ZsAw%{#`T9STm4^4uPVt}T9hgOj5?hxS3&mK?*uj#VGe%>8~k z0&H$=i8m7LdZuRAM@`@Oi~^^&GUsS?ZSmU|oZ8Ck&KPuUsckISvFaJy1x}9g8Zr)D zTm1F|Cr4S!{^;70;{dQ@)ieJC!RFSMcn5(qf8#lSHGSiAFgUf9^Ew1wTl@|Mr?&DK z9)_+hwH*$2ta`>C0Zxu`?09r-@jDWn9OXJ4g|00*js`ncJ@Y>XY;J9dcPu#bH?Q+o z(>FfHfm2(#=EtLJi{A<0)K<>nM09Pb?bl$(s%Pv;;N&RhbuzlP_?-eyj?(W`bZyCT z8rZSwng8ivb8AbyGr*a@@tnV!zVSH|oZ8Cce-^s7_?->5w$Ev$-#O^oQro#;$Es)S zdEn$I*Y13DZSngJI62B=@mq9l$#DVLvFe%sgKS_} z*tN@8*G^5}_*@3gyvlknN7oj=-+?o)a_z1_*Oqx*33jY{#$E+Zj&e_2jjkTntH~|%kjp^``15!&5?M21iMDYbG%%itk3;m>n+#k z0W|fj&x2q!_eI7%1aDo}325q%uok`WeHiRm^^AK2Y(3_)9=U$8KMHQG=P@+()bqHQ z=699+eDw*qy5~`1Jqb@P*TN=T)$|OP|-!)RX&lu;bJ- z?hUZH9cOO2ey*KAw|TR+`!gMJ`7@mxvHSTwZ575&PJexkucN<#=3c*vK0il)hZvL9 zT>p0q?$3PQFZc)W?+UK}_XVGWy)Z2gr)9k93ce@y#S8BEB?|7}Q?hlzwQp1K=dtfn zaP7O+-1RZ*LeCg0Gz&m5oQ@&h=>#XTeUxcGMk{0R2%15meC_n4aVc3uAsR&(8rHzWOj;Og=D zsrGU0ukYhVN_8eQ!ce%%QPR7gt-kM(BTwWj4GXELDYUQz=39cTW znQNbNjb=ep&l=4N&arj8_KcqmY!1)s?2Xys>KQ)=IOC0_-SJ)*<^;E{@my%?S>w6E zYR1XhdJbi8=&Mg^ng`rE?|ISG<1=6FQ_j0LntJN)1GaAWhU2wo{QTh7c`tybp79HU zGu~L*9q+ss0=Le4VKnv3dl9gjar_u7^Y?h`tB*0=TZ@9ry(MqmTV7wXx0b}7z2&_^ z?%wJ{tfj!))2o}ydy87;zcg5_+*`}Q)#I~l?NhGta%k#VqvgTQYayEBwP*YaU~`mv zYel$vd{(M`@;vGFYh^U`+p2}lTJ$xx$7nTh>oHm#O+CkG4X~PVa%{@I zsjoh%cTI5Xx~+w#9-p;qpK{&SK~qoN>w=w=d(-jSGk!gA>$~){ydUR0+7^4xqiw))&!d&8V>|F( z^y=2OHNBd(_}sERSS{y~@8EWTtH)MS?uI?}?h2M$ z@7m6f^p!L4!kMN?1Q nF<>>DF}x=x?$}y0u73G5<6U5LYR|t-H4d!q{9Hr1Yxw^Fz%T1f literal 45536 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 2; + uint old; + uint actual; + do { + old = tile[tile_el]; + actual = atomicCompSwap(tile[tile_el], old, tile_offset); + } while (actual != old); + tile_seg.next.offset = old; + TileSeg_write(TileSegRef(tile_offset), tile_seg); + tile_offset += TileSeg_size; + } + // TODO for fills: backdrop + t += b; + base += stride; + } +} diff --git a/piet-gpu/shader/path_coarse.spv b/piet-gpu/shader/path_coarse.spv new file mode 100644 index 0000000000000000000000000000000000000000..ed212d751db1a2b8dda69fe4f66ed7937402aafc GIT binary patch literal 13268 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 2; + uint raw0 = pathseg[ix + 0]; + uint raw1 = pathseg[ix + 1]; + uint raw2 = pathseg[ix + 2]; + uint raw3 = pathseg[ix + 3]; + uint raw4 = pathseg[ix + 4]; + PathFillLine s; + s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); + s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.path_ix = raw4; + return s; +} + +void PathFillLine_write(PathFillLineRef ref, PathFillLine s) { + uint ix = ref.offset >> 2; + pathseg[ix + 0] = floatBitsToUint(s.p0.x); + pathseg[ix + 1] = floatBitsToUint(s.p0.y); + pathseg[ix + 2] = floatBitsToUint(s.p1.x); + pathseg[ix + 3] = floatBitsToUint(s.p1.y); + pathseg[ix + 4] = s.path_ix; +} + +PathStrokeLine PathStrokeLine_read(PathStrokeLineRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = pathseg[ix + 0]; + uint raw1 = pathseg[ix + 1]; + uint raw2 = pathseg[ix + 2]; + uint raw3 = pathseg[ix + 3]; + uint raw4 = pathseg[ix + 4]; + uint raw5 = pathseg[ix + 5]; + uint raw6 = pathseg[ix + 6]; + PathStrokeLine s; + s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); + s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.path_ix = raw4; + s.stroke = vec2(uintBitsToFloat(raw5), uintBitsToFloat(raw6)); + return s; +} + +void PathStrokeLine_write(PathStrokeLineRef ref, PathStrokeLine s) { + uint ix = ref.offset >> 2; + pathseg[ix + 0] = floatBitsToUint(s.p0.x); + pathseg[ix + 1] = floatBitsToUint(s.p0.y); + pathseg[ix + 2] = floatBitsToUint(s.p1.x); + pathseg[ix + 3] = floatBitsToUint(s.p1.y); + pathseg[ix + 4] = s.path_ix; + pathseg[ix + 5] = floatBitsToUint(s.stroke.x); + pathseg[ix + 6] = floatBitsToUint(s.stroke.y); +} + +uint PathSeg_tag(PathSegRef ref) { + return pathseg[ref.offset >> 2]; +} + +PathFillLine PathSeg_FillLine_read(PathSegRef ref) { + return PathFillLine_read(PathFillLineRef(ref.offset + 4)); +} + +PathStrokeLine PathSeg_StrokeLine_read(PathSegRef ref) { + return PathStrokeLine_read(PathStrokeLineRef(ref.offset + 4)); +} + +void PathSeg_Nop_write(PathSegRef ref) { + pathseg[ref.offset >> 2] = PathSeg_Nop; +} + +void PathSeg_FillLine_write(PathSegRef ref, PathFillLine s) { + pathseg[ref.offset >> 2] = PathSeg_FillLine; + PathFillLine_write(PathFillLineRef(ref.offset + 4), s); +} + +void PathSeg_StrokeLine_write(PathSegRef ref, PathStrokeLine s) { + pathseg[ref.offset >> 2] = PathSeg_StrokeLine; + PathStrokeLine_write(PathStrokeLineRef(ref.offset + 4), s); +} + diff --git a/piet-gpu/shader/setup.h b/piet-gpu/shader/setup.h index b913086..03b3353 100644 --- a/piet-gpu/shader/setup.h +++ b/piet-gpu/shader/setup.h @@ -31,6 +31,7 @@ // TODO: compute all these #define WIDTH_IN_TILES 128 +#define HEIGHT_IN_TILES 96 #define TILEGROUP_WIDTH_TILES 32 #define TILE_WIDTH_PX 16 #define TILE_HEIGHT_PX 16 diff --git a/piet-gpu/shader/state.h b/piet-gpu/shader/state.h index 2547b93..eacab52 100644 --- a/piet-gpu/shader/state.h +++ b/piet-gpu/shader/state.h @@ -10,9 +10,11 @@ struct State { vec4 bbox; float linewidth; uint flags; + uint path_count; + uint pathseg_count; }; -#define State_size 48 +#define State_size 56 StateRef State_index(StateRef ref, uint index) { return StateRef(ref.offset + index * State_size); @@ -32,12 +34,16 @@ State State_read(StateRef ref) { uint raw9 = state[ix + 9]; uint raw10 = state[ix + 10]; uint raw11 = state[ix + 11]; + uint raw12 = state[ix + 12]; + uint raw13 = state[ix + 13]; State s; s.mat = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); s.translate = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); s.bbox = vec4(uintBitsToFloat(raw6), uintBitsToFloat(raw7), uintBitsToFloat(raw8), uintBitsToFloat(raw9)); s.linewidth = uintBitsToFloat(raw10); s.flags = raw11; + s.path_count = raw12; + s.pathseg_count = raw13; return s; } @@ -55,5 +61,7 @@ void State_write(StateRef ref, State s) { state[ix + 9] = floatBitsToUint(s.bbox.w); state[ix + 10] = floatBitsToUint(s.linewidth); state[ix + 11] = s.flags; + state[ix + 12] = s.path_count; + state[ix + 13] = s.pathseg_count; } diff --git a/piet-gpu/shader/tile.h b/piet-gpu/shader/tile.h new file mode 100644 index 0000000..b4a8c9b --- /dev/null +++ b/piet-gpu/shader/tile.h @@ -0,0 +1,105 @@ +// Code auto-generated by piet-gpu-derive + +struct PathRef { + uint offset; +}; + +struct TileRef { + uint offset; +}; + +struct TileSegRef { + uint offset; +}; + +struct Path { + uvec4 bbox; + TileRef tiles; +}; + +#define Path_size 12 + +PathRef Path_index(PathRef ref, uint index) { + return PathRef(ref.offset + index * Path_size); +} + +struct Tile { + TileSegRef tile; + int backdrop; +}; + +#define Tile_size 8 + +TileRef Tile_index(TileRef ref, uint index) { + return TileRef(ref.offset + index * Tile_size); +} + +struct TileSeg { + vec2 start; + vec2 end; + TileSegRef next; +}; + +#define TileSeg_size 20 + +TileSegRef TileSeg_index(TileSegRef ref, uint index) { + return TileSegRef(ref.offset + index * TileSeg_size); +} + +Path Path_read(PathRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = tile[ix + 0]; + uint raw1 = tile[ix + 1]; + uint raw2 = tile[ix + 2]; + Path s; + s.bbox = uvec4(raw0 & 0xffff, raw0 >> 16, raw1 & 0xffff, raw1 >> 16); + s.tiles = TileRef(raw2); + return s; +} + +void Path_write(PathRef ref, Path s) { + uint ix = ref.offset >> 2; + tile[ix + 0] = s.bbox.x | (s.bbox.y << 16); + tile[ix + 1] = s.bbox.z | (s.bbox.w << 16); + tile[ix + 2] = s.tiles.offset; +} + +Tile Tile_read(TileRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = tile[ix + 0]; + uint raw1 = tile[ix + 1]; + Tile s; + s.tile = TileSegRef(raw0); + s.backdrop = int(raw1); + return s; +} + +void Tile_write(TileRef ref, Tile s) { + uint ix = ref.offset >> 2; + tile[ix + 0] = s.tile.offset; + tile[ix + 1] = uint(s.backdrop); +} + +TileSeg TileSeg_read(TileSegRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = tile[ix + 0]; + uint raw1 = tile[ix + 1]; + uint raw2 = tile[ix + 2]; + uint raw3 = tile[ix + 3]; + uint raw4 = tile[ix + 4]; + TileSeg s; + s.start = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); + s.end = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.next = TileSegRef(raw4); + return s; +} + +void TileSeg_write(TileSegRef ref, TileSeg s) { + uint ix = ref.offset >> 2; + tile[ix + 0] = floatBitsToUint(s.start.x); + tile[ix + 1] = floatBitsToUint(s.start.y); + tile[ix + 2] = floatBitsToUint(s.end.x); + tile[ix + 3] = floatBitsToUint(s.end.y); + tile[ix + 4] = s.next.offset; +} + diff --git a/piet-gpu/shader/tile_alloc.comp b/piet-gpu/shader/tile_alloc.comp new file mode 100644 index 0000000..d8b1eb9 --- /dev/null +++ b/piet-gpu/shader/tile_alloc.comp @@ -0,0 +1,73 @@ +// Allocation and initialization of tiles for paths. + +#version 450 +#extension GL_GOOGLE_include_directive : enable + +#include "setup.h" + +#define TILE_ALLOC_WG 32 + +layout(local_size_x = TILE_ALLOC_WG, local_size_y = 1) in; + +layout(set = 0, binding = 0) buffer AnnotatedBuf { + uint[] annotated; +}; + +layout(set = 0, binding = 1) buffer AllocBuf { + uint n_elements; + uint n_pathseg; + uint alloc; +}; + +layout(set = 0, binding = 2) buffer TileBuf { + uint[] tile; +}; + +#include "annotated.h" +#include "tile.h" + +// scale factors useful for converting coordinates to tiles +#define SX (1.0 / float(TILE_WIDTH_PX)) +#define SY (1.0 / float(TILE_HEIGHT_PX)) + +void main() { + uint element_ix = gl_GlobalInvocationID.x; + PathRef path_ref = PathRef(element_ix * Path_size); + AnnotatedRef ref = AnnotatedRef(element_ix * Annotated_size); + + uint tag = Annotated_Nop; + if (element_ix < n_elements) { + tag = Annotated_tag(ref); + } + int x0 = 0, y0 = 0, x1 = 0, y1 = 0; + switch (tag) { + case Annotated_Fill: + case Annotated_Stroke: + // Note: we take advantage of the fact that fills and strokes + // have compatible layout. + AnnoFill fill = Annotated_Fill_read(ref); + x0 = int(floor(fill.bbox.x * SX)); + y0 = int(floor(fill.bbox.y * SY)); + x1 = int(ceil(fill.bbox.z * SX)); + y1 = int(ceil(fill.bbox.w * SY)); + break; + } + x0 = clamp(x0, 0, WIDTH_IN_TILES); + y0 = clamp(y0, 0, HEIGHT_IN_TILES); + x1 = clamp(x1, 0, WIDTH_IN_TILES); + y1 = clamp(y1, 0, HEIGHT_IN_TILES); + + Path path; + path.bbox = uvec4(x0, y0, x1, y1); + uint n_tiles = (x1 - x0) * (y1 - y0); + path.tiles = TileRef(0); + if (n_tiles > 0) { + path.tiles.offset = atomicAdd(alloc, n_tiles * Tile_size); + Tile init_tile = Tile(TileSegRef(0), 0); + // TODO: improve load balancing + for (uint i = 0; i < n_tiles; i++) { + Tile_write(Tile_index(path.tiles, i), init_tile); + } + } + Path_write(path_ref, path); +} diff --git a/piet-gpu/shader/tile_alloc.spv b/piet-gpu/shader/tile_alloc.spv new file mode 100644 index 0000000000000000000000000000000000000000..08359033f250844e7e885dc0ed35fe986c7c4d6d GIT binary patch literal 8248 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 X6S~o#DY)qUS?uU-O?%537kmFddul(M literal 0 HcmV?d00001 diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 3ec7e1d..19e9b43 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -129,12 +129,23 @@ pub struct Renderer { pub state_buf: D::Buffer, pub anno_buf: D::Buffer, + pub pathseg_buf: D::Buffer, + pub tile_buf: D::Buffer, pub bin_buf: D::Buffer, pub ptcl_buf: D::Buffer, el_pipeline: D::Pipeline, el_ds: D::DescriptorSet, + tile_pipeline: D::Pipeline, + tile_ds: D::DescriptorSet, + + path_pipeline: D::Pipeline, + path_ds: D::DescriptorSet, + + tile_alloc_buf_host: D::Buffer, + tile_alloc_buf_dev: D::Buffer, + bin_pipeline: D::Pipeline, bin_ds: D::DescriptorSet, @@ -151,10 +162,12 @@ pub struct Renderer { k4_ds: D::DescriptorSet, n_elements: usize, + n_paths: usize, + n_pathseg: usize, } impl Renderer { - pub unsafe fn new(device: &D, scene: &[u8]) -> Result { + pub unsafe fn new(device: &D, scene: &[u8], n_paths: usize, n_pathseg: usize) -> Result { let host = MemFlags::host_coherent(); let dev = MemFlags::device_local(); @@ -170,16 +183,44 @@ impl Renderer { device.write_buffer(&scene_buf, &scene)?; let state_buf = device.create_buffer(1 * 1024 * 1024, dev)?; - let anno_buf = device.create_buffer(64 * 1024 * 1024, dev)?; + let anno_buf = device.create_buffer(64 * 1024 * 1024, host)?; + let pathseg_buf = device.create_buffer(64 * 1024 * 1024, host)?; + let tile_buf = device.create_buffer(64 * 1024 * 1024, host)?; let bin_buf = device.create_buffer(64 * 1024 * 1024, dev)?; let ptcl_buf = device.create_buffer(48 * 1024 * 1024, dev)?; let image_dev = device.create_image2d(WIDTH as u32, HEIGHT as u32, dev)?; let el_code = include_bytes!("../shader/elements.spv"); - let el_pipeline = device.create_simple_compute_pipeline(el_code, 3, 0)?; + let el_pipeline = device.create_simple_compute_pipeline(el_code, 4, 0)?; let el_ds = device.create_descriptor_set( &el_pipeline, - &[&scene_dev, &state_buf, &anno_buf], + &[&scene_dev, &state_buf, &anno_buf, &pathseg_buf], + &[], + )?; + + let tile_alloc_buf_host = device.create_buffer(12, host)?; + let tile_alloc_buf_dev = device.create_buffer(12, dev)?; + + // TODO: constants + const PATH_SIZE: usize = 12; + let tile_alloc_start = ((n_paths + 31) & !31) * PATH_SIZE; + device.write_buffer( + &tile_alloc_buf_host, + &[n_paths as u32, n_pathseg as u32, tile_alloc_start as u32], + )?; + let tile_alloc_code = include_bytes!("../shader/tile_alloc.spv"); + let tile_pipeline = device.create_simple_compute_pipeline(tile_alloc_code, 3, 0)?; + let tile_ds = device.create_descriptor_set( + &tile_pipeline, + &[&anno_buf, &tile_alloc_buf_dev, &tile_buf], + &[], + )?; + + let path_alloc_code = include_bytes!("../shader/path_coarse.spv"); + let path_pipeline = device.create_simple_compute_pipeline(path_alloc_code, 3, 0)?; + let path_ds = device.create_descriptor_set( + &path_pipeline, + &[&pathseg_buf, &tile_alloc_buf_dev, &tile_buf], &[], )?; @@ -226,6 +267,10 @@ impl Renderer { image_dev, el_pipeline, el_ds, + tile_pipeline, + tile_ds, + path_pipeline, + path_ds, bin_pipeline, bin_ds, coarse_pipeline, @@ -234,18 +279,25 @@ impl Renderer { k4_ds, state_buf, anno_buf, + pathseg_buf, + tile_buf, bin_buf, ptcl_buf, + tile_alloc_buf_host, + tile_alloc_buf_dev, bin_alloc_buf_host, bin_alloc_buf_dev, coarse_alloc_buf_host, coarse_alloc_buf_dev, n_elements, + n_paths, + n_pathseg, }) } pub unsafe fn record(&self, cmd_buf: &mut impl CmdBuf, query_pool: &D::QueryPool) { cmd_buf.copy_buffer(&self.scene_buf, &self.scene_dev); + cmd_buf.copy_buffer(&self.tile_alloc_buf_host, &self.tile_alloc_buf_dev); cmd_buf.copy_buffer(&self.bin_alloc_buf_host, &self.bin_alloc_buf_dev); cmd_buf.copy_buffer(&self.coarse_alloc_buf_host, &self.coarse_alloc_buf_dev); cmd_buf.clear_buffer(&self.state_buf); @@ -264,26 +316,44 @@ impl Renderer { ); cmd_buf.write_timestamp(&query_pool, 1); cmd_buf.memory_barrier(); + cmd_buf.dispatch( + &self.tile_pipeline, + &self.tile_ds, + (((self.n_paths + 31) / 32) as u32, 1, 1), + ); + cmd_buf.write_timestamp(&query_pool, 2); + cmd_buf.memory_barrier(); + cmd_buf.dispatch( + &self.path_pipeline, + &self.path_ds, + (((self.n_pathseg + 31) / 32) as u32, 1, 1), + ); + /* cmd_buf.dispatch( &self.bin_pipeline, &self.bin_ds, (((self.n_elements + 255) / 256) as u32, 1, 1), ); - cmd_buf.write_timestamp(&query_pool, 2); + */ + cmd_buf.write_timestamp(&query_pool, 3); cmd_buf.memory_barrier(); + /* cmd_buf.dispatch( &self.coarse_pipeline, &self.coarse_ds, (WIDTH as u32 / 256, HEIGHT as u32 / 256, 1), ); - cmd_buf.write_timestamp(&query_pool, 3); + */ + cmd_buf.write_timestamp(&query_pool, 4); cmd_buf.memory_barrier(); + /* cmd_buf.dispatch( &self.k4_pipeline, &self.k4_ds, ((WIDTH / TILE_W) as u32, (HEIGHT / TILE_H) as u32, 1), ); - cmd_buf.write_timestamp(&query_pool, 4); + cmd_buf.write_timestamp(&query_pool, 5); + */ cmd_buf.memory_barrier(); cmd_buf.image_barrier(&self.image_dev, ImageLayout::General, ImageLayout::BlitSrc); } diff --git a/piet-gpu/src/render_ctx.rs b/piet-gpu/src/render_ctx.rs index da234de..7908ff2 100644 --- a/piet-gpu/src/render_ctx.rs +++ b/piet-gpu/src/render_ctx.rs @@ -35,6 +35,10 @@ pub struct PietGpuRenderContext { // Will probably need direct accesss to hal Device to create images etc. inner_text: PietGpuText, stroke_width: f32, + // We're tallying these cpu-side for expedience, but will probably + // move this to some kind of readback from element processing. + path_count: usize, + pathseg_count: usize, } #[derive(Clone)] @@ -56,6 +60,8 @@ impl PietGpuRenderContext { elements, inner_text, stroke_width, + path_count: 0, + pathseg_count: 0, } } @@ -63,6 +69,14 @@ impl PietGpuRenderContext { self.elements.encode(&mut self.encoder); self.encoder.buf() } + + pub fn path_count(&self) -> usize { + self.path_count + } + + pub fn pathseg_count(&self) -> usize { + self.pathseg_count + } } impl RenderContext for PietGpuRenderContext { @@ -99,6 +113,7 @@ impl RenderContext for PietGpuRenderContext { PietGpuBrush::Solid(rgba_color) => { let stroke = Stroke { rgba_color }; self.elements.push(Element::Stroke(stroke)); + self.path_count += 1; } _ => (), } @@ -121,6 +136,7 @@ impl RenderContext for PietGpuRenderContext { PietGpuBrush::Solid(rgba_color) => { let fill = Fill { rgba_color }; self.elements.push(Element::Fill(fill)); + self.path_count += 1; } _ => (), } @@ -204,6 +220,7 @@ impl PietGpuRenderContext { } else { self.elements.push(Element::StrokeLine(seg)); } + self.pathseg_count += 1; } fn encode_path(&mut self, path: impl Iterator, is_fill: bool) {