Skip to content

Commit

Permalink
[coarse] Remove branch conditionals in even-odd handling
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
armansito committed Oct 31, 2023
1 parent 161a31c commit 7a84914
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 88 deletions.
69 changes: 31 additions & 38 deletions shader/coarse.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +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 {
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).
Expand All @@ -94,30 +92,18 @@ fn write_path(tile: Tile, tile_ix: u32, draw_flags: u32) -> bool {
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;
ptcl[cmd_offset + 2u] = fill.seg_data;
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) {
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand All @@ -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: {
Expand Down
91 changes: 41 additions & 50 deletions src/cpu_shader/coarse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,46 +60,32 @@ 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,
bump: &mut BumpAllocators,
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;
tile.segment_count_or_ix = !seg_ix;
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(
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 7a84914

Please sign in to comment.