From b2a232e8e018ae3ad18c136abad98e97cf9e149f Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Tue, 31 Oct 2023 01:16:29 -0700 Subject: [PATCH] [coarse] Remove branch conditionals in even-odd handling Moved the per-drawobject tile discard logic near the existing tile inclusion code in the main loop. This avoids the branch conditionals that were present in each draw tag case branch. --- shader/coarse.wgsl | 67 +++++++++++++---------------- src/cpu_shader/coarse.rs | 91 ++++++++++++++++++---------------------- 2 files changed, 71 insertions(+), 87 deletions(-) diff --git a/shader/coarse.wgsl b/shader/coarse.wgsl index e7c93e0a9..5429c349f 100644 --- a/shader/coarse.wgsl +++ b/shader/coarse.wgsl @@ -82,8 +82,7 @@ fn alloc_cmd(size: u32) { } } -// Returns true if this path should paint the tile. -fn write_path(tile: Tile, tile_ix: u32, draw_flags: u32) -> bool { +fn write_path(tile: Tile, tile_ix: u32, draw_flags: u32) { let even_odd = (draw_flags & DRAW_INFO_FLAGS_FILL_RULE_BIT) != 0u; // We overload the "segments" field to store both count (written by // path_count stage) and segment allocation (used by path_tiling and @@ -101,23 +100,10 @@ fn write_path(tile: Tile, tile_ix: u32, draw_flags: u32) -> bool { ptcl[cmd_offset + 3u] = u32(fill.backdrop); cmd_offset += 4u; } else { - // If no line segments cross this tile then the tile is completely inside a - // subregion of the path. If the rule is non-zero, the tile should get completely - // painted based on the draw tag (either a solid fill, gradient fill, or an image fill; - // writing CMD_SOLID below ensures that the pixel area coverage is 100%). - // - // If the rule is even-odd then this path shouldn't paint the tile at all if its - // winding number is even. We check this by simply looking at the backdrop, which - // contains the winding number of the top-left corner of the tile (and in this case, it - // applies to the whole tile). - if even_odd && (abs(tile.backdrop) & 1) == 0 { - return false; - } alloc_cmd(1u); ptcl[cmd_offset] = CMD_SOLID; cmd_offset += 1u; } - return true; } fn write_color(color: CmdColor) { @@ -320,7 +306,20 @@ fn main( let blend = scene[dd]; is_blend = blend != BLEND_CLIP; } - let include_tile = tile.segment_count_or_ix != 0u || (tile.backdrop == 0) == is_clip || is_blend; + + let di = draw_monoids[drawobj_ix].info_offset; + let draw_flags = info_bin_data[di]; + let even_odd = (draw_flags & DRAW_INFO_FLAGS_FILL_RULE_BIT) != 0u; + let n_segs = tile.segment_count_or_ix; + + // If this draw object represents an even-odd fill and we know that no line segment + // crosses this tile and then this draw object should not contribute to the tile if its + // backdrop (i.e. the winding number of its top-left corner) is even. + // + // NOTE: A clip should never get encoded with an even-odd fill. + let even_odd_discard = n_segs == 0u && even_odd && (abs(tile.backdrop) & 1) == 0; + let include_tile = !even_odd_discard + && (n_segs != 0u || (tile.backdrop == 0) == is_clip || is_blend); if include_tile { let el_slice = el_ix / 32u; let el_mask = 1u << (el_ix & 31u); @@ -346,6 +345,7 @@ fn main( continue; } } + let el_ix = slice_ix * 32u + firstTrailingBit(bitmap); drawobj_ix = sh_drawobj_ix[el_ix]; // clear LSB of bitmap, using bit magic @@ -354,42 +354,35 @@ fn main( let dm = draw_monoids[drawobj_ix]; let dd = config.drawdata_base + dm.scene_offset; let di = dm.info_offset; + let draw_flags = info_bin_data[di]; if clip_zero_depth == 0u { let tile_ix = sh_tile_base[el_ix] + sh_tile_stride[el_ix] * tile_y + tile_x; let tile = tiles[tile_ix]; switch drawtag { // DRAWTAG_FILL_COLOR case 0x44u: { - let draw_flags = info_bin_data[di]; - if write_path(tile, tile_ix, draw_flags) { - let rgba_color = scene[dd]; - write_color(CmdColor(rgba_color)); - } + write_path(tile, tile_ix, draw_flags); + let rgba_color = scene[dd]; + write_color(CmdColor(rgba_color)); } // DRAWTAG_FILL_LIN_GRADIENT case 0x114u: { - let draw_flags = info_bin_data[di]; - if write_path(tile, tile_ix, draw_flags) { - let index = scene[dd]; - let info_offset = di + 1u; - write_grad(CMD_LIN_GRAD, index, info_offset); - } + write_path(tile, tile_ix, draw_flags); + let index = scene[dd]; + let info_offset = di + 1u; + write_grad(CMD_LIN_GRAD, index, info_offset); } // DRAWTAG_FILL_RAD_GRADIENT case 0x29cu: { - let draw_flags = info_bin_data[di]; - if write_path(tile, tile_ix, draw_flags) { - let index = scene[dd]; - let info_offset = di + 1u; - write_grad(CMD_RAD_GRAD, index, info_offset); - } + write_path(tile, tile_ix, draw_flags); + let index = scene[dd]; + let info_offset = di + 1u; + write_grad(CMD_RAD_GRAD, index, info_offset); } // DRAWTAG_FILL_IMAGE case 0x248u: { - let draw_flags = info_bin_data[di]; - if write_path(tile, tile_ix, draw_flags) { - write_image(di + 1u); - } + write_path(tile, tile_ix, draw_flags); + write_image(di + 1u); } // DRAWTAG_BEGIN_CLIP case 0x9u: { diff --git a/src/cpu_shader/coarse.rs b/src/cpu_shader/coarse.rs index a2d4d47da..54a62e4f5 100644 --- a/src/cpu_shader/coarse.rs +++ b/src/cpu_shader/coarse.rs @@ -60,7 +60,6 @@ impl TileState { ptcl[(self.cmd_offset + offset) as usize] = value; } - // Returns true if this path should paint the tile. fn write_path( &mut self, config: &ConfigUniform, @@ -68,8 +67,7 @@ impl TileState { ptcl: &mut [u32], tile: &mut Tile, draw_flags: u32, - ) -> bool { - let even_odd = (draw_flags & DRAW_INFO_FLAGS_FILL_RULE_BIT) != 0; + ) { let n_segs = tile.segment_count_or_ix; if n_segs != 0 { let seg_ix = bump.segments; @@ -77,29 +75,17 @@ impl TileState { bump.segments += n_segs; self.alloc_cmd(4, config, bump, ptcl); self.write(ptcl, 0, CMD_FILL); + let even_odd = (draw_flags & DRAW_INFO_FLAGS_FILL_RULE_BIT) != 0; let size_and_rule = (n_segs << 1) | (even_odd as u32); self.write(ptcl, 1, size_and_rule); self.write(ptcl, 2, seg_ix); self.write(ptcl, 3, tile.backdrop as u32); self.cmd_offset += 4; } else { - // If no line segments cross this tile then the tile is completely inside a - // subregion of the path. If the rule is non-zero, the tile should get completely - // painted based on the draw tag (either a solid fill, gradient fill, or an image fill; - // writing CMD_SOLID below ensures that the pixel area coverage is 100%). - // - // If the rule is even-odd then this path shouldn't paint the tile at all if its - // winding number is even. We check this by simply looking at the backdrop, which - // contains the winding number of the top-left corner of the tile (and in this case, it - // applies to the whole tile). - if even_odd && (tile.backdrop.abs() & 1) == 0 { - return false; - } self.alloc_cmd(1, config, bump, ptcl); self.write(ptcl, 0, CMD_SOLID); self.cmd_offset += 1; } - true } fn write_color( @@ -256,50 +242,55 @@ fn coarse_main( let blend = scene[dd as usize]; is_blend = blend != BLEND_CLIP; } + + let draw_flags = info_bin_data[di as usize]; + let even_odd = (draw_flags & DRAW_INFO_FLAGS_FILL_RULE_BIT) != 0; let n_segs = tile.segment_count_or_ix; - let include_tile = n_segs != 0 || (tile.backdrop == 0) == is_clip || is_blend; + + // If this draw object represents an even-odd fill and we know that no line segment + // crosses this tile and then this draw object should not contribute to the tile if its + // backdrop (i.e. the winding number of its top-left corner) is even. + // + // NOTE: A clip should never get encoded with an even-odd fill. + assert!(!even_odd || !is_clip); + let even_odd_discard = + n_segs == 0 && even_odd && (tile.backdrop.abs() & 1) == 0; + let include_tile = !even_odd_discard + && (n_segs != 0 || (tile.backdrop == 0) == is_clip || is_blend); if include_tile { match DrawTag(drawtag) { DrawTag::COLOR => { - let draw_flags = info_bin_data[di as usize]; - if tile_state.write_path(config, bump, ptcl, tile, draw_flags) { - let rgba_color = scene[dd as usize]; - tile_state.write_color(config, bump, ptcl, rgba_color); - } + tile_state.write_path(config, bump, ptcl, tile, draw_flags); + let rgba_color = scene[dd as usize]; + tile_state.write_color(config, bump, ptcl, rgba_color); } DrawTag::IMAGE => { - let draw_flags = info_bin_data[di as usize]; - if tile_state.write_path(config, bump, ptcl, tile, draw_flags) { - tile_state.write_image(config, bump, ptcl, di + 1); - } + tile_state.write_path(config, bump, ptcl, tile, draw_flags); + tile_state.write_image(config, bump, ptcl, di + 1); } DrawTag::LINEAR_GRADIENT => { - let draw_flags = info_bin_data[di as usize]; - if tile_state.write_path(config, bump, ptcl, tile, draw_flags) { - let index = scene[dd as usize]; - tile_state.write_grad( - config, - bump, - ptcl, - CMD_LIN_GRAD, - index, - di + 1, - ); - } + tile_state.write_path(config, bump, ptcl, tile, draw_flags); + let index = scene[dd as usize]; + tile_state.write_grad( + config, + bump, + ptcl, + CMD_LIN_GRAD, + index, + di + 1, + ); } DrawTag::RADIAL_GRADIENT => { - let draw_flags = info_bin_data[di as usize]; - if tile_state.write_path(config, bump, ptcl, tile, draw_flags) { - let index = scene[dd as usize]; - tile_state.write_grad( - config, - bump, - ptcl, - CMD_RAD_GRAD, - index, - di + 1, - ); - } + tile_state.write_path(config, bump, ptcl, tile, draw_flags); + let index = scene[dd as usize]; + tile_state.write_grad( + config, + bump, + ptcl, + CMD_RAD_GRAD, + index, + di + 1, + ); } DrawTag::BEGIN_CLIP => { if tile.segment_count_or_ix == 0 && tile.backdrop == 0 {