From af0a1af8e17d43ac352ca306e7ed0f3391474f16 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Fri, 5 Jun 2020 15:07:02 -0700 Subject: [PATCH] Make fills work The backdrop propagation is slow but it does work. --- piet-gpu-types/src/ptcl.rs | 3 +- piet-gpu-types/src/tile.rs | 1 + piet-gpu/bin/cli.rs | 9 +++-- piet-gpu/shader/backdrop.comp | 56 ++++++++++++++++++++++++++++ piet-gpu/shader/backdrop.spv | Bin 0 -> 4676 bytes piet-gpu/shader/build.ninja | 2 + piet-gpu/shader/coarse.comp | 62 +++++++++---------------------- piet-gpu/shader/coarse.spv | Bin 25796 -> 31312 bytes piet-gpu/shader/kernel4.comp | 46 ++++++++++------------- piet-gpu/shader/kernel4.spv | Bin 14620 -> 20924 bytes piet-gpu/shader/path_coarse.comp | 37 ++++++++++++++++-- piet-gpu/shader/path_coarse.spv | Bin 17100 -> 20364 bytes piet-gpu/shader/ptcl.h | 6 +-- piet-gpu/shader/tile.h | 10 +++-- piet-gpu/src/lib.rs | 28 ++++++++++++-- piet-gpu/src/pico_svg.rs | 4 +- 16 files changed, 174 insertions(+), 90 deletions(-) create mode 100644 piet-gpu/shader/backdrop.comp create mode 100644 piet-gpu/shader/backdrop.spv diff --git a/piet-gpu-types/src/ptcl.rs b/piet-gpu-types/src/ptcl.rs index 98c4d44..96c0ecc 100644 --- a/piet-gpu-types/src/ptcl.rs +++ b/piet-gpu-types/src/ptcl.rs @@ -20,7 +20,8 @@ piet_gpu! { rgba_color: u32, } struct CmdFill { - seg_ref: Ref, + // As above, really Ref + tile_ref: u32, backdrop: i32, rgba_color: u32, } diff --git a/piet-gpu-types/src/tile.rs b/piet-gpu-types/src/tile.rs index 5a28037..18318e3 100644 --- a/piet-gpu-types/src/tile.rs +++ b/piet-gpu-types/src/tile.rs @@ -15,6 +15,7 @@ piet_gpu! { struct TileSeg { start: [f32; 2], end: [f32; 2], + y_edge: f32, next: Ref, } } diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index 5b293d3..df2f894 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(7)?; + let query_pool = device.create_query_pool(8)?; let mut ctx = PietGpuRenderContext::new(); if let Some(input) = matches.value_of("INPUT") { @@ -204,9 +204,10 @@ 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!("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); + println!("Backdrop kernel time: {:.3}ms", (ts[3] - ts[2]) * 1e3); + println!("Binning kernel time: {:.3}ms", (ts[4] - ts[3]) * 1e3); + println!("Coarse raster kernel time: {:.3}ms", (ts[5] - ts[4]) * 1e3); + println!("Render kernel time: {:.3}ms", (ts[6] - ts[5]) * 1e3); /* let mut data: Vec = Default::default(); diff --git a/piet-gpu/shader/backdrop.comp b/piet-gpu/shader/backdrop.comp new file mode 100644 index 0000000..c0f58c4 --- /dev/null +++ b/piet-gpu/shader/backdrop.comp @@ -0,0 +1,56 @@ +// Propagation of tile backdrop for filling. + +#version 450 +#extension GL_GOOGLE_include_directive : enable + +#include "setup.h" + +#define BACKDROP_WG 256 + +layout(local_size_x = BACKDROP_WG, local_size_y = 1) in; + +layout(set = 0, binding = 0) buffer AnnotatedBuf { + uint[] annotated; +}; + +// This is really only used for n_elements; maybe we can handle that +// a different way, but it's convenient to have the same signature as +// tile allocation. +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" + +void main() { + uint element_ix = gl_GlobalInvocationID.x; + AnnotatedRef ref = AnnotatedRef(element_ix * Annotated_size); + + uint tag = Annotated_Nop; + if (element_ix < n_elements) { + tag = Annotated_tag(ref); + } + if (tag == Annotated_Fill) { + PathRef path_ref = PathRef(element_ix * Path_size); + Path path = Path_read(path_ref); + uint width = path.bbox.z - path.bbox.x; + uint height = path.bbox.w - path.bbox.y; + // slightly handrolling the tile structure here... + uint tile_el_ix = (path.tiles.offset >> 2) + 1; + for (uint y = 0; y < height; y++) { + uint sum = 0; + for (uint x = 0; x < width; x++) { + sum += tile[tile_el_ix]; + tile[tile_el_ix] = sum; + tile_el_ix += 2; + } + } + } +} diff --git a/piet-gpu/shader/backdrop.spv b/piet-gpu/shader/backdrop.spv new file mode 100644 index 0000000000000000000000000000000000000000..0b4828ea488083271e36e542462870e2e63aaf58 GIT binary patch literal 4676 zcmZ9NcXL!_6o;>DHbg-n6tO@^U=C{7!k#dEfWmEzDatKS_>DI{5vZw4Tmn9!!!fNZM09 zFt~rPce**;yLrnwMl4DSX`nHSi7BGnknwV@q2p#`3A|jV1cAmX1%C6;$1vKG6Ow_! z(!icQ1A|wTDwC6?>E^+DwN$S)s-@xDRJGEq%~p-;$i>wfmHNzZKA=cMe*GeAGgQ*w zXiPNA&Fb*J>Ii1qsl3m`$jEfHnb7+}=FG*Gn&r_mrkhhUmFA{=flV`gecO#M&~=hb zQPOo!1n(_3$6Ugl(DwPiej9s{B-hvK)risd$;Y@)){(bPx^Lj2p^1C)oO{@$)-<|E zC(F_1m8PoY;pjVSC~KLxLb8mwR?qFpO7MT9T{@HPO^e5slw;q(=AI(SBVLcL(UG%n z08f<&qnK|f%?QyxxTwGJ5eb&YZHy#mlNw+x1(D#7m^B?lZ7uPHq&3PPgHWVrwzh4 zO4%wb+|EzlJ#UmI+2!f#DA?KLy~kzipj}U&kF)tAOqQq0Y&or*yxKxi8~T zY0jb4j-0mSLF@yy;q+1%{}}dIwKh7I-rHjGEVg?~%h{e3l9%8PBKC6xJv}p?;-g?M z(g1Z%LboHnGrRD5AAHue6fuRA^DQ^t^I!X)uVwe-?2X`jy*T%LJ-Mjo9mv+?>srTke?q&i^Dd(s`{^OueDMlm zoUB2J~-QxU8C8;gfL+tUHPu^I+4H3HtY@FOmVi#vVXED}qgt1-t{1)h!v+h!~ zoHbSx8|P`BXQ|yy+{tMBmfN1$&K9$r3hqXneq&POyEs2a$gn_@96NpW{kw_sjLK zK^yNL#vFUV*4Muet>1Xh;zqPRdG7jeLK`b@|JvTs$iF4S=0rbtfStE1yTAHEtT}@1 zJl;=hYL8~zay)l}qt+DI+^~I*7v}88!S)*dAHg$q+YtF3?11xXhpz;ddz5@*%ZS_^$X@cTJ%q@c@BV0u z-JAS6!`Rlcmmc&OGK!pr#Co-i|DG7n{w~Bk`QNBHj`sPJwe{5zInPaO9ebKU?8QDj z6YZ_p43ppmh})7m>Fw?~bM^c~7-V0?^>b>g(1#H@_xd6vdU^T%t!4a1~yGP*lJ)G^~qhNF8UC(QhM;+hGSno-&zKDA&7w0<}aZiKw#d^B^lONZ{@2m^)v^8?U~7c^CfMHNe!m4aM&A3Q{R(1jYl-c{_u4+hM>Fi2Z-b3f z+ta(~_Yfcb@1ymrqlXW`)(HDUu;&*&eFQc}-k!AIL9A^papZmscE3D_9`q+jFX9>K zi?~n0`ojJU>`ZY6pM&L$i+SZD{tK|{g#9Jh{8;xZaI9;rHROzooUg&wh&8_f%lSSS zuTRc>a}S+W9r@pa-M7g94lL)Li*u37pNo5@?LD!cI_iB7_HIPIAHZ@^PoJFmare9z z+J1Aar~ZpSxeJl=v-`XN&gXdK0_N&KpMV&@5bgJO6?%2Xdsfj?SH{Qt(v2;*28q31 z3qA!guN#TnbzpsevyM1=>ID03@>z=5(<1aT#GY25t2j4=RJ?QJHSy_f8<^Q)@NP!Ry%x` zf}P=)?4Df?mb(mzm@B}>#NG~o<*q~`W*69=qt0%yT)xg#SxnTq8e7gfd$}Qt(SwLR zxP~>1@!YRLUz_6AxjT!t@zKLxu=Dx9y99k*id*^n!21#JQoM`W#+cuUz8=x 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); - } - 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; - } - - AnnoFill fill = Annotated_Fill_read(ref); - CmdFill cmd_fill; - cmd_fill.seg_ref = first_seg_chunk; - cmd_fill.backdrop = backdrop; - cmd_fill.rgba_color = fill.rgba_color; - alloc_cmd(cmd_ref, cmd_limit); - Cmd_Fill_write(cmd_ref, cmd_fill); - cmd_ref.offset += Cmd_size; - last_chunk_n = 0; - } else if (backdrop != 0) { - AnnoFill fill = Annotated_Fill_read(ref); - alloc_cmd(cmd_ref, cmd_limit); - Cmd_Solid_write(cmd_ref, CmdSolid(fill.rgba_color)); - cmd_ref.offset += Cmd_size; - } - seg_start += seg_count; - seg_count = 0; - backdrop = 0; - break; - */ - case Annotated_Stroke: Tile tile = Tile_read(TileRef(sh_tile_base[element_ref_ix] + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); + AnnoFill fill = Annotated_Fill_read(ref); + alloc_cmd(cmd_ref, cmd_limit); + if (tile.tile.offset != 0) { + CmdFill cmd_fill; + cmd_fill.tile_ref = tile.tile.offset; + cmd_fill.backdrop = tile.backdrop; + cmd_fill.rgba_color = fill.rgba_color; + Cmd_Fill_write(cmd_ref, cmd_fill); + } else { + AnnoFill fill = Annotated_Fill_read(ref); + Cmd_Solid_write(cmd_ref, CmdSolid(fill.rgba_color)); + } + cmd_ref.offset += Cmd_size; + break; + case Annotated_Stroke: + tile = Tile_read(TileRef(sh_tile_base[element_ref_ix] + + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); AnnoStroke stroke = Annotated_Stroke_read(ref); CmdStroke cmd_stroke; cmd_stroke.tile_ref = tile.tile.offset; diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index c5a304bcf52b5c799a40b151b874cc92e7878e59..ad24e6bd5fc181294585fac7eff1dd9e9bbc850e 100644 GIT binary patch literal 31312 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 F{{>QY+?)Ua diff --git a/piet-gpu/shader/path_coarse.comp b/piet-gpu/shader/path_coarse.comp index 9abcbf0..0b3bc8f 100644 --- a/piet-gpu/shader/path_coarse.comp +++ b/piet-gpu/shader/path_coarse.comp @@ -36,9 +36,11 @@ layout(set = 0, binding = 2) buffer TileBuf { shared uint sh_tile_count[COARSE_WG]; shared uint sh_width[COARSE_WG]; shared uint sh_draw_width[COARSE_WG]; +shared uint sh_tag[COARSE_WG]; shared vec2 sh_p0[COARSE_WG]; shared vec2 sh_p1[COARSE_WG]; shared int sh_x0[COARSE_WG]; +shared int sh_bbox_x1[COARSE_WG]; shared int sh_y0[COARSE_WG]; shared float sh_a[COARSE_WG]; shared float sh_b[COARSE_WG]; @@ -56,6 +58,7 @@ void main() { if (element_ix < n_pathseg) { tag = PathSeg_tag(ref); } + sh_tag[th_ix] = tag; // Setup for coverage algorithm. float a, b, c; // Bounding box of element in pixel coordinates. @@ -96,6 +99,7 @@ void main() { x1 = clamp(x1, bbox.x, bbox.z); y1 = clamp(y1, bbox.y, bbox.w); sh_x0[th_ix] = x0; + sh_bbox_x1[th_ix] = bbox.z; // TODO: can get rid of this (fold into base), with care (also need to update `a`) sh_y0[th_ix] = y0; int stride = bbox.z - bbox.x; @@ -138,7 +142,8 @@ void main() { int x1 = x0 + int(sh_width[el_ix]); int dx = int(seq_ix % draw_width); uint y = sh_y0[el_ix] + seq_ix / draw_width; - float t = sh_a[el_ix] + sh_b[el_ix] * float(y); + float b = sh_b[el_ix]; + float t = sh_a[el_ix] + b * float(y); float c = sh_c[el_ix]; int xx0 = clamp(int(floor(t - c)), x0, x1); int xx1 = clamp(int(ceil(t + c)), x0, x1); @@ -148,8 +153,34 @@ void main() { uint tile_el = (sh_base[el_ix] + uint(y * sh_stride[el_ix] + x) * Tile_size) >> 2; uint old = atomicExchange(tile[tile_el], tile_offset); TileSeg tile_seg; - tile_seg.start = sh_p0[el_ix]; - tile_seg.end = sh_p1[el_ix]; + vec2 p0 = sh_p0[el_ix]; + vec2 p1 = sh_p1[el_ix]; + float y_edge = 0.0; + if (sh_tag[el_ix] == PathSeg_FillLine) { + vec2 tile_xy = vec2(x * TILE_WIDTH_PX, y * TILE_HEIGHT_PX); + if (dx == 0 && min(p0.y, p1.y) <= tile_xy.y) { + // TODO: need a little more work to make sure this triggers even + // when line is to the left of bbox. + int xray = max(int(ceil(t - 0.5 * b)), x0); + if (xray < sh_bbox_x1[el_ix]) { + int backdrop = p1.y < p0.y ? 1 : -1; + atomicAdd(tile[tile_el + 1 + 2 * (xray - x)], backdrop); + } + } + y_edge = mix(p0.y, p1.y, (tile_xy.x - p0.x) / (p1.x - p0.x)); + if (min(p0.x, p1.x) < tile_xy.x && y_edge >= tile_xy.y && y_edge < tile_xy.y + TILE_HEIGHT_PX) { + if (p0.x > p1.x) { + p1 = vec2(tile_xy.x, y_edge); + } else { + p0 = vec2(tile_xy.x, y_edge); + } + } else { + y_edge = 1e9; + } + } + tile_seg.start = p0; + tile_seg.end = p1; + tile_seg.y_edge = y_edge; tile_seg.next.offset = old; TileSeg_write(TileSegRef(tile_offset), tile_seg); } diff --git a/piet-gpu/shader/path_coarse.spv b/piet-gpu/shader/path_coarse.spv index 53cb75906859c9ae09d317cdde3550a9fbdeb45f..8c4801be420d2b7e76c75b50b16adb706dc56b66 100644 GIT binary patch literal 20364 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 diff --git a/piet-gpu/shader/ptcl.h b/piet-gpu/shader/ptcl.h index d337598..0c20a89 100644 --- a/piet-gpu/shader/ptcl.h +++ b/piet-gpu/shader/ptcl.h @@ -80,7 +80,7 @@ CmdStrokeRef CmdStroke_index(CmdStrokeRef ref, uint index) { } struct CmdFill { - SegChunkRef seg_ref; + uint tile_ref; int backdrop; uint rgba_color; }; @@ -239,7 +239,7 @@ CmdFill CmdFill_read(CmdFillRef ref) { uint raw1 = ptcl[ix + 1]; uint raw2 = ptcl[ix + 2]; CmdFill s; - s.seg_ref = SegChunkRef(raw0); + s.tile_ref = raw0; s.backdrop = int(raw1); s.rgba_color = raw2; return s; @@ -247,7 +247,7 @@ CmdFill CmdFill_read(CmdFillRef ref) { void CmdFill_write(CmdFillRef ref, CmdFill s) { uint ix = ref.offset >> 2; - ptcl[ix + 0] = s.seg_ref.offset; + ptcl[ix + 0] = s.tile_ref; ptcl[ix + 1] = uint(s.backdrop); ptcl[ix + 2] = s.rgba_color; } diff --git a/piet-gpu/shader/tile.h b/piet-gpu/shader/tile.h index b4a8c9b..d7659ff 100644 --- a/piet-gpu/shader/tile.h +++ b/piet-gpu/shader/tile.h @@ -37,10 +37,11 @@ TileRef Tile_index(TileRef ref, uint index) { struct TileSeg { vec2 start; vec2 end; + float y_edge; TileSegRef next; }; -#define TileSeg_size 20 +#define TileSeg_size 24 TileSegRef TileSeg_index(TileSegRef ref, uint index) { return TileSegRef(ref.offset + index * TileSeg_size); @@ -87,10 +88,12 @@ TileSeg TileSeg_read(TileSegRef ref) { uint raw2 = tile[ix + 2]; uint raw3 = tile[ix + 3]; uint raw4 = tile[ix + 4]; + uint raw5 = tile[ix + 5]; TileSeg s; s.start = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.end = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); - s.next = TileSegRef(raw4); + s.y_edge = uintBitsToFloat(raw4); + s.next = TileSegRef(raw5); return s; } @@ -100,6 +103,7 @@ void TileSeg_write(TileSegRef ref, TileSeg s) { 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; + tile[ix + 4] = floatBitsToUint(s.y_edge); + tile[ix + 5] = s.next.offset; } diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 63d80fa..1e839d2 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -57,8 +57,8 @@ pub fn render_scene(rc: &mut impl RenderContext) { let circle = Circle::new(center, radius); rc.fill(circle, &color); } - /* let mut path = BezPath::new(); + /* path.move_to((100.0, 1150.0)); path.line_to((200.0, 1200.0)); path.line_to((150.0, 1250.0)); @@ -143,6 +143,9 @@ pub struct Renderer { path_pipeline: D::Pipeline, path_ds: D::DescriptorSet, + backdrop_pipeline: D::Pipeline, + backdrop_ds: D::DescriptorSet, + tile_alloc_buf_host: D::Buffer, tile_alloc_buf_dev: D::Buffer, @@ -224,6 +227,14 @@ impl Renderer { &[], )?; + let backdrop_alloc_code = include_bytes!("../shader/backdrop.spv"); + let backdrop_pipeline = device.create_simple_compute_pipeline(backdrop_alloc_code, 3, 0)?; + let backdrop_ds = device.create_descriptor_set( + &backdrop_pipeline, + &[&anno_buf, &tile_alloc_buf_dev, &tile_buf], + &[], + )?; + let bin_alloc_buf_host = device.create_buffer(12, host)?; let bin_alloc_buf_dev = device.create_buffer(12, dev)?; @@ -275,6 +286,8 @@ impl Renderer { tile_ds, path_pipeline, path_ds, + backdrop_pipeline, + backdrop_ds, bin_pipeline, bin_ds, coarse_pipeline, @@ -333,6 +346,13 @@ impl Renderer { (((self.n_pathseg + 31) / 32) as u32, 1, 1), ); cmd_buf.write_timestamp(&query_pool, 3); + cmd_buf.memory_barrier(); + cmd_buf.dispatch( + &self.backdrop_pipeline, + &self.backdrop_ds, + (((self.n_paths + 255) / 256) as u32, 1, 1), + ); + cmd_buf.write_timestamp(&query_pool, 4); // 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. @@ -342,21 +362,21 @@ impl Renderer { &self.bin_ds, (((self.n_paths + 255) / 256) as u32, 1, 1), ); - cmd_buf.write_timestamp(&query_pool, 4); + cmd_buf.write_timestamp(&query_pool, 5); 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, 5); + cmd_buf.write_timestamp(&query_pool, 6); 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, 6); + cmd_buf.write_timestamp(&query_pool, 7); 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 0aac61a..140c42d 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);