From 55cfd472a5fc52a76ce9a2a7faff8f2a3d40b7d2 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Fri, 9 Oct 2020 12:42:29 +0200 Subject: [PATCH 1/2] shader: delete unused code Signed-off-by: Elias Naur --- piet-gpu/shader/elements.comp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/piet-gpu/shader/elements.comp b/piet-gpu/shader/elements.comp index 3d21020..3c38f38 100644 --- a/piet-gpu/shader/elements.comp +++ b/piet-gpu/shader/elements.comp @@ -289,10 +289,6 @@ void main() { for (uint i = 0; i < N_ROWS; i++) { State st = combine_state(row, th_state[i]); - // We write the state now for development purposes, but the - // actual goal is to write transformed and annotated elements. - //State_write(StateRef((ix + i) * State_size), st); - // Here we read again from the original scene. There may be // gains to be had from stashing in shared memory or possibly // registers (though register pressure is an issue). From 8fab45544e33b9a3dcde9b8eec066b484c496ddf Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Fri, 9 Oct 2020 12:43:29 +0200 Subject: [PATCH 2/2] shader: implement clip paths Expand the the final kernel4 stage to maintain a per-pixel mask. Introduce two new path elements, FillMask and FillMaskInv, to fill the mask. FillMask acts like Fill, while FillMaskInv fills the area outside the path. SVG clipPaths is then representable by a FillMaskInv(0.0) for every nested path, preceded by a FillMask(1.0) to clear the mask. The bounding box for FillMaskInv elements is the entire screen; tightening of the bounding box is left for future work. Note that a fullscreen bounding box is not hopelessly inefficient because completely filling a tile with a mask is just a single CmdSolidMask per tile. Fixes #30 Signed-off-by: Elias Naur --- piet-gpu-types/src/annotated.rs | 6 ++ piet-gpu-types/src/ptcl.rs | 11 ++++ piet-gpu-types/src/scene.rs | 5 ++ piet-gpu/shader/annotated.h | 57 +++++++++++++++++++ piet-gpu/shader/backdrop.comp | 5 +- piet-gpu/shader/backdrop.spv | Bin 7256 -> 7256 bytes piet-gpu/shader/binning.comp | 2 + piet-gpu/shader/binning.spv | Bin 10136 -> 10152 bytes piet-gpu/shader/coarse.comp | 33 ++++++++++- piet-gpu/shader/coarse.spv | Bin 30904 -> 37332 bytes piet-gpu/shader/elements.comp | 19 +++++++ piet-gpu/shader/elements.spv | Bin 52988 -> 57244 bytes piet-gpu/shader/kernel4.comp | 92 ++++++++++++++++++++---------- piet-gpu/shader/kernel4.spv | Bin 20760 -> 26192 bytes piet-gpu/shader/ptcl.h | 98 +++++++++++++++++++++++++++++++- piet-gpu/shader/scene.h | 32 +++++++++++ piet-gpu/shader/tile_alloc.comp | 2 + piet-gpu/shader/tile_alloc.spv | Bin 8800 -> 8816 bytes 18 files changed, 326 insertions(+), 36 deletions(-) diff --git a/piet-gpu-types/src/annotated.rs b/piet-gpu-types/src/annotated.rs index 0e36f62..681a7ec 100644 --- a/piet-gpu-types/src/annotated.rs +++ b/piet-gpu-types/src/annotated.rs @@ -7,6 +7,10 @@ piet_gpu! { rgba_color: u32, bbox: [f32; 4], } + struct AnnoFillMask { + mask: f32, + bbox: [f32; 4], + } struct AnnoStroke { rgba_color: u32, bbox: [f32; 4], @@ -18,6 +22,8 @@ piet_gpu! { Nop, Stroke(AnnoStroke), Fill(AnnoFill), + FillMask(AnnoFillMask), + FillMaskInv(AnnoFillMask), } } } diff --git a/piet-gpu-types/src/ptcl.rs b/piet-gpu-types/src/ptcl.rs index 1deac38..95dcdc6 100644 --- a/piet-gpu-types/src/ptcl.rs +++ b/piet-gpu-types/src/ptcl.rs @@ -25,9 +25,17 @@ piet_gpu! { backdrop: i32, rgba_color: u32, } + struct CmdFillMask { + tile_ref: u32, + backdrop: i32, + mask: f32, + } struct CmdSolid { rgba_color: u32, } + struct CmdSolidMask { + mask: f32, + } struct CmdJump { new_ref: u32, } @@ -36,8 +44,11 @@ piet_gpu! { Circle(CmdCircle), Line(CmdLine), Fill(CmdFill), + FillMask(CmdFillMask), + FillMaskInv(CmdFillMask), Stroke(CmdStroke), Solid(CmdSolid), + SolidMask(CmdSolidMask), Jump(CmdJump), } diff --git a/piet-gpu-types/src/scene.rs b/piet-gpu-types/src/scene.rs index 0d72650..1359c1b 100644 --- a/piet-gpu-types/src/scene.rs +++ b/piet-gpu-types/src/scene.rs @@ -23,6 +23,9 @@ piet_gpu! { struct Fill { rgba_color: u32, } + struct FillMask { + mask: f32, + } struct Stroke { rgba_color: u32, } @@ -50,6 +53,8 @@ piet_gpu! { Fill(Fill), SetLineWidth(SetLineWidth), Transform(Transform), + FillMask(FillMask), + FillMaskInv(FillMask), } } } diff --git a/piet-gpu/shader/annotated.h b/piet-gpu/shader/annotated.h index 5dcb4ad..847ca06 100644 --- a/piet-gpu/shader/annotated.h +++ b/piet-gpu/shader/annotated.h @@ -4,6 +4,10 @@ struct AnnoFillRef { uint offset; }; +struct AnnoFillMaskRef { + uint offset; +}; + struct AnnoStrokeRef { uint offset; }; @@ -23,6 +27,17 @@ AnnoFillRef AnnoFill_index(AnnoFillRef ref, uint index) { return AnnoFillRef(ref.offset + index * AnnoFill_size); } +struct AnnoFillMask { + float mask; + vec4 bbox; +}; + +#define AnnoFillMask_size 20 + +AnnoFillMaskRef AnnoFillMask_index(AnnoFillMaskRef ref, uint index) { + return AnnoFillMaskRef(ref.offset + index * AnnoFillMask_size); +} + struct AnnoStroke { uint rgba_color; vec4 bbox; @@ -38,6 +53,8 @@ AnnoStrokeRef AnnoStroke_index(AnnoStrokeRef ref, uint index) { #define Annotated_Nop 0 #define Annotated_Stroke 1 #define Annotated_Fill 2 +#define Annotated_FillMask 3 +#define Annotated_FillMaskInv 4 #define Annotated_size 28 AnnotatedRef Annotated_index(AnnotatedRef ref, uint index) { @@ -66,6 +83,28 @@ void AnnoFill_write(AnnoFillRef ref, AnnoFill s) { annotated[ix + 4] = floatBitsToUint(s.bbox.w); } +AnnoFillMask AnnoFillMask_read(AnnoFillMaskRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = annotated[ix + 0]; + uint raw1 = annotated[ix + 1]; + uint raw2 = annotated[ix + 2]; + uint raw3 = annotated[ix + 3]; + uint raw4 = annotated[ix + 4]; + AnnoFillMask s; + s.mask = uintBitsToFloat(raw0); + s.bbox = vec4(uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3), uintBitsToFloat(raw4)); + return s; +} + +void AnnoFillMask_write(AnnoFillMaskRef ref, AnnoFillMask s) { + uint ix = ref.offset >> 2; + annotated[ix + 0] = floatBitsToUint(s.mask); + annotated[ix + 1] = floatBitsToUint(s.bbox.x); + annotated[ix + 2] = floatBitsToUint(s.bbox.y); + annotated[ix + 3] = floatBitsToUint(s.bbox.z); + annotated[ix + 4] = floatBitsToUint(s.bbox.w); +} + AnnoStroke AnnoStroke_read(AnnoStrokeRef ref) { uint ix = ref.offset >> 2; uint raw0 = annotated[ix + 0]; @@ -103,6 +142,14 @@ AnnoFill Annotated_Fill_read(AnnotatedRef ref) { return AnnoFill_read(AnnoFillRef(ref.offset + 4)); } +AnnoFillMask Annotated_FillMask_read(AnnotatedRef ref) { + return AnnoFillMask_read(AnnoFillMaskRef(ref.offset + 4)); +} + +AnnoFillMask Annotated_FillMaskInv_read(AnnotatedRef ref) { + return AnnoFillMask_read(AnnoFillMaskRef(ref.offset + 4)); +} + void Annotated_Nop_write(AnnotatedRef ref) { annotated[ref.offset >> 2] = Annotated_Nop; } @@ -117,3 +164,13 @@ void Annotated_Fill_write(AnnotatedRef ref, AnnoFill s) { AnnoFill_write(AnnoFillRef(ref.offset + 4), s); } +void Annotated_FillMask_write(AnnotatedRef ref, AnnoFillMask s) { + annotated[ref.offset >> 2] = Annotated_FillMask; + AnnoFillMask_write(AnnoFillMaskRef(ref.offset + 4), s); +} + +void Annotated_FillMaskInv_write(AnnotatedRef ref, AnnoFillMask s) { + annotated[ref.offset >> 2] = Annotated_FillMaskInv; + AnnoFillMask_write(AnnoFillMaskRef(ref.offset + 4), s); +} + diff --git a/piet-gpu/shader/backdrop.comp b/piet-gpu/shader/backdrop.comp index beba683..20c6ce9 100644 --- a/piet-gpu/shader/backdrop.comp +++ b/piet-gpu/shader/backdrop.comp @@ -53,7 +53,10 @@ void main() { uint row_count = 0; if (element_ix < n_elements) { uint tag = Annotated_tag(ref); - if (tag == Annotated_Fill) { + switch (tag) { + case Annotated_Fill: + case Annotated_FillMask: + case Annotated_FillMaskInv: PathRef path_ref = PathRef(element_ix * Path_size); Path path = Path_read(path_ref); sh_row_width[th_ix] = path.bbox.z - path.bbox.x; diff --git a/piet-gpu/shader/backdrop.spv b/piet-gpu/shader/backdrop.spv index fdbb4f1b1a11f1c4901d4b7fa46b750ae3efe68c..e2093d9a02e92895c45174a57d2701d2e434dd20 100644 GIT binary patch delta 1019 zcmYk5&2NlR6vpp6Gp&rECRLGWf<;$N#7f)vh^{RVk*;W(NYtW8iD?yYjn?N-ygr5^ zv1($~=KT-sti-O?*4Dlgzu$PTnB65XpBee40|uq>;U;F6@{lv!dWdE<;x(`d=$rQpdU5j`@DVV~*1g1=z?!XKFNk%w;O1i8ZTR0h zI`8_g@DAK$qd3i7xQq3N@qChdz-MxEhf-S?n=ioQ5I$EN;sM-Ttb1skCv)1}AQnD? zn~Xzba2M;l;VGhCtbYvGd&_`lh);l?J$>G1L_a^ca&u3Co==%7VpDJ8J7!E8<0jAH zBOp%y0`4gf0S^%C7UAaJ6*u@2uD1=$7TznkJ_GLjcdf56{589g1F0=^Ab*ffJC*fy z4J6I{c-KslG_y||GrlKkDb|KB;L-uDc&m4C!mUfsM@N*ejf HmZkC^OWcG` delta 1019 zcmYk5%}bO~6vpp6?`TX|I`~1LP%c~v(aH}j$+e4$h*l;-pe?kpX#6POH^=NlC#%=e z$v$W$v{K&xpk1qIr|Z_PEivf#jQ7Y4JaeAsIp>~x?!7ZRJv%*HTVXJhY=@1#k#p|e z$b&J0t8rNuSM=n_ctzK`MdJOj;mNy&;liEUR#)T7>A&!2HhM(54s^1#mRJNXaIOu# z05$I%`YAdK0X-UiSwWd#6WqSL7r{?41N;~uyq^pIZ#b2&_S&31zb6+ zdkH_PcZHQI47R%R$qeOYu_#IrHJ>`42JoVI5t`qgqQ!2Lx9 zy!xs)&>H><&~;ngN4Rp|{SAJC%R38V?|p{LXTbBv*ZKuRzh-TufuvQaf$WDjopjW9 z2c>kVel`e7$!pZH5N` diff --git a/piet-gpu/shader/binning.comp b/piet-gpu/shader/binning.comp index 046c4fb..ee3301b 100644 --- a/piet-gpu/shader/binning.comp +++ b/piet-gpu/shader/binning.comp @@ -60,6 +60,8 @@ void main() { float my_right_edge = INFINITY; switch (tag) { case Annotated_Fill: + case Annotated_FillMask: + case Annotated_FillMaskInv: case Annotated_Stroke: // Note: we take advantage of the fact that fills and strokes // have compatible layout. diff --git a/piet-gpu/shader/binning.spv b/piet-gpu/shader/binning.spv index d1c469c2e11c240d2bdbe8ee79f361d45cc6d934..50070a155475828ee9b613dcdfcf18281d1d7b5e 100644 GIT binary patch delta 40 mcmbQ?zrugRIRR;Ih9e9N499?&35bsZF*B5A*?d!AzZd|`<_RGH delta 24 ecmZ4CKf`~+IRS2Vh9e9N499?&Y4b~g{bB%RI0uXX diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 57cbc8b..16573e8 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -173,6 +173,8 @@ void main() { uint tile_count; switch (tag) { case Annotated_Fill: + case Annotated_FillMask: + case Annotated_FillMaskInv: case Annotated_Stroke: // Because the only elements we're processing right now are // paths, we can just use the element index as the path index. @@ -222,12 +224,20 @@ void main() { el_ix = probe; } } + AnnotatedRef ref = AnnotatedRef(el_ix * Annotated_size); + uint tag = Annotated_tag(ref); uint seq_ix = ix - (el_ix > 0 ? sh_tile_count[el_ix - 1] : 0); uint width = sh_tile_width[el_ix]; uint x = sh_tile_x0[el_ix] + seq_ix % width; uint y = sh_tile_y0[el_ix] + seq_ix / width; Tile tile = Tile_read(TileRef(sh_tile_base[el_ix] + (sh_tile_stride[el_ix] * y + x) * Tile_size)); - if (tile.tile.offset != 0 || tile.backdrop != 0) { + // Include the path in the tile if + // - the tile contains at least a segment (tile offset non-zero) + // - the tile is completely covered (backdrop non-zero) + // - the tile is not covered and we're filling everything outside the path (backdrop zero, inverse fills). + bool inside = tile.backdrop != 0; + bool fill = tag != Annotated_FillMaskInv; + if (tile.tile.offset != 0 || inside == fill) { uint el_slice = el_ix / 32; uint el_mask = 1 << (el_ix & 31); atomicOr(sh_bitmaps[el_slice][y * N_TILE_X + x], el_mask); @@ -281,6 +291,27 @@ void main() { } 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)); diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index 334e1f52bc8e29efd23b1a54a5f058a4a0761b45..7758b96fff62df8bc92500896b487d3bfa54cb8d 100644 GIT binary patch literal 37332 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 WLU8p3DaLSa)%^Q*=FK|$<9`6dj^sN4 diff --git a/piet-gpu/shader/elements.comp b/piet-gpu/shader/elements.comp index 3c38f38..b0e4779 100644 --- a/piet-gpu/shader/elements.comp +++ b/piet-gpu/shader/elements.comp @@ -130,6 +130,8 @@ State map_element(ElementRef ref, inout bool is_fill) { c.pathseg_count = 1; break; case Element_Fill: + case Element_FillMask: + case Element_FillMaskInv: is_fill = true; // fall-through case Element_Stroke: @@ -381,6 +383,23 @@ void main() { out_ref = AnnotatedRef((st.path_count - 1) * Annotated_size); Annotated_Fill_write(out_ref, anno_fill); break; + case Element_FillMask: + FillMask fill_mask = Element_FillMask_read(this_ref); + AnnoFillMask anno_fill_mask; + anno_fill_mask.mask = fill_mask.mask; + anno_fill_mask.bbox = st.bbox; + out_ref = AnnotatedRef((st.path_count - 1) * Annotated_size); + Annotated_FillMask_write(out_ref, anno_fill_mask); + break; + case Element_FillMaskInv: + fill_mask = Element_FillMaskInv_read(this_ref); + anno_fill_mask.mask = fill_mask.mask; + // The inverse fill conceptually takes up the entire screen. + // TODO: Tighten bounds to contain only affected paths. + anno_fill_mask.bbox = vec4(0, 0, 1e9, 1e9); + out_ref = AnnotatedRef((st.path_count - 1) * Annotated_size); + Annotated_FillMaskInv_write(out_ref, anno_fill_mask); + break; } } } diff --git a/piet-gpu/shader/elements.spv b/piet-gpu/shader/elements.spv index 4b7a1c276b801ca7ae7d8c0000482e77756210e5..599465af04eaf3593fd5c651d2ac394e15f4cc4b 100644 GIT binary patch literal 57244 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 Ot_G_+Ki5$18vZ|le<%h3 diff --git a/piet-gpu/shader/kernel4.comp b/piet-gpu/shader/kernel4.comp index 70c879f..d21aa87 100644 --- a/piet-gpu/shader/kernel4.comp +++ b/piet-gpu/shader/kernel4.comp @@ -27,6 +27,40 @@ layout(rgba8, set = 0, binding = 2) uniform writeonly image2D image; #include "ptcl.h" #include "tile.h" +// Calculate coverage based on backdrop + coverage of each line segment +float[CHUNK] computeArea(vec2 xy, int backdrop, uint tile_ref) { + // Probably better to store as float, but conversion is no doubt cheap. + float area[CHUNK]; + for (uint k = 0; k < CHUNK; k++) area[k] = float(backdrop); + TileSegRef tile_seg_ref = TileSegRef(tile_ref); + do { + TileSeg seg = TileSeg_read(tile_seg_ref); + for (uint k = 0; k < CHUNK; k++) { + vec2 my_xy = vec2(xy.x, xy.y + float(k * CHUNK_DY)); + vec2 start = seg.start - my_xy; + vec2 end = seg.end - my_xy; + vec2 window = clamp(vec2(start.y, end.y), 0.0, 1.0); + if (window.x != window.y) { + vec2 t = (window - start.y) / (end.y - start.y); + vec2 xs = vec2(mix(start.x, end.x, t.x), mix(start.x, end.x, t.y)); + float xmin = min(min(xs.x, xs.y), 1.0) - 1e-6; + float xmax = max(xs.x, xs.y); + float b = min(xmax, 1.0); + float c = max(b, 0.0); + float d = max(xmin, 0.0); + float a = (b + 0.5 * (d * d - c * c) - xmin) / (xmax - xmin); + area[k] += a * (window.x - window.y); + } + area[k] += sign(end.x - start.x) * clamp(my_xy.y - seg.y_edge + 1.0, 0.0, 1.0); + } + tile_seg_ref = seg.next; + } while (tile_seg_ref.offset != 0); + for (uint k = 0; k < CHUNK; k++) { + area[k] = min(abs(area[k]), 1.0); + } + return area; +} + void main() { uint tile_ix = gl_WorkGroupID.y * WIDTH_IN_TILES + gl_WorkGroupID.x; CmdRef cmd_ref = CmdRef(tile_ix * PTCL_INITIAL_ALLOC); @@ -34,8 +68,10 @@ void main() { uvec2 xy_uint = uvec2(gl_GlobalInvocationID.x, gl_LocalInvocationID.y + TILE_HEIGHT_PX * gl_WorkGroupID.y); vec2 xy = vec2(xy_uint); vec3 rgb[CHUNK]; + float mask[CHUNK]; for (uint i = 0; i < CHUNK; i++) { rgb[i] = vec3(0.5); + mask[i] = 1.0; } while (true) { @@ -52,7 +88,7 @@ void main() { float r = length(vec2(xy.x, xy.y + dy) + vec2(0.5, 0.5) - circle.center.xy); float alpha = clamp(0.5 + circle.radius - r, 0.0, 1.0); // TODO: sRGB - rgb[i] = mix(rgb[i], fg_rgba.rgb, alpha * fg_rgba.a); + rgb[i] = mix(rgb[i], fg_rgba.rgb, mask[i] * alpha * fg_rgba.a); } break; case Cmd_Stroke: @@ -75,49 +111,43 @@ void main() { fg_rgba = unpackUnorm4x8(stroke.rgba_color).wzyx; for (uint k = 0; k < CHUNK; k++) { float alpha = clamp(stroke.half_width + 0.5 - df[k], 0.0, 1.0); - rgb[k] = mix(rgb[k], fg_rgba.rgb, alpha * fg_rgba.a); + rgb[k] = mix(rgb[k], fg_rgba.rgb, mask[k] * alpha * fg_rgba.a); } break; case Cmd_Fill: - // Calculate coverage based on backdrop + coverage of each line segment CmdFill fill = Cmd_Fill_read(cmd_ref); - // Probably better to store as float, but conversion is no doubt cheap. float area[CHUNK]; - for (uint k = 0; k < CHUNK; k++) area[k] = float(fill.backdrop); - tile_seg_ref = TileSegRef(fill.tile_ref); - do { - TileSeg seg = TileSeg_read(tile_seg_ref); - for (uint k = 0; k < CHUNK; k++) { - vec2 my_xy = vec2(xy.x, xy.y + float(k * CHUNK_DY)); - vec2 start = seg.start - my_xy; - vec2 end = seg.end - my_xy; - vec2 window = clamp(vec2(start.y, end.y), 0.0, 1.0); - if (window.x != window.y) { - vec2 t = (window - start.y) / (end.y - start.y); - vec2 xs = vec2(mix(start.x, end.x, t.x), mix(start.x, end.x, t.y)); - float xmin = min(min(xs.x, xs.y), 1.0) - 1e-6; - float xmax = max(xs.x, xs.y); - float b = min(xmax, 1.0); - float c = max(b, 0.0); - float d = max(xmin, 0.0); - float a = (b + 0.5 * (d * d - c * c) - xmin) / (xmax - xmin); - area[k] += a * (window.x - window.y); - } - area[k] += sign(end.x - start.x) * clamp(my_xy.y - seg.y_edge + 1.0, 0.0, 1.0); - } - tile_seg_ref = seg.next; - } while (tile_seg_ref.offset != 0); + area = computeArea(xy, fill.backdrop, fill.tile_ref); fg_rgba = unpackUnorm4x8(fill.rgba_color).wzyx; for (uint k = 0; k < CHUNK; k++) { - float alpha = min(abs(area[k]), 1.0); - rgb[k] = mix(rgb[k], fg_rgba.rgb, alpha * fg_rgba.a); + rgb[k] = mix(rgb[k], fg_rgba.rgb, mask[k] * area[k] * fg_rgba.a); + } + break; + case Cmd_FillMask: + CmdFillMask fill_mask = Cmd_FillMask_read(cmd_ref); + area = computeArea(xy, fill_mask.backdrop, fill_mask.tile_ref); + for (uint k = 0; k < CHUNK; k++) { + mask[k] = mix(mask[k], fill_mask.mask, area[k]); + } + break; + case Cmd_FillMaskInv: + fill_mask = Cmd_FillMask_read(cmd_ref); + area = computeArea(xy, fill_mask.backdrop, fill_mask.tile_ref); + for (uint k = 0; k < CHUNK; k++) { + mask[k] = mix(mask[k], fill_mask.mask, 1.0 - area[k]); } break; case Cmd_Solid: CmdSolid solid = Cmd_Solid_read(cmd_ref); fg_rgba = unpackUnorm4x8(solid.rgba_color).wzyx; for (uint k = 0; k < CHUNK; k++) { - rgb[k] = mix(rgb[k], fg_rgba.rgb, fg_rgba.a); + rgb[k] = mix(rgb[k], fg_rgba.rgb, mask[k] * fg_rgba.a); + } + break; + case Cmd_SolidMask: + CmdSolidMask solid_mask = Cmd_SolidMask_read(cmd_ref); + for (uint k = 0; k < CHUNK; k++) { + mask[k] = solid_mask.mask; } break; case Cmd_Jump: diff --git a/piet-gpu/shader/kernel4.spv b/piet-gpu/shader/kernel4.spv index 84919e07df997d9280b068322fe37e750b44a3a7..d159fe54ca5caf49dd92dbdf5f5f6c3317aed27e 100644 GIT binary patch literal 26192 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 literal 20760 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 Muy)@+V&6ai1HHn0E&u=k diff --git a/piet-gpu/shader/ptcl.h b/piet-gpu/shader/ptcl.h index c9be302..d1cc83d 100644 --- a/piet-gpu/shader/ptcl.h +++ b/piet-gpu/shader/ptcl.h @@ -16,10 +16,18 @@ struct CmdFillRef { uint offset; }; +struct CmdFillMaskRef { + uint offset; +}; + struct CmdSolidRef { uint offset; }; +struct CmdSolidMaskRef { + uint offset; +}; + struct CmdJumpRef { uint offset; }; @@ -83,6 +91,18 @@ CmdFillRef CmdFill_index(CmdFillRef ref, uint index) { return CmdFillRef(ref.offset + index * CmdFill_size); } +struct CmdFillMask { + uint tile_ref; + int backdrop; + float mask; +}; + +#define CmdFillMask_size 12 + +CmdFillMaskRef CmdFillMask_index(CmdFillMaskRef ref, uint index) { + return CmdFillMaskRef(ref.offset + index * CmdFillMask_size); +} + struct CmdSolid { uint rgba_color; }; @@ -93,6 +113,16 @@ CmdSolidRef CmdSolid_index(CmdSolidRef ref, uint index) { return CmdSolidRef(ref.offset + index * CmdSolid_size); } +struct CmdSolidMask { + float mask; +}; + +#define CmdSolidMask_size 4 + +CmdSolidMaskRef CmdSolidMask_index(CmdSolidMaskRef ref, uint index) { + return CmdSolidMaskRef(ref.offset + index * CmdSolidMask_size); +} + struct CmdJump { uint new_ref; }; @@ -107,9 +137,12 @@ CmdJumpRef CmdJump_index(CmdJumpRef ref, uint index) { #define Cmd_Circle 1 #define Cmd_Line 2 #define Cmd_Fill 3 -#define Cmd_Stroke 4 -#define Cmd_Solid 5 -#define Cmd_Jump 6 +#define Cmd_FillMask 4 +#define Cmd_FillMaskInv 5 +#define Cmd_Stroke 6 +#define Cmd_Solid 7 +#define Cmd_SolidMask 8 +#define Cmd_Jump 9 #define Cmd_size 20 CmdRef Cmd_index(CmdRef ref, uint index) { @@ -219,6 +252,25 @@ void CmdFill_write(CmdFillRef ref, CmdFill s) { ptcl[ix + 2] = s.rgba_color; } +CmdFillMask CmdFillMask_read(CmdFillMaskRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = ptcl[ix + 0]; + uint raw1 = ptcl[ix + 1]; + uint raw2 = ptcl[ix + 2]; + CmdFillMask s; + s.tile_ref = raw0; + s.backdrop = int(raw1); + s.mask = uintBitsToFloat(raw2); + return s; +} + +void CmdFillMask_write(CmdFillMaskRef ref, CmdFillMask s) { + uint ix = ref.offset >> 2; + ptcl[ix + 0] = s.tile_ref; + ptcl[ix + 1] = uint(s.backdrop); + ptcl[ix + 2] = floatBitsToUint(s.mask); +} + CmdSolid CmdSolid_read(CmdSolidRef ref) { uint ix = ref.offset >> 2; uint raw0 = ptcl[ix + 0]; @@ -232,6 +284,19 @@ void CmdSolid_write(CmdSolidRef ref, CmdSolid s) { ptcl[ix + 0] = s.rgba_color; } +CmdSolidMask CmdSolidMask_read(CmdSolidMaskRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = ptcl[ix + 0]; + CmdSolidMask s; + s.mask = uintBitsToFloat(raw0); + return s; +} + +void CmdSolidMask_write(CmdSolidMaskRef ref, CmdSolidMask s) { + uint ix = ref.offset >> 2; + ptcl[ix + 0] = floatBitsToUint(s.mask); +} + CmdJump CmdJump_read(CmdJumpRef ref) { uint ix = ref.offset >> 2; uint raw0 = ptcl[ix + 0]; @@ -261,6 +326,14 @@ CmdFill Cmd_Fill_read(CmdRef ref) { return CmdFill_read(CmdFillRef(ref.offset + 4)); } +CmdFillMask Cmd_FillMask_read(CmdRef ref) { + return CmdFillMask_read(CmdFillMaskRef(ref.offset + 4)); +} + +CmdFillMask Cmd_FillMaskInv_read(CmdRef ref) { + return CmdFillMask_read(CmdFillMaskRef(ref.offset + 4)); +} + CmdStroke Cmd_Stroke_read(CmdRef ref) { return CmdStroke_read(CmdStrokeRef(ref.offset + 4)); } @@ -269,6 +342,10 @@ CmdSolid Cmd_Solid_read(CmdRef ref) { return CmdSolid_read(CmdSolidRef(ref.offset + 4)); } +CmdSolidMask Cmd_SolidMask_read(CmdRef ref) { + return CmdSolidMask_read(CmdSolidMaskRef(ref.offset + 4)); +} + CmdJump Cmd_Jump_read(CmdRef ref) { return CmdJump_read(CmdJumpRef(ref.offset + 4)); } @@ -292,6 +369,16 @@ void Cmd_Fill_write(CmdRef ref, CmdFill s) { CmdFill_write(CmdFillRef(ref.offset + 4), s); } +void Cmd_FillMask_write(CmdRef ref, CmdFillMask s) { + ptcl[ref.offset >> 2] = Cmd_FillMask; + CmdFillMask_write(CmdFillMaskRef(ref.offset + 4), s); +} + +void Cmd_FillMaskInv_write(CmdRef ref, CmdFillMask s) { + ptcl[ref.offset >> 2] = Cmd_FillMaskInv; + CmdFillMask_write(CmdFillMaskRef(ref.offset + 4), s); +} + void Cmd_Stroke_write(CmdRef ref, CmdStroke s) { ptcl[ref.offset >> 2] = Cmd_Stroke; CmdStroke_write(CmdStrokeRef(ref.offset + 4), s); @@ -302,6 +389,11 @@ void Cmd_Solid_write(CmdRef ref, CmdSolid s) { CmdSolid_write(CmdSolidRef(ref.offset + 4), s); } +void Cmd_SolidMask_write(CmdRef ref, CmdSolidMask s) { + ptcl[ref.offset >> 2] = Cmd_SolidMask; + CmdSolidMask_write(CmdSolidMaskRef(ref.offset + 4), s); +} + void Cmd_Jump_write(CmdRef ref, CmdJump s) { ptcl[ref.offset >> 2] = Cmd_Jump; CmdJump_write(CmdJumpRef(ref.offset + 4), s); diff --git a/piet-gpu/shader/scene.h b/piet-gpu/shader/scene.h index eee8075..6823fe6 100644 --- a/piet-gpu/shader/scene.h +++ b/piet-gpu/shader/scene.h @@ -16,6 +16,10 @@ struct FillRef { uint offset; }; +struct FillMaskRef { + uint offset; +}; + struct StrokeRef { uint offset; }; @@ -78,6 +82,16 @@ FillRef Fill_index(FillRef ref, uint index) { return FillRef(ref.offset + index * Fill_size); } +struct FillMask { + float mask; +}; + +#define FillMask_size 4 + +FillMaskRef FillMask_index(FillMaskRef ref, uint index) { + return FillMaskRef(ref.offset + index * FillMask_size); +} + struct Stroke { uint rgba_color; }; @@ -120,6 +134,8 @@ TransformRef Transform_index(TransformRef ref, uint index) { #define Element_Fill 8 #define Element_SetLineWidth 9 #define Element_Transform 10 +#define Element_FillMask 11 +#define Element_FillMaskInv 12 #define Element_size 36 ElementRef Element_index(ElementRef ref, uint index) { @@ -179,6 +195,14 @@ Fill Fill_read(FillRef ref) { return s; } +FillMask FillMask_read(FillMaskRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + FillMask s; + s.mask = uintBitsToFloat(raw0); + return s; +} + Stroke Stroke_read(StrokeRef ref) { uint ix = ref.offset >> 2; uint raw0 = scene[ix + 0]; @@ -253,3 +277,11 @@ Transform Element_Transform_read(ElementRef ref) { return Transform_read(TransformRef(ref.offset + 4)); } +FillMask Element_FillMask_read(ElementRef ref) { + return FillMask_read(FillMaskRef(ref.offset + 4)); +} + +FillMask Element_FillMaskInv_read(ElementRef ref) { + return FillMask_read(FillMaskRef(ref.offset + 4)); +} + diff --git a/piet-gpu/shader/tile_alloc.comp b/piet-gpu/shader/tile_alloc.comp index b0c99a5..3e1e52f 100644 --- a/piet-gpu/shader/tile_alloc.comp +++ b/piet-gpu/shader/tile_alloc.comp @@ -47,6 +47,8 @@ void main() { int x0 = 0, y0 = 0, x1 = 0, y1 = 0; switch (tag) { case Annotated_Fill: + case Annotated_FillMask: + case Annotated_FillMaskInv: case Annotated_Stroke: // Note: we take advantage of the fact that fills and strokes // have compatible layout. diff --git a/piet-gpu/shader/tile_alloc.spv b/piet-gpu/shader/tile_alloc.spv index e012b7c5d0f5f04473cc628eba3bbdedfee36b25..af52665184d64b41d3f1e8b3f9174a3ea38b1c49 100644 GIT binary patch delta 40 mcmaFh^1)?;7QZw%!!8B}hCM*c1jM_6m>Ej5Y&PZB76t&q{s)o( delta 24 ecmez1^1x+-7C$#T!!8B}hCM*cwAqzkTNnUeSOyUQ