From 6f2a83e99057227f0e4409af238fdec3b3ab6add Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Mon, 30 Oct 2023 11:58:01 -0700 Subject: [PATCH] Move numerical robustness into tiling Change representation to tile-relative coordinates for segment endpoints. --- crates/encoding/src/path.rs | 4 +- shader/fine.wgsl | 84 +++++++++++++---------------------- shader/path_tiling.wgsl | 41 +++++++++++++---- shader/shared/segment.wgsl | 5 ++- src/cpu_shader/fine.rs | 20 +++++---- src/cpu_shader/path_tiling.rs | 54 +++++++++++++++------- 6 files changed, 118 insertions(+), 90 deletions(-) diff --git a/crates/encoding/src/path.rs b/crates/encoding/src/path.rs index 83e537042..c1c521d30 100644 --- a/crates/encoding/src/path.rs +++ b/crates/encoding/src/path.rs @@ -207,8 +207,8 @@ pub struct SegmentCount { #[derive(Clone, Copy, Debug, Zeroable, Pod, Default)] #[repr(C)] pub struct PathSegment { - pub origin: [f32; 2], - pub delta: [f32; 2], + pub point0: [f32; 2], + pub point1: [f32; 2], pub y_edge: f32, pub _padding: u32, } diff --git a/shader/fine.wgsl b/shader/fine.wgsl index cc5c1c8e3..016095327 100644 --- a/shader/fine.wgsl +++ b/shader/fine.wgsl @@ -162,29 +162,18 @@ fn fill_path_ms(fill: CmdFill, local_id: vec2, result: ptr, result: ptr= xy0_in.y; let xy0 = select(xy1_in, xy0_in, is_down); let xy1 = select(xy0_in, xy1_in, is_down); @@ -514,27 +503,17 @@ fn fill_path_ms_evenodd(fill: CmdFill, local_id: vec2, result: ptr, result: ptr 0u); let seg_off = fill.seg_data + batch * WG_SIZE + el_ix; let segment = segments[seg_off]; - let xy0_in = segment.origin; - let xy1_in = xy0_in + segment.delta; + let xy0_in = segment.point0; + let xy1_in = segment.point1; let is_down = xy1_in.y >= xy0_in.y; let xy0 = select(xy1_in, xy0_in, is_down); let xy1 = select(xy0_in, xy1_in, is_down); @@ -807,17 +786,18 @@ fn fill_path(fill: CmdFill, xy: vec2, result: ptr, result: ptr, - delta: vec2, + // Points are relative to tile origin + point0: vec2, + point1: vec2, y_edge: f32, } diff --git a/src/cpu_shader/fine.rs b/src/cpu_shader/fine.rs index c64c87627..dc3203959 100644 --- a/src/cpu_shader/fine.rs +++ b/src/cpu_shader/fine.rs @@ -58,20 +58,24 @@ fn fill_path(area: &mut [f32], segments: &[PathSegment], fill: &CmdFill, x_tile: *a = backdrop_f; } for segment in &segments[fill.seg_data as usize..][..n_segs as usize] { + let delta = [ + segment.point1[0] - segment.point0[0], + segment.point1[1] - segment.point0[1], + ]; for yi in 0..TILE_HEIGHT { - let y = segment.origin[1] - (y_tile + yi as f32); + let y = segment.point0[1] - (y_tile + yi as f32); let y0 = y.clamp(0.0, 1.0); - let y1 = (y + segment.delta[1]).clamp(0.0, 1.0); + let y1 = (y + delta[1]).clamp(0.0, 1.0); let dy = y0 - y1; - let y_edge = segment.delta[0].signum() - * (y_tile + yi as f32 - segment.y_edge + 1.0).clamp(0.0, 1.0); + let y_edge = + delta[0].signum() * (y_tile + yi as f32 - segment.y_edge + 1.0).clamp(0.0, 1.0); if dy != 0.0 { - let vec_y_recip = segment.delta[1].recip(); + let vec_y_recip = delta[1].recip(); let t0 = (y0 - y) * vec_y_recip; let t1 = (y1 - y) * vec_y_recip; - let startx = segment.origin[0] - x_tile; - let x0 = startx + t0 * segment.delta[0]; - let x1 = startx + t1 * segment.delta[0]; + let startx = segment.point0[0] - x_tile; + let x0 = startx + t0 * delta[0]; + let x1 = startx + t1 * delta[0]; let xmin0 = x0.min(x1); let xmax0 = x0.max(x1); for i in 0..TILE_WIDTH { diff --git a/src/cpu_shader/path_tiling.rs b/src/cpu_shader/path_tiling.rs index bf2975233..849474cf9 100644 --- a/src/cpu_shader/path_tiling.rs +++ b/src/cpu_shader/path_tiling.rs @@ -115,28 +115,48 @@ fn path_tiling_main( xy1 = Vec2::new(x_clip, yt); } } + let mut y_edge = 1e9; + // Apply numerical robustness logic + let mut p0 = xy0 - tile_xy; + let mut p1 = xy1 - tile_xy; + const EPSILON: f32 = 1e-6; + if p0.x == 0.0 { + if p1.x == 0.0 { + p0.x = EPSILON; + if p0.y == 0.0 { + // Entire tile + p1.x = EPSILON; + p1.y = TILE_HEIGHT as f32; + } else { + // Make segment disappear + p1.x = 2.0 * EPSILON; + p1.y = p0.y; + } + } else if p0.y == 0.0 { + p0.x = EPSILON; + } else { + y_edge = p0.y; + } + } else if p1.x == 0.0 { + if p1.y == 0.0 { + p1.x = EPSILON; + } else { + y_edge = p1.y; + } + } if !is_down { - (xy0, xy1) = (xy1, xy0); + (p0, p1) = (p1, p0); } - // TODO (part of move to 8 byte encoding for segments): don't store y_edge at all, - // resolve this in fine. - let y_edge = if xy0.x == tile_xy.x && xy1.x != tile_xy.x && xy0.y != tile_xy.y { - xy0.y - } else if xy1.x == tile_xy.x && xy1.y != tile_xy.y { - xy1.y - } else { - 1e9 - }; let segment = PathSegment { - origin: (xy0 - tile_xy).to_array(), - delta: (xy1 - xy0).to_array(), - y_edge: y_edge - tile_xy.y, + point0: p0.to_array(), + point1: p1.to_array(), + y_edge, _padding: Default::default(), }; - assert!(xy0.x >= tile_xy.x && xy0.x <= tile_xy1.x); - assert!(xy0.y >= tile_xy.y && xy0.y <= tile_xy1.y); - assert!(xy1.x >= tile_xy.x && xy1.x <= tile_xy1.x); - assert!(xy1.y >= tile_xy.y && xy1.y <= tile_xy1.y); + assert!(p0.x >= 0.0 && p0.x <= TILE_WIDTH as f32); + assert!(p0.y >= 0.0 && p0.y <= TILE_HEIGHT as f32); + assert!(p1.x >= 0.0 && p1.x <= TILE_WIDTH as f32); + assert!(p1.y >= 0.0 && p1.y <= TILE_HEIGHT as f32); segments[(seg_start + seg_within_slice) as usize] = segment; } }