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 : : }
|