LCOV - code coverage report
Current view: top level - src/shaders - sampling.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 440 492 89.4 %
Date: 2025-03-29 09:04:10 Functions: 25 26 96.2 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 244 353 69.1 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * This file is part of libplacebo.
       3                 :            :  *
       4                 :            :  * libplacebo is free software; you can redistribute it and/or
       5                 :            :  * modify it under the terms of the GNU Lesser General Public
       6                 :            :  * License as published by the Free Software Foundation; either
       7                 :            :  * version 2.1 of the License, or (at your option) any later version.
       8                 :            :  *
       9                 :            :  * libplacebo is distributed in the hope that it will be useful,
      10                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12                 :            :  * GNU Lesser General Public License for more details.
      13                 :            :  *
      14                 :            :  * You should have received a copy of the GNU Lesser General Public
      15                 :            :  * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
      16                 :            :  */
      17                 :            : 
      18                 :            : #include <math.h>
      19                 :            : #include "shaders.h"
      20                 :            : 
      21                 :            : #include <libplacebo/colorspace.h>
      22                 :            : #include <libplacebo/shaders/sampling.h>
      23                 :            : 
      24                 :            : const struct pl_deband_params pl_deband_default_params = { PL_DEBAND_DEFAULTS };
      25                 :            : 
      26                 :            : static inline struct pl_tex_params src_params(const struct pl_sample_src *src)
      27                 :            : {
      28   [ +  +  +  + ]:        272 :     if (src->tex)
      29                 :        270 :         return src->tex->params;
      30                 :            : 
      31                 :          2 :     return (struct pl_tex_params) {
      32                 :          1 :         .w = src->tex_w,
      33                 :          1 :         .h = src->tex_h,
      34                 :            :     };
      35                 :            : }
      36                 :            : 
      37                 :            : enum filter {
      38                 :            :     NEAREST = PL_TEX_SAMPLE_NEAREST,
      39                 :            :     LINEAR  = PL_TEX_SAMPLE_LINEAR,
      40                 :            :     BEST,
      41                 :            :     FASTEST,
      42                 :            : };
      43                 :            : 
      44                 :            : // Helper function to compute the src/dst sizes and upscaling ratios
      45                 :       1235 : static bool setup_src(pl_shader sh, const struct pl_sample_src *src,
      46                 :            :                       ident_t *src_tex, ident_t *pos, ident_t *pt,
      47                 :            :                       float *ratio_x, float *ratio_y, uint8_t *comp_mask,
      48                 :            :                       float *scale, bool resizeable,
      49                 :            :                       enum filter filter)
      50                 :            : {
      51                 :            :     enum pl_shader_sig sig;
      52                 :            :     float src_w, src_h;
      53                 :            :     enum pl_tex_sample_mode sample_mode;
      54         [ +  + ]:       1235 :     if (src->tex) {
      55                 :       1234 :         pl_fmt fmt = src->tex->params.format;
      56         [ +  - ]:       1234 :         bool can_linear = fmt->caps & PL_FMT_CAP_LINEAR;
      57                 :          0 :         pl_assert(pl_tex_params_dimension(src->tex->params) == 2);
      58                 :            :         sig = PL_SHADER_SIG_NONE;
      59                 :       1234 :         src_w = pl_rect_w(src->rect);
      60                 :       1234 :         src_h = pl_rect_h(src->rect);
      61   [ +  +  +  - ]:       1234 :         switch (filter) {
      62                 :        175 :         case FASTEST:
      63                 :            :         case NEAREST:
      64                 :            :             sample_mode = PL_TEX_SAMPLE_NEAREST;
      65                 :        175 :             break;
      66                 :        228 :         case LINEAR:
      67         [ -  + ]:        228 :             if (!can_linear) {
      68                 :          0 :                 SH_FAIL(sh, "Trying to use a shader that requires linear "
      69                 :            :                         "sampling with a texture whose format (%s) does not "
      70                 :            :                         "support PL_FMT_CAP_LINEAR", fmt->name);
      71                 :          0 :                 return false;
      72                 :            :             }
      73                 :            :             sample_mode = PL_TEX_SAMPLE_LINEAR;
      74                 :            :             break;
      75                 :        831 :         case BEST:
      76                 :        831 :             sample_mode = can_linear ? PL_TEX_SAMPLE_LINEAR : PL_TEX_SAMPLE_NEAREST;
      77                 :        831 :             break;
      78                 :            :         }
      79                 :            :     } else {
      80   [ +  -  -  + ]:          1 :         pl_assert(src->tex_w && src->tex_h);
      81                 :            :         sig = PL_SHADER_SIG_SAMPLER;
      82                 :          1 :         src_w = src->sampled_w;
      83                 :          1 :         src_h = src->sampled_h;
      84         [ +  - ]:          1 :         if (filter == BEST || filter == FASTEST) {
      85                 :          1 :             sample_mode = src->mode;
      86                 :            :         } else {
      87                 :            :             sample_mode = (enum pl_tex_sample_mode) filter;
      88         [ #  # ]:          0 :             if (sample_mode != src->mode) {
      89                 :          0 :                 SH_FAIL(sh, "Trying to use a shader that requires a different "
      90                 :            :                         "filter mode than the external sampler.");
      91                 :          0 :                 return false;
      92                 :            :             }
      93                 :            :         }
      94                 :            :     }
      95                 :            : 
      96         [ +  + ]:       1371 :     src_w = PL_DEF(src_w, src_params(src).w);
      97         [ +  + ]:       1371 :     src_h = PL_DEF(src_h, src_params(src).h);
      98         [ -  + ]:       1235 :     pl_assert(src_w && src_h);
      99                 :            : 
     100         [ +  + ]:       1235 :     int out_w = PL_DEF(src->new_w, roundf(fabs(src_w)));
     101         [ +  + ]:       1235 :     int out_h = PL_DEF(src->new_h, roundf(fabs(src_h)));
     102         [ -  + ]:       1235 :     pl_assert(out_w && out_h);
     103                 :            : 
     104         [ +  + ]:       1235 :     if (ratio_x)
     105                 :        306 :         *ratio_x = out_w / fabs(src_w);
     106         [ +  + ]:       1235 :     if (ratio_y)
     107                 :        306 :         *ratio_y = out_h / fabs(src_h);
     108         [ +  - ]:       1235 :     if (scale)
     109         [ +  + ]:       2135 :         *scale = PL_DEF(src->scale, 1.0);
     110                 :            : 
     111         [ +  + ]:       1235 :     if (comp_mask) {
     112                 :            :         uint8_t tex_mask = 0x0Fu;
     113         [ +  + ]:        322 :         if (src->tex) {
     114                 :            :             // Mask containing only the number of components in the texture
     115                 :        321 :             tex_mask = (1 << src->tex->params.format->num_components) - 1;
     116                 :            :         }
     117                 :            : 
     118                 :        322 :         uint8_t src_mask = src->component_mask;
     119         [ +  - ]:        322 :         if (!src_mask)
     120         [ +  + ]:        322 :             src_mask = (1 << PL_DEF(src->components, 4)) - 1;
     121                 :            : 
     122                 :            :         // Only actually sample components that are both requested and
     123                 :            :         // available in the texture being sampled
     124                 :        322 :         *comp_mask = tex_mask & src_mask;
     125                 :            :     }
     126                 :            : 
     127         [ +  + ]:       1235 :     if (resizeable)
     128                 :            :         out_w = out_h = 0;
     129         [ -  + ]:       1235 :     if (!sh_require(sh, sig, out_w, out_h))
     130                 :            :         return false;
     131                 :            : 
     132         [ +  + ]:       1235 :     if (src->tex) {
     133                 :       1234 :         pl_rect2df rect = {
     134                 :       1234 :             .x0 = src->rect.x0,
     135                 :       1234 :             .y0 = src->rect.y0,
     136                 :       1234 :             .x1 = src->rect.x0 + src_w,
     137                 :       1234 :             .y1 = src->rect.y0 + src_h,
     138                 :            :         };
     139                 :            : 
     140                 :       1234 :         *src_tex = sh_bind(sh, src->tex, src->address_mode, sample_mode,
     141                 :            :                            "src_tex", &rect, pos, pt);
     142                 :            :     } else {
     143         [ +  - ]:          1 :         if (pt) {
     144                 :          1 :             float sx = 1.0 / src->tex_w, sy = 1.0 / src->tex_h;
     145         [ -  + ]:          1 :             if (src->sampler == PL_SAMPLER_RECT)
     146                 :            :                 sx = sy = 1.0;
     147                 :            : 
     148                 :          1 :             *pt = sh_var(sh, (struct pl_shader_var) {
     149                 :          1 :                 .var = pl_var_vec2("tex_pt"),
     150                 :          1 :                 .data = &(float[2]) { sx, sy },
     151                 :            :             });
     152                 :            :         }
     153                 :            : 
     154                 :          1 :         sh->sampler_type = src->sampler;
     155                 :            : 
     156         [ -  + ]:          1 :         pl_assert(src->format);
     157   [ +  -  -  -  :          1 :         switch (src->format) {
                      - ]
     158                 :          1 :         case PL_FMT_UNKNOWN:
     159                 :            :         case PL_FMT_FLOAT:
     160                 :            :         case PL_FMT_UNORM:
     161                 :          1 :         case PL_FMT_SNORM: sh->sampler_prefix = ' '; break;
     162                 :          0 :         case PL_FMT_UINT: sh->sampler_prefix = 'u'; break;
     163                 :          0 :         case PL_FMT_SINT: sh->sampler_prefix = 's'; break;
     164                 :            :         case PL_FMT_TYPE_COUNT:
     165                 :          0 :             pl_unreachable();
     166                 :            :         }
     167                 :            : 
     168                 :          1 :         *src_tex = sh_fresh(sh, "src_tex");
     169                 :          1 :         *pos     = sh_fresh(sh, "pos");
     170                 :            : 
     171                 :          1 :         GLSLH("#define "$" src_tex  \n"
     172                 :            :               "#define "$" pos      \n",
     173                 :            :               *src_tex, *pos);
     174                 :            :     }
     175                 :            : 
     176                 :            :     return true;
     177                 :            : }
     178                 :            : 
     179                 :         36 : void pl_shader_deband(pl_shader sh, const struct pl_sample_src *src,
     180                 :            :                       const struct pl_deband_params *params)
     181                 :            : {
     182                 :            :     float scale;
     183                 :            :     ident_t tex, pos, pt;
     184                 :            :     uint8_t mask;
     185         [ +  - ]:         36 :     if (!setup_src(sh, src, &tex, &pos, &pt, NULL, NULL, &mask, &scale, false, NEAREST))
     186                 :          0 :         return;
     187                 :            : 
     188         [ -  + ]:         36 :     params = PL_DEF(params, &pl_deband_default_params);
     189                 :         36 :     sh_describe(sh, "debanding");
     190                 :         36 :     GLSL("vec4 color;                       \n"
     191                 :            :          "// pl_shader_deband               \n"
     192                 :            :          "{                                 \n"
     193                 :            :          "vec2 pos = "$", pt = "$";         \n"
     194                 :            :          "color = textureLod("$", pos, 0.0);\n",
     195                 :            :          pos, pt, tex);
     196                 :            : 
     197         [ -  + ]:         36 :     mask &= ~0x8u; // ignore alpha channel
     198                 :            :     uint8_t num_comps = sh_num_comps(mask);
     199                 :            :     const char *swiz = sh_swizzle(mask);
     200                 :            :     pl_assert(num_comps <= 3);
     201         [ -  + ]:         36 :     if (!num_comps) {
     202                 :          0 :         GLSL("color *= "$"; \n"
     203                 :            :              "}             \n",
     204                 :            :              SH_FLOAT(scale));
     205                 :          0 :         return;
     206                 :            :     }
     207                 :            : 
     208                 :         36 :     GLSL("#define GET(X, Y)                                   \\\n"
     209                 :            :          "    (textureLod("$", pos + pt * vec2(X, Y), 0.0).%s)  \n"
     210                 :            :          "#define T %s                                          \n",
     211                 :            :          tex, swiz, sh_float_type(mask));
     212                 :            : 
     213                 :         36 :     ident_t prng = sh_prng(sh, true, NULL);
     214                 :         36 :     GLSL("T avg, diff, bound;   \n"
     215                 :            :          "T res = color.%s;     \n"
     216                 :            :          "vec2 d;               \n",
     217                 :            :          swiz);
     218                 :            : 
     219         [ +  + ]:         36 :     if (params->iterations > 0) {
     220                 :         12 :         ident_t radius = sh_const_float(sh, "radius", params->radius);
     221                 :         12 :         ident_t threshold = sh_const_float(sh, "threshold",
     222                 :         12 :                                            params->threshold / (1000 * scale));
     223                 :            : 
     224                 :            :         // For each iteration, compute the average at a given distance and
     225                 :            :         // pick it instead of the color if the difference is below the threshold.
     226         [ +  + ]:         36 :         for (int i = 1; i <= params->iterations; i++) {
     227                 :         24 :             GLSL(// Compute a random angle and distance
     228                 :            :                  "d = "$".xy * vec2(%d.0 * "$", %f);    \n"
     229                 :            :                  "d = d.x * vec2(cos(d.y), sin(d.y));   \n"
     230                 :            :                  // Sample at quarter-turn intervals around the source pixel
     231                 :            :                  "avg = T(0.0);                         \n"
     232                 :            :                  "avg += GET(+d.x, +d.y);               \n"
     233                 :            :                  "avg += GET(-d.x, +d.y);               \n"
     234                 :            :                  "avg += GET(-d.x, -d.y);               \n"
     235                 :            :                  "avg += GET(+d.x, -d.y);               \n"
     236                 :            :                  "avg *= 0.25;                          \n"
     237                 :            :                  // Compare the (normalized) average against the pixel
     238                 :            :                  "diff = abs(res - avg);                \n"
     239                 :            :                  "bound = T("$" / %d.0);                \n",
     240                 :            :                  prng, i, radius, M_PI * 2,
     241                 :            :                  threshold, i);
     242                 :            : 
     243         [ -  + ]:         24 :             if (num_comps > 1) {
     244                 :          0 :                 GLSL("res = mix(avg, res, greaterThan(diff, bound)); \n");
     245                 :            :             } else {
     246                 :         24 :                 GLSL("res = mix(avg, res, diff > bound); \n");
     247                 :            :             }
     248                 :            :         }
     249                 :            :     }
     250                 :            : 
     251                 :            :     // Add some random noise to smooth out residual differences
     252         [ +  + ]:         36 :     if (params->grain > 0) {
     253                 :            :         // Avoid adding grain near true black
     254                 :         16 :         GLSL("bound = T(\n");
     255         [ +  + ]:         32 :         for (int c = 0; c < num_comps; c++) {
     256         [ +  - ]:         32 :             GLSL("%c"$, c > 0 ? ',' : ' ',
     257                 :            :                  SH_FLOAT(params->grain_neutral[c] / scale));
     258                 :            :         }
     259                 :         16 :         GLSL(");                                        \n"
     260                 :            :              "T strength = min(abs(res - bound), "$");  \n"
     261                 :            :              "res += strength * (T("$") - T(0.5));      \n",
     262                 :            :              SH_FLOAT(params->grain / (1000.0 * scale)), prng);
     263                 :            :     }
     264                 :            : 
     265                 :         36 :     GLSL("color.%s = res;   \n"
     266                 :            :          "color *= "$";     \n"
     267                 :            :          "#undef T          \n"
     268                 :            :          "#undef GET        \n"
     269                 :            :          "}                 \n",
     270                 :            :          swiz, SH_FLOAT(scale));
     271                 :            : }
     272                 :            : 
     273                 :        831 : bool pl_shader_sample_direct(pl_shader sh, const struct pl_sample_src *src)
     274                 :            : {
     275                 :            :     float scale;
     276                 :            :     ident_t tex, pos;
     277         [ +  - ]:        831 :     if (!setup_src(sh, src, &tex, &pos, NULL, NULL, NULL, NULL, &scale, true, BEST))
     278                 :            :         return false;
     279                 :            : 
     280                 :        831 :     GLSL("// pl_shader_sample_direct                            \n"
     281                 :            :          "vec4 color = vec4("$") * textureLod("$", "$", 0.0);   \n",
     282                 :            :          SH_FLOAT(scale), tex, pos);
     283                 :        831 :     return true;
     284                 :            : }
     285                 :            : 
     286                 :         62 : bool pl_shader_sample_nearest(pl_shader sh, const struct pl_sample_src *src)
     287                 :            : {
     288                 :            :     float scale;
     289                 :            :     ident_t tex, pos;
     290         [ +  - ]:         62 :     if (!setup_src(sh, src, &tex, &pos,  NULL, NULL, NULL, NULL, &scale, true, NEAREST))
     291                 :            :         return false;
     292                 :            : 
     293                 :         62 :     sh_describe(sh, "nearest");
     294                 :         62 :     GLSL("// pl_shader_sample_nearest                           \n"
     295                 :            :          "vec4 color = vec4("$") * textureLod("$", "$", 0.0);   \n",
     296                 :            :          SH_FLOAT(scale), tex, pos);
     297                 :         62 :     return true;
     298                 :            : }
     299                 :            : 
     300                 :          0 : bool pl_shader_sample_bilinear(pl_shader sh, const struct pl_sample_src *src)
     301                 :            : {
     302                 :            :     float scale;
     303                 :            :     ident_t tex, pos;
     304         [ #  # ]:          0 :     if (!setup_src(sh, src, &tex, &pos, NULL, NULL, NULL, NULL, &scale, true, LINEAR))
     305                 :            :         return false;
     306                 :            : 
     307                 :          0 :     sh_describe(sh, "bilinear");
     308                 :          0 :     GLSL("// pl_shader_sample_bilinear                          \n"
     309                 :            :          "vec4 color = vec4("$") * textureLod("$", "$", 0.0);   \n",
     310                 :            :          SH_FLOAT(scale), tex, pos);
     311                 :          0 :     return true;
     312                 :            : }
     313                 :            : 
     314                 :          4 : bool pl_shader_sample_bicubic(pl_shader sh, const struct pl_sample_src *src)
     315                 :            : {
     316                 :            :     ident_t tex, pos, pt;
     317                 :            :     float rx, ry, scale;
     318         [ +  - ]:          4 :     if (!setup_src(sh, src, &tex, &pos, &pt, &rx, &ry, NULL, &scale, true, LINEAR))
     319                 :            :         return false;
     320                 :            : 
     321   [ +  -  -  + ]:          4 :     if (rx < 1 || ry < 1) {
     322                 :          0 :         PL_TRACE(sh, "Using fast bicubic sampling when downscaling. This "
     323                 :            :                  "will most likely result in nasty aliasing!");
     324                 :            :     }
     325                 :            : 
     326                 :            :     // Explanation of how bicubic scaling with only 4 texel fetches is done:
     327                 :            :     //   http://www.mate.tue.nl/mate/pdfs/10318.pdf
     328                 :            :     //   'Efficient GPU-Based Texture Interpolation using Uniform B-Splines'
     329                 :            : 
     330                 :          4 :     sh_describe(sh, "bicubic");
     331                 :          4 : {
     332                 :          4 :     const struct __attribute__((__packed__)) {
     333                 :            :     ident_t pos;
     334                 :            :     ident_t tex;
     335                 :            :     ident_t pt;
     336                 :            :     ident_t scale;
     337                 :          4 : } _glsl_331_args = {
     338                 :            : #line 334
     339                 :            :         .pos = pos,
     340                 :            : #line 335
     341                 :            :         .tex = tex,
     342                 :            : #line 349
     343                 :            :         .pt = pt,
     344                 :            : #line 356
     345                 :            :         .scale = sh_const_float(sh, "scale", scale),
     346                 :            :     };
     347                 :            : #line 331
     348                 :            :     size_t _glsl_331_fn(void *, pl_str *, const uint8_t *);
     349                 :            :     pl_str_builder_append(sh->buffers[SH_BUF_BODY], _glsl_331_fn,
     350                 :            :                           &_glsl_331_args, sizeof(_glsl_331_args));
     351                 :            : }
     352                 :            : #line 358
     353                 :            : 
     354                 :            :     return true;
     355                 :            : }
     356                 :          4 : 
     357                 :            : bool pl_shader_sample_hermite(pl_shader sh, const struct pl_sample_src *src)
     358                 :            : {
     359                 :          4 :     ident_t tex, pos, pt;
     360                 :            :     float rx, ry, scale;
     361                 :            :     if (!setup_src(sh, src, &tex, &pos, &pt, &rx, &ry, NULL, &scale, true, LINEAR))
     362                 :          4 :         return false;
     363                 :            : 
     364                 :            :     if (rx < 1 || ry < 1) {
     365                 :            :         PL_TRACE(sh, "Using fast hermite sampling when downscaling. This "
     366         [ +  - ]:          4 :                  "will most likely result in nasty aliasing!");
     367                 :            :     }
     368                 :            : 
     369   [ +  -  -  + ]:          4 :     sh_describe(sh, "hermite");
     370                 :          0 : {
     371                 :            :     const struct __attribute__((__packed__)) {
     372                 :            :     ident_t pos;
     373                 :            :     ident_t tex;
     374                 :          4 :     ident_t pt;
     375                 :          4 :     ident_t scale;
     376                 :          4 : } _glsl_375_args = {
     377                 :            : #line 378
     378                 :            :         .pos = pos,
     379                 :            : #line 379
     380                 :            :         .tex = tex,
     381                 :          4 : #line 381
     382                 :          4 :         .pt = pt,
     383                 :            : #line 382
     384                 :            :         .scale = sh_const_float(sh, "scale", scale),
     385                 :          4 :     };
     386                 :            : #line 375
     387                 :            :     size_t _glsl_375_fn(void *, pl_str *, const uint8_t *);
     388                 :          4 :     pl_str_builder_append(sh->buffers[SH_BUF_BODY], _glsl_375_fn,
     389                 :            :                           &_glsl_375_args, sizeof(_glsl_375_args));
     390                 :            : }
     391                 :            : #line 384
     392         [ +  - ]:          4 : 
     393                 :          4 :     return true;
     394                 :            : }
     395   [ +  -  -  + ]:          4 : 
     396                 :          0 : bool pl_shader_sample_gaussian(pl_shader sh, const struct pl_sample_src *src)
     397                 :            : {
     398                 :            :     ident_t tex, pos, pt;
     399                 :            :     float rx, ry, scale;
     400                 :          4 :     if (!setup_src(sh, src, &tex, &pos, &pt, &rx, &ry, NULL, &scale, true, LINEAR))
     401                 :          4 :         return false;
     402                 :          4 : 
     403                 :            :     if (rx < 1 || ry < 1) {
     404                 :            :         PL_TRACE(sh, "Using fast gaussian sampling when downscaling. This "
     405                 :            :                  "will most likely result in nasty aliasing!");
     406                 :            :     }
     407                 :          4 : 
     408                 :            :     sh_describe(sh, "gaussian");
     409                 :            : {
     410                 :            :     const struct __attribute__((__packed__)) {
     411                 :            :     ident_t pos;
     412                 :            :     ident_t tex;
     413                 :            :     ident_t pt;
     414                 :            :     ident_t scale;
     415                 :            : } _glsl_401_args = {
     416                 :            : #line 404
     417                 :            :         .pos = pos,
     418                 :            : #line 405
     419                 :            :         .tex = tex,
     420                 :            : #line 419
     421                 :            :         .pt = pt,
     422                 :            : #line 426
     423                 :            :         .scale = sh_const_float(sh, "scale", scale),
     424                 :            :     };
     425                 :            : #line 401
     426                 :          4 :     size_t _glsl_401_fn(void *, pl_str *, const uint8_t *);
     427                 :            :     pl_str_builder_append(sh->buffers[SH_BUF_BODY], _glsl_401_fn,
     428                 :            :                           &_glsl_401_args, sizeof(_glsl_401_args));
     429                 :          4 : }
     430                 :            : #line 428
     431                 :            : 
     432                 :          8 :     return true;
     433                 :            : }
     434                 :            : 
     435                 :            : bool pl_shader_sample_oversample(pl_shader sh, const struct pl_sample_src *src,
     436                 :            :                                  float threshold)
     437         [ +  - ]:          8 : {
     438                 :            :     ident_t tex, pos, pt;
     439                 :            :     float rx, ry, scale;
     440   [ +  -  +  - ]:          8 :     if (!setup_src(sh, src, &tex, &pos, &pt, &rx, &ry, NULL, &scale, true, LINEAR))
     441                 :          8 :         return false;
     442                 :          8 : 
     443                 :          8 :     threshold = PL_CLAMP(threshold, 0.0f, 0.5f);
     444                 :            :     sh_describe(sh, "oversample");
     445                 :            : {
     446                 :            :     const struct __attribute__((__packed__)) {
     447                 :            :     ident_t pos;
     448                 :            :     ident_t tex;
     449                 :          8 :     ident_t rx;
     450                 :          8 :     ident_t ry;
     451                 :            :     ident_t threshold;
     452                 :          8 :     ident_t pt;
     453                 :          8 :     ident_t scale;
     454                 :          8 :     bool threshold_0;
     455                 :          8 : } _glsl_442_args = {
     456                 :          8 : #line 445
     457                 :          8 :         .pos = pos,
     458                 :            : #line 446
     459                 :            :         .tex = tex,
     460         [ -  + ]:          8 : #line 449
     461                 :          8 :         .rx = sh_var_float(sh, "rx", rx, true),
     462                 :            : #line 450
     463                 :          8 :         .ry = sh_var_float(sh, "ry", ry, true),
     464                 :            : #line 454
     465                 :            :         .threshold = sh_const_float(sh, "threshold", threshold),
     466                 :          8 : #line 462
     467                 :          8 :         .pt = pt,
     468                 :            : #line 463
     469                 :        286 :         .scale = sh_const_float(sh, "scale", scale),
     470                 :            : #line 453
     471                 :            :         .threshold_0 = threshold > 0,
     472                 :          8 :     };
     473   [ +  +  -  + ]:        286 : #line 442
     474                 :            :     size_t _glsl_442_fn(void *, pl_str *, const uint8_t *);
     475   [ +  -  -  + ]:        164 :     pl_str_builder_append(sh->buffers[SH_BUF_BODY], _glsl_442_fn,
     476                 :            :                           &_glsl_442_args, sizeof(_glsl_442_args));
     477         [ #  # ]:          0 : }
     478                 :            : #line 465
     479                 :            : 
     480                 :            :     return true;
     481                 :            : }
     482                 :            : 
     483         [ +  - ]:        286 : static void describe_filter(pl_shader sh, const struct pl_filter_config *cfg,
     484                 :        286 :                             const char *stage, float rx, float ry)
     485         [ #  # ]:          0 : {
     486                 :          0 :     const char *dir;
     487         [ #  # ]:          0 :     if (rx > 1 && ry > 1) {
     488         [ #  # ]:          0 :         dir = "up";
     489                 :            :     } else if (rx < 1 && ry < 1) {
     490                 :          0 :         dir = "down";
     491         [ #  # ]:          0 :     } else if (rx == 1 && ry == 1) {
     492                 :            :         dir = "noop";
     493                 :        286 :     } else {
     494                 :            :         dir = "ana";
     495                 :            :     }
     496                 :            : 
     497                 :            :     if (cfg->name) {
     498                 :            :         sh_describef(sh, "%s %sscaling (%s)", stage, dir, cfg->name);
     499                 :       8544 :     } else if (cfg->window) {
     500                 :            :         sh_describef(sh, "%s %sscaling (%s+%s)", stage, dir,
     501                 :            :                      PL_DEF(cfg->kernel->name, "unknown"),
     502                 :            :                      PL_DEF(cfg->window->name, "unknown"));
     503                 :            :     } else {
     504                 :            :         sh_describef(sh, "%s %sscaling (%s)", stage, dir,
     505                 :            :                      PL_DEF(cfg->kernel->name, "unknown"));
     506         [ +  + ]:       8544 :     }
     507         [ +  + ]:       8544 : }
     508                 :       8544 : 
     509                 :            : // Subroutine for computing and adding an individual texel contribution
     510         [ +  + ]:       8544 : // If `in` is NULL, samples directly
     511                 :            : // If `in` is set, takes the pixel from inX[idx] where X is the component,
     512                 :            : // `in` is the given identifier, and `idx` must be defined by the caller
     513                 :            : static void polar_sample(pl_shader sh, pl_filter filter,
     514                 :       6648 :                          ident_t tex, ident_t lut, ident_t radius,
     515                 :            :                          int x, int y, uint8_t comp_mask, ident_t in,
     516                 :            :                          bool use_ar, ident_t scale)
     517                 :       6648 : {
     518                 :       6648 :     // Since we can't know the subpixel position in advance, assume a
     519                 :            :     // worst case scenario
     520                 :       5912 :     int yy = y > 0 ? y-1 : y;
     521                 :       5912 :     int xx = x > 0 ? x-1 : x;
     522                 :            :     float dmin = sqrt(xx*xx + yy*yy);
     523                 :            :     // Skip samples definitely outside the radius
     524                 :       2728 :     if (dmin >= filter->radius)
     525                 :       5912 :         return;
     526                 :       2728 : 
     527                 :       6648 :     // Check for samples that might be skippable
     528         [ +  + ]:       5912 :     bool maybe_skippable = dmin >= filter->radius - M_SQRT2;
     529                 :       5912 : 
     530                 :            :     // Check for samples that definitely won't contribute to anti-ringing
     531                 :       3412 :     const float ar_radius = filter->radius_zero;
     532         [ +  + ]:       5912 :     use_ar &= dmin < ar_radius;
     533         [ +  + ]:      13324 : 
     534                 :       6648 : {
     535                 :            :     const struct __attribute__((__packed__)) {
     536                 :        144 :     int x;
     537         [ +  + ]:      23384 :     int y;
     538                 :        144 :     float ar_radius;
     539                 :            :     ident_t radius;
     540         [ +  + ]:       5912 :     ident_t lut;
     541         [ +  + ]:        576 :     ident_t in;
     542                 :            :     ident_t tex;
     543                 :            :     ident_t scale;
     544                 :            :     bool maybe_skippable;
     545                 :            :     bool in_null_ident;
     546                 :            :     uint8_t comp_mask;
     547                 :            :     bool use_ar;
     548                 :            : } _glsl_520_args = {
     549                 :            : #line 521
     550                 :        576 :         .x = x,
     551                 :            : #line 521
     552                 :            :         .y = y,
     553                 :       2728 : #line 536
     554                 :            :         .ar_radius = ar_radius,
     555         [ +  + ]:       5912 : #line 524
     556                 :            :         .radius = radius,
     557                 :            : #line 525
     558                 :       5912 :         .lut = lut,
     559                 :            : #line 529
     560                 :            :         .in = in,
     561                 :            : #line 531
     562                 :            :         .tex = tex,
     563                 :            : #line 538
     564                 :            :         .scale = scale,
     565                 :         21 : #line 523
     566                 :            :         .maybe_skippable = maybe_skippable,
     567                 :            : #line 527
     568                 :         21 :         .in_null_ident = in != NULL_IDENT,
     569                 :         21 : #line 528
     570                 :         21 :         .comp_mask = comp_mask,
     571                 :         21 : #line 535
     572                 :         21 :         .use_ar = use_ar,
     573                 :            :     };
     574                 :         77 : #line 520
     575                 :            :     size_t _glsl_520_fn(void *, pl_str *, const uint8_t *);
     576                 :         77 :     pl_str_builder_append(sh->buffers[SH_BUF_BODY], _glsl_520_fn,
     577                 :         77 :                           &_glsl_520_args, sizeof(_glsl_520_args));
     578                 :            : }
     579   [ +  -  -  + ]:         77 : #line 554
     580                 :         77 : }
     581                 :         77 : 
     582                 :            : struct sh_sampler_obj {
     583                 :         78 :     pl_filter filter;
     584                 :            :     pl_shader_obj lut;
     585                 :            :     pl_shader_obj pass2; // for pl_shader_sample_ortho
     586         [ -  + ]:         78 : };
     587         [ -  + ]:         78 : 
     588                 :          0 : #define SCALER_LUT_SIZE     256
     589                 :          0 : #define SCALER_LUT_CUTOFF   1e-3f
     590                 :            : 
     591                 :            : static void sh_sampler_uninit(pl_gpu gpu, void *ptr)
     592                 :            : {
     593                 :            :     struct sh_sampler_obj *obj = ptr;
     594                 :            :     pl_shader_obj_destroy(&obj->lut);
     595         [ -  + ]:         78 :     pl_shader_obj_destroy(&obj->pass2);
     596                 :            :     pl_filter_free(&obj->filter);
     597                 :            :     *obj = (struct sh_sampler_obj) {0};
     598                 :            : }
     599                 :         78 : 
     600                 :            : static void fill_polar_lut(void *data, const struct sh_lut_params *params)
     601         [ -  + ]:         78 : {
     602                 :            :     const struct sh_sampler_obj *obj = params->priv;
     603                 :            :     pl_filter filt = obj->filter;
     604         [ +  - ]:         78 : 
     605         [ +  + ]:         78 :     pl_assert(params->width == filt->params.lut_entries && params->comps == 1);
     606         [ -  + ]:         78 :     memcpy(data, filt->weights, params->width * sizeof(float));
     607                 :            : }
     608                 :         78 : 
     609                 :            : bool pl_shader_sample_polar(pl_shader sh, const struct pl_sample_src *src,
     610                 :         78 :                             const struct pl_sample_filter_params *params)
     611         [ +  + ]:         78 : {
     612         [ +  + ]:         78 :     pl_assert(params);
     613   [ +  +  +  + ]:         78 :     if (!params->filter.polar) {
     614                 :            :         SH_FAIL(sh, "Trying to use polar sampling with a non-polar filter?");
     615                 :         77 :         return false;
     616                 :         77 :     }
     617                 :            : 
     618                 :            :     uint8_t cmask;
     619                 :            :     float rx, ry, scalef;
     620                 :            :     ident_t src_tex, pos, pt, scale;
     621                 :            :     if (!setup_src(sh, src, &src_tex, &pos, &pt, &rx, &ry, &cmask, &scalef, false, FASTEST))
     622         [ -  + ]:         77 :         return false;
     623                 :            : 
     624                 :          0 :     struct sh_sampler_obj *obj;
     625                 :          0 :     obj = SH_OBJ(sh, params->lut, PL_SHADER_OBJ_SAMPLER, struct sh_sampler_obj,
     626                 :            :                  sh_sampler_uninit);
     627                 :            :     if (!obj)
     628                 :            :         return false;
     629                 :         78 : 
     630                 :         78 :     float inv_scale = 1.0 / PL_MIN(rx, ry);
     631                 :            :     inv_scale = PL_MAX(inv_scale, 1.0);
     632                 :            :     if (params->no_widening)
     633                 :            :         inv_scale = 1.0;
     634                 :            :     scale = sh_const_float(sh, "scale", scalef);
     635                 :            : 
     636                 :            :     struct pl_filter_config cfg = params->filter;
     637                 :            :     cfg.antiring = PL_DEF(cfg.antiring, params->antiring);
     638                 :            :     cfg.blur = PL_DEF(cfg.blur, 1.0f) * inv_scale;
     639                 :            :     bool update = !obj->filter || !pl_filter_config_eq(&obj->filter->params.config, &cfg);
     640                 :            :     if (update) {
     641                 :            :         pl_filter_free(&obj->filter);
     642                 :            :         obj->filter = pl_filter_generate(sh->log, pl_filter_params(
     643                 :            :             .config         = cfg,
     644                 :         78 :             .lut_entries    = SCALER_LUT_SIZE,
     645         [ +  + ]:         78 :             .cutoff         = SCALER_LUT_CUTOFF,
     646                 :          8 :         ));
     647                 :          8 : 
     648                 :            :         if (!obj->filter) {
     649                 :          8 :             // This should never happen, but just in case ..
     650                 :            :             SH_FAIL(sh, "Failed initializing polar filter!");
     651         [ +  + ]:         32 :             return false;
     652                 :         78 :         }
     653                 :         78 :     }
     654         [ -  + ]:         78 : 
     655   [ +  -  +  + ]:        156 :     describe_filter(sh, &cfg, "polar", rx, ry);
     656                 :          8 :     GLSL("// pl_shader_sample_polar                     \n"
     657                 :         78 :          "vec4 color = vec4(0.0);                       \n"
     658                 :         78 :          "{                                             \n"
     659                 :         78 :          "vec2 pos = "$", pt = "$";                     \n"
     660                 :            :          "vec2 size = vec2(textureSize("$", 0));        \n"
     661                 :            :          "vec2 fcoord = fract(pos * size - vec2(0.5));  \n"
     662                 :            :          "vec2 base = pos - pt * fcoord;                \n"
     663                 :            :          "vec2 center = base + pt * vec2(0.5);          \n"
     664                 :         78 :          "ivec2 offset;                                 \n"
     665                 :            :          "float w, d, wsum = 0.0;                       \n"
     666                 :            :          "int idx;                                      \n"
     667                 :            :          "vec4 c;                                       \n",
     668                 :            :          pos, pt, src_tex);
     669   [ +  +  -  + ]:         78 : 
     670                 :         78 :     bool use_ar = cfg.antiring > 0;
     671                 :            :     if (use_ar) {
     672         [ +  + ]:         78 : {
     673                 :            :     const struct __attribute__((__packed__)) {
     674                 :            :     uint8_t cmask;
     675                 :            : } _glsl_646_args = {
     676                 :            : #line 648
     677                 :         26 :         .cmask = cmask,
     678                 :         26 :     };
     679                 :            : #line 646
     680         [ +  + ]:         26 :     size_t _glsl_646_fn(void *, pl_str *, const uint8_t *);
     681                 :            :     pl_str_builder_append(sh->buffers[SH_BUF_BODY], _glsl_646_fn,
     682                 :         14 :                           &_glsl_646_args, sizeof(_glsl_646_args));
     683                 :         14 : }
     684                 :            : #line 650
     685                 :            :     }
     686                 :         26 : 
     687   [ -  +  -  - ]:         26 :     pl_gpu gpu = SH_GPU(sh);
     688                 :            :     const int num_comps = __builtin_popcount(cmask);
     689                 :          0 :     const bool dynamic_size = SH_PARAMS(sh).dynamic_constants ||
     690                 :          0 :                               !gpu || !gpu->limits.array_size_constants;
     691                 :            : 
     692                 :            :     int bound   = ceil(obj->filter->radius);
     693                 :         26 :     int offset  = bound - 1; // padding top/left
     694                 :         26 :     int padding = offset + bound; // total padding
     695                 :            : 
     696                 :            :     // Determined experimentally on modern AMD and Nvidia hardware. 32 is a
     697                 :            :     // good tradeoff for the horizontal work group size. Apart from that,
     698                 :            :     // just use as many threads as possible.
     699                 :         78 :     int bw = 32, bh = sh_glsl(sh).max_group_threads / bw;
     700                 :            :     int sizew, sizeh, iw, ih;
     701                 :            : 
     702                 :            :     // Disable compute shaders after a (hard-coded) radius of 6, since the
     703                 :            :     // gather kernel generally pulls ahead here.
     704                 :            :     bool is_compute = !params->no_compute && sh_glsl(sh).compute;
     705                 :            :     is_compute &= obj->filter->radius < 6.0;
     706                 :            : 
     707                 :            :     while (is_compute) {
     708                 :            :         // We need to sample everything from base_min to base_max, so make sure
     709                 :            :         // we have enough room in shmem. The extra margin on the ceilf guards
     710                 :            :         // against floating point inaccuracy on near-integer scaling ratios.
     711         [ -  + ]:         78 :         const float margin = 1e-5;
     712                 :          0 :         sizew = iw = (int) ceilf(bw / rx - margin) + padding + 1;
     713                 :          0 :         sizeh = ih = (int) ceilf(bh / ry - margin) + padding + 1;
     714                 :            : 
     715                 :            :         if (dynamic_size) {
     716                 :         78 :             // Overallocate slightly to reduce recompilation overhead
     717                 :         78 :             sizew = PL_ALIGN2(sizew, 8);
     718                 :            :             sizeh = PL_ALIGN2(sizeh, 8);
     719         [ +  + ]:         78 :         }
     720                 :            : 
     721                 :            :         const int shmem_req = (sizew * sizeh * num_comps + 2) * sizeof(float);
     722                 :         26 :         if (shmem_req > sh_glsl(sh).max_shmem_size && bh > 1) {
     723         [ -  + ]:         26 :             // Try again with smaller work group size
     724                 :          0 :             bh >>= 1;
     725         [ -  + ]:         26 :             continue;
     726                 :          0 :         }
     727                 :            : 
     728                 :         26 :         is_compute = sh_try_compute(sh, bw, bh, false, shmem_req);
     729                 :         26 :         break;
     730                 :            :     }
     731                 :            : 
     732                 :            :     // Note: SH_LUT_LITERAL might be faster in some specific cases, but not by
     733                 :            :     // much, and it's catastrophically slow on other platforms.
     734                 :            :     ident_t lut = sh_lut(sh, sh_lut_params(
     735                 :         26 :         .object     = &obj->lut,
     736                 :            :         .lut_type   = SH_LUT_TEXTURE,
     737                 :            :         .var_type   = PL_VAR_FLOAT,
     738                 :            :         .method     = SH_LUT_LINEAR,
     739                 :            :         .width      = SCALER_LUT_SIZE,
     740                 :            :         .comps      = 1,
     741                 :            :         .update     = update,
     742                 :         26 :         .fill       = fill_polar_lut,
     743                 :            :         .priv       = obj,
     744                 :            :     ));
     745                 :            : 
     746                 :            :     if (!lut) {
     747                 :            :         SH_FAIL(sh, "Failed initializing polar LUT!");
     748                 :            :         return false;
     749                 :            :     }
     750         [ +  + ]:         26 : 
     751                 :         14 :     ident_t radius_c = sh_const_float(sh, "radius", obj->filter->radius);
     752                 :         14 :     ident_t in = sh_fresh(sh, "in");
     753                 :            : 
     754                 :            :     if (is_compute) {
     755                 :            : 
     756                 :         26 :         // Compute shader kernel
     757                 :            :         GLSL("uvec2 base_id = uvec2(0u); \n");
     758                 :            :         if (src->rect.x0 > src->rect.x1)
     759                 :            :             GLSL("base_id.x = gl_WorkGroupSize.x - 1u; \n");
     760                 :            :         if (src->rect.y0 > src->rect.y1)
     761         [ +  + ]:        102 :             GLSL("base_id.y = gl_WorkGroupSize.y - 1u; \n");
     762                 :         76 : 
     763                 :         76 :         GLSLH("shared vec2 "$"_base; \n", in);
     764                 :         76 :         GLSL("if (gl_LocalInvocationID.xy == base_id)               \n"
     765                 :         76 :              "    "$"_base = base;                                  \n"
     766                 :            :              "barrier();                                            \n"
     767                 :            :              "ivec2 rel = ivec2(round((base - "$"_base) * size));   \n",
     768                 :         26 :              in, in);
     769                 :            : 
     770                 :            :         ident_t sizew_c = sh_const(sh, (struct pl_shader_const) {
     771                 :            :             .type = PL_VAR_SINT,
     772         [ +  + ]:        218 :             .compile_time = true,
     773         [ +  + ]:       1664 :             .name = "sizew",
     774                 :       1472 :             .data = &sizew,
     775                 :            :         });
     776                 :       1472 : 
     777                 :            :         ident_t sizeh_c = sh_const(sh, (struct pl_shader_const) {
     778                 :            :             .type = PL_VAR_SINT,
     779                 :            :             .compile_time = true,
     780                 :            :             .name = "sizeh",
     781                 :            :             .data = &sizeh,
     782         [ +  + ]:        204 :         });
     783                 :        152 : 
     784                 :        152 :         ident_t iw_c = sizew_c, ih_c = sizeh_c;
     785                 :        152 :         if (dynamic_size) {
     786                 :            :             iw_c = sh_const_int(sh, "iw", iw);
     787                 :            :             ih_c = sh_const_int(sh, "ih", ih);
     788                 :            :         }
     789                 :            : 
     790                 :            :         // Load all relevant texels into shmem
     791                 :            :         GLSL("for (int y = int(gl_LocalInvocationID.y); y < "$"; y += %d) {     \n"
     792                 :            :              "for (int x = int(gl_LocalInvocationID.x); x < "$"; x += %d) {     \n"
     793                 :            :              "c = textureLod("$", "$"_base + pt * vec2(x - %d, y - %d), 0.0);   \n",
     794                 :            :              ih_c, bh, iw_c, bw, src_tex, in, offset, offset);
     795                 :         52 : 
     796                 :            :         for (uint8_t comps = cmask; comps;) {
     797                 :            :             uint8_t c = __builtin_ctz(comps);
     798         [ -  + ]:         52 :             GLSLH("shared float "$"_%d["$" * "$"]; \n", in, c, sizeh_c, sizew_c);
     799                 :          0 :             GLSL(""$"_%d["$" * y + x] = c[%d]; \n", in, c, sizew_c, c);
     800                 :            :             comps &= ~(1 << c);
     801                 :          0 :         }
     802                 :            : 
     803                 :            :         GLSL("}}                     \n"
     804         [ +  + ]:        628 :              "barrier();             \n");
     805         [ +  + ]:       7188 : 
     806                 :            :         // Dispatch the actual samples
     807                 :       6612 :         for (int y = 1 - bound; y <= bound; y++) {
     808         [ +  + ]:       6612 :             for (int x = 1 - bound; x <= bound; x++) {
     809                 :        702 :                 GLSL("idx = "$" * rel.y + rel.x + "$" * %d + %d; \n",
     810                 :            :                      sizew_c, sizew_c, y + offset, x + offset);
     811                 :            :                 polar_sample(sh, obj->filter, src_tex, lut, radius_c,
     812                 :            :                              x, y, cmask, in, use_ar, scale);
     813                 :            :             }
     814                 :            :         }
     815                 :            :     } else {
     816                 :       5910 :         // Fragment shader sampling
     817                 :       5910 :         for (uint8_t comps = cmask; comps;) {
     818                 :       5910 :             uint8_t c = __builtin_ctz(comps);
     819                 :       5910 :             GLSL("vec4 "$"_%d; \n", in, c);
     820                 :       5910 :             comps &= ~(1 << c);
     821   [ +  -  +  + ]:       5910 :         }
     822                 :            : 
     823                 :            :         // For maximum efficiency, we want to use textureGather() if
     824                 :            :         // possible, rather than direct sampling. Since this is not
     825                 :            :         // always possible/sensible, we need to possibly intermix gathering
     826                 :            :         // with regular sampling. This requires keeping track of which
     827                 :            :         // pixels in the next row were already gathered by the previous
     828                 :            :         // row.
     829   [ +  +  -  + ]:       5910 :         uint64_t gathered_cur = 0x0, gathered_next = 0x0;
     830                 :       5782 :         const float radius2 = PL_SQUARE(obj->filter->radius);
     831                 :            :         const int base = bound - 1;
     832         [ +  + ]:       5910 : 
     833                 :            :         if (base + bound >= 8 * sizeof(gathered_cur)) {
     834                 :       5450 :             SH_FAIL(sh, "Polar radius %f exceeds implementation capacity!",
     835                 :            :                     obj->filter->radius);
     836                 :       5450 :             return false;
     837                 :            :         }
     838                 :            : 
     839                 :            :         for (int y = 1 - bound; y <= bound; y++) {
     840         [ +  + ]:       1840 :             for (int x = 1 - bound; x <= bound; x++) {
     841                 :       1380 :                 // Skip already gathered texels
     842         [ +  + ]:       1380 :                 uint64_t bit = 1llu << (base + x);
     843         [ +  + ]:       1374 :                 if (gathered_cur & bit)
     844                 :        916 :                     continue;
     845                 :            : 
     846                 :            :                 // Using texture gathering is only more efficient than direct
     847                 :            :                 // sampling in the case where we expect to be able to use all
     848                 :        458 :                 // four gathered texels, without having to discard any. So
     849                 :            :                 // only do it if we suspect it will be a win rather than a
     850                 :            :                 // loss.
     851                 :            :                 int xx = x*x, xx1 = (x+1)*(x+1);
     852                 :            :                 int yy = y*y, yy1 = (y+1)*(y+1);
     853         [ +  + ]:          6 :                 bool use_gather = PL_MAX(xx, xx1) + PL_MAX(yy, yy1) < radius2;
     854                 :          4 :                 use_gather &= PL_MAX(x, y) <= sh_glsl(sh).max_gather_offset;
     855                 :            :                 use_gather &= PL_MIN(x, y) >= sh_glsl(sh).min_gather_offset;
     856                 :            :                 use_gather &= !src->tex || src->tex->params.format->gatherable;
     857                 :          2 : 
     858                 :            :                 // Gathering from components other than the R channel requires
     859                 :            :                 // support for GLSL 400, which introduces the overload of
     860                 :            :                 // textureGather* that allows specifying the component.
     861                 :       1380 :                 //
     862                 :            :                 // This is also the minimum requirement if we don't know the
     863                 :            :                 // texture format capabilities, for the sampler2D interface
     864                 :            :                 if (cmask != 0x1 || !src->tex)
     865         [ +  + ]:       2300 :                     use_gather &= sh_glsl(sh).version >= 400;
     866                 :            : 
     867                 :            :                 if (!use_gather) {
     868                 :            :                     // Switch to direct sampling instead
     869                 :            :                     polar_sample(sh, obj->filter, src_tex, lut, radius_c,
     870   [ +  -  -  + ]:       1840 :                                  x, y, cmask, NULL_IDENT, use_ar, scale);
     871                 :          0 :                     continue;
     872                 :            :                 }
     873   [ +  +  +  + ]:       1840 : 
     874                 :        218 :                 // Gather the four surrounding texels simultaneously
     875                 :            :                 for (uint8_t comps = cmask; comps;) {
     876                 :       1622 :                     uint8_t c = __builtin_ctz(comps);
     877                 :       1622 :                     if (x || y) {
     878                 :            :                         if (c) {
     879                 :            :                             GLSL($"_%d = textureGatherOffset("$", "
     880                 :            :                                  "center, ivec2(%d, %d), %d); \n",
     881                 :            :                                  in, c, src_tex, x, y, c);
     882                 :        460 :                         } else {
     883                 :            :                             GLSL($"_0 = textureGatherOffset("$", "
     884                 :            :                                  "center, ivec2(%d, %d)); \n",
     885                 :            :                                  in, src_tex, x, y);
     886                 :            :                         }
     887                 :            :                     } else {
     888                 :            :                         if (c) {
     889                 :            :                             GLSL($"_%d = textureGather("$", center, %d); \n",
     890                 :            :                                  in, c, src_tex, c);
     891                 :            :                         } else {
     892                 :         70 :                             GLSL($"_0 = textureGather("$", center); \n",
     893                 :        148 :                                  in, src_tex);
     894                 :            :                         }
     895                 :         70 :                     }
     896                 :         24 :                     comps &= ~(1 << c);
     897                 :            :                 }
     898         [ +  + ]:         70 : 
     899         [ +  + ]:         32 :                 // Mix in all of the points with their weights
     900                 :         78 :                 for (int p = 0; p < 4; p++) {
     901                 :            :                     // The four texels are gathered counterclockwise starting
     902                 :            :                     // from the bottom left
     903                 :         78 :                     static const int xo[4] = {0, 1, 1, 0};
     904                 :         68 :                     static const int yo[4] = {1, 1, 0, 0};
     905                 :         70 :                     if (x+xo[p] > bound || y+yo[p] > bound)
     906                 :            :                         continue; // next subpixel
     907                 :        102 : 
     908                 :            :                     if (!yo[p] && (gathered_cur & (bit << xo[p])))
     909                 :            :                         continue; // already sampled
     910                 :        254 : 
     911                 :            :                     GLSL("idx = %d;\n", p);
     912         [ +  + ]:         70 :                     polar_sample(sh, obj->filter, src_tex, lut, radius_c,
     913                 :        184 :                                  x+xo[p], y+yo[p], cmask, in, use_ar, scale);
     914                 :            :                 }
     915         [ +  + ]:        184 : 
     916                 :            :                 // Mark the other next row's pixels as already gathered
     917                 :            :                 gathered_next |= bit | (bit << 1);
     918         [ +  + ]:      12336 :                 x++; // skip adjacent pixel
     919                 :      12288 :             }
     920                 :      12288 : 
     921         [ -  + ]:      12288 :             // Prepare for new row
     922                 :            :             gathered_cur = gathered_next;
     923         [ +  + ]:      43008 :             gathered_next = 0;
     924                 :      30720 :         }
     925         [ -  + ]:      30720 :     }
     926                 :      30720 : 
     927                 :      30720 : {
     928                 :            :     const struct __attribute__((__packed__)) {
     929         [ -  + ]:      12288 :     ident_t scale;
     930         [ +  + ]:      16384 :     ident_t cfg_antiring;
     931         [ -  + ]:       4096 :     bool use_ar;
     932                 :            :     uint8_t cmask;
     933                 :            :     bool cmask_1_pl_channel_a;
     934                 :        136 : } _glsl_892_args = {
     935         [ -  + ]:        136 : #line 893
     936                 :        136 :         .scale = scale,
     937                 :            : #line 900
     938                 :        184 :         .cfg_antiring = sh_const_float(sh, "cfg_antiring", cfg.antiring),
     939                 :            : #line 894
     940                 :            :         .use_ar = use_ar,
     941                 :            : #line 895
     942                 :            :         .cmask = cmask,
     943                 :            : #line 903
     944                 :            :         .cmask_1_pl_channel_a = !(cmask & (1 << PL_CHANNEL_A)),
     945                 :            :     };
     946                 :        208 : #line 892
     947                 :            :     size_t _glsl_892_fn(void *, pl_str *, const uint8_t *);
     948                 :            :     pl_str_builder_append(sh->buffers[SH_BUF_BODY], _glsl_892_fn,
     949         [ -  + ]:        208 :                           &_glsl_892_args, sizeof(_glsl_892_args));
     950         [ -  + ]:        208 : }
     951                 :          0 : #line 906
     952                 :          0 : 
     953                 :            :     return true;
     954                 :            : }
     955                 :        208 : 
     956         [ -  + ]:        208 : static void fill_ortho_lut(void *data, const struct sh_lut_params *params)
     957                 :            : {
     958                 :            :     const struct sh_sampler_obj *obj = params->priv;
     959                 :            :     pl_filter filt = obj->filter;
     960                 :            : 
     961         [ -  + ]:        208 :     if (filt->radius == filt->radius_zero) {
     962                 :            :         // Main lobe covers entire radius, so all weights are positive, meaning
     963                 :            :         // we can use the linear resampling trick
     964                 :            :         for (int n = 0; n < SCALER_LUT_SIZE; n++) {
     965                 :            :             const float *weights = filt->weights + n * filt->row_stride;
     966                 :            :             float *row = (float *) data + n * filt->row_stride;
     967                 :            :             pl_assert(filt->row_size % 2 == 0);
     968         [ +  + ]:        208 :             int i = 0;
     969                 :            :             for (; i < filt->row_size; i += 2) {
     970         [ -  + ]:        104 :                 const float w0 = weights[i], w1 = weights[i+1];
     971                 :            :                 assert(w0 + w1 >= 0.0f);
     972                 :            :                 row[i] = w0 + w1;
     973                 :          0 :                 row[i+1] = w1 / (w0 + w1);
     974                 :            :             }
     975                 :            :             pl_assert(filt->params.row_stride_align == 4); // always 4 components
     976                 :            :             for (; i < filt->row_stride; i++)
     977                 :          0 :                 row[i] = i >= 4 ? row[i - 4] : 0;
     978                 :            :         }
     979                 :            :     } else {
     980                 :            :         size_t entries = SCALER_LUT_SIZE * filt->row_stride;
     981                 :            :         pl_assert(params->width * params->height * params->comps == entries);
     982                 :            :         memcpy(data, filt->weights, entries * sizeof(float));
     983                 :            :     }
     984                 :            : }
     985                 :            : 
     986                 :            : enum {
     987                 :            :     SEP_VERT = 0,
     988                 :            :     SEP_HORIZ,
     989                 :        208 :     SEP_PASSES
     990                 :            : };
     991         [ -  + ]:        208 : 
     992                 :            : bool pl_shader_sample_ortho2(pl_shader sh, const struct pl_sample_src *src,
     993                 :            :                              const struct pl_sample_filter_params *params)
     994         [ +  + ]:        208 : {
     995                 :        104 :     pl_assert(params);
     996                 :            :     if (params->filter.polar) {
     997         [ -  + ]:        104 :         SH_FAIL(sh, "Trying to use separated sampling with a polar filter?");
     998                 :            :         return false;
     999                 :            :     }
    1000                 :        208 : 
    1001         [ +  + ]:        208 :     pl_gpu gpu = SH_GPU(sh);
    1002         [ -  + ]:        208 :     pl_assert(gpu);
    1003                 :            : 
    1004                 :            :     uint8_t comps;
    1005                 :        208 :     float ratio[SEP_PASSES], scale;
    1006         [ +  - ]:        208 :     ident_t src_tex, pos, pt;
    1007         [ -  + ]:        208 :     if (!setup_src(sh, src, &src_tex, &pos, &pt,
    1008   [ +  +  +  + ]:        208 :                    &ratio[SEP_HORIZ], &ratio[SEP_VERT],
    1009                 :            :                    &comps, &scale, false, LINEAR))
    1010                 :            :         return false;
    1011                 :        184 : 
    1012                 :        184 : 
    1013                 :            :     int pass;
    1014                 :            :     if (fabs(ratio[SEP_HORIZ] - 1.0f) < 1e-6f) {
    1015                 :            :         pass = SEP_VERT;
    1016                 :            :     } else if (fabs(ratio[SEP_VERT] - 1.0f) < 1e-6f) {
    1017                 :            :         pass = SEP_HORIZ;
    1018                 :            :     } else {
    1019         [ -  + ]:        184 :         SH_FAIL(sh, "Trying to use pl_shader_sample_ortho with a "
    1020                 :            :                 "pl_sample_src that requires scaling in multiple directions "
    1021                 :          0 :                 "(rx=%f, ry=%f), this is not possible!",
    1022                 :          0 :                 ratio[SEP_HORIZ], ratio[SEP_VERT]);
    1023                 :            :         return false;
    1024                 :            :     }
    1025                 :            : 
    1026                 :        208 :     // We can store a separate sampler object per dimension, so dispatch the
    1027                 :        208 :     // right one. This is needed for two reasons:
    1028                 :        208 :     // 1. Anamorphic content can have a different scaling ratio for each
    1029                 :            :     //    dimension. In particular, you could be upscaling in one and
    1030                 :            :     //    downscaling in the other.
    1031                 :            :     // 2. After fixing the source for `setup_src`, we lose information about
    1032                 :            :     //    the scaling ratio of the other component. (Although this is only a
    1033                 :            :     //    minor reason and could easily be changed with some boilerplate)
    1034                 :            :     struct sh_sampler_obj *obj;
    1035                 :            :     obj = SH_OBJ(sh, params->lut, PL_SHADER_OBJ_SAMPLER,
    1036                 :            :                  struct sh_sampler_obj, sh_sampler_uninit);
    1037                 :            :     if (!obj)
    1038                 :            :         return false;
    1039         [ -  + ]:        208 : 
    1040                 :          0 :     if (pass != 0) {
    1041                 :          0 :         obj = SH_OBJ(sh, &obj->pass2, PL_SHADER_OBJ_SAMPLER,
    1042                 :            :                      struct sh_sampler_obj, sh_sampler_uninit);
    1043                 :            :         assert(obj);
    1044                 :        208 :     }
    1045                 :            : 
    1046                 :            :     float inv_scale = 1.0 / ratio[pass];
    1047                 :            :     inv_scale = PL_MAX(inv_scale, 1.0);
    1048                 :            :     if (params->no_widening)
    1049                 :            :         inv_scale = 1.0;
    1050                 :            : 
    1051                 :            :     struct pl_filter_config cfg = params->filter;
    1052                 :            :     cfg.antiring = PL_DEF(cfg.antiring, params->antiring);
    1053                 :            :     cfg.blur = PL_DEF(cfg.blur, 1.0f) * inv_scale;
    1054                 :        208 :     bool update = !obj->filter || !pl_filter_config_eq(&obj->filter->params.config, &cfg);
    1055                 :            : 
    1056                 :        208 :     if (update) {
    1057   [ -  +  -  - ]:        208 :         pl_filter_free(&obj->filter);
    1058                 :        208 :         obj->filter = pl_filter_generate(sh->log, pl_filter_params(
    1059                 :        208 :             .config             = cfg,
    1060                 :            :             .lut_entries        = SCALER_LUT_SIZE,
    1061                 :         68 :             .max_row_size       = gpu->limits.max_tex_2d_dim / 4,
    1062                 :        208 :             .row_stride_align   = 4,
    1063                 :            :         ));
    1064                 :            : 
    1065                 :            :         if (!obj->filter) {
    1066                 :        208 :             // This should never happen, but just in case ..
    1067                 :            :             SH_FAIL(sh, "Failed initializing separated filter!");
    1068                 :            :             return false;
    1069                 :            :         }
    1070                 :        208 :     }
    1071                 :            : 
    1072                 :            :     int N = obj->filter->row_size; // number of samples to convolve
    1073                 :            :     int width = obj->filter->row_stride / 4; // width of the LUT texture
    1074                 :            :     ident_t lut = sh_lut(sh, sh_lut_params(
    1075                 :          0 :         .object     = &obj->lut,
    1076                 :         68 :         .var_type   = PL_VAR_FLOAT,
    1077                 :         68 :         .method     = SH_LUT_LINEAR,
    1078                 :        208 :         .width      = width,
    1079         [ +  + ]:        344 :         .height     = SCALER_LUT_SIZE,
    1080                 :         68 :         .comps      = 4,
    1081                 :            :         .update     = update,
    1082                 :         68 :         .fill       = fill_ortho_lut,
    1083                 :            :         .priv       = obj,
    1084                 :         24 :     ));
    1085         [ -  + ]:        136 :     if (!lut) {
    1086                 :         68 :         SH_FAIL(sh, "Failed initializing separated LUT!");
    1087                 :         68 :         return false;
    1088                 :            :     }
    1089                 :            : 
    1090         [ +  + ]:         68 :     const int dir[SEP_PASSES][2] = {
    1091         [ -  + ]:         68 :         [SEP_HORIZ] = {1, 0},
    1092                 :         68 :         [SEP_VERT]  = {0, 1},
    1093                 :            :     };
    1094                 :            : 
    1095                 :        208 :     static const char *names[SEP_PASSES] = {
    1096                 :        208 :         [SEP_HORIZ] = "ortho (horiz)",
    1097         [ -  + ]:         68 :         [SEP_VERT]  = "ortho (vert)",
    1098                 :            :     };
    1099                 :        208 : 
    1100                 :         68 :     describe_filter(sh, &cfg, names[pass], ratio[pass], ratio[pass]);
    1101                 :            : 
    1102                 :            :     float denom = PL_MAX(1, width - 1); // avoid division by zero
    1103                 :            :     bool use_ar = cfg.antiring > 0 && ratio[pass] > 1.0;
    1104                 :         92 :     bool use_linear = obj->filter->radius == obj->filter->radius_zero;
    1105                 :            :     use_ar &= !use_linear; // filter has no negative weights
    1106                 :            : 
    1107         [ -  + ]:         24 : {
    1108         [ -  + ]:         24 :     const struct __attribute__((__packed__)) {
    1109                 :          0 :     float dir_pass_0;
    1110                 :            :     float dir_pass_1;
    1111                 :         24 :     float n_2_1;
    1112                 :            :     unsigned use_linear_2u_1u;
    1113         [ -  + ]:         24 :     float denom;
    1114                 :          0 :     ident_t pos;
    1115                 :            :     ident_t pt;
    1116                 :         24 :     ident_t src_tex;
    1117                 :            :     ident_t n;
    1118                 :            :     ident_t lut;
    1119                 :            :     ident_t cfg_antiring;
    1120                 :         24 :     ident_t scale;
    1121                 :            :     uint8_t comps;
    1122                 :         24 :     bool use_ar;
    1123                 :         24 :     bool use_linear;
    1124                 :            : } _glsl_1061_args = {
    1125                 :         24 : #line 1066
    1126                 :            :         .dir_pass_0 = dir[pass][0],
    1127                 :            : #line 1066
    1128                 :            :         .dir_pass_1 = dir[pass][1],
    1129         [ +  - ]:         24 : #line 1070
    1130         [ +  - ]:         24 :         .n_2_1 = N / 2 - 1,
    1131                 :         24 : #line 1079
    1132                 :            :         .use_linear_2u_1u = use_linear ? 2u : 1u,
    1133                 :         24 : #line 1081
    1134                 :         24 :         .denom = denom,
    1135                 :            : #line 1064
    1136                 :            :         .pos = pos,
    1137                 :            : #line 1064
    1138                 :         24 :         .pt = pt,
    1139                 :         24 : #line 1065
    1140                 :         24 :         .src_tex = src_tex,
    1141                 :            : #line 1079
    1142         [ +  + ]:         24 :         .n = sh_const_uint(sh, "n",  N),
    1143                 :          4 : #line 1081
    1144                 :            :         .lut = lut,
    1145                 :            : #line 1095
    1146                 :          4 :         .cfg_antiring = sh_const_float(sh, "cfg_antiring",  cfg.antiring),
    1147                 :          4 : #line 1096
    1148                 :            :         .scale = sh_const_float(sh, "scale",  scale),
    1149                 :            : #line 1073
    1150                 :            :         .comps = comps,
    1151                 :            : #line 1074
    1152                 :            :         .use_ar = use_ar,
    1153                 :            : #line 1083
    1154                 :            :         .use_linear = use_linear,
    1155                 :            :     };
    1156                 :            : #line 1061
    1157                 :         24 :     size_t _glsl_1061_fn(void *, pl_str *, const uint8_t *);
    1158                 :         24 :     pl_str_builder_append(sh->buffers[SH_BUF_BODY], _glsl_1061_fn,
    1159                 :            :                           &_glsl_1061_args, sizeof(_glsl_1061_args));
    1160                 :            : }
    1161                 :            : #line 1098
    1162                 :         24 : 
    1163                 :         24 :     return true;
    1164                 :         24 : }
    1165                 :         24 : 
    1166                 :            : const struct pl_distort_params pl_distort_default_params = { PL_DISTORT_DEFAULTS };
    1167                 :            : 
    1168                 :         24 : void pl_shader_distort(pl_shader sh, pl_tex src_tex, int out_w, int out_h,
    1169                 :         24 :                        const struct pl_distort_params *params)
    1170                 :            : {
    1171                 :            :     pl_assert(params);
    1172                 :            :     if (!sh_require(sh, PL_SHADER_SIG_NONE, out_w, out_h))
    1173                 :            :         return;
    1174                 :         24 : 
    1175                 :         12 :     const int src_w = src_tex->params.w, src_h = src_tex->params.h;
    1176                 :         24 :     float rx = 1.0f, ry = 1.0f;
    1177                 :            :     if (src_w > src_h) {
    1178                 :            :         ry = (float) src_h / src_w;
    1179                 :            :     } else {
    1180                 :         24 :         rx = (float) src_w / src_h;
    1181                 :         16 :     }
    1182                 :         12 : 
    1183                 :         12 :     // Map from texel coordinates [0,1]² to aspect-normalized representation
    1184                 :         12 :     const pl_transform2x2 tex2norm = {
    1185                 :         24 :         .mat.m = {
    1186                 :            :             { 2 * rx, 0 },
    1187         [ +  + ]:         12 :             { 0, -2 * ry },
    1188                 :            :         },
    1189                 :            :         .c = { -rx, ry },
    1190                 :            :     };
    1191                 :            : 
    1192                 :            :     // Map from aspect-normalized representation to canvas coords [-1,1]²
    1193                 :            :     const float sx = params->unscaled ? (float) src_w / out_w : 1.0f;
    1194                 :            :     const float sy = params->unscaled ? (float) src_h / out_h : 1.0f;
    1195                 :            :     const pl_transform2x2 norm2canvas = {
    1196                 :            :         .mat.m = {
    1197                 :            :             { sx / rx, 0 },
    1198                 :            :             { 0, sy / ry },
    1199                 :            :         },
    1200                 :            :     };
    1201                 :            : 
    1202                 :          8 :     struct pl_transform2x2 transform = params->transform;
    1203                 :            :     pl_transform2x2_mul(&transform, &tex2norm);
    1204                 :          8 :     pl_transform2x2_rmul(&norm2canvas, &transform);
    1205                 :          4 : 
    1206                 :          4 :     if (params->constrain) {
    1207                 :         24 :         pl_rect2df bb = pl_transform2x2_bounds(&transform, &(pl_rect2df) {
    1208         [ +  + ]:         12 :             .x1 = 1, .y1 = 1,
    1209                 :            :         });
    1210         [ -  + ]:          4 :         const float k = fmaxf(fmaxf(pl_rect_w(bb), pl_rect_h(bb)), 2.0f);
    1211                 :            :         pl_transform2x2_scale(&transform, 2.0f / k);
    1212                 :         12 :     };
    1213                 :            : 
    1214                 :            :     // Bind the canvas coordinates as [-1,1]², flipped vertically to correspond
    1215                 :            :     // to normal mathematical axis conventions
    1216                 :            :     static const pl_rect2df canvas = {
    1217                 :         12 :         .x0 = -1.0f, .x1 =  1.0f,
    1218                 :            :         .y0 =  1.0f, .y1 = -1.0f,
    1219                 :            :     };
    1220                 :            : 
    1221                 :            :     ident_t pos = sh_attr_vec2(sh, "pos", &canvas);
    1222                 :            :     ident_t pt, tex = sh_bind(sh, src_tex, params->address_mode,
    1223                 :            :                               PL_TEX_SAMPLE_LINEAR, "tex", NULL, NULL, &pt);
    1224                 :            : 
    1225                 :            :     // Bind the inverse of the tex2canvas transform (i.e. canvas2tex)
    1226                 :            :     pl_transform2x2_invert(&transform);
    1227                 :            :     ident_t tf = sh_var(sh, (struct pl_shader_var) {
    1228                 :            :         .var  = pl_var_mat2("tf"),
    1229                 :            :         .data = PL_TRANSPOSE_2X2(transform.mat.m),
    1230                 :            :     });
    1231                 :            : 
    1232                 :            :     ident_t tf_c = sh_var(sh, (struct pl_shader_var) {
    1233                 :            :         .var  = pl_var_vec2("tf_c"),
    1234                 :            :         .data = transform.c,
    1235                 :            :     });
    1236                 :            : 
    1237                 :            :     // See pl_shader_sample_bicubic
    1238                 :            :     sh_describe(sh, "distortion");
    1239                 :            : {
    1240                 :            :     const struct __attribute__((__packed__)) {
    1241                 :            :     ident_t tf;
    1242                 :            :     ident_t pos;
    1243                 :            :     ident_t tf_c;
    1244                 :            :     ident_t pt;
    1245                 :            :     ident_t tex;
    1246                 :            :     bool params_bicubic;
    1247                 :            :     bool params_alpha_mode;
    1248                 :            :     bool params_alpha_mode_pl_alpha_premultiplied;
    1249                 :            : } _glsl_1175_args = {
    1250                 :            : #line 1178
    1251                 :            :         .tf = tf,
    1252                 :            : #line 1178
    1253                 :            :         .pos = pos,
    1254                 :            : #line 1178
    1255                 :            :         .tf_c = tf_c,
    1256                 :            : #line 1179
    1257                 :            :         .pt = pt,
    1258                 :            : #line 1181
    1259                 :            :         .tex = tex,
    1260                 :            : #line 1180
    1261                 :            :         .params_bicubic = params->bicubic,
    1262                 :            : #line 1204
    1263                 :            :         .params_alpha_mode = params->alpha_mode,
    1264                 :            : #line 1207
    1265                 :            :         .params_alpha_mode_pl_alpha_premultiplied = params->alpha_mode == PL_ALPHA_PREMULTIPLIED,
    1266                 :            :     };
    1267                 :            : #line 1175
    1268                 :            :     size_t _glsl_1175_fn(void *, pl_str *, const uint8_t *);
    1269                 :            :     pl_str_builder_append(sh->buffers[SH_BUF_BODY], _glsl_1175_fn,
    1270                 :            :                           &_glsl_1175_args, sizeof(_glsl_1175_args));
    1271                 :            : }
    1272                 :            : #line 1213
    1273                 :            : 
    1274                 :            : }
    1275                 :            : 
    1276                 :            : // Auto-generated template functions:
    1277                 :            : #line 330
    1278                 :            : size_t _glsl_331_fn(void *alloc, pl_str *buf, const uint8_t *ptr);
    1279                 :            : size_t _glsl_331_fn(void *alloc, pl_str *buf, const uint8_t *ptr)
    1280                 :            : {
    1281                 :            : struct __attribute__((__packed__)) {
    1282                 :            :     ident_t pos;
    1283                 :            :     ident_t tex;
    1284                 :            :     ident_t pt;
    1285                 :            :     ident_t scale;
    1286                 :            : } vars;
    1287                 :            : memcpy(&vars, ptr, sizeof(vars));
    1288                 :            : 
    1289                 :            : #line 331
    1290                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1291                 :            :         "\n"
    1292                 :            :         "vec4 color;\n"
    1293                 :            :         "{\n"
    1294                 :            :         "vec2 pos = _%hx;\n"
    1295                 :            :         "vec2 size = vec2(textureSize(_%hx, 0));\n"
    1296                 :            :         "vec2 frac  = fract(pos * size + vec2(0.5));\n"
    1297                 :            :         "vec2 frac2 = frac * frac;\n"
    1298                 :            :         "vec2 inv   = vec2(1.0) - frac;\n"
    1299                 :            :         "vec2 inv2  = inv * inv;\n"
    1300                 :            :         "\n"
    1301                 :            :         "vec2 w0 = 1.0/6.0 * inv2 * inv;\n"
    1302                 :            :         "vec2 w1 = 2.0/3.0 - 0.5 * frac2 * (2.0 - frac);\n"
    1303                 :            :         "vec2 w2 = 2.0/3.0 - 0.5 * inv2  * (2.0 - inv);\n"
    1304                 :            :         "vec2 w3 = 1.0/6.0 * frac2 * frac;\n"
    1305                 :            :         "vec4 g = vec4(w0 + w1, w2 + w3);\n"
    1306                 :            :         "vec4 h = vec4(w1, w3) / g + inv.xyxy;\n"
    1307                 :            :         "h.xy -= vec2(2.0);\n"
    1308                 :            :         "\n"
    1309                 :            :         "vec4 p = pos.xyxy + _%hx.xyxy * h;\n"
    1310                 :            :         "vec4 c00 = textureLod(_%hx, p.xy, 0.0);\n"
    1311                 :            :         "vec4 c01 = textureLod(_%hx, p.xw, 0.0);\n"
    1312                 :            :         "vec4 c0 = mix(c01, c00, g.y);\n"
    1313                 :            :         "vec4 c10 = textureLod(_%hx, p.zy, 0.0);\n"
    1314                 :            :         "vec4 c11 = textureLod(_%hx, p.zw, 0.0);\n"
    1315                 :            :         "vec4 c1 = mix(c11, c10, g.y);\n"
    1316                 :            :         "color = _%hx * mix(c1, c0, g.x);\n"
    1317                 :            :         "}\n",
    1318                 :            :         vars.pos,
    1319                 :            :         vars.tex,
    1320                 :            :         vars.pt,
    1321                 :            :         vars.tex,
    1322                 :            :         vars.tex,
    1323                 :            :         vars.tex,
    1324                 :            :         vars.tex,
    1325                 :            :         vars.scale
    1326                 :            :     );
    1327                 :            : 
    1328                 :            : 
    1329                 :            : return sizeof(vars);
    1330                 :            : }
    1331                 :            : #line 374
    1332                 :            : size_t _glsl_375_fn(void *alloc, pl_str *buf, const uint8_t *ptr);
    1333                 :            : size_t _glsl_375_fn(void *alloc, pl_str *buf, const uint8_t *ptr)
    1334                 :            : {
    1335                 :            : struct __attribute__((__packed__)) {
    1336                 :            :     ident_t pos;
    1337                 :            :     ident_t tex;
    1338                 :            :     ident_t pt;
    1339                 :            :     ident_t scale;
    1340                 :            : } vars;
    1341                 :            : memcpy(&vars, ptr, sizeof(vars));
    1342                 :            : 
    1343                 :            : #line 375
    1344                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1345                 :            :         "\n"
    1346                 :            :         "vec4 color;\n"
    1347                 :            :         "{\n"
    1348                 :            :         "vec2 pos  = _%hx;\n"
    1349                 :            :         "vec2 size = vec2(textureSize(_%hx, 0));\n"
    1350                 :            :         "vec2 frac = fract(pos * size + vec2(0.5));\n"
    1351                 :            :         "pos += _%hx * (smoothstep(0.0, 1.0, frac) - frac);\n"
    1352                 :            :         "color = _%hx * textureLod(_%hx, pos, 0.0);\n"
    1353                 :            :         "}\n",
    1354                 :            :         vars.pos,
    1355                 :            :         vars.tex,
    1356                 :            :         vars.pt,
    1357                 :            :         vars.scale,
    1358                 :            :         vars.tex
    1359                 :            :     );
    1360                 :            : 
    1361                 :            : 
    1362                 :            : return sizeof(vars);
    1363                 :            : }
    1364                 :            : #line 400
    1365                 :            : size_t _glsl_401_fn(void *alloc, pl_str *buf, const uint8_t *ptr);
    1366                 :            : size_t _glsl_401_fn(void *alloc, pl_str *buf, const uint8_t *ptr)
    1367                 :            : {
    1368                 :            : struct __attribute__((__packed__)) {
    1369                 :            :     ident_t pos;
    1370                 :            :     ident_t tex;
    1371                 :            :     ident_t pt;
    1372                 :            :     ident_t scale;
    1373                 :            : } vars;
    1374                 :            : memcpy(&vars, ptr, sizeof(vars));
    1375                 :            : 
    1376                 :            : #line 401
    1377                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1378                 :            :         "\n"
    1379                 :            :         "vec4 color;\n"
    1380                 :            :         "{\n"
    1381                 :            :         "vec2 pos  = _%hx;\n"
    1382                 :            :         "vec2 size = vec2(textureSize(_%hx, 0));\n"
    1383                 :            :         "vec2 off  = -fract(pos * size + vec2(0.5));\n"
    1384                 :            :         "vec2 off2 = -2.0 * off * off;\n"
    1385                 :            :         "\n"
    1386                 :            :         "vec2 w0 = exp(off2 + 4.0 * off - vec2(2.0));\n"
    1387                 :            :         "vec2 w1 = exp(off2);\n"
    1388                 :            :         "vec2 w2 = exp(off2 - 4.0 * off - vec2(2.0));\n"
    1389                 :            :         "vec2 w3 = exp(off2 - 8.0 * off - vec2(8.0));\n"
    1390                 :            :         "vec4 g = vec4(w0 + w1, w2 + w3);\n"
    1391                 :            :         "vec4 h = vec4(w1, w3) / g;\n"
    1392                 :            :         "h.xy -= vec2(1.0);\n"
    1393                 :            :         "h.zw += vec2(1.0);\n"
    1394                 :            :         "g.xy /= g.xy + g.zw; \n"
    1395                 :            :         "\n"
    1396                 :            :         "vec4 p = pos.xyxy + _%hx.xyxy * (h + off.xyxy);\n"
    1397                 :            :         "vec4 c00 = textureLod(_%hx, p.xy, 0.0);\n"
    1398                 :            :         "vec4 c01 = textureLod(_%hx, p.xw, 0.0);\n"
    1399                 :            :         "vec4 c0 = mix(c01, c00, g.y);\n"
    1400                 :            :         "vec4 c10 = textureLod(_%hx, p.zy, 0.0);\n"
    1401                 :            :         "vec4 c11 = textureLod(_%hx, p.zw, 0.0);\n"
    1402                 :            :         "vec4 c1 = mix(c11, c10, g.y);\n"
    1403                 :            :         "color = _%hx * mix(c1, c0, g.x);\n"
    1404                 :            :         "}\n",
    1405                 :            :         vars.pos,
    1406                 :            :         vars.tex,
    1407                 :            :         vars.pt,
    1408                 :            :         vars.tex,
    1409                 :            :         vars.tex,
    1410                 :            :         vars.tex,
    1411                 :            :         vars.tex,
    1412                 :            :         vars.scale
    1413                 :            :     );
    1414                 :            : 
    1415                 :            : 
    1416                 :            : return sizeof(vars);
    1417                 :            : }
    1418                 :            : #line 441
    1419                 :            : size_t _glsl_442_fn(void *alloc, pl_str *buf, const uint8_t *ptr);
    1420                 :            : size_t _glsl_442_fn(void *alloc, pl_str *buf, const uint8_t *ptr)
    1421                 :            : {
    1422                 :            : struct __attribute__((__packed__)) {
    1423                 :            :     ident_t pos;
    1424                 :            :     ident_t tex;
    1425                 :            :     ident_t rx;
    1426                 :            :     ident_t ry;
    1427                 :            :     ident_t threshold;
    1428                 :            :     ident_t pt;
    1429                 :            :     ident_t scale;
    1430                 :            :     bool threshold_0;
    1431                 :            : } vars;
    1432                 :            : memcpy(&vars, ptr, sizeof(vars));
    1433                 :            : 
    1434                 :            : #line 442
    1435                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1436                 :            :         "\n"
    1437                 :            :         "vec4 color;\n"
    1438                 :            :         "{\n"
    1439                 :            :         "vec2 pos = _%hx;\n"
    1440                 :            :         "vec2 size = vec2(textureSize(_%hx, 0));\n"
    1441                 :            :         "\n"
    1442                 :            :         "vec2 fcoord = fract(pos * size - vec2(0.5));\n"
    1443                 :            :         "float rx = _%hx;\n"
    1444                 :            :         "float ry = _%hx;\n"
    1445                 :            :         "vec2 coeff = (fcoord - vec2(0.5)) * vec2(rx, ry);\n"
    1446                 :            :         "coeff = clamp(coeff + vec2(0.5), 0.0, 1.0);\n",
    1447                 :            :         vars.pos,
    1448                 :            :         vars.tex,
    1449                 :            :         vars.rx,
    1450                 :            :         vars.ry
    1451                 :            :     );
    1452                 :            : 
    1453                 :            : if (vars.threshold_0) {
    1454                 :            : #line 454
    1455                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1456                 :            :         "float thresh = _%hx;\n"
    1457                 :            :         "coeff = mix(coeff, vec2(0.0),\n"
    1458                 :            :         "lessThan(coeff, vec2(thresh)));\n"
    1459                 :            :         "coeff = mix(coeff, vec2(1.0),\n"
    1460                 :            :         "greaterThan(coeff, vec2(1.0 - thresh)));\n",
    1461                 :            :         vars.threshold
    1462                 :            :     );
    1463                 :            : 
    1464                 :            : }
    1465                 :            : #line 461
    1466                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1467                 :            :         "\n"
    1468                 :            :         "pos += (coeff - fcoord) * _%hx;\n"
    1469                 :            :         "color = _%hx * textureLod(_%hx, pos, 0.0);\n"
    1470                 :            :         "}\n",
    1471                 :            :         vars.pt,
    1472                 :            :         vars.scale,
    1473                 :            :         vars.tex
    1474                 :            :     );
    1475                 :            : 
    1476                 :            : 
    1477                 :            : return sizeof(vars);
    1478                 :            : }
    1479                 :            : #line 519
    1480                 :            : size_t _glsl_520_fn(void *alloc, pl_str *buf, const uint8_t *ptr);
    1481                 :            : size_t _glsl_520_fn(void *alloc, pl_str *buf, const uint8_t *ptr)
    1482                 :            : {
    1483                 :            : struct __attribute__((__packed__)) {
    1484                 :            :     int x;
    1485                 :            :     int y;
    1486                 :            :     float ar_radius;
    1487                 :            :     ident_t radius;
    1488                 :            :     ident_t lut;
    1489                 :            :     ident_t in;
    1490                 :            :     ident_t tex;
    1491                 :            :     ident_t scale;
    1492                 :            :     bool maybe_skippable;
    1493                 :            :     bool in_null_ident;
    1494                 :            :     uint8_t comp_mask;
    1495                 :            :     bool use_ar;
    1496                 :            : } vars;
    1497                 :            : memcpy(&vars, ptr, sizeof(vars));
    1498                 :            : 
    1499                 :            : #line 521
    1500                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1501                 :            :         "offset = ivec2(%d, %d);\n"
    1502                 :            :         "d = length(vec2(offset) - fcoord);\n",
    1503                 :            :         vars.x,
    1504                 :            :         vars.y
    1505                 :            :     );
    1506                 :            : 
    1507                 :            : if (vars.maybe_skippable)
    1508                 :            : #line 524
    1509                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1510                 :            :         "if (d < _%hx) {\n",
    1511                 :            :         vars.radius
    1512                 :            :     );
    1513                 :            : 
    1514                 :            : #line 525
    1515                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1516                 :            :         "w = _%hx(d * 1.0 / _%hx);\n"
    1517                 :            :         "wsum += w;\n",
    1518                 :            :         vars.lut,
    1519                 :            :         vars.radius
    1520                 :            :     );
    1521                 :            : 
    1522                 :            : if (vars.in_null_ident) {
    1523                 :            : for (uint8_t _mask = vars.comp_mask, c; _mask && (c = __builtin_ctz(_mask), 1); _mask &= ~(1u << c))
    1524                 :            : #line 529
    1525                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1526                 :            :         "c[%d] = _%hx_%d[idx];\n",
    1527                 :            :         c,
    1528                 :            :         vars.in,
    1529                 :            :         c
    1530                 :            :     );
    1531                 :            : 
    1532                 :            : } else {
    1533                 :            : #line 531
    1534                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1535                 :            :         "c = textureLod(_%hx, base + pt * vec2(offset), 0.0);\n",
    1536                 :            :         vars.tex
    1537                 :            :     );
    1538                 :            : 
    1539                 :            : }
    1540                 :            : for (uint8_t _mask = vars.comp_mask, c; _mask && (c = __builtin_ctz(_mask), 1); _mask &= ~(1u << c))
    1541                 :            : #line 534
    1542                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1543                 :            :         "color[%d] += w * c[%d];\n",
    1544                 :            :         c,
    1545                 :            :         c
    1546                 :            :     );
    1547                 :            : 
    1548                 :            : if (vars.use_ar) {
    1549                 :            : #line 536
    1550                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1551                 :            :         "if (d <= float(%f)) {\n",
    1552                 :            :         vars.ar_radius
    1553                 :            :     );
    1554                 :            : 
    1555                 :            : for (uint8_t _mask = vars.comp_mask, c; _mask && (c = __builtin_ctz(_mask), 1); _mask &= ~(1u << c)) {
    1556                 :            : #line 538
    1557                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1558                 :            :         "cc = vec2(_%hx * c[%d]);\n"
    1559                 :            :         "cc.x = 1.0 - cc.x;\n"
    1560                 :            :         "ww = cc + vec2(0.10);\n"
    1561                 :            :         "ww = ww * ww;\n"
    1562                 :            :         "ww = ww * ww;\n"
    1563                 :            :         "ww = ww * ww;\n"
    1564                 :            :         "ww = ww * ww;\n"
    1565                 :            :         "ww = ww * ww;\n"
    1566                 :            :         "ww = w * ww;\n"
    1567                 :            :         "ar%d += ww * cc;\n"
    1568                 :            :         "wwsum%d += ww;\n",
    1569                 :            :         vars.scale,
    1570                 :            :         c,
    1571                 :            :         c,
    1572                 :            :         c
    1573                 :            :     );
    1574                 :            : 
    1575                 :            : }
    1576                 :            : #line 550
    1577                 :            :     pl_str_append(alloc, buf, pl_str0(
    1578                 :            :         "}\n"
    1579                 :            :     ));
    1580                 :            : 
    1581                 :            : }
    1582                 :            : if (vars.maybe_skippable)
    1583                 :            : #line 553
    1584                 :            :     pl_str_append(alloc, buf, pl_str0(
    1585                 :            :         "}\n"
    1586                 :            :     ));
    1587                 :            : 
    1588                 :            : 
    1589                 :            : return sizeof(vars);
    1590                 :            : }
    1591                 :            : #line 645
    1592                 :            : size_t _glsl_646_fn(void *alloc, pl_str *buf, const uint8_t *ptr);
    1593                 :            : size_t _glsl_646_fn(void *alloc, pl_str *buf, const uint8_t *ptr)
    1594                 :            : {
    1595                 :            : struct __attribute__((__packed__)) {
    1596                 :            :     uint8_t cmask;
    1597                 :            : } vars;
    1598                 :            : memcpy(&vars, ptr, sizeof(vars));
    1599                 :            : 
    1600                 :            : #line 647
    1601                 :            :     pl_str_append(alloc, buf, pl_str0(
    1602                 :            :         "vec2 ww, cc;\n"
    1603                 :            :     ));
    1604                 :            : 
    1605                 :            : for (uint8_t _mask = vars.cmask, c; _mask && (c = __builtin_ctz(_mask), 1); _mask &= ~(1u << c))
    1606                 :            : #line 649
    1607                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1608                 :            :         "vec2 ar%d = vec2(0.0), wwsum%d = vec2(0.0);\n",
    1609                 :            :         c,
    1610                 :            :         c
    1611                 :            :     );
    1612                 :            : 
    1613                 :            : 
    1614                 :            : return sizeof(vars);
    1615                 :            : }
    1616                 :            : #line 891
    1617                 :            : size_t _glsl_892_fn(void *alloc, pl_str *buf, const uint8_t *ptr);
    1618                 :            : size_t _glsl_892_fn(void *alloc, pl_str *buf, const uint8_t *ptr)
    1619                 :            : {
    1620                 :            : struct __attribute__((__packed__)) {
    1621                 :            :     ident_t scale;
    1622                 :            :     ident_t cfg_antiring;
    1623                 :            :     bool use_ar;
    1624                 :            :     uint8_t cmask;
    1625                 :            :     bool cmask_1_pl_channel_a;
    1626                 :            : } vars;
    1627                 :            : memcpy(&vars, ptr, sizeof(vars));
    1628                 :            : 
    1629                 :            : #line 893
    1630                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1631                 :            :         "color = _%hx / wsum * color;\n",
    1632                 :            :         vars.scale
    1633                 :            :     );
    1634                 :            : 
    1635                 :            : if (vars.use_ar) {
    1636                 :            : for (uint8_t _mask = vars.cmask, c; _mask && (c = __builtin_ctz(_mask), 1); _mask &= ~(1u << c)) {
    1637                 :            : #line 896
    1638                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1639                 :            :         "ww = ar%d / wwsum%d;\n"
    1640                 :            :         "ww.x = 1.0 - ww.x;\n"
    1641                 :            :         "w = clamp(color[%d], ww.x, ww.y);\n"
    1642                 :            :         "w = mix(w, dot(ww, vec2(0.5)), ww.x > ww.y);\n"
    1643                 :            :         "color[%d] = mix(color[%d], w, _%hx);\n",
    1644                 :            :         c,
    1645                 :            :         c,
    1646                 :            :         c,
    1647                 :            :         c,
    1648                 :            :         c,
    1649                 :            :         vars.cfg_antiring
    1650                 :            :     );
    1651                 :            : 
    1652                 :            : }
    1653                 :            : }
    1654                 :            : if (vars.cmask_1_pl_channel_a)
    1655                 :            : #line 904
    1656                 :            :     pl_str_append(alloc, buf, pl_str0(
    1657                 :            :         "color.a = 1.0;\n"
    1658                 :            :     ));
    1659                 :            : 
    1660                 :            : #line 905
    1661                 :            :     pl_str_append(alloc, buf, pl_str0(
    1662                 :            :         "}\n"
    1663                 :            :     ));
    1664                 :            : 
    1665                 :            : 
    1666                 :            : return sizeof(vars);
    1667                 :            : }
    1668                 :            : #line 1060
    1669                 :            : size_t _glsl_1061_fn(void *alloc, pl_str *buf, const uint8_t *ptr);
    1670                 :            : size_t _glsl_1061_fn(void *alloc, pl_str *buf, const uint8_t *ptr)
    1671                 :            : {
    1672                 :            : struct __attribute__((__packed__)) {
    1673                 :            :     float dir_pass_0;
    1674                 :            :     float dir_pass_1;
    1675                 :            :     float n_2_1;
    1676                 :            :     unsigned use_linear_2u_1u;
    1677                 :            :     float denom;
    1678                 :            :     ident_t pos;
    1679                 :            :     ident_t pt;
    1680                 :            :     ident_t src_tex;
    1681                 :            :     ident_t n;
    1682                 :            :     ident_t lut;
    1683                 :            :     ident_t cfg_antiring;
    1684                 :            :     ident_t scale;
    1685                 :            :     uint8_t comps;
    1686                 :            :     bool use_ar;
    1687                 :            :     bool use_linear;
    1688                 :            : } vars;
    1689                 :            : memcpy(&vars, ptr, sizeof(vars));
    1690                 :            : 
    1691                 :            : #line 1061
    1692                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1693                 :            :         "\n"
    1694                 :            :         "vec4 color = vec4(0.0, 0.0, 0.0, 1.0);\n"
    1695                 :            :         "{\n"
    1696                 :            :         "vec2 pos = _%hx, pt = _%hx;\n"
    1697                 :            :         "vec2 size = vec2(textureSize(_%hx, 0));\n"
    1698                 :            :         "vec2 dir = vec2(float(%f), float(%f));\n"
    1699                 :            :         "pt *= dir;\n"
    1700                 :            :         "vec2 fcoord2 = fract(pos * size - vec2(0.5));\n"
    1701                 :            :         "float fcoord = dot(fcoord2, dir);\n"
    1702                 :            :         "vec2 base = pos - fcoord * pt - pt * vec2(float(%f));\n"
    1703                 :            :         "vec4 ws;\n"
    1704                 :            :         "float off;\n"
    1705                 :            :         "%s c, ca = %s(0.0);\n",
    1706                 :            :         vars.pos,
    1707                 :            :         vars.pt,
    1708                 :            :         vars.src_tex,
    1709                 :            :         vars.dir_pass_0,
    1710                 :            :         vars.dir_pass_1,
    1711                 :            :         vars.n_2_1,
    1712                 :            :         sh_float_type(vars.comps),
    1713                 :            :         sh_float_type(vars.comps)
    1714                 :            :     );
    1715                 :            : 
    1716                 :            : if (vars.use_ar) {
    1717                 :            : #line 1075
    1718                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1719                 :            :         "%s hi = %s(0.0);\n"
    1720                 :            :         "%s lo = %s(1e9);\n",
    1721                 :            :         sh_float_type(vars.comps),
    1722                 :            :         sh_float_type(vars.comps),
    1723                 :            :         sh_float_type(vars.comps),
    1724                 :            :         sh_float_type(vars.comps)
    1725                 :            :     );
    1726                 :            : 
    1727                 :            : }
    1728                 :            : #line 1078
    1729                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1730                 :            :         "#pragma unroll 4\n"
    1731                 :            :         "for (uint n = 0u; n < _%hx; n += uint(%u)) {\n"
    1732                 :            :         "if (n %% 4u == 0u)\n"
    1733                 :            :         "ws = _%hx(vec2(float(n / 4u) / float(%f), fcoord));\n"
    1734                 :            :         "off = float(n);\n",
    1735                 :            :         vars.n,
    1736                 :            :         vars.use_linear_2u_1u,
    1737                 :            :         vars.lut,
    1738                 :            :         vars.denom
    1739                 :            :     );
    1740                 :            : 
    1741                 :            : if (vars.use_linear)
    1742                 :            : #line 1084
    1743                 :            :     pl_str_append(alloc, buf, pl_str0(
    1744                 :            :         "off += ws[n % 4u + 1u];\n"
    1745                 :            :     ));
    1746                 :            : 
    1747                 :            : #line 1085
    1748                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1749                 :            :         "c = textureLod(_%hx, base + pt * off, 0.0).%s;\n",
    1750                 :            :         vars.src_tex,
    1751                 :            :         sh_swizzle(vars.comps)
    1752                 :            :     );
    1753                 :            : 
    1754                 :            : if (vars.use_ar) {
    1755                 :            : #line 1087
    1756                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1757                 :            :         "if (n == _%hx / 2u - 1u || n == _%hx / 2u) {\n"
    1758                 :            :         "lo = min(lo, c);\n"
    1759                 :            :         "hi = max(hi, c);\n"
    1760                 :            :         "}\n",
    1761                 :            :         vars.n,
    1762                 :            :         vars.n
    1763                 :            :     );
    1764                 :            : 
    1765                 :            : }
    1766                 :            : #line 1092
    1767                 :            :     pl_str_append(alloc, buf, pl_str0(
    1768                 :            :         "ca += ws[n % 4u] * c;\n"
    1769                 :            :         "}\n"
    1770                 :            :     ));
    1771                 :            : 
    1772                 :            : if (vars.use_ar)
    1773                 :            : #line 1095
    1774                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1775                 :            :         "ca = mix(ca, clamp(ca, lo, hi), _%hx);\n",
    1776                 :            :         vars.cfg_antiring
    1777                 :            :     );
    1778                 :            : 
    1779                 :            : #line 1096
    1780                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1781                 :            :         "color.%s = _%hx * ca;\n"
    1782                 :            :         "}\n",
    1783                 :            :         sh_swizzle(vars.comps),
    1784                 :            :         vars.scale
    1785                 :            :     );
    1786                 :            : 
    1787                 :            : 
    1788                 :            : return sizeof(vars);
    1789                 :            : }
    1790                 :            : #line 1174
    1791                 :            : size_t _glsl_1175_fn(void *alloc, pl_str *buf, const uint8_t *ptr);
    1792                 :            : size_t _glsl_1175_fn(void *alloc, pl_str *buf, const uint8_t *ptr)
    1793                 :            : {
    1794                 :            : struct __attribute__((__packed__)) {
    1795                 :            :     ident_t tf;
    1796                 :            :     ident_t pos;
    1797                 :            :     ident_t tf_c;
    1798                 :            :     ident_t pt;
    1799                 :            :     ident_t tex;
    1800                 :            :     bool params_bicubic;
    1801                 :            :     bool params_alpha_mode;
    1802                 :            :     bool params_alpha_mode_pl_alpha_premultiplied;
    1803                 :            : } vars;
    1804                 :            : memcpy(&vars, ptr, sizeof(vars));
    1805                 :            : 
    1806                 :            : #line 1175
    1807                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1808                 :            :         "\n"
    1809                 :            :         "vec4 color;\n"
    1810                 :            :         "{\n"
    1811                 :            :         "vec2 pos = _%hx * _%hx + _%hx;\n"
    1812                 :            :         "vec2 pt = _%hx;\n",
    1813                 :            :         vars.tf,
    1814                 :            :         vars.pos,
    1815                 :            :         vars.tf_c,
    1816                 :            :         vars.pt
    1817                 :            :     );
    1818                 :            : 
    1819                 :            : if (vars.params_bicubic) {
    1820                 :            : #line 1181
    1821                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1822                 :            :         "vec2 size = vec2(textureSize(_%hx, 0));\n"
    1823                 :            :         "vec2 frac  = fract(pos * size + vec2(0.5));\n"
    1824                 :            :         "vec2 frac2 = frac * frac;\n"
    1825                 :            :         "vec2 inv   = vec2(1.0) - frac;\n"
    1826                 :            :         "vec2 inv2  = inv * inv;\n"
    1827                 :            :         "vec2 w0 = 1.0/6.0 * inv2 * inv;\n"
    1828                 :            :         "vec2 w1 = 2.0/3.0 - 0.5 * frac2 * (2.0 - frac);\n"
    1829                 :            :         "vec2 w2 = 2.0/3.0 - 0.5 * inv2  * (2.0 - inv);\n"
    1830                 :            :         "vec2 w3 = 1.0/6.0 * frac2 * frac;\n"
    1831                 :            :         "vec4 g = vec4(w0 + w1, w2 + w3);\n"
    1832                 :            :         "vec4 h = vec4(w1, w3) / g + inv.xyxy;\n"
    1833                 :            :         "h.xy -= vec2(2.0);\n"
    1834                 :            :         "vec4 p = pos.xyxy + pt.xyxy * h;\n"
    1835                 :            :         "vec4 c00 = textureLod(_%hx, p.xy, 0.0);\n"
    1836                 :            :         "vec4 c01 = textureLod(_%hx, p.xw, 0.0);\n"
    1837                 :            :         "vec4 c0 = mix(c01, c00, g.y);\n"
    1838                 :            :         "vec4 c10 = textureLod(_%hx, p.zy, 0.0);\n"
    1839                 :            :         "vec4 c11 = textureLod(_%hx, p.zw, 0.0);\n"
    1840                 :            :         "vec4 c1 = mix(c11, c10, g.y);\n"
    1841                 :            :         "color = mix(c1, c0, g.x);\n",
    1842                 :            :         vars.tex,
    1843                 :            :         vars.tex,
    1844                 :            :         vars.tex,
    1845                 :            :         vars.tex,
    1846                 :            :         vars.tex
    1847                 :            :     );
    1848                 :            : 
    1849                 :            : } else {
    1850                 :            : #line 1202
    1851                 :            :     pl_str_append_asprintf_c(alloc, buf,
    1852                 :            :         "color = texture(_%hx, pos);\n",
    1853                 :            :         vars.tex
    1854                 :            :     );
    1855                 :            : 
    1856                 :            : }
    1857                 :            : if (vars.params_alpha_mode) {
    1858                 :            : #line 1205
    1859                 :            :     pl_str_append(alloc, buf, pl_str0(
    1860                 :            :         "vec2 border = min(pos, vec2(1.0) - pos);\n"
    1861                 :            :         "border = smoothstep(vec2(0.0), pt, border);\n"
    1862                 :            :     ));
    1863                 :            : 
    1864                 :            : if (vars.params_alpha_mode_pl_alpha_premultiplied)
    1865                 :            : #line 1208
    1866                 :            :     pl_str_append(alloc, buf, pl_str0(
    1867                 :            :         "color.rgba *= border.x * border.y;\n"
    1868                 :            :     ));
    1869                 :            : 
    1870                 :            : else
    1871                 :            : #line 1210
    1872                 :            :     pl_str_append(alloc, buf, pl_str0(
    1873                 :            :         "color.a *= border.x * border.y;\n"
    1874                 :            :     ));
    1875                 :            : 
    1876                 :            : }
    1877                 :            : #line 1212
    1878                 :            :     pl_str_append(alloc, buf, pl_str0(
    1879                 :            :         "}\n"
    1880                 :            :     ));
    1881                 :            : 
    1882                 :            : 
    1883                 :            : return sizeof(vars);
    1884                 :            : }

Generated by: LCOV version 1.16