Skip to content

Commit

Permalink
[cpu_shaders} Correctly write PTCL commands for even-odd fill
Browse files Browse the repository at this point in the history
The CPU version of the coarse stage was missing logic to correctly
handle the even-odd fill rule. This change copies over the existing
logic from the GPU version with some added documentation.
  • Loading branch information
armansito committed Oct 28, 2023
1 parent 64b8946 commit 184104c
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 28 deletions.
13 changes: 12 additions & 1 deletion shader/coarse.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +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;
// We overload the "segments" field to store both count (written by
Expand All @@ -100,6 +101,15 @@ 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. We check if
// the winding number for the whole tile is even simply by looking at the backdrop
// (which contains the winding number of the top-left corner of the tile) and
// report back that nothing should get painted.
if even_odd && (abs(tile.backdrop) & 1) == 0 {
return false;
}
Expand Down Expand Up @@ -395,7 +405,8 @@ fn main(
// DRAWTAG_END_CLIP
case 0x21u: {
clip_depth -= 1u;
write_path(tile, tile_ix, 0u);
// A clip shape is always a non-zero fill (draw_flags=0).
write_path(tile, tile_ix, /*draw_flags=*/0u);
let blend = scene[dd];
let alpha = bitcast<f32>(scene[dd + 1u]);
write_end_clip(CmdEndClip(blend, alpha));
Expand Down
73 changes: 46 additions & 27 deletions src/cpu_shader/coarse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,32 +60,46 @@ 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. We check if
// the winding number for the whole tile is even simply by looking at the backdrop
// (which contains the winding number of the top-left corner of the tile) and
// report back that nothing should get painted.
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 @@ -248,40 +262,44 @@ fn coarse_main(
match DrawTag(drawtag) {
DrawTag::COLOR => {
let draw_flags = info_bin_data[di as usize];
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);
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);
}
}
DrawTag::IMAGE => {
let draw_flags = info_bin_data[di as usize];
tile_state.write_path(config, bump, ptcl, tile, draw_flags);
tile_state.write_image(config, bump, ptcl, di + 1);
if 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];
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,
);
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,
);
}
}
DrawTag::RADIAL_GRADIENT => {
let draw_flags = info_bin_data[di as usize];
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,
);
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,
);
}
}
DrawTag::BEGIN_CLIP => {
if tile.segment_count_or_ix == 0 && tile.backdrop == 0 {
Expand All @@ -294,6 +312,7 @@ fn coarse_main(
}
DrawTag::END_CLIP => {
clip_depth -= 1;
// A clip shape is always a non-zero fill (draw_flags=0).
tile_state.write_path(config, bump, ptcl, tile, 0);
let blend = scene[dd as usize];
let alpha = f32::from_bits(scene[dd as usize + 1]);
Expand Down

0 comments on commit 184104c

Please sign in to comment.