[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.
diff --git a/shader/coarse.wgsl b/shader/coarse.wgsl
index e7c93e0..c23d088 100644
--- a/shader/coarse.wgsl
+++ b/shader/coarse.wgsl
@@ -82,9 +82,7 @@
}
}
-// Returns true if this path should paint the tile.
-fn write_path(tile: Tile, tile_ix: u32, draw_flags: u32) -> bool {
- let even_odd = (draw_flags & DRAW_INFO_FLAGS_FILL_RULE_BIT) != 0u;
+fn write_path(tile: Tile, tile_ix: u32, draw_flags: u32) {
// We overload the "segments" field to store both count (written by
// path_count stage) and segment allocation (used by path_tiling and
// fine).
@@ -94,6 +92,7 @@
tiles[tile_ix].segment_count_or_ix = ~seg_ix;
alloc_cmd(4u);
ptcl[cmd_offset] = CMD_FILL;
+ let even_odd = (draw_flags & DRAW_INFO_FLAGS_FILL_RULE_BIT) != 0u;
let size_and_rule = (n_segs << 1u) | u32(even_odd);
let fill = CmdFill(size_and_rule, seg_ix, tile.backdrop);
ptcl[cmd_offset + 1u] = fill.size_and_rule;
@@ -101,23 +100,10 @@
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 @@
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 @@
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 @@
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 a2d4d47..54a62e4 100644
--- a/src/cpu_shader/coarse.rs
+++ b/src/cpu_shader/coarse.rs
@@ -60,7 +60,6 @@
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 @@
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 @@
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 @@
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 {