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  }