diff --git a/piet-gpu/shader/kernel4.comp b/piet-gpu/shader/kernel4.comp index 1abcc2b..2c068aa 100644 --- a/piet-gpu/shader/kernel4.comp +++ b/piet-gpu/shader/kernel4.comp @@ -6,10 +6,13 @@ #version 450 #extension GL_GOOGLE_include_directive : enable +#extension GL_KHR_shader_subgroup_basic : enable -layout(local_size_x = 16, local_size_y = 16) in; +#define CHUNK 8 +#define CHUNK_DY (16 / CHUNK) +layout(local_size_x = 16, local_size_y = 2) in; -// This should be annotated readonly but infra doesn't support that yet. +// Same concern that this should be readonly as in kernel 3. layout(set = 0, binding = 0) buffer PtclBuf { uint[] ptcl; }; @@ -24,11 +27,14 @@ 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; + uvec2 xy_uint = uvec2(gl_GlobalInvocationID.x, gl_LocalInvocationID.y + TILE_HEIGHT_PX * gl_WorkGroupID.y); 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); + vec3 rgb[CHUNK]; + for (uint i = 0; i < CHUNK; i++) { + rgb[i] = vec3(0.5); + } while (true) { uint tag = Cmd_tag(cmd_ref); @@ -38,15 +44,19 @@ void main() { 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); + for (uint i = 0; i < CHUNK; i++) { + float dy = float(i * CHUNK_DY); + 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); + } break; case Cmd_Stroke: CmdStroke stroke = Cmd_Stroke_read(cmd_ref); - float df = 1e9; + float df[CHUNK]; + for (uint k = 0; k < CHUNK; k++) df[k] = 1e9; SegChunkRef seg_chunk_ref = stroke.seg_ref; do { SegChunk seg_chunk = SegChunk_read(seg_chunk_ref); @@ -54,52 +64,65 @@ void main() { for (int i = 0; i < seg_chunk.n; i++) { Segment seg = Segment_read(Segment_index(segs, 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)); + for (uint k = 0; k < CHUNK; k++) { + vec2 dpos = xy + vec2(0.5, 0.5) - seg.start; + dpos.y += float(k * CHUNK_DY); + float t = clamp(dot(line_vec, dpos) / dot(line_vec, line_vec), 0.0, 1.0); + df[k] = min(df[k], 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); + 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); + } 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); + float area[CHUNK]; + for (uint k = 0; k < CHUNK; k++) area[k] = float(fill.backdrop); SegChunkRef fill_seg_chunk_ref = fill.seg_ref; do { SegChunk seg_chunk = SegChunk_read(fill_seg_chunk_ref); SegmentRef segs = seg_chunk.segs; for (int i = 0; i < seg_chunk.n; i++) { Segment seg = Segment_read(Segment_index(segs, 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); + 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); } - area += sign(end.x - start.x) * clamp(xy.y - seg.y_edge + 1.0, 0.0, 1.0); } 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); + 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); + } 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); + for (uint k = 0; k < CHUNK; k++) { + rgb[k] = mix(rgb[k], fg_rgba.rgb, fg_rgba.a); + } break; case Cmd_Jump: cmd_ref = CmdRef(Cmd_Jump_read(cmd_ref).new_ref); @@ -108,5 +131,8 @@ void main() { cmd_ref.offset += Cmd_size; } - imageStore(image, ivec2(xy_uint), vec4(rgb, 1.0)); + // TODO: sRGB + for (uint i = 0; i < CHUNK; i++) { + imageStore(image, ivec2(xy_uint.x, xy_uint.y + CHUNK_DY * i), vec4(rgb[i], 1.0)); + } } diff --git a/piet-gpu/shader/kernel4.spv b/piet-gpu/shader/kernel4.spv index 8060f1f..5215e2f 100644 Binary files a/piet-gpu/shader/kernel4.spv and b/piet-gpu/shader/kernel4.spv differ