From 2068171f96c492c71040cd70edafd65b4b50b4b5 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Tue, 1 Dec 2020 17:59:37 +0100 Subject: [PATCH 1/3] path_coarse.comp: tighten variable scopes, delete unused variables No functional changes. Signed-off-by: Elias Naur --- piet-gpu/shader/path_coarse.comp | 30 +++++++++++++----------------- piet-gpu/shader/path_coarse.spv | Bin 28876 -> 28916 bytes 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/piet-gpu/shader/path_coarse.comp b/piet-gpu/shader/path_coarse.comp index 658af0e..069d13b 100644 --- a/piet-gpu/shader/path_coarse.comp +++ b/piet-gpu/shader/path_coarse.comp @@ -101,12 +101,6 @@ void main() { if (element_ix < n_pathseg) { tag = PathSeg_tag(ref); } - // Setup for coverage algorithm. - float a, b, c; - // Bounding box of element in pixel coordinates. - float xmin, xmax, ymin, ymax; - PathStrokeLine line; - float dx; switch (tag) { case PathSeg_FillCubic: case PathSeg_StrokeCubic: @@ -162,22 +156,24 @@ void main() { } // Output line segment - xmin = min(p0.x, p1.x) - cubic.stroke.x; - xmax = max(p0.x, p1.x) + cubic.stroke.x; - ymin = min(p0.y, p1.y) - cubic.stroke.y; - ymax = max(p0.y, p1.y) + cubic.stroke.y; + + // Bounding box of element in pixel coordinates. + float xmin = min(p0.x, p1.x) - cubic.stroke.x; + float xmax = max(p0.x, p1.x) + cubic.stroke.x; + float ymin = min(p0.y, p1.y) - cubic.stroke.y; + float ymax = max(p0.y, p1.y) + cubic.stroke.y; float dx = p1.x - p0.x; float dy = p1.y - p0.y; // Set up for per-scanline coverage formula, below. float invslope = abs(dy) < 1e-9 ? 1e9 : dx / dy; - c = (cubic.stroke.x + abs(invslope) * (0.5 * float(TILE_HEIGHT_PX) + cubic.stroke.y)) * SX; - b = invslope; // Note: assumes square tiles, otherwise scale. - a = (p0.x - (p0.y - 0.5 * float(TILE_HEIGHT_PX)) * b) * SX; + float c = (cubic.stroke.x + abs(invslope) * (0.5 * float(TILE_HEIGHT_PX) + cubic.stroke.y)) * SX; + float b = invslope; // Note: assumes square tiles, otherwise scale. + float a = (p0.x - (p0.y - 0.5 * float(TILE_HEIGHT_PX)) * b) * SX; - int x0 = int(floor((xmin) * SX)); - int x1 = int(ceil((xmax) * SX)); - int y0 = int(floor((ymin) * SY)); - int y1 = int(ceil((ymax) * SY)); + int x0 = int(floor(xmin * SX)); + int x1 = int(ceil(xmax * SX)); + int y0 = int(floor(ymin * SY)); + int y1 = int(ceil(ymax * SY)); x0 = clamp(x0, bbox.x, bbox.z); y0 = clamp(y0, bbox.y, bbox.w); diff --git a/piet-gpu/shader/path_coarse.spv b/piet-gpu/shader/path_coarse.spv index f82a031295caaf30b5e4075547981235bfe9d90c..3ba2cff61fa355dc14b10e644fa7c579fdf4a0bf 100644 GIT binary patch delta 6341 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 JF#gtF{|Cj*3D^Jt From 19f4d9fa95fb1867687443f581c04c10083a8985 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Tue, 1 Dec 2020 18:06:09 +0100 Subject: [PATCH 2/3] change tile segment representation to (origin, vector) Eliminates the precision loss of the subtraction in the sign(end.x - start.x) expression in kernel4. That's important for the next change that avoids inconsistent line intersections in path_coarse. Updates #23 Signed-off-by: Elias Naur --- piet-gpu-types/src/tile.rs | 4 ++-- piet-gpu/shader/kernel4.comp | 10 +++++----- piet-gpu/shader/kernel4.spv | Bin 31784 -> 31680 bytes piet-gpu/shader/path_coarse.comp | 10 ++++++---- piet-gpu/shader/path_coarse.spv | Bin 28916 -> 29056 bytes piet-gpu/shader/tile.h | 16 ++++++++-------- 6 files changed, 21 insertions(+), 19 deletions(-) diff --git a/piet-gpu-types/src/tile.rs b/piet-gpu-types/src/tile.rs index 18318e3..38ee93b 100644 --- a/piet-gpu-types/src/tile.rs +++ b/piet-gpu-types/src/tile.rs @@ -13,8 +13,8 @@ piet_gpu! { } // Segments within a tile are represented as a linked list. struct TileSeg { - start: [f32; 2], - end: [f32; 2], + origin: [f32; 2], + vector: [f32; 2], y_edge: f32, next: Ref, } diff --git a/piet-gpu/shader/kernel4.comp b/piet-gpu/shader/kernel4.comp index 72ab396..bf9ec44 100644 --- a/piet-gpu/shader/kernel4.comp +++ b/piet-gpu/shader/kernel4.comp @@ -65,8 +65,8 @@ float[CHUNK] computeArea(vec2 xy, int backdrop, uint tile_ref) { TileSeg seg = TileSeg_read(tile_seg_ref); for (uint k = 0; k < CHUNK; k++) { vec2 my_xy = vec2(xy.x, xy.y + float(k * CHUNK_DY)); - vec2 start = seg.start - my_xy; - vec2 end = seg.end - my_xy; + vec2 start = seg.origin - my_xy; + vec2 end = start + seg.vector; vec2 window = clamp(vec2(start.y, end.y), 0.0, 1.0); if (window.x != window.y) { vec2 t = (window - start.y) / (end.y - start.y); @@ -79,7 +79,7 @@ float[CHUNK] computeArea(vec2 xy, int backdrop, uint tile_ref) { float a = (b + 0.5 * (d * d - c * c) - xmin) / (xmax - xmin); area[k] += a * (window.x - window.y); } - area[k] += sign(end.x - start.x) * clamp(my_xy.y - seg.y_edge + 1.0, 0.0, 1.0); + area[k] += sign(seg.vector.x) * clamp(my_xy.y - seg.y_edge + 1.0, 0.0, 1.0); } tile_seg_ref = seg.next; } while (tile_seg_ref.offset != 0); @@ -131,9 +131,9 @@ void main() { TileSegRef tile_seg_ref = TileSegRef(stroke.tile_ref); do { TileSeg seg = TileSeg_read(tile_seg_ref); - vec2 line_vec = seg.end - seg.start; + vec2 line_vec = seg.vector; for (uint k = 0; k < CHUNK; k++) { - vec2 dpos = xy + vec2(0.5, 0.5) - seg.start; + vec2 dpos = xy + vec2(0.5, 0.5) - seg.origin; dpos.y += float(k * CHUNK_DY); float t = clamp(dot(line_vec, dpos) / dot(line_vec, line_vec), 0.0, 1.0); df[k] = min(df[k], length(line_vec * t - dpos)); diff --git a/piet-gpu/shader/kernel4.spv b/piet-gpu/shader/kernel4.spv index 808d7077de31636dd101bade005c49aba8df1c5a..fffdc4a97ab267eb9762f51ebb82bfe4503ccb49 100644 GIT binary patch literal 31680 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 2; uint old = atomicExchange(tile[tile_el], tile_offset); - tile_seg.start = p0; - tile_seg.end = p1; + tile_seg.origin = p0; + tile_seg.vector = p1 - p0; float y_edge = 0.0; if (tag == PathSeg_FillCubic) { y_edge = mix(p0.y, p1.y, (tile_x0 - p0.x) / dx); if (min(p0.x, p1.x) < tile_x0 && y_edge >= tile_y0 && y_edge < tile_y0 + TILE_HEIGHT_PX) { + vec2 p = vec2(tile_x0, y_edge); if (p0.x > p1.x) { - tile_seg.end = vec2(tile_x0, y_edge); + tile_seg.vector = p - p0; } else { - tile_seg.start = vec2(tile_x0, y_edge); + tile_seg.origin = p; + tile_seg.vector = p1 - p; } } else { y_edge = 1e9; diff --git a/piet-gpu/shader/path_coarse.spv b/piet-gpu/shader/path_coarse.spv index 3ba2cff61fa355dc14b10e644fa7c579fdf4a0bf..952863cb4c91267d7d3174a3aa3a37127969a46b 100644 GIT binary patch delta 5731 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 E|Fk!@Y5)KL diff --git a/piet-gpu/shader/tile.h b/piet-gpu/shader/tile.h index d7659ff..b6c5e14 100644 --- a/piet-gpu/shader/tile.h +++ b/piet-gpu/shader/tile.h @@ -35,8 +35,8 @@ TileRef Tile_index(TileRef ref, uint index) { } struct TileSeg { - vec2 start; - vec2 end; + vec2 origin; + vec2 vector; float y_edge; TileSegRef next; }; @@ -90,8 +90,8 @@ TileSeg TileSeg_read(TileSegRef ref) { 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.origin = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); + s.vector = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); s.y_edge = uintBitsToFloat(raw4); s.next = TileSegRef(raw5); return s; @@ -99,10 +99,10 @@ TileSeg TileSeg_read(TileSegRef ref) { 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 + 0] = floatBitsToUint(s.origin.x); + tile[ix + 1] = floatBitsToUint(s.origin.y); + tile[ix + 2] = floatBitsToUint(s.vector.x); + tile[ix + 3] = floatBitsToUint(s.vector.y); tile[ix + 4] = floatBitsToUint(s.y_edge); tile[ix + 5] = s.next.offset; } From 29cfb8b63edc28517b16e3ba1da7790e360ed557 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Tue, 1 Dec 2020 18:13:33 +0100 Subject: [PATCH 3/3] eliminate inconsistent line intersections from path_coarse.comp The finite precision of floating point computations can lead the coarse renderer into inconsistent tile intersections, which implies impossible line segments such as lines with gaps or double intersections. The winding number algorithm is sensitive to these errors which show up as incorrectly filled paths. This change forces all intersections to be consistent. First, the floating point top edge intersection test is removed; top edge intersections are completely determined by left edge intersections. Then, left edge intersections are inserted from the tile with the last top edge intersection. The next top edge is then fixed to be the last tile with a left edge intersection. More details in the patch comments. Fixes #23 Signed-off-by: Elias Naur --- piet-gpu/shader/path_coarse.comp | 56 ++++++++++++++++++++++++------- piet-gpu/shader/path_coarse.spv | Bin 29056 -> 30896 bytes 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/piet-gpu/shader/path_coarse.comp b/piet-gpu/shader/path_coarse.comp index b018973..eb3509b 100644 --- a/piet-gpu/shader/path_coarse.comp +++ b/piet-gpu/shader/path_coarse.comp @@ -171,9 +171,9 @@ void main() { float a = (p0.x - (p0.y - 0.5 * float(TILE_HEIGHT_PX)) * b) * SX; int x0 = int(floor(xmin * SX)); - int x1 = int(ceil(xmax * SX)); + int x1 = int(floor(xmax * SX) + 1); int y0 = int(floor(ymin * SY)); - int y1 = int(ceil(ymax * SY)); + int y1 = int(floor(ymax * SY) + 1); x0 = clamp(x0, bbox.x, bbox.z); y0 = clamp(y0, bbox.y, bbox.w); @@ -187,19 +187,30 @@ void main() { // Consider using subgroups to aggregate atomic add. uint tile_offset = atomicAdd(alloc, n_tile_alloc * TileSeg_size); TileSeg tile_seg; + + int xray = int(floor(p0.x*SX)); + int last_xray = int(floor(p1.x*SX)); + if (p0.y > p1.y) { + int tmp = xray; + xray = last_xray; + last_xray = tmp; + } for (int y = y0; y < y1; y++) { - float tile_y0 = float(y * TILE_HEIGHT_PX); - if (tag == PathSeg_FillCubic && min(p0.y, p1.y) <= tile_y0) { - int xray = max(int(ceil(xc - 0.5 * b)), bbox.x); - if (xray < bbox.z) { - int backdrop = p1.y < p0.y ? 1 : -1; - TileRef tile_ref = Tile_index(path.tiles, uint(base + xray)); - uint tile_el = tile_ref.offset >> 2; - atomicAdd(tile[tile_el + 1], backdrop); - } + int xbackdrop = max(xray + 1, bbox.x); + if (tag == PathSeg_FillCubic && y > y0 && xbackdrop < bbox.z) { + int backdrop = p1.y < p0.y ? 1 : -1; + TileRef tile_ref = Tile_index(path.tiles, uint(base + xbackdrop)); + uint tile_el = tile_ref.offset >> 2; + atomicAdd(tile[tile_el + 1], backdrop); } + int xx0 = clamp(int(floor(xc - c)), x0, x1); int xx1 = clamp(int(ceil(xc + c)), x0, x1); + xx1 = max(xx1, xray + 1); + + // next_xray is the xray for the next scanline; it is derived + // by left edge intersections computed below. + int next_xray = xray; for (int x = xx0; x < xx1; x++) { float tile_x0 = float(x * TILE_WIDTH_PX); TileRef tile_ref = Tile_index(path.tiles, uint(base + x)); @@ -209,8 +220,10 @@ void main() { tile_seg.vector = p1 - p0; float y_edge = 0.0; if (tag == PathSeg_FillCubic) { + float tile_y0 = float(y * TILE_HEIGHT_PX); y_edge = mix(p0.y, p1.y, (tile_x0 - p0.x) / dx); if (min(p0.x, p1.x) < tile_x0 && y_edge >= tile_y0 && y_edge < tile_y0 + TILE_HEIGHT_PX) { + // Left edge intersection. vec2 p = vec2(tile_x0, y_edge); if (p0.x > p1.x) { tile_seg.vector = p - p0; @@ -218,7 +231,25 @@ void main() { tile_seg.origin = p; tile_seg.vector = p1 - p; } - } else { + // kernel4 uses sign(vector.x) for the sign of the intersection backdrop. + // Nudge zeroes towards the intended sign. + if (tile_seg.vector.x == 0) { + tile_seg.vector.x += sign(p1.x - p0.x)*1e-9; + } + // Move next_xray consistently with previous intersections. + if (x > next_xray && next_xray >= xray) { + next_xray = x; + } else if (x <= next_xray && next_xray <= xray) { + next_xray = x - 1; + } + } + // Force last xray on the last scanline for consistency with later + // line segments. + if (y == y1 - 1) { + next_xray = last_xray; + } + // Drop inconsistent intersections. + if (x <= min(xray, next_xray) || max(xray, next_xray) < x) { y_edge = 1e9; } } @@ -229,6 +260,7 @@ void main() { } xc += b; base += stride; + xray = next_xray; } n_out += 1; diff --git a/piet-gpu/shader/path_coarse.spv b/piet-gpu/shader/path_coarse.spv index 952863cb4c91267d7d3174a3aa3a37127969a46b..767bbda65aa53889f1e3bd5a6aaac9595df6fb08 100644 GIT binary patch delta 8340 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