// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense // The leaf scan pass for draw tag scan implemented as a tree reduction. // This stage can be fused with its consumer but is separate now. #version 450 #extension GL_GOOGLE_include_directive : enable #include "mem.h" #include "setup.h" #define N_ROWS 8 #define LG_WG_SIZE 9 #define WG_SIZE (1 << LG_WG_SIZE) #define PARTITION_SIZE (WG_SIZE * N_ROWS) layout(local_size_x = WG_SIZE, local_size_y = 1) in; layout(binding = 1) readonly buffer ConfigBuf { Config conf; }; layout(binding = 2) readonly buffer SceneBuf { uint[] scene; }; #include "scene.h" #include "tile.h" #include "drawtag.h" #include "annotated.h" #define Monoid DrawMonoid layout(set = 0, binding = 3) readonly buffer ParentBuf { Monoid[] parent; }; shared Monoid sh_scratch[WG_SIZE]; void main() { Monoid local[N_ROWS]; uint ix = gl_GlobalInvocationID.x * N_ROWS; ElementRef ref = ElementRef(ix * Element_size); uint tag_word = Element_tag(ref).tag; Monoid agg = map_tag(tag_word); local[0] = agg; for (uint i = 1; i < N_ROWS; i++) { tag_word = Element_tag(Element_index(ref, i)).tag; agg = combine_tag_monoid(agg, map_tag(tag_word)); local[i] = agg; } sh_scratch[gl_LocalInvocationID.x] = agg; for (uint i = 0; i < LG_WG_SIZE; i++) { barrier(); if (gl_LocalInvocationID.x >= (1u << i)) { Monoid other = sh_scratch[gl_LocalInvocationID.x - (1u << i)]; agg = combine_tag_monoid(other, agg); } barrier(); sh_scratch[gl_LocalInvocationID.x] = agg; } barrier(); Monoid row = tag_monoid_identity(); if (gl_WorkGroupID.x > 0) { row = parent[gl_WorkGroupID.x - 1]; } if (gl_LocalInvocationID.x > 0) { row = combine_tag_monoid(row, sh_scratch[gl_LocalInvocationID.x - 1]); } uint out_ix = gl_GlobalInvocationID.x * N_ROWS; uint out_base = (conf.drawmonoid_alloc.offset >> 2) + out_ix * 2; AnnotatedRef out_ref = AnnotatedRef(conf.anno_alloc.offset + out_ix * Annotated_size); for (uint i = 0; i < N_ROWS; i++) { Monoid m = combine_tag_monoid(row, local[i]); memory[out_base + i * 2] = m.path_ix; memory[out_base + i * 2 + 1] = m.clip_ix; // For compatibility, we'll generate an Annotated object, same as old // pipeline. However, going forward we'll get rid of that, and have // later stages read scene + bbox etc. ElementRef this_ref = Element_index(ref, i); tag_word = Element_tag(this_ref).tag; if (tag_word == Element_FillColor || tag_word == Element_FillLinGradient || tag_word == Element_FillImage) { uint bbox_offset = (conf.bbox_alloc.offset >> 2) + 6 * (m.path_ix - 1); float bbox_l = float(memory[bbox_offset]) - 32768.0; float bbox_t = float(memory[bbox_offset + 1]) - 32768.0; float bbox_r = float(memory[bbox_offset + 2]) - 32768.0; float bbox_b = float(memory[bbox_offset + 3]) - 32768.0; vec4 bbox = vec4(bbox_l, bbox_t, bbox_r, bbox_b); float linewidth = uintBitsToFloat(memory[bbox_offset + 4]); uint fill_mode = uint(linewidth >= 0.0); vec4 mat; vec2 translate; if (linewidth >= 0.0 || tag_word == Element_FillLinGradient) { uint trans_ix = memory[bbox_offset + 5]; uint t = (conf.trans_alloc.offset >> 2) + 6 * trans_ix; mat = uintBitsToFloat(uvec4(memory[t], memory[t + 1], memory[t + 2], memory[t + 3])); if (tag_word == Element_FillLinGradient) { translate = uintBitsToFloat(uvec2(memory[t + 4], memory[t + 5])); } } if (linewidth >= 0.0) { // TODO: need to deal with anisotropic case linewidth *= sqrt(abs(mat.x * mat.w - mat.y * mat.z)); } linewidth = max(linewidth, 0.0); switch (tag_word) { case Element_FillColor: FillColor fill = Element_FillColor_read(this_ref); AnnoColor anno_fill; anno_fill.bbox = bbox; anno_fill.linewidth = linewidth; anno_fill.rgba_color = fill.rgba_color; Annotated_Color_write(conf.anno_alloc, out_ref, fill_mode, anno_fill); break; case Element_FillLinGradient: FillLinGradient lin = Element_FillLinGradient_read(this_ref); AnnoLinGradient anno_lin; anno_lin.bbox = bbox; anno_lin.linewidth = linewidth; anno_lin.index = lin.index; vec2 p0 = mat.xy * lin.p0.x + mat.zw * lin.p0.y + translate; vec2 p1 = mat.xy * lin.p1.x + mat.zw * lin.p1.y + translate; vec2 dxy = p1 - p0; float scale = 1.0 / (dxy.x * dxy.x + dxy.y * dxy.y); float line_x = dxy.x * scale; float line_y = dxy.y * scale; anno_lin.line_x = line_x; anno_lin.line_y = line_y; anno_lin.line_c = -(p0.x * line_x + p0.y * line_y); Annotated_LinGradient_write(conf.anno_alloc, out_ref, fill_mode, anno_lin); break; case Element_FillImage: FillImage fill_img = Element_FillImage_read(this_ref); AnnoImage anno_img; anno_img.bbox = bbox; anno_img.linewidth = linewidth; anno_img.index = fill_img.index; anno_img.offset = fill_img.offset; Annotated_Image_write(conf.anno_alloc, out_ref, fill_mode, anno_img); break; } } else if (tag_word == Element_BeginClip) { Clip begin_clip = Element_BeginClip_read(this_ref); AnnoBeginClip anno_begin_clip; // This is the absolute bbox, it's been transformed during encoding. anno_begin_clip.bbox = begin_clip.bbox; anno_begin_clip.linewidth = 0.0; // don't support clip-with-stroke Annotated_BeginClip_write(conf.anno_alloc, out_ref, 0, anno_begin_clip); } else if (tag_word == Element_EndClip) { Clip end_clip = Element_EndClip_read(this_ref); AnnoEndClip anno_end_clip; // This bbox is expected to be the same as the begin one. anno_end_clip.bbox = end_clip.bbox; Annotated_EndClip_write(conf.anno_alloc, out_ref, anno_end_clip); } out_ref.offset += Annotated_size; } }