Skip to content

Commit

Permalink
path_coarse.comp: fix intersection inconsistencies, take 2
Browse files Browse the repository at this point in the history
The previous attempt to fix inconsistent intersections because of floating
point inaccuracy[0] missed two cases.

The first case is that for top intersections with the very first row would fail
the test

tag == PathSeg_FillCubic && y > y0 && xbackdrop < bbox.z

In particular, y is not larger than y0 when y0 has been clipped to 0.

Fix that by re-introducing the min(p0.y, p1.y) < tile_y0 check that does work
and is just as consistent. Add similar check, min(p0.x, p1.x) < tile_x0, for
deciding when to clip the segment to the left edge (but keep consistent xray check
for deciding left edge *intersections*).

The second case is that the tracking left intersections in the [xray, next_xray]
range of tiles may fail when next_xray is forced to last_xray, the final xray value.

Fix that case by computing next_xray explicitly, before looping over the
x tiles. The code is now much simpler.

Finally, ensure that xx0 and xx1 doesn't overflow the allocated number of tiles
by clamping them *after* setting them. Adjust xx0 to include xray, just as xx1
is adjusted; I haven't seen corruption without it, but it's not obvious xx0
always includes xray.

While here, replace a "+=" on a guaranteed zero value to just "=".

Updates #23

[0] 29cfb8b

Signed-off-by: Elias Naur <mail@eliasnaur.com>
  • Loading branch information
eliasnaur committed Dec 27, 2020
1 parent 14e922e commit a2a2d12
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 24 deletions.
45 changes: 21 additions & 24 deletions piet-gpu/shader/path_coarse.comp
Original file line number Diff line number Diff line change
Expand Up @@ -198,21 +198,31 @@ void main() {
last_xray = tmp;
}
for (int y = y0; y < y1; y++) {
float tile_y0 = float(y * TILE_HEIGHT_PX);
int xbackdrop = max(xray + 1, bbox.x);
if (tag == PathSeg_FillCubic && y > y0 && xbackdrop < bbox.z) {
if (tag == PathSeg_FillCubic && min(p0.y, p1.y) < tile_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; the line segment intersects
// all tiles between xray and next_xray.
int next_xray = last_xray;
if (y < y1 - 1) {
float tile_y1 = float((y + 1) * TILE_HEIGHT_PX);
float x_edge = mix(p0.x, p1.x, (tile_y1 - p0.y) / dy);
next_xray = int(floor(x_edge*SX));
}

int min_xray = min(xray, next_xray);
int max_xray = max(xray, next_xray);
int xx0 = min(int(floor(xc - c)), min_xray);
int xx1 = max(int(ceil(xc + c)), max_xray + 1);
xx0 = clamp(xx0, x0, x1);
xx1 = clamp(xx1, x0, x1);

// 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));
Expand All @@ -222,10 +232,8 @@ 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.
if (min(p0.x, p1.x) < tile_x0) {
vec2 p = vec2(tile_x0, y_edge);
if (p0.x > p1.x) {
tile_seg.vector = p - p0;
Expand All @@ -236,22 +244,11 @@ void main() {
// 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;
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) {
if (x <= min_xray || max_xray < x) {
// Reject inconsistent intersections.
y_edge = 1e9;
}
}
Expand Down
Binary file modified piet-gpu/shader/path_coarse.spv
Binary file not shown.

0 comments on commit a2a2d12

Please sign in to comment.