From 70a9c17e2377955894b0b69eefd9393a47cddb2f Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Wed, 3 Jun 2020 09:28:43 -0700 Subject: [PATCH] Continue building out pipeline Plumbs the new tiling scheme to k4. This works (stroke only) but still has some performance issues. --- piet-gpu-types/src/ptcl.rs | 5 +- piet-gpu/bin/cli.rs | 12 ++- piet-gpu/shader/coarse.comp | 159 ++++++++----------------------- piet-gpu/shader/coarse.spv | Bin 49028 -> 26696 bytes piet-gpu/shader/kernel4.comp | 33 ++++--- piet-gpu/shader/kernel4.spv | Bin 23052 -> 14620 bytes piet-gpu/shader/path_coarse.comp | 2 +- piet-gpu/shader/path_coarse.spv | Bin 13268 -> 13264 bytes piet-gpu/shader/ptcl.h | 6 +- piet-gpu/src/lib.rs | 41 ++++---- piet-gpu/src/pico_svg.rs | 4 +- 11 files changed, 94 insertions(+), 168 deletions(-) diff --git a/piet-gpu-types/src/ptcl.rs b/piet-gpu-types/src/ptcl.rs index bdf342b..98c4d44 100644 --- a/piet-gpu-types/src/ptcl.rs +++ b/piet-gpu-types/src/ptcl.rs @@ -13,8 +13,9 @@ piet_gpu! { end: [f32; 2], } struct CmdStroke { - // Consider a specialization to one segment. - seg_ref: Ref, + // This is really a Ref, but we don't have cross-module + // references. + tile_ref: u32, half_width: f32, rgba_color: u32, } diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index 04a20ba..0714f00 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -171,7 +171,7 @@ fn main() -> Result<(), Error> { let fence = device.create_fence(false)?; let mut cmd_buf = device.create_cmd_buf()?; - let query_pool = device.create_query_pool(5)?; + let query_pool = device.create_query_pool(7)?; let mut ctx = PietGpuRenderContext::new(); if let Some(input) = matches.value_of("INPUT") { @@ -204,14 +204,16 @@ fn main() -> Result<(), Error> { println!("Element kernel time: {:.3}ms", ts[0] * 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); - */ + println!("Binning kernel time: {:.3}ms", (ts[3] - ts[2]) * 1e3); + println!("Coarse raster kernel time: {:.3}ms", (ts[4] - ts[3]) * 1e3); + println!("Render kernel time: {:.3}ms", (ts[5] - ts[4]) * 1e3); + /* let mut data: Vec = Default::default(); - device.read_buffer(&renderer.tile_buf, &mut data).unwrap(); + device.read_buffer(&renderer.ptcl_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/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 3656f77..28efd16 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -15,17 +15,22 @@ layout(set = 0, binding = 1) buffer BinsBuf { uint[] bins; }; -layout(set = 0, binding = 2) buffer AllocBuf { +layout(set = 0, binding = 2) buffer TileBuf { + uint[] tile; +}; + +layout(set = 0, binding = 3) buffer AllocBuf { uint n_elements; uint alloc; }; -layout(set = 0, binding = 3) buffer PtclBuf { +layout(set = 0, binding = 4) buffer PtclBuf { uint[] ptcl; }; #include "annotated.h" #include "bins.h" +#include "tile.h" #include "ptcl.h" #define LG_N_PART_READ 8 @@ -197,37 +202,11 @@ void main() { tag = Annotated_tag(ref); } - // Setup for coverage algorithm. - float a, b, c; // Bounding box of element in pixel coordinates. float xmin, xmax, ymin, ymax; uint my_slice = th_ix / 32; uint my_mask = 1 << (th_ix & 31); switch (tag) { - case Annotated_FillLine: - case Annotated_StrokeLine: - AnnoStrokeLineSeg line = Annotated_StrokeLine_read(ref); - xmin = min(line.p0.x, line.p1.x) - line.stroke.x; - xmax = max(line.p0.x, line.p1.x) + line.stroke.x; - ymin = min(line.p0.y, line.p1.y) - line.stroke.y; - ymax = max(line.p0.y, line.p1.y) + line.stroke.y; - float dx = line.p1.x - line.p0.x; - float dy = line.p1.y - line.p0.y; - if (tag == Annotated_FillLine) { - // Set bit for backdrop sign calculation, 1 is +1, 0 is -1. - if (dy < 0) { - atomicOr(sh_bd_sign[my_slice], my_mask); - } else { - 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; - b = invslope; // Note: assumes square tiles, otherwise scale. - a = (line.p0.x - xy0.x - (line.p0.y - 0.5 * float(TILE_HEIGHT_PX) - xy0.y) * b) * SX; - break; case Annotated_Fill: case Annotated_Stroke: // Note: we take advantage of the fact that fills and strokes @@ -237,10 +216,6 @@ void main() { xmax = fill.bbox.z; ymin = fill.bbox.y; ymax = fill.bbox.w; - // Just let the clamping to xmin and xmax determine the bounds. - a = 0.0; - b = 0.0; - c = 1e9; break; default: ymin = 0; @@ -254,37 +229,23 @@ void main() { // Compute bounding box in tiles and clip to this bin. int x0 = int(floor((xmin - xy0.x) * SX)); int x1 = int(ceil((xmax - xy0.x) * SX)); - int xr = int(ceil((right_edge - xy0.x) * SX)); int y0 = int(floor((ymin - xy0.y) * SY)); int y1 = int(ceil((ymax - xy0.y) * SY)); x0 = clamp(x0, 0, N_TILE_X); x1 = clamp(x1, x0, N_TILE_X); - xr = clamp(xr, 0, N_TILE_X); y0 = clamp(y0, 0, N_TILE_Y); y1 = clamp(y1, y0, N_TILE_Y); - float t = a + b * float(y0); for (uint y = y0; y < y1; y++) { - uint xx0 = clamp(int(floor(t - c)), x0, x1); - uint xx1 = clamp(int(ceil(t + c)), x0, x1); - for (uint x = xx0; x < xx1; x++) { + for (uint x = x0; x < x1; x++) { atomicOr(sh_bitmaps[my_slice][y * N_TILE_X + x], my_mask); } - if (tag == Annotated_FillLine && ymin <= xy0.y + float(y * TILE_HEIGHT_PX)) { - // Assign backdrop to all tiles to the right of the ray crossing the - // top edge of this tile, up to the right edge of the fill bbox. - float xray = t - 0.5 * b; - xx0 = max(int(ceil(xray)), 0); - for (uint x = xx0; x < xr; x++) { - atomicOr(sh_backdrop[my_slice][y * N_TILE_X + x], my_mask); - } - } - t += b; } barrier(); // 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++) { seg_count += bitCount(sh_bitmaps[i][th_ix] & sh_is_segment[i]); @@ -372,45 +333,29 @@ void main() { Segment seg = Segment(line.p0, line.p1, y_edge); Segment_write(SegmentRef(seg_alloc + Segment_size * ix), 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) { - uint nonseg_bitmap = bitmap & ~is_segment; - if (nonseg_bitmap == 0) { - backdrop += count_backdrop(bd_bitmap, bd_sign); - seg_count += bitCount(bitmap & is_segment); + if (bitmap == 0) { slice_ix++; if (slice_ix == N_SLICE) { break; } bitmap = sh_bitmaps[slice_ix][th_ix]; - bd_bitmap = sh_backdrop[slice_ix][th_ix]; - bd_sign = sh_bd_sign[slice_ix]; - is_segment = sh_is_segment[slice_ix]; - nonseg_bitmap = bitmap & ~is_segment; - if (nonseg_bitmap == 0) { + if (bitmap == 0) { continue; } } - uint element_ref_ix = slice_ix * 32 + findLSB(nonseg_bitmap); + uint element_ref_ix = slice_ix * 32 + findLSB(bitmap); uint element_ix = sh_elements[element_ref_ix]; - // Bits up to and including the lsb - 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; + // Clear LSB + bitmap &= bitmap - 1; // At this point, we read the element again from global memory. // If that turns out to be expensive, maybe we can pack it into @@ -419,6 +364,7 @@ void main() { tag = Annotated_tag(ref); switch (tag) { + /* case Annotated_Fill: if (last_chunk_n > 0 || seg_count > 0) { SegChunkRef chunk_ref = SegChunkRef(0); @@ -460,63 +406,34 @@ void main() { seg_count = 0; backdrop = 0; break; + */ case Annotated_Stroke: - // TODO: reduce divergence & code duplication? Much of the - // fill and stroke processing is in common. - if (last_chunk_n > 0 || seg_count > 0) { - SegChunkRef chunk_ref = SegChunkRef(0); - if (seg_count > 0) { - chunk_ref = alloc_seg_chunk(); - SegChunk chunk; - chunk.n = seg_count; - chunk.next = SegChunkRef(0); - uint seg_offset = seg_alloc + seg_start * Segment_size; - chunk.segs = SegmentRef(seg_offset); - SegChunk_write(chunk_ref, chunk); + // 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. + uint path_ix = element_ix; + Path path = Path_read(PathRef(path_ix * Path_size)); + // It may be we have a strong guarantee this will always be `true`, but + // I prefer not to take chances. + if (tile_x >= path.bbox.x && tile_x < path.bbox.z && tile_y >= path.bbox.y && tile_y < path.bbox.w) { + uint stride = path.bbox.z - path.bbox.x; + uint tile_subix = (tile_y - path.bbox.y) * stride + tile_x - path.bbox.x; + Tile tile = Tile_read(Tile_index(path.tiles, tile_subix)); + if (tile.tile.offset != 0) { + AnnoStroke stroke = Annotated_Stroke_read(ref); + CmdStroke cmd_stroke; + cmd_stroke.tile_ref = tile.tile.offset; + cmd_stroke.half_width = 0.5 * stroke.linewidth; + cmd_stroke.rgba_color = stroke.rgba_color; + alloc_cmd(cmd_ref, cmd_limit); + Cmd_Stroke_write(cmd_ref, cmd_stroke); + cmd_ref.offset += Cmd_size; } - if (last_chunk_n > 0) { - SegChunk chunk; - chunk.n = last_chunk_n; - chunk.next = chunk_ref; - chunk.segs = last_chunk_segs; - SegChunk_write(last_chunk_ref, chunk); - } else { - first_seg_chunk = chunk_ref; - } - - AnnoStroke stroke = Annotated_Stroke_read(ref); - CmdStroke cmd_stroke; - cmd_stroke.seg_ref = first_seg_chunk; - cmd_stroke.half_width = 0.5 * stroke.linewidth; - cmd_stroke.rgba_color = stroke.rgba_color; - alloc_cmd(cmd_ref, cmd_limit); - Cmd_Stroke_write(cmd_ref, cmd_stroke); - cmd_ref.offset += Cmd_size; - last_chunk_n = 0; } - seg_start += seg_count; - seg_count = 0; - break; - default: - // This shouldn't happen, but just in case. - seg_start++; break; } } - if (seg_count > 0) { - SegChunkRef chunk_ref = alloc_seg_chunk(); - if (last_chunk_n > 0) { - SegChunk_write(last_chunk_ref, SegChunk(last_chunk_n, chunk_ref, last_chunk_segs)); - } else { - first_seg_chunk = chunk_ref; - } - // TODO: free two registers by writing count and segments ref now, - // as opposed to deferring SegChunk write until all fields are known. - last_chunk_ref = chunk_ref; - last_chunk_n = seg_count; - uint seg_offset = seg_alloc + seg_start * Segment_size; - last_chunk_segs = SegmentRef(seg_offset); - } barrier(); rd_ix += N_TILE; diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index 4b7e1c4da977a05b992d3869cdf4105a5b8cbc06..6b2afafb9dfc2fd16bd974762c9ddfc43ac15b64 100644 GIT binary patch literal 26696 zcmai+37lTj{r@jBlLWDgeMxLV?E6k)iJ+xot8Gk@Neq)o%uJA?jaa(rrf8||wfs=D zc2!kNDMgD`SJj2K6m6}g_`ToHb3ZdDPk;Z{|8ktq=kxuZ@A;l{&pr3v=Xplk;_Hp9 zs>P~N{IgGc)i{=~7DuV7rK%B)dh&r&58QlUaK`4_Znvcl%T#R*KYf<1MphlvHl5wo z)2HEK41c98M|<_<72*0@+W2R2>U$vV)sofZ13M=lbkO7j_vxHIcW&pv;QZe1&fcEB z?#>xK{oT_Cdrs-rZ&cye(>J|$-i*?r1CKI?4zglk)x^HOIs5kX_8!tblWdwAay4h> z%z^H~3Z;W_4*O{v!9Qvq{j;WZbxxntJEvd0>FXG#O`CI?_Ks>a^D*4Mv%kA*#zq5! z{qv>|ZdP=@*}Sc`+NI&&RxM54Pd*()T&`|Ab+CWVN!@1I>_^?B%yR{}A)CIl} z^HeYMbWCc_yK{SL&y2wnz#U{WhI%;ee~n-$wQ>wA(I#V9rEWjiHP}6)IfTYsse25M zVRW?y+`byVor7Jo%DGB_x}y%($?Etw`&p)1XOXtewEipG|0BlQMU3?piQ&rl|3z?) z+NyPjVvMLZ08i@ao6K?=6U8b4{?u7m!bStks3cF&l_wd6VX zLN2;rWD*KJX*F`%!Z?j}+g@!AKD4K|F?h!=_ZS}gQq>M{eHI;b-D+sG&H3F9-^AV? zJUrg^YA5)?U4ti(Ok-WiJ%;;guXcs&qi#NOk6fqGoQpwn8ldiA@7aUec8g3v-C4Qb zcgL@pUq>|&>?o#o&oZS(e`+3OPJ6-iQ@4M)hy7c7e$>oqTG#ZGX7taQt8QNVQrmWs zycFoM%xfR~nt6?=_5<%Vd&bc93T^bO?lGKON3}nE$QNubY98iCzivfCx;dWE)jPAZ z-ZX)7yyIH59Y0ng(v>zFg&nWL}Q+|C!iu^x+Aiow>Srnz^=Bvxed> zSLu{puKq6P+nirtg!2T(1)aP`f`{j>y;=g!Jz&6TbKjDCOhdDFzLu&rV=vciUyq}$ zS{a`4j|Zop2~EGD@oxrqOG`g;5BDEy``Mz_>}T6%`=R;Tsmbg4YOD5ur{AgI^m|0p zZ>Zm6ntUkVX+^ufqTK-a;KAv=$xrSv7tPxI=GB_`yrS)wz~%g3*21rB;a9cr@50@q zxCZE_qq-43(d(vI%W58do%LOx!F#^cH8rj?YID1}_H`a_2cPJM*s!%%cf!kD?`q)> zz&ZDgX9}+^pYLb&b{;XO|D?(NbLP#RvbTMVsGexH8|)eEnbY)c>QAEgb<#uAGPH(% zhkn}pEos|c{RPZj*i!}{)p&oPj|1mS@9Le>cM6q3?MZuk^@9H1r|Rqfm2fU(b9uWF zuV-Dazkp~NzG~5MfJwJlIM0gbo%qT&F&igskQdE!lLczbb!{Qsxj#G zo@x$s=$lwipZl(JwKDep8JXMmY8CjY{oy03RpFi;UcLNpeGWRR)ojP>%>1TAtd43d z+Q12&(`IxI^vq%eYU`sd`ux&XO+f2u^xsi!U$^NQoZU5-vu~T7Yb}pG&d=UWjg!XE zoQ*pF!_cSCnK#6z!RzHnq>)u$vt45d!&id+aDD3Ah%=%(tMD6+*H&GvPrZ(A1vdgA z*#5Sm_U+Z(+WO~Ab8~VG&uHT%pm~PX+N)=44VMOg&Njt9{MQ!#!Xmt*dJ)e24h_S3 zd9|sPkMP89t46nR4Rp-{^F$9W9n@AvE8hofvzC61=c)E;UGQ*jyuZL_&+i=Q?U`Qh zzwOn=O>K79fNQ(G+6F$8x2BA5+eKouS3AH@o89~zK>wW^yvxhF(SA4h{KCFxgU8-h z?W;dKwYeT7W|tNpp`;&XYOZy|46HXAz(FYFrE7u!V2b!gpVU zkEkZWS-*`9vA$% z#qPb|&&K>*#yR$LKDnRk$R`q1z8YmkM&{VY(c1RnG59#fu@sNBvG_S%Q}a{4s=?J( z#~z+IQVF4HDefeIqJk+9&AjtwV8(%YFlF3enm8OeQm!= z?K2kJs$koz>FfB6tu9}S+P2~2z~&-1)&|tcWkayB)Qmm8=Gw=R+eWobKW&cH&wR8y ze*4>;T1_A0`w*<%c-v4LU(I;FFo@Ng@!A@nsl_e5&_J-3sn$5IxfnD<`P+SSc_Z)!DjvF(1;$zuxGJk&f#ejcICF~{csus+%x z+kw=sE8A)_<_D^Uh!kFv0Wd=e+bUM4c+*1?-^>^e^O)Hq`#*$QjGT-YR|p-=>J=|^R1qE-gEeO zsQo*T1#suc_q2?4DX?wTHYJwz(iE=;eSOD@@A6=M)%3Mqk)p48tOVBAVjgneyX0%a z*RHW^RsVJ1UL)%I%g5H5_H}D)`;E!P^R)?@v9_UBcV6V%!qpOcJFv0UjV<>bOD){@ zt8n}F-AX=||IV(lG4+@G4i$g-SvAi&Iv4J>CZ9x{%i%2fA$|{h0X*?ugd5NN{{{A( zIv)FfrM9W-FMqYxw7*&7>;cxxxWG7)&pPl;3qBq`w%`-seqW@&?GI?QU$9%+KL+=C zA@)^FwyO4seFA(XgiSdUu5q7h%zq)8CFAsY*QRpMsn5CLt`VPaJ(t_F<3SrXs>b#5 zCW`x@ZO2plIg4vB{D~I+WWl{I{QRW!_cN4|ztF;8Y2kiG691#=cQFzS9}oAlk#Mgi zKkq2HpL2xk@8=ugUQd2r5w6|OAxiG&5GD6>h?4vHL%93m`UM|PJU?TIU4B>#pVq?X zweYK3xbOaDJm39Ges>Fhvfvxi|1$;me&c5WX|LVS0>a0`{VX6{yPpMwYxn&=-20;M z_~F`pzYq60(|7xl`+gs;zwh@Y_uW3+`?&A+;l}gbKHUC&w=cQx_9ge-zT|rr-1FzV zee81I?@R7Ge#w2$4Ibj$&+e^YtE}mVW*Q_W7us>*wL>`g@O%$N%r(*73cFrk=cB z0((x?)9z((Yd`-$Q~!89xBmpI**53pUtr^DGoKfz)e`qraO*t1hNhl;UI!afJ?-8A zx90OEntIOlzrku{K5xN|r_Fr4N2+CA{Rga;zTX9_`OF!g_rQ)LWAsL^pSm%<&#J|L zaj;rqw!zhIMLU@FA~L5gT0rFGY6}~U5hu6+jZ1yz}0-l zeS^N%0(-p0d}v#fqUJM`IB~{-jpN+-{IwosEJfX%<@Wn;+NHge%j1etvoNgz{?oB!pj)D!F{*TPn$8+ zGG@nTUe?KDcd#+DHunIl`8h%6OwD=DoH@R<-xFM}m5Fe*td+gM9?p-pNfb5n5GT$) z;Bu|(3*VcfZXWWCBkirtYcjR*tUZ@gsQ0IMXy324Tc^JRz^?o71HoP&SJ!L)ePDgm z%;N9RG1}{nVZJ z^ke-RPb=;vonZ5DtooaSd3I4JPkoN3sF|lYeNG3LdCq{Bd3M9~QO_Pe6Rhr>B+e}O z1I74Gfa|B8c0FKqKXY^Lr-9WH<3zC9M4qmE_Bjc>6fxbmw8gI%tS#qH9@`wS`G?Pi z8#D8MGFWXPG1E^!JpE`-+X1k)?6-s9K8k(Vw_G33P3G2Go9|z_zD@x<@77*Vr%}(R zc-ZzswcR>>pAI%gu7xweYS|m*r&5e%Y;oG233d+B_QP`th7w&V?ubdEhe7kHXcG=f}Yw=Be#t6gBe{ zC(b9pWuBjeJ9qc7XH25~`C#{7^)ra&v-_vO>eto#%BR7`QTO`!EVYMyYx@jE&A!Fy z`*Yy3?+f5%-=Bxuw|e@%5UieOm5ac}QP13740dj{rLQl5)x*CCK8%vJeF<1izg!z? zo{!{iEzdk!x1JyEu208mo&GKbJGS)qC9qoVZQ9gqpEd9LO#92g<@x?HTrKDOt6&f3 zT-#SDYUUwMoUeh)IsZC*)x!P_a5*pEgzKZ8`TZ8y`8AevCD+f~)2DUo+@$>#*ld3} zC3#&5RyW>i)biM_0=v&tHe4Td<6TXy zmiE_y)xxg_uR_WCzd=krE&F@j)N%7GB{n~DwobLh~BRT#6td@OKeh0-^<{(bnAA-## z{9drJ?ydKc`@r|eDY=&K2dn2i{0M9ub;olzwOV36050eBLAXBI3w{h%TgVu*)*gbp z*0d$g!(jECzn_4Ot?s-(O6}pbr0o%knqw9x&SPNv&v}qLxAC=>JMXr&PTQY?ou7>T zXJEDL2inxibLqUN{o`Qg``UUBegdrKT5t@%0DCwFZ9k{{hGHJ##CZyAoXp`b!D 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

NWVIO}<9> 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 Z!9@J`?$9=(>$9=$ZZLVi0 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 F{{>QY+?)Ua literal 23052 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

}mVUT*vZ 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 delta 94 zcmcbRekFaw9XZ35j0_Bp46F>PKzcV2w*hf50|SFC0}F!*klq6ohpE{+`J%M2=suw6 bULbA)>IA9U4`lC|Y$z|xxNEbke76Vyt11&y diff --git a/piet-gpu/shader/ptcl.h b/piet-gpu/shader/ptcl.h index dd1f9a8..d337598 100644 --- a/piet-gpu/shader/ptcl.h +++ b/piet-gpu/shader/ptcl.h @@ -68,7 +68,7 @@ CmdLineRef CmdLine_index(CmdLineRef ref, uint index) { } struct CmdStroke { - SegChunkRef seg_ref; + uint tile_ref; float half_width; uint rgba_color; }; @@ -220,7 +220,7 @@ CmdStroke CmdStroke_read(CmdStrokeRef ref) { uint raw1 = ptcl[ix + 1]; uint raw2 = ptcl[ix + 2]; CmdStroke s; - s.seg_ref = SegChunkRef(raw0); + s.tile_ref = raw0; s.half_width = uintBitsToFloat(raw1); s.rgba_color = raw2; return s; @@ -228,7 +228,7 @@ CmdStroke CmdStroke_read(CmdStrokeRef ref) { void CmdStroke_write(CmdStrokeRef ref, CmdStroke s) { uint ix = ref.offset >> 2; - ptcl[ix + 0] = s.seg_ref.offset; + ptcl[ix + 0] = s.tile_ref; ptcl[ix + 1] = floatBitsToUint(s.half_width); ptcl[ix + 2] = s.rgba_color; } diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 19e9b43..b568827 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -183,9 +183,9 @@ 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, host)?; - let pathseg_buf = device.create_buffer(64 * 1024 * 1024, host)?; - let tile_buf = device.create_buffer(64 * 1024 * 1024, host)?; + let anno_buf = device.create_buffer(64 * 1024 * 1024, dev)?; + let pathseg_buf = device.create_buffer(64 * 1024 * 1024, dev)?; + let tile_buf = device.create_buffer(64 * 1024 * 1024, dev)?; 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)?; @@ -228,10 +228,10 @@ impl Renderer { let bin_alloc_buf_dev = device.create_buffer(12, dev)?; // TODO: constants - let bin_alloc_start = ((n_elements + 255) & !255) * 8; + let bin_alloc_start = ((n_paths + 255) & !255) * 8; device.write_buffer( &bin_alloc_buf_host, - &[n_elements as u32, 0, bin_alloc_start as u32], + &[n_paths as u32, 0, bin_alloc_start as u32], )?; let bin_code = include_bytes!("../shader/binning.spv"); let bin_pipeline = device.create_simple_compute_pipeline(bin_code, 4, 0)?; @@ -250,16 +250,20 @@ impl Renderer { &[n_elements as u32, coarse_alloc_start as u32], )?; let coarse_code = include_bytes!("../shader/coarse.spv"); - let coarse_pipeline = device.create_simple_compute_pipeline(coarse_code, 4, 0)?; + let coarse_pipeline = device.create_simple_compute_pipeline(coarse_code, 5, 0)?; let coarse_ds = device.create_descriptor_set( &coarse_pipeline, - &[&anno_buf, &bin_buf, &coarse_alloc_buf_dev, &ptcl_buf], + &[&anno_buf, &bin_buf, &tile_buf, &coarse_alloc_buf_dev, &ptcl_buf], &[], )?; let k4_code = include_bytes!("../shader/kernel4.spv"); - let k4_pipeline = device.create_simple_compute_pipeline(k4_code, 1, 1)?; - let k4_ds = device.create_descriptor_set(&k4_pipeline, &[&ptcl_buf], &[&image_dev])?; + let k4_pipeline = device.create_simple_compute_pipeline(k4_code, 2, 1)?; + let k4_ds = device.create_descriptor_set( + &k4_pipeline, + &[&ptcl_buf, &tile_buf], + &[&image_dev] + )?; Ok(Renderer { scene_buf, @@ -328,32 +332,31 @@ impl Renderer { &self.path_ds, (((self.n_pathseg + 31) / 32) as u32, 1, 1), ); - /* + cmd_buf.write_timestamp(&query_pool, 3); + // Note: this barrier is not needed as an actual dependency between + // pipeline stages, but I am keeping it in so that timer queries are + // easier to interpret. + cmd_buf.memory_barrier(); cmd_buf.dispatch( &self.bin_pipeline, &self.bin_ds, - (((self.n_elements + 255) / 256) as u32, 1, 1), + (((self.n_paths + 255) / 256) as u32, 1, 1), ); - */ - cmd_buf.write_timestamp(&query_pool, 3); + cmd_buf.write_timestamp(&query_pool, 4); 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, 4); + cmd_buf.write_timestamp(&query_pool, 5); 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, 5); - */ + cmd_buf.write_timestamp(&query_pool, 6); cmd_buf.memory_barrier(); cmd_buf.image_barrier(&self.image_dev, ImageLayout::General, ImageLayout::BlitSrc); } diff --git a/piet-gpu/src/pico_svg.rs b/piet-gpu/src/pico_svg.rs index 140c42d..0aac61a 100644 --- a/piet-gpu/src/pico_svg.rs +++ b/piet-gpu/src/pico_svg.rs @@ -49,8 +49,8 @@ impl PicoSvg { for item in &self.items { match item { Item::Fill(fill_item) => { - 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);