// This is "kernel 4" in a 4-kernel pipeline. It renders the commands // in the per-tile command list to an image. // Right now, this kernel stores the image in a buffer, but a better // plan is to use a texture. This is because of limited support. #version 450 #extension GL_GOOGLE_include_directive : enable layout(local_size_x = 16, local_size_y = 16) in; // Same concern that this should be readonly as in kernel 3. layout(set = 0, binding = 0) buffer PtclBuf { uint[] ptcl; }; // Used readonly layout(set = 0, binding = 1) buffer SegmentBuf { uint[] segment; }; // Used readonly layout(set = 0, binding = 2) buffer FillSegBuf { uint[] fill_seg; }; layout(rgba8, set = 0, binding = 3) uniform writeonly image2D image; #include "ptcl.h" #include "segment.h" #include "fill_seg.h" #include "setup.h" void main() { uint tile_ix = gl_WorkGroupID.y * WIDTH_IN_TILES + gl_WorkGroupID.x; CmdRef cmd_ref = CmdRef(tile_ix * PTCL_INITIAL_ALLOC); uvec2 xy_uint = gl_GlobalInvocationID.xy; vec2 xy = vec2(xy_uint); vec2 uv = xy * vec2(1.0 / IMAGE_WIDTH, 1.0 / IMAGE_HEIGHT); //vec3 rgb = uv.xyy; vec3 rgb = vec3(0.75); while (true) { uint tag = Cmd_tag(cmd_ref); if (tag == Cmd_End) { break; } switch (tag) { case Cmd_Circle: CmdCircle circle = Cmd_Circle_read(cmd_ref); float r = length(xy + vec2(0.5, 0.5) - circle.center.xy); float alpha = clamp(0.5 + circle.radius - r, 0.0, 1.0); vec4 fg_rgba = unpackUnorm4x8(circle.rgba_color).wzyx; // TODO: sRGB rgb = mix(rgb, fg_rgba.rgb, alpha * fg_rgba.a); break; case Cmd_Stroke: CmdStroke stroke = Cmd_Stroke_read(cmd_ref); float df = 1e9; SegChunkRef seg_chunk_ref = SegChunkRef(stroke.seg_ref); do { SegChunk seg_chunk = SegChunk_read(seg_chunk_ref); for (int i = 0; i < seg_chunk.n; i++) { Segment seg = Segment_read(SegmentRef(seg_chunk_ref.offset + SegChunk_size + Segment_size * i)); vec2 line_vec = seg.end - seg.start; vec2 dpos = xy + vec2(0.5, 0.5) - seg.start; float t = clamp(dot(line_vec, dpos) / dot(line_vec, line_vec), 0.0, 1.0); df = min(df, length(line_vec * t - dpos)); } seg_chunk_ref = seg_chunk.next; } while (seg_chunk_ref.offset != 0); fg_rgba = unpackUnorm4x8(stroke.rgba_color).wzyx; alpha = clamp(stroke.half_width + 0.5 - df, 0.0, 1.0); rgb = mix(rgb, fg_rgba.rgb, alpha * fg_rgba.a); break; case Cmd_Fill: CmdFill fill = Cmd_Fill_read(cmd_ref); // Probably better to store as float, but conversion is no doubt cheap. float area = float(fill.backdrop); FillSegChunkRef fill_seg_chunk_ref = FillSegChunkRef(fill.seg_ref); do { FillSegChunk seg_chunk = FillSegChunk_read(fill_seg_chunk_ref); for (int i = 0; i < seg_chunk.n; i++) { FillSegment seg = FillSegment_read(FillSegmentRef(fill_seg_chunk_ref.offset + FillSegChunk_size + FillSegment_size * i)); vec2 start = seg.start - xy; vec2 end = seg.end - 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 += a * (window.x - window.y); } } fill_seg_chunk_ref = seg_chunk.next; } while (fill_seg_chunk_ref.offset != 0); fg_rgba = unpackUnorm4x8(fill.rgba_color).wzyx; alpha = min(abs(area), 1.0); rgb = mix(rgb, fg_rgba.rgb, alpha * fg_rgba.a); break; case Cmd_Solid: CmdSolid solid = Cmd_Solid_read(cmd_ref); fg_rgba = unpackUnorm4x8(solid.rgba_color).wzyx; rgb = mix(rgb, fg_rgba.rgb, fg_rgba.a); break; case Cmd_Jump: cmd_ref = CmdRef(Cmd_Jump_read(cmd_ref).new_ref); continue; } cmd_ref.offset += Cmd_size; } imageStore(image, ivec2(xy_uint), vec4(rgb, 1.0)); }