github.com/cybriq/giocore@v0.0.7-0.20210703034601-cfb9cb5f3900/gpu/shaders/kernel4.comp (about) 1 // SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense 2 3 // This is "kernel 4" in a 4-kernel pipeline. It renders the commands 4 // in the per-tile command list to an image. 5 6 // Right now, this kernel stores the image in a buffer, but a better 7 // plan is to use a texture. This is because of limited support. 8 9 #version 450 10 #extension GL_GOOGLE_include_directive : enable 11 #ifdef ENABLE_IMAGE_INDICES 12 #extension GL_EXT_nonuniform_qualifier : enable 13 #endif 14 15 #include "mem.h" 16 #include "setup.h" 17 18 #define CHUNK_X 2 19 #define CHUNK_Y 4 20 #define CHUNK CHUNK_X * CHUNK_Y 21 #define CHUNK_DX (TILE_WIDTH_PX / CHUNK_X) 22 #define CHUNK_DY (TILE_HEIGHT_PX / CHUNK_Y) 23 layout(local_size_x = CHUNK_DX, local_size_y = CHUNK_DY) in; 24 25 layout(set = 0, binding = 1) restrict readonly buffer ConfigBuf { 26 Config conf; 27 }; 28 29 layout(rgba8, set = 0, binding = 2) uniform restrict writeonly image2D image; 30 31 #ifdef ENABLE_IMAGE_INDICES 32 layout(rgba8, set = 0, binding = 3) uniform restrict readonly image2D images[]; 33 #else 34 layout(rgba8, set = 0, binding = 3) uniform restrict readonly image2D images[1]; 35 #endif 36 37 #include "ptcl.h" 38 #include "tile.h" 39 40 mediump vec3 tosRGB(mediump vec3 rgb) { 41 bvec3 cutoff = greaterThanEqual(rgb, vec3(0.0031308)); 42 mediump vec3 below = vec3(12.92)*rgb; 43 mediump vec3 above = vec3(1.055)*pow(rgb, vec3(0.41666)) - vec3(0.055); 44 return mix(below, above, cutoff); 45 } 46 47 mediump vec3 fromsRGB(mediump vec3 srgb) { 48 // Formula from EXT_sRGB. 49 bvec3 cutoff = greaterThanEqual(srgb, vec3(0.04045)); 50 mediump vec3 below = srgb/vec3(12.92); 51 mediump vec3 above = pow((srgb + vec3(0.055))/vec3(1.055), vec3(2.4)); 52 return mix(below, above, cutoff); 53 } 54 55 // unpacksRGB unpacks a color in the sRGB color space to a vec4 in the linear color 56 // space. 57 mediump vec4 unpacksRGB(uint srgba) { 58 mediump vec4 color = unpackUnorm4x8(srgba).wzyx; 59 return vec4(fromsRGB(color.rgb), color.a); 60 } 61 62 // packsRGB packs a color in the linear color space into its 8-bit sRGB equivalent. 63 uint packsRGB(mediump vec4 rgba) { 64 rgba = vec4(tosRGB(rgba.rgb), rgba.a); 65 return packUnorm4x8(rgba.wzyx); 66 } 67 68 uvec2 chunk_offset(uint i) { 69 return uvec2(i % CHUNK_X * CHUNK_DX, i / CHUNK_X * CHUNK_DY); 70 } 71 72 mediump vec4[CHUNK] fillImage(uvec2 xy, CmdImage cmd_img) { 73 mediump vec4 rgba[CHUNK]; 74 for (uint i = 0; i < CHUNK; i++) { 75 ivec2 uv = ivec2(xy + chunk_offset(i)) + cmd_img.offset; 76 mediump vec4 fg_rgba; 77 #ifdef ENABLE_IMAGE_INDICES 78 fg_rgba = imageLoad(images[cmd_img.index], uv); 79 #else 80 fg_rgba = imageLoad(images[0], uv); 81 #endif 82 fg_rgba.rgb = fromsRGB(fg_rgba.rgb); 83 rgba[i] = fg_rgba; 84 } 85 return rgba; 86 } 87 88 void main() { 89 uint tile_ix = gl_WorkGroupID.y * conf.width_in_tiles + gl_WorkGroupID.x; 90 Alloc cmd_alloc = slice_mem(conf.ptcl_alloc, tile_ix * PTCL_INITIAL_ALLOC, PTCL_INITIAL_ALLOC); 91 CmdRef cmd_ref = CmdRef(cmd_alloc.offset); 92 93 // Read scrach space allocation, written first in the command list. 94 Alloc scratch_alloc = alloc_read(cmd_alloc, cmd_ref.offset); 95 cmd_ref.offset += Alloc_size; 96 97 uvec2 xy_uint = uvec2(gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_WorkGroupID.x, gl_LocalInvocationID.y + TILE_HEIGHT_PX * gl_WorkGroupID.y); 98 vec2 xy = vec2(xy_uint); 99 mediump vec4 rgba[CHUNK]; 100 for (uint i = 0; i < CHUNK; i++) { 101 rgba[i] = vec4(0.0); 102 // TODO: remove this debug image support when the actual image method is plumbed. 103 #ifdef DEBUG_IMAGES 104 #ifdef ENABLE_IMAGE_INDICES 105 if (xy_uint.x < 1024 && xy_uint.y < 1024) { 106 rgba[i] = imageLoad(images[gl_WorkGroupID.x / 64], ivec2(xy_uint + chunk_offset(i))/4); 107 } 108 #else 109 if (xy_uint.x < 1024 && xy_uint.y < 1024) { 110 rgb[i] = imageLoad(images[0], ivec2(xy_uint + chunk_offset(i))/4).rgb; 111 } 112 #endif 113 #endif 114 } 115 116 mediump float area[CHUNK]; 117 uint clip_depth = 0; 118 bool mem_ok = mem_error == NO_ERROR; 119 while (mem_ok) { 120 uint tag = Cmd_tag(cmd_alloc, cmd_ref).tag; 121 if (tag == Cmd_End) { 122 break; 123 } 124 switch (tag) { 125 case Cmd_Stroke: 126 // Calculate distance field from all the line segments in this tile. 127 CmdStroke stroke = Cmd_Stroke_read(cmd_alloc, cmd_ref); 128 mediump float df[CHUNK]; 129 for (uint k = 0; k < CHUNK; k++) df[k] = 1e9; 130 TileSegRef tile_seg_ref = TileSegRef(stroke.tile_ref); 131 do { 132 TileSeg seg = TileSeg_read(new_alloc(tile_seg_ref.offset, TileSeg_size, mem_ok), tile_seg_ref); 133 vec2 line_vec = seg.vector; 134 for (uint k = 0; k < CHUNK; k++) { 135 vec2 dpos = xy + vec2(0.5, 0.5) - seg.origin; 136 dpos += vec2(chunk_offset(k)); 137 float t = clamp(dot(line_vec, dpos) / dot(line_vec, line_vec), 0.0, 1.0); 138 df[k] = min(df[k], length(line_vec * t - dpos)); 139 } 140 tile_seg_ref = seg.next; 141 } while (tile_seg_ref.offset != 0); 142 for (uint k = 0; k < CHUNK; k++) { 143 area[k] = clamp(stroke.half_width + 0.5 - df[k], 0.0, 1.0); 144 } 145 cmd_ref.offset += 4 + CmdStroke_size; 146 break; 147 case Cmd_Fill: 148 CmdFill fill = Cmd_Fill_read(cmd_alloc, cmd_ref); 149 for (uint k = 0; k < CHUNK; k++) area[k] = float(fill.backdrop); 150 tile_seg_ref = TileSegRef(fill.tile_ref); 151 // Calculate coverage based on backdrop + coverage of each line segment 152 do { 153 TileSeg seg = TileSeg_read(new_alloc(tile_seg_ref.offset, TileSeg_size, mem_ok), tile_seg_ref); 154 for (uint k = 0; k < CHUNK; k++) { 155 vec2 my_xy = xy + vec2(chunk_offset(k)); 156 vec2 start = seg.origin - my_xy; 157 vec2 end = start + seg.vector; 158 vec2 window = clamp(vec2(start.y, end.y), 0.0, 1.0); 159 if (window.x != window.y) { 160 vec2 t = (window - start.y) / seg.vector.y; 161 vec2 xs = vec2(mix(start.x, end.x, t.x), mix(start.x, end.x, t.y)); 162 float xmin = min(min(xs.x, xs.y), 1.0) - 1e-6; 163 float xmax = max(xs.x, xs.y); 164 float b = min(xmax, 1.0); 165 float c = max(b, 0.0); 166 float d = max(xmin, 0.0); 167 float a = (b + 0.5 * (d * d - c * c) - xmin) / (xmax - xmin); 168 area[k] += a * (window.x - window.y); 169 } 170 area[k] += sign(seg.vector.x) * clamp(my_xy.y - seg.y_edge + 1.0, 0.0, 1.0); 171 } 172 tile_seg_ref = seg.next; 173 } while (tile_seg_ref.offset != 0); 174 for (uint k = 0; k < CHUNK; k++) { 175 area[k] = min(abs(area[k]), 1.0); 176 } 177 cmd_ref.offset += 4 + CmdFill_size; 178 break; 179 case Cmd_Solid: 180 for (uint k = 0; k < CHUNK; k++) { 181 area[k] = 1.0; 182 } 183 cmd_ref.offset += 4; 184 break; 185 case Cmd_Alpha: 186 CmdAlpha alpha = Cmd_Alpha_read(cmd_alloc, cmd_ref); 187 for (uint k = 0; k < CHUNK; k++) { 188 area[k] = alpha.alpha; 189 } 190 cmd_ref.offset += 4 + CmdAlpha_size; 191 break; 192 case Cmd_Color: 193 CmdColor color = Cmd_Color_read(cmd_alloc, cmd_ref); 194 mediump vec4 fg = unpacksRGB(color.rgba_color); 195 for (uint k = 0; k < CHUNK; k++) { 196 mediump vec4 fg_k = fg * area[k]; 197 rgba[k] = rgba[k] * (1.0 - fg_k.a) + fg_k; 198 } 199 cmd_ref.offset += 4 + CmdColor_size; 200 break; 201 case Cmd_Image: 202 CmdImage fill_img = Cmd_Image_read(cmd_alloc, cmd_ref); 203 mediump vec4 img[CHUNK] = fillImage(xy_uint, fill_img); 204 for (uint k = 0; k < CHUNK; k++) { 205 mediump vec4 fg_k = img[k] * area[k]; 206 rgba[k] = rgba[k] * (1.0 - fg_k.a) + fg_k; 207 } 208 cmd_ref.offset += 4 + CmdImage_size; 209 break; 210 case Cmd_BeginClip: 211 uint base_ix = (scratch_alloc.offset >> 2) + CLIP_STATE_SIZE * (clip_depth * TILE_WIDTH_PX * TILE_HEIGHT_PX + 212 gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y); 213 for (uint k = 0; k < CHUNK; k++) { 214 uvec2 offset = chunk_offset(k); 215 uint srgb = packsRGB(vec4(rgba[k])); 216 mediump float alpha = clamp(abs(area[k]), 0.0, 1.0); 217 write_mem(scratch_alloc, base_ix + 0 + CLIP_STATE_SIZE * (offset.x + offset.y * TILE_WIDTH_PX), srgb); 218 write_mem(scratch_alloc, base_ix + 1 + CLIP_STATE_SIZE * (offset.x + offset.y * TILE_WIDTH_PX), floatBitsToUint(alpha)); 219 rgba[k] = vec4(0.0); 220 } 221 clip_depth++; 222 cmd_ref.offset += 4; 223 break; 224 case Cmd_EndClip: 225 clip_depth--; 226 base_ix = (scratch_alloc.offset >> 2) + CLIP_STATE_SIZE * (clip_depth * TILE_WIDTH_PX * TILE_HEIGHT_PX + 227 gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y); 228 for (uint k = 0; k < CHUNK; k++) { 229 uvec2 offset = chunk_offset(k); 230 uint srgb = read_mem(scratch_alloc, base_ix + 0 + CLIP_STATE_SIZE * (offset.x + offset.y * TILE_WIDTH_PX)); 231 uint alpha = read_mem(scratch_alloc, base_ix + 1 + CLIP_STATE_SIZE * (offset.x + offset.y * TILE_WIDTH_PX)); 232 mediump vec4 bg = unpacksRGB(srgb); 233 mediump vec4 fg = rgba[k] * area[k] * uintBitsToFloat(alpha); 234 rgba[k] = bg * (1.0 - fg.a) + fg; 235 } 236 cmd_ref.offset += 4; 237 break; 238 case Cmd_Jump: 239 cmd_ref = CmdRef(Cmd_Jump_read(cmd_alloc, cmd_ref).new_ref); 240 cmd_alloc.offset = cmd_ref.offset; 241 break; 242 } 243 } 244 245 for (uint i = 0; i < CHUNK; i++) { 246 imageStore(image, ivec2(xy_uint + chunk_offset(i)), vec4(tosRGB(rgba[i].rgb), rgba[i].a)); 247 } 248 }