From 4138f8a5165257622880157e49b6485c6c66c73b Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Fri, 27 Nov 2020 08:42:21 -0800 Subject: [PATCH] Optimize clips Optimize tiles with clip masks that are all-zero or all-one. Part of #36 --- piet-gpu/shader/coarse.comp | 176 ++++++++++++++++++++++-------------- piet-gpu/shader/coarse.spv | Bin 42212 -> 43964 bytes piet-gpu/src/lib.rs | 8 +- 3 files changed, 113 insertions(+), 71 deletions(-) diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 12ebb98..a2be143 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -84,11 +84,22 @@ void main() { // Coordinates of top left of bin, in tiles. uint bin_tile_x = N_TILE_X * gl_WorkGroupID.x; uint bin_tile_y = N_TILE_Y * gl_WorkGroupID.y; + + // Per-tile state uint tile_x = gl_LocalInvocationID.x % N_TILE_X; uint tile_y = gl_LocalInvocationID.x / N_TILE_X; uint this_tile_ix = (bin_tile_y + tile_y) * WIDTH_IN_TILES + bin_tile_x + tile_x; CmdRef cmd_ref = CmdRef(this_tile_ix * PTCL_INITIAL_ALLOC); uint cmd_limit = cmd_ref.offset + PTCL_INITIAL_ALLOC - 2 * Cmd_size; + // The nesting depth of the clip stack + uint clip_depth = 0; + // State for the "clip zero" optimization. If it's nonzero, then we are + // currently in a clip for which the entire tile has an alpha of zero, and + // the value is the depth after the "begin clip" of that element. + uint clip_zero_depth = 0; + // State for the "clip one" optimization. If bit `i` is set, then that means + // that the clip pushed at depth `i` has an alpha of all one. + uint clip_one_mask = 0; // I'm sure we can figure out how to do this with at least one fewer register... // Items up to rd_ix have been read from sh_elements @@ -98,6 +109,7 @@ void main() { // Items between part_start_ix and ready_ix are ready to be transferred from sh_part_elements uint part_start_ix = 0; uint ready_ix = 0; + while (true) { for (uint i = 0; i < N_SLICE; i++) { sh_bitmaps[i][th_ix] = 0; @@ -274,77 +286,105 @@ void main() { ref = AnnotatedRef(element_ix * Annotated_size); tag = Annotated_tag(ref); - switch (tag) { - case Annotated_Fill: - Tile tile = Tile_read(TileRef(sh_tile_base[element_ref_ix] - + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); - AnnoFill fill = Annotated_Fill_read(ref); - alloc_cmd(cmd_ref, cmd_limit); - if (tile.tile.offset != 0) { - CmdFill cmd_fill; - cmd_fill.tile_ref = tile.tile.offset; - cmd_fill.backdrop = tile.backdrop; - cmd_fill.rgba_color = fill.rgba_color; - Cmd_Fill_write(cmd_ref, cmd_fill); - } else { - Cmd_Solid_write(cmd_ref, CmdSolid(fill.rgba_color)); - } - cmd_ref.offset += Cmd_size; - break; - case Annotated_BeginClip: - tile = Tile_read(TileRef(sh_tile_base[element_ref_ix] - + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); - alloc_cmd(cmd_ref, cmd_limit); - if (tile.tile.offset != 0) { - CmdBeginClip cmd_begin_clip; - cmd_begin_clip.tile_ref = tile.tile.offset; - cmd_begin_clip.backdrop = tile.backdrop; - Cmd_BeginClip_write(cmd_ref, cmd_begin_clip); - } else { - // TODO: here is where a bunch of optimization magic should happen - float alpha = tile.backdrop == 0 ? 0.0 : 1.0; - Cmd_BeginSolidClip_write(cmd_ref, CmdBeginSolidClip(alpha)); - } - cmd_ref.offset += Cmd_size; - break; - case Annotated_EndClip: - alloc_cmd(cmd_ref, cmd_limit); - Cmd_EndClip_write(cmd_ref, CmdEndClip(1.0)); - cmd_ref.offset += Cmd_size; - break; - case Annotated_FillMask: - case Annotated_FillMaskInv: - tile = Tile_read(TileRef(sh_tile_base[element_ref_ix] - + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); - AnnoFillMask fill_mask = Annotated_FillMask_read(ref); - alloc_cmd(cmd_ref, cmd_limit); - if (tile.tile.offset != 0) { - CmdFillMask cmd_fill; - cmd_fill.tile_ref = tile.tile.offset; - cmd_fill.backdrop = tile.backdrop; - cmd_fill.mask = fill_mask.mask; - if (tag == Annotated_FillMask) { - Cmd_FillMask_write(cmd_ref, cmd_fill); + if (clip_zero_depth == 0) { + switch (tag) { + case Annotated_Fill: + Tile tile = Tile_read(TileRef(sh_tile_base[element_ref_ix] + + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); + AnnoFill fill = Annotated_Fill_read(ref); + alloc_cmd(cmd_ref, cmd_limit); + if (tile.tile.offset != 0) { + CmdFill cmd_fill; + cmd_fill.tile_ref = tile.tile.offset; + cmd_fill.backdrop = tile.backdrop; + cmd_fill.rgba_color = fill.rgba_color; + Cmd_Fill_write(cmd_ref, cmd_fill); } else { - Cmd_FillMaskInv_write(cmd_ref, cmd_fill); + Cmd_Solid_write(cmd_ref, CmdSolid(fill.rgba_color)); } - } else { - Cmd_SolidMask_write(cmd_ref, CmdSolidMask(fill_mask.mask)); + cmd_ref.offset += Cmd_size; + break; + case Annotated_BeginClip: + tile = Tile_read(TileRef(sh_tile_base[element_ref_ix] + + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); + if (tile.tile.offset == 0 && tile.backdrop == 0) { + clip_zero_depth = clip_depth + 1; + } else if (tile.tile.offset == 0 && clip_depth < 32) { + clip_one_mask |= (1 << clip_depth); + } else { + alloc_cmd(cmd_ref, cmd_limit); + if (tile.tile.offset != 0) { + CmdBeginClip cmd_begin_clip; + cmd_begin_clip.tile_ref = tile.tile.offset; + cmd_begin_clip.backdrop = tile.backdrop; + Cmd_BeginClip_write(cmd_ref, cmd_begin_clip); + } else { + // TODO: here is where a bunch of optimization magic should happen + float alpha = tile.backdrop == 0 ? 0.0 : 1.0; + Cmd_BeginSolidClip_write(cmd_ref, CmdBeginSolidClip(alpha)); + } + cmd_ref.offset += Cmd_size; + if (clip_depth < 32) { + clip_one_mask &= ~(1 << clip_depth); + } + } + clip_depth++; + break; + case Annotated_EndClip: + clip_depth--; + if (clip_depth >= 32 || (clip_one_mask & (1 << clip_depth)) == 0) { + alloc_cmd(cmd_ref, cmd_limit); + Cmd_EndClip_write(cmd_ref, CmdEndClip(1.0)); + cmd_ref.offset += Cmd_size; + } + break; + case Annotated_FillMask: + case Annotated_FillMaskInv: + tile = Tile_read(TileRef(sh_tile_base[element_ref_ix] + + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); + AnnoFillMask fill_mask = Annotated_FillMask_read(ref); + alloc_cmd(cmd_ref, cmd_limit); + if (tile.tile.offset != 0) { + CmdFillMask cmd_fill; + cmd_fill.tile_ref = tile.tile.offset; + cmd_fill.backdrop = tile.backdrop; + cmd_fill.mask = fill_mask.mask; + if (tag == Annotated_FillMask) { + Cmd_FillMask_write(cmd_ref, cmd_fill); + } else { + Cmd_FillMaskInv_write(cmd_ref, cmd_fill); + } + } else { + Cmd_SolidMask_write(cmd_ref, CmdSolidMask(fill_mask.mask)); + } + cmd_ref.offset += Cmd_size; + break; + case Annotated_Stroke: + tile = Tile_read(TileRef(sh_tile_base[element_ref_ix] + + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); + AnnoStroke stroke = Annotated_Stroke_read(ref); + CmdStroke cmd_stroke; + cmd_stroke.tile_ref = tile.tile.offset; + cmd_stroke.half_width = 0.5 * stroke.linewidth; + cmd_stroke.rgba_color = stroke.rgba_color; + alloc_cmd(cmd_ref, cmd_limit); + Cmd_Stroke_write(cmd_ref, cmd_stroke); + cmd_ref.offset += Cmd_size; + break; + } + } else { + // In "clip zero" state, suppress all drawing + switch (tag) { + case Annotated_BeginClip: + clip_depth++; + break; + case Annotated_EndClip: + if (clip_depth == clip_zero_depth) { + clip_zero_depth = 0; + } + clip_depth--; + break; } - cmd_ref.offset += Cmd_size; - break; - case Annotated_Stroke: - tile = Tile_read(TileRef(sh_tile_base[element_ref_ix] - + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); - AnnoStroke stroke = Annotated_Stroke_read(ref); - CmdStroke cmd_stroke; - cmd_stroke.tile_ref = tile.tile.offset; - cmd_stroke.half_width = 0.5 * stroke.linewidth; - cmd_stroke.rgba_color = stroke.rgba_color; - alloc_cmd(cmd_ref, cmd_limit); - Cmd_Stroke_write(cmd_ref, cmd_stroke); - cmd_ref.offset += Cmd_size; - break; } } barrier(); diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index 98dde1b67218125314ac038ecdef8ad4b1dccea4..c7693302f046404a62e2640a6d47c1eedbe91b21 100644 GIT binary patch literal 43964 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 N{{4j1%|7SH{{aHc!tVe8 literal 42212 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 diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 0a6152d..85b2e0c 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -99,9 +99,11 @@ fn render_cardioid(rc: &mut impl RenderContext) { fn render_clip_test(rc: &mut impl RenderContext) { const N: usize = 16; const X0: f64 = 50.0; - const Y0: f64 = 50.0; - const X1: f64 = 100.0; - const Y1: f64 = 100.0; + const Y0: f64 = 450.0; + // Note: if it gets much larger, it will exceed the 1MB scratch buffer. + // But this is a pretty demanding test. + const X1: f64 = 550.0; + const Y1: f64 = 950.0; let step = 1.0 / ((N + 1) as f64); for i in 0..N { let t = ((i + 1) as f64) * step;