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 <ctype.h>
20 : :
21 : : #include "shaders.h"
22 : :
23 : : #include <libplacebo/shaders/lut.h>
24 : :
25 : : static inline bool isnumeric(char c)
26 : : {
27 : 35 : return (c >= '0' && c <= '9') || c == '-';
28 : : }
29 : :
30 : 11 : void pl_lut_free(struct pl_custom_lut **lut)
31 : : {
32 : 11 : pl_free_ptr(lut);
33 : 11 : }
34 : :
35 : 11 : struct pl_custom_lut *pl_lut_parse_cube(pl_log log, const char *cstr, size_t cstr_len)
36 : : {
37 : 11 : struct pl_custom_lut *lut = pl_zalloc_ptr(NULL, lut);
38 : 11 : pl_str str = (pl_str) { (uint8_t *) cstr, cstr_len };
39 : 11 : lut->signature = pl_str_hash(str);
40 : : int entries = 0;
41 : :
42 : 11 : float min[3] = { 0.0, 0.0, 0.0 };
43 : 11 : float max[3] = { 1.0, 1.0, 1.0 };
44 : :
45 : : // Parse header
46 [ + - + + ]: 35 : while (str.len && !isnumeric(str.buf[0])) {
47 : 24 : pl_str line = pl_str_strip(pl_str_getline(str, &str));
48 [ - + ]: 24 : if (!line.len)
49 : 24 : continue; // skip empty line
50 : :
51 [ + + ]: 24 : if (pl_str_eatstart0(&line, "TITLE")) {
52 [ + - ]: 11 : pl_info(log, "Loading LUT: %.*s", PL_STR_FMT(pl_str_strip(line)));
53 : 11 : continue;
54 : : }
55 : :
56 [ + + ]: 13 : if (pl_str_eatstart0(&line, "LUT_3D_SIZE")) {
57 : 5 : line = pl_str_strip(line);
58 : : int size;
59 [ - + ]: 5 : if (!pl_str_parse_int(line, &size)) {
60 [ # # ]: 0 : pl_err(log, "Failed parsing dimension '%.*s'", PL_STR_FMT(line));
61 : 0 : goto error;
62 : : }
63 [ - + ]: 5 : if (size <= 0 || size > 1024) {
64 : 0 : pl_err(log, "Invalid 3DLUT size: %dx%d%x", size, size, size);
65 : 0 : goto error;
66 : : }
67 : :
68 : 5 : lut->size[0] = lut->size[1] = lut->size[2] = size;
69 : 5 : entries = size * size * size;
70 : 5 : continue;
71 : : }
72 : :
73 [ + + ]: 8 : if (pl_str_eatstart0(&line, "LUT_1D_SIZE")) {
74 : 6 : line = pl_str_strip(line);
75 : : int size;
76 [ - + ]: 6 : if (!pl_str_parse_int(line, &size)) {
77 [ # # ]: 0 : pl_err(log, "Failed parsing dimension '%.*s'", PL_STR_FMT(line));
78 : 0 : goto error;
79 : : }
80 [ - + ]: 6 : if (size <= 0 || size > 65536) {
81 : 0 : pl_err(log, "Invalid 1DLUT size: %d", size);
82 : 0 : goto error;
83 : : }
84 : :
85 : 6 : lut->size[0] = size;
86 : 6 : lut->size[1] = lut->size[2] = 0;
87 : : entries = size;
88 : 6 : continue;
89 : : }
90 : :
91 [ - + ]: 2 : if (pl_str_eatstart0(&line, "DOMAIN_MIN")) {
92 : 0 : line = pl_str_strip(line);
93 [ # # # # ]: 0 : if (!pl_str_parse_float(pl_str_split_char(line, ' ', &line), &min[0]) ||
94 [ # # ]: 0 : !pl_str_parse_float(pl_str_split_char(line, ' ', &line), &min[1]) ||
95 : 0 : !pl_str_parse_float(line, &min[2]))
96 : : {
97 [ # # ]: 0 : pl_err(log, "Failed parsing domain: '%.*s'", PL_STR_FMT(line));
98 : 0 : goto error;
99 : : }
100 : 0 : continue;
101 : : }
102 : :
103 [ + + ]: 2 : if (pl_str_eatstart0(&line, "DOMAIN_MAX")) {
104 : 1 : line = pl_str_strip(line);
105 [ + - + - ]: 2 : if (!pl_str_parse_float(pl_str_split_char(line, ' ', &line), &max[0]) ||
106 [ - + ]: 2 : !pl_str_parse_float(pl_str_split_char(line, ' ', &line), &max[1]) ||
107 : 1 : !pl_str_parse_float(line, &max[2]))
108 : : {
109 [ # # ]: 0 : pl_err(log, "Failed parsing domain: '%.*s'", PL_STR_FMT(line));
110 : 0 : goto error;
111 : : }
112 : 1 : continue;
113 : : }
114 : :
115 [ + - ]: 1 : if (pl_str_eatstart0(&line, "#")) {
116 [ + - ]: 1 : pl_debug(log, "Unhandled .cube comment: %.*s",
117 : : PL_STR_FMT(pl_str_strip(line)));
118 : 1 : continue;
119 : : }
120 : :
121 [ # # ]: 0 : pl_warn(log, "Unhandled .cube line: %.*s", PL_STR_FMT(pl_str_strip(line)));
122 : : }
123 : :
124 [ - + ]: 11 : if (!entries) {
125 : 0 : pl_err(log, "Missing LUT size specification?");
126 : 0 : goto error;
127 : : }
128 : :
129 [ + + ]: 44 : for (int i = 0; i < 3; i++) {
130 [ - + ]: 33 : if (max[i] - min[i] < 1e-6) {
131 : 0 : pl_err(log, "Invalid domain range: [%f, %f]", min[i], max[i]);
132 : 0 : goto error;
133 : : }
134 : : }
135 : :
136 : 11 : float *data = pl_alloc(lut, sizeof(float[3]) * entries);
137 : 11 : lut->data = data;
138 : :
139 : : // Parse LUT body
140 : : pl_clock_t start = pl_clock_now();
141 [ + + ]: 92 : for (int n = 0; n < entries; n++) {
142 [ + + ]: 324 : for (int c = 0; c < 3; c++) {
143 : : static const char * const digits = "0123456789.-+e";
144 : :
145 : : // Extract valid digit sequence
146 : 243 : size_t len = pl_strspn(str, digits);
147 : 243 : pl_str entry = (pl_str) { str.buf, len };
148 : 243 : str.buf += len;
149 : 243 : str.len -= len;
150 : :
151 [ - + ]: 243 : if (!entry.len) {
152 [ # # ]: 0 : if (!str.len) {
153 : 0 : pl_err(log, "Failed parsing LUT: Unexpected EOF, expected "
154 : : "%d entries, got %d", entries * 3, n * 3 + c + 1);
155 : : } else {
156 : 0 : pl_err(log, "Failed parsing LUT: Unexpected '%c', expected "
157 : : "digit", str.buf[0]);
158 : : }
159 : 0 : goto error;
160 : : }
161 : :
162 : : float num;
163 [ - + ]: 243 : if (!pl_str_parse_float(entry, &num)) {
164 [ # # ]: 0 : pl_err(log, "Failed parsing float value '%.*s'", PL_STR_FMT(entry));
165 : 0 : goto error;
166 : : }
167 : :
168 : : // Rescale to range 0.0 - 1.0
169 : 243 : *data++ = (num - min[c]) / (max[c] - min[c]);
170 : :
171 : : // Skip whitespace between digits
172 : 243 : str = pl_str_strip(str);
173 : : }
174 : : }
175 : :
176 : 11 : str = pl_str_strip(str);
177 [ - + ]: 11 : if (str.len)
178 : 0 : pl_warn(log, "Extra data after LUT?... ignoring '%c'", str.buf[0]);
179 : :
180 : 11 : pl_log_cpu_time(log, start, pl_clock_now(), "parsing .cube LUT");
181 : 11 : return lut;
182 : :
183 : 0 : error:
184 : 0 : pl_free(lut);
185 : 0 : return NULL;
186 : : }
187 : :
188 : 27 : static void fill_lut(void *datap, const struct sh_lut_params *params)
189 : : {
190 : 27 : const struct pl_custom_lut *lut = params->priv;
191 : :
192 : 27 : int dim_r = params->width;
193 [ + + ]: 27 : int dim_g = PL_DEF(params->height, 1);
194 [ + + ]: 27 : int dim_b = PL_DEF(params->depth, 1);
195 : :
196 : : float *data = datap;
197 [ + + ]: 68 : for (int b = 0; b < dim_b; b++) {
198 [ + + ]: 112 : for (int g = 0; g < dim_g; g++) {
199 [ + + ]: 232 : for (int r = 0; r < dim_r; r++) {
200 : 161 : size_t offset = (b * dim_g + g) * dim_r + r;
201 : 161 : const float *src = &lut->data[offset * 3];
202 : 161 : float *dst = &data[offset * 4];
203 : 161 : dst[0] = src[0];
204 : 161 : dst[1] = src[1];
205 : 161 : dst[2] = src[2];
206 : 161 : dst[3] = 0.0f;
207 : : }
208 : : }
209 : : }
210 : 27 : }
211 : :
212 : 99 : void pl_shader_custom_lut(pl_shader sh, const struct pl_custom_lut *lut,
213 : : pl_shader_obj *lut_state)
214 : : {
215 [ + - ]: 99 : if (!lut)
216 : 0 : return;
217 : :
218 : : int dims;
219 [ + - + + : 99 : if (lut->size[0] > 0 && lut->size[1] > 0 && lut->size[2] > 0) {
- + ]
220 : : dims = 3;
221 [ + - + - : 50 : } else if (lut->size[0] > 0 && !lut->size[1] && !lut->size[2]) {
- + ]
222 : : dims = 1;
223 : : } else {
224 : 0 : SH_FAIL(sh, "Invalid dimensions %dx%dx%d for pl_custom_lut, must be 1D "
225 : : "or 3D!", lut->size[0], lut->size[1], lut->size[2]);
226 : 0 : return;
227 : : }
228 : :
229 [ + - ]: 99 : if (!sh_require(sh, PL_SHADER_SIG_COLOR, 0, 0))
230 : : return;
231 : :
232 : 99 : ident_t fun = sh_lut(sh, sh_lut_params(
233 : : .object = lut_state,
234 : : .var_type = PL_VAR_FLOAT,
235 : : .method = SH_LUT_TETRAHEDRAL,
236 : : .width = lut->size[0],
237 : : .height = lut->size[1],
238 : : .depth = lut->size[2],
239 : : .comps = 4, // for better texel alignment
240 : : .signature = lut->signature,
241 : : .fill = fill_lut,
242 : : .priv = (void *) lut,
243 : : ));
244 : :
245 [ - + ]: 99 : if (!fun) {
246 : 0 : SH_FAIL(sh, "pl_shader_custom_lut: failed generating LUT object");
247 : 0 : return;
248 : : }
249 : :
250 : 99 : GLSL("// pl_shader_custom_lut \n");
251 : :
252 : : static const pl_matrix3x3 zero = {0};
253 [ - + ]: 99 : if (memcmp(&lut->shaper_in, &zero, sizeof(zero)) != 0) {
254 : 0 : GLSL("color.rgb = "$" * color.rgb; \n", sh_var(sh, (struct pl_shader_var) {
255 : : .var = pl_var_mat3("shaper_in"),
256 : : .data = PL_TRANSPOSE_3X3(lut->shaper_in.m),
257 : : }));
258 : : }
259 : :
260 [ + + - ]: 99 : switch (dims) {
261 : 50 : case 1:
262 : 50 : sh_describe(sh, "custom 1DLUT");
263 : 50 : GLSL("color.rgb = vec3("$"(color.r).r, \n"
264 : : " "$"(color.g).g, \n"
265 : : " "$"(color.b).b); \n",
266 : : fun, fun, fun);
267 : : break;
268 : 49 : case 3:
269 : 49 : sh_describe(sh, "custom 3DLUT");
270 : 49 : GLSL("color.rgb = "$"(color.rgb).rgb; \n", fun);
271 : : break;
272 : : }
273 : :
274 [ - + ]: 99 : if (memcmp(&lut->shaper_out, &zero, sizeof(zero)) != 0) {
275 : 0 : GLSL("color.rgb = "$" * color.rgb; \n", sh_var(sh, (struct pl_shader_var) {
276 : : .var = pl_var_mat3("shaper_out"),
277 : : .data = PL_TRANSPOSE_3X3(lut->shaper_out.m),
278 : : }));
279 : : }
280 : : }
281 : :
282 : : // Defines a LUT position helper macro. This translates from an absolute texel
283 : : // scale (either in texels, or normalized to [0,1]) to the texture coordinate
284 : : // scale for the corresponding sample in a texture of dimension `lut_size`.
285 : 672 : static ident_t texel_scale(pl_shader sh, int lut_size, bool normalized)
286 : : {
287 : 672 : const float base = 0.5f / lut_size;
288 : 672 : const float end = 1.0f - 0.5f / lut_size;
289 [ - + ]: 672 : const float scale = (end - base) / (normalized ? 1.0f : (lut_size - 1));
290 : :
291 : 672 : ident_t name = sh_fresh(sh, "LUT_SCALE");
292 : 672 : GLSLH("#define "$"(x) ("$" * (x) + "$") \n",
293 : : name, SH_FLOAT(scale), SH_FLOAT(base));
294 : 672 : return name;
295 : : }
296 : :
297 : : struct sh_lut_obj {
298 : : enum sh_lut_type type;
299 : : enum sh_lut_method method;
300 : : enum pl_var_type vartype;
301 : : pl_fmt fmt;
302 : : int width, height, depth, comps;
303 : : uint64_t signature;
304 : : bool error; // reset if params change
305 : :
306 : : // weights, depending on the lut type
307 : : pl_tex tex;
308 : : pl_str str;
309 : : void *data;
310 : : };
311 : :
312 : 83 : static void sh_lut_uninit(pl_gpu gpu, void *ptr)
313 : : {
314 : : struct sh_lut_obj *lut = ptr;
315 : 83 : pl_tex_destroy(gpu, &lut->tex);
316 : 83 : pl_free(lut->str.buf);
317 : 83 : pl_free(lut->data);
318 : :
319 : 83 : *lut = (struct sh_lut_obj) {0};
320 : 83 : }
321 : :
322 : : // Maximum number of floats to embed as a literal array (when using SH_LUT_AUTO)
323 : : #define SH_LUT_MAX_LITERAL_SOFT 64
324 : : #define SH_LUT_MAX_LITERAL_HARD 256
325 : :
326 : 628 : ident_t sh_lut(pl_shader sh, const struct sh_lut_params *params)
327 : : {
328 : 628 : pl_gpu gpu = SH_GPU(sh);
329 : 628 : pl_cache_obj obj = { .key = CACHE_KEY_SH_LUT ^ params->signature };
330 : :
331 : 628 : const enum pl_var_type vartype = params->var_type;
332 [ - + ]: 628 : pl_assert(vartype != PL_VAR_INVALID);
333 [ + + - + ]: 628 : pl_assert(params->method == SH_LUT_NONE || vartype == PL_VAR_FLOAT);
334 [ + - + - : 628 : pl_assert(params->width > 0 && params->height >= 0 && params->depth >= 0);
- + ]
335 [ - + ]: 628 : pl_assert(params->comps > 0);
336 [ - + - - ]: 628 : pl_assert(!params->cache || params->signature);
337 : :
338 : 628 : int sizes[] = { params->width, params->height, params->depth };
339 [ + + + + ]: 1090 : int size = params->width * PL_DEF(params->height, 1) * PL_DEF(params->depth, 1);
340 [ + + + + ]: 628 : int dims = params->depth ? 3 : params->height ? 2 : 1;
341 : : enum sh_lut_method method = params->method;
342 [ + + ]: 628 : if (method == SH_LUT_TETRAHEDRAL && dims != 3)
343 : : method = SH_LUT_LINEAR;
344 [ - + ]: 628 : if (method == SH_LUT_CUBIC && dims != 3)
345 : : method = SH_LUT_LINEAR;
346 : :
347 : : int texdim = 0;
348 : 1884 : uint32_t max_tex_dim[] = {
349 [ + + ]: 628 : gpu ? gpu->limits.max_tex_1d_dim : 0,
350 [ + + ]: 628 : gpu ? gpu->limits.max_tex_2d_dim : 0,
351 [ + + + - ]: 628 : (gpu && gpu->glsl.version > 100) ? gpu->limits.max_tex_3d_dim : 0,
352 : : };
353 : :
354 : 628 : struct sh_lut_obj *lut = SH_OBJ(sh, params->object, PL_SHADER_OBJ_LUT,
355 : : struct sh_lut_obj, sh_lut_uninit);
356 : :
357 [ - + ]: 628 : if (!lut)
358 : : return NULL_IDENT;
359 : :
360 [ + + ]: 327 : bool update = params->update || lut->signature != params->signature ||
361 [ - + - + ]: 259 : vartype != lut->vartype || params->fmt != lut->fmt ||
362 [ - + ]: 259 : params->width != lut->width || params->height != lut->height ||
363 [ + + - + ]: 887 : params->depth != lut->depth || params->comps != lut->comps;
364 : :
365 [ + - - - ]: 628 : if (lut->error && !update)
366 : : return NULL_IDENT; // suppress error spam until something changes
367 : :
368 : : // Try picking the right number of dimensions for the texture LUT. This
369 : : // allows e.g. falling back to 2D textures if 1D textures are unsupported.
370 [ + + ]: 630 : for (int d = dims; d <= PL_ARRAY_SIZE(max_tex_dim); d++) {
371 : : // For a given dimension to be compatible, all coordinates need to be
372 : : // within the maximum texture size for that dimension
373 [ + + ]: 1812 : for (int i = 0; i < d; i++) {
374 [ + + ]: 1185 : if (sizes[i] > max_tex_dim[d - 1])
375 : 2 : goto next_dim;
376 : : }
377 : :
378 : : // All dimensions are compatible, so pick this texture dimension
379 : : texdim = d;
380 : : break;
381 : :
382 : : next_dim: ; // `continue` out of the inner loop
383 : : }
384 : :
385 : : static const enum pl_fmt_type fmt_type[PL_VAR_TYPE_COUNT] = {
386 : : [PL_VAR_SINT] = PL_FMT_SINT,
387 : : [PL_VAR_UINT] = PL_FMT_UINT,
388 : : [PL_VAR_FLOAT] = PL_FMT_FLOAT,
389 : : };
390 : :
391 : : enum pl_fmt_caps texcaps = PL_FMT_CAP_SAMPLEABLE;
392 : 628 : bool is_linear = method == SH_LUT_LINEAR || method == SH_LUT_CUBIC;
393 [ + + ]: 628 : if (is_linear)
394 : : texcaps |= PL_FMT_CAP_LINEAR;
395 : :
396 : 628 : pl_fmt texfmt = params->fmt;
397 [ + + ]: 628 : if (texfmt) {
398 : : bool ok;
399 [ - - + ]: 46 : switch (texfmt->type) {
400 : 0 : case PL_FMT_SINT: ok = vartype == PL_VAR_SINT; break;
401 : 0 : case PL_FMT_UINT: ok = vartype == PL_VAR_UINT; break;
402 : 46 : default: ok = vartype == PL_VAR_FLOAT; break;
403 : : }
404 : :
405 [ - + ]: 46 : if (!ok) {
406 : 0 : PL_ERR(sh, "Specified texture format '%s' does not match LUT "
407 : : "data type!", texfmt->name);
408 : 0 : goto error;
409 : : }
410 : :
411 [ - + ]: 46 : if (~texfmt->caps & texcaps) {
412 : 0 : PL_ERR(sh, "Specified texture format '%s' does not match "
413 : : "required capabilities 0x%x!\n", texfmt->name, texcaps);
414 : 0 : goto error;
415 : : }
416 : : }
417 : :
418 [ + + ]: 628 : if (texdim && !texfmt) {
419 [ + + ]: 589 : texfmt = pl_find_fmt(gpu, fmt_type[vartype], params->comps,
420 : : vartype == PL_VAR_FLOAT ? 16 : 32,
421 : 581 : pl_var_type_size(vartype) * 8,
422 : : texcaps);
423 : : }
424 : :
425 : 628 : enum sh_lut_type type = params->lut_type;
426 : :
427 : : // The linear sampling code currently only supports 1D linear interpolation
428 [ + + ]: 628 : if (is_linear && dims > 1) {
429 [ - + ]: 238 : if (texfmt) {
430 : : type = SH_LUT_TEXTURE;
431 : : } else {
432 : 0 : PL_ERR(sh, "Can't emulate linear LUTs for 2D/3D LUTs and no "
433 : : "texture support available!");
434 : 0 : goto error;
435 : : }
436 : : }
437 : :
438 [ + + + + ]: 628 : bool can_uniform = gpu && gpu->limits.max_variable_comps >= size * params->comps;
439 : 628 : bool can_literal = sh_glsl(sh).version > 110; // needed for literal arrays
440 [ + + + + ]: 628 : can_literal &= size <= SH_LUT_MAX_LITERAL_HARD && !params->dynamic;
441 : :
442 : : // Deselect unsupported methods
443 [ - + ]: 628 : if (type == SH_LUT_UNIFORM && !can_uniform)
444 : : type = SH_LUT_AUTO;
445 [ - + ]: 628 : if (type == SH_LUT_LITERAL && !can_literal)
446 : : type = SH_LUT_AUTO;
447 [ - + ]: 628 : if (type == SH_LUT_TEXTURE && !texfmt)
448 : : type = SH_LUT_AUTO;
449 : :
450 : : // Sorted by priority
451 [ + + - + ]: 628 : if (!type && can_literal && !method && size <= SH_LUT_MAX_LITERAL_SOFT)
452 : : type = SH_LUT_LITERAL;
453 [ + + ]: 628 : if (!type && texfmt)
454 : : type = SH_LUT_TEXTURE;
455 [ + + ]: 628 : if (!type && can_uniform)
456 : : type = SH_LUT_UNIFORM;
457 [ + - ]: 628 : if (!type && can_literal)
458 : : type = SH_LUT_LITERAL;
459 : :
460 [ + + ]: 628 : if (!type) {
461 : 1 : PL_ERR(sh, "Can't generate LUT: no compatible methods!");
462 : 1 : goto error;
463 : : }
464 : :
465 : : // Reinitialize the existing LUT if needed
466 : 627 : update |= type != lut->type;
467 : 627 : update |= method != lut->method;
468 : :
469 [ + + ]: 627 : if (update) {
470 [ + + ]: 368 : if (params->dynamic)
471 : 34 : pl_log_level_cap(sh->log, PL_LOG_TRACE);
472 : :
473 : 368 : size_t el_size = params->comps * pl_var_type_size(vartype);
474 [ + + ]: 368 : if (type == SH_LUT_TEXTURE)
475 : 363 : el_size = texfmt->texel_size;
476 : :
477 : 368 : size_t buf_size = size * el_size;
478 [ - + - - ]: 368 : if (pl_cache_get(params->cache, &obj) && obj.size == buf_size) {
479 : 0 : PL_DEBUG(sh, "Re-using cached LUT (0x%"PRIx64") with size %zu",
480 : : obj.key, obj.size);
481 : : } else {
482 : 368 : PL_DEBUG(sh, "LUT invalidated, regenerating..");
483 : 368 : pl_cache_obj_resize(NULL, &obj, buf_size);
484 : : pl_clock_t start = pl_clock_now();
485 : 368 : params->fill(obj.data, params);
486 : 368 : pl_log_cpu_time(sh->log, start, pl_clock_now(), "generating shader LUT");
487 : : }
488 : :
489 [ + - - + ]: 368 : pl_assert(obj.data && obj.size);
490 [ + + ]: 368 : if (params->dynamic)
491 : 34 : pl_log_level_cap(sh->log, PL_LOG_NONE);
492 : :
493 [ + + - - ]: 368 : switch (type) {
494 : 363 : case SH_LUT_TEXTURE: {
495 [ - + ]: 363 : if (!texdim) {
496 : 0 : PL_ERR(sh, "Texture LUT exceeds texture dimensions!");
497 : 0 : goto error;
498 : : }
499 : :
500 [ - + ]: 363 : if (!texfmt) {
501 : 0 : PL_ERR(sh, "Found no compatible texture format for LUT!");
502 : 0 : goto error;
503 : : }
504 : :
505 : 1452 : struct pl_tex_params tex_params = {
506 : 363 : .w = params->width,
507 [ + + ]: 363 : .h = PL_DEF(params->height, texdim >= 2 ? 1 : 0),
508 [ + + ]: 363 : .d = PL_DEF(params->depth, texdim >= 3 ? 1 : 0),
509 : : .format = texfmt,
510 : : .sampleable = true,
511 : 363 : .host_writable = params->dynamic,
512 [ + + ]: 363 : .initial_data = params->dynamic ? NULL : obj.data,
513 : 363 : .debug_tag = params->debug_tag,
514 : : };
515 : :
516 : : bool ok;
517 [ + + ]: 363 : if (params->dynamic) {
518 : 29 : ok = pl_tex_recreate(gpu, &lut->tex, &tex_params);
519 [ + - ]: 29 : if (ok) {
520 : 29 : ok = pl_tex_upload(gpu, pl_tex_transfer_params(
521 : : .tex = lut->tex,
522 : : .ptr = obj.data,
523 : : ));
524 : : }
525 : : } else {
526 : : // Can't use pl_tex_recreate because of `initial_data`
527 : 334 : pl_tex_destroy(gpu, &lut->tex);
528 : 334 : lut->tex = pl_tex_create(gpu, &tex_params);
529 : 334 : ok = lut->tex;
530 : : }
531 : :
532 [ - + ]: 363 : if (!ok) {
533 : 0 : PL_ERR(sh, "Failed creating LUT texture!");
534 : 0 : goto error;
535 : : }
536 : 363 : break;
537 : : }
538 : :
539 : 5 : case SH_LUT_UNIFORM:
540 : 5 : pl_free(lut->data);
541 : 5 : lut->data = pl_memdup(NULL, obj.data, obj.size);
542 : 5 : break;
543 : :
544 : 0 : case SH_LUT_LITERAL: {
545 : 0 : lut->str.len = 0;
546 : : static const char prefix[PL_VAR_TYPE_COUNT] = {
547 : : [PL_VAR_SINT] = 'i',
548 : : [PL_VAR_UINT] = 'u',
549 : : [PL_VAR_FLOAT] = ' ',
550 : : };
551 : :
552 [ # # ]: 0 : for (int i = 0; i < size * params->comps; i += params->comps) {
553 [ # # ]: 0 : if (i > 0)
554 : 0 : pl_str_append_asprintf_c(lut, &lut->str, ",");
555 [ # # ]: 0 : if (params->comps > 1) {
556 : 0 : pl_str_append_asprintf_c(lut, &lut->str, "%cvec%d(",
557 : 0 : prefix[vartype], params->comps);
558 : : }
559 [ # # ]: 0 : for (int c = 0; c < params->comps; c++) {
560 [ # # # # : 0 : switch (vartype) {
# ]
561 : 0 : case PL_VAR_FLOAT:
562 : 0 : pl_str_append_asprintf_c(lut, &lut->str, "%s%f",
563 : : c > 0 ? "," : "",
564 [ # # ]: 0 : ((float *) obj.data)[i+c]);
565 : 0 : break;
566 : 0 : case PL_VAR_UINT:
567 : 0 : pl_str_append_asprintf_c(lut, &lut->str, "%s%u",
568 : : c > 0 ? "," : "",
569 [ # # ]: 0 : ((unsigned int *) obj.data)[i+c]);
570 : 0 : break;
571 : 0 : case PL_VAR_SINT:
572 : 0 : pl_str_append_asprintf_c(lut, &lut->str, "%s%d",
573 : : c > 0 ? "," : "",
574 [ # # ]: 0 : ((int *) obj.data)[i+c]);
575 : 0 : break;
576 : : case PL_VAR_INVALID:
577 : : case PL_VAR_TYPE_COUNT:
578 : 0 : pl_unreachable();
579 : : }
580 : : }
581 [ # # ]: 0 : if (params->comps > 1)
582 : 0 : pl_str_append_asprintf_c(lut, &lut->str, ")");
583 : : }
584 : : break;
585 : : }
586 : :
587 : : case SH_LUT_AUTO:
588 : : pl_unreachable();
589 : : }
590 : :
591 : 368 : lut->type = type;
592 : 368 : lut->method = method;
593 : 368 : lut->vartype = vartype;
594 : 368 : lut->fmt = params->fmt;
595 : 368 : lut->width = params->width;
596 : 368 : lut->height = params->height;
597 : 368 : lut->depth = params->depth;
598 : 368 : lut->comps = params->comps;
599 : 368 : lut->signature = params->signature;
600 : 368 : pl_cache_set(params->cache, &obj);
601 : : }
602 : :
603 : : // Done updating, generate the GLSL
604 : 627 : ident_t name = sh_fresh(sh, "lut");
605 : : ident_t arr_name = NULL_IDENT;
606 : :
607 : : static const char * const swizzles[] = {"x", "xy", "xyz", "xyzw"};
608 : : static const char * const vartypes[PL_VAR_TYPE_COUNT][4] = {
609 : : [PL_VAR_SINT] = { "int", "ivec2", "ivec3", "ivec4" },
610 : : [PL_VAR_UINT] = { "uint", "uvec2", "uvec3", "uvec4" },
611 : : [PL_VAR_FLOAT] = { "float", "vec2", "vec3", "vec4" },
612 : : };
613 : :
614 [ + + - - ]: 627 : switch (type) {
615 : 622 : case SH_LUT_TEXTURE: {
616 [ - + ]: 622 : assert(texdim);
617 : 622 : ident_t tex = sh_desc(sh, (struct pl_shader_desc) {
618 : : .desc = {
619 : : .name = "weights",
620 : : .type = PL_DESC_SAMPLED_TEX,
621 : : },
622 : : .binding = {
623 : 622 : .object = lut->tex,
624 : : .sample_mode = is_linear ? PL_TEX_SAMPLE_LINEAR
625 : 622 : : PL_TEX_SAMPLE_NEAREST,
626 : : }
627 : : });
628 : :
629 [ + + ]: 622 : if (is_linear) {
630 : 404 : ident_t pos_macros[PL_ARRAY_SIZE(sizes)] = {0};
631 [ + + ]: 1076 : for (int i = 0; i < dims; i++)
632 : 672 : pos_macros[i] = texel_scale(sh, sizes[i], true);
633 : :
634 : 404 : GLSLH("#define "$"(pos) (textureLod("$", %s(\\\n",
635 : : name, tex, vartypes[PL_VAR_FLOAT][texdim - 1]);
636 : :
637 [ + + ]: 1076 : for (int i = 0; i < texdim; i++) {
638 [ + + ]: 672 : char sep = i == 0 ? ' ' : ',';
639 [ + - ]: 672 : if (pos_macros[i]) {
640 [ + + ]: 672 : if (dims > 1) {
641 : 506 : GLSLH(" %c"$"(%s(pos).%c)\\\n", sep, pos_macros[i],
642 : : vartypes[PL_VAR_FLOAT][dims - 1], "xyzw"[i]);
643 : : } else {
644 : 166 : GLSLH(" %c"$"(float(pos))\\\n", sep, pos_macros[i]);
645 : : }
646 : : } else {
647 : 0 : GLSLH(" %c%f\\\n", sep, 0.5);
648 : : }
649 : : }
650 : 404 : GLSLH(" ), 0.0).%s)\n", swizzles[params->comps - 1]);
651 : : } else {
652 : 218 : GLSLH("#define "$"(pos) (texelFetch("$", %s(pos",
653 : : name, tex, vartypes[PL_VAR_SINT][texdim - 1]);
654 : :
655 : : // Fill up extra components of the index
656 [ - + ]: 218 : for (int i = dims; i < texdim; i++)
657 : 0 : GLSLH(", 0");
658 : :
659 : 218 : GLSLH("), 0).%s)\n", swizzles[params->comps - 1]);
660 : : }
661 : : break;
662 : : }
663 : :
664 : 5 : case SH_LUT_UNIFORM:
665 : 5 : arr_name = sh_var(sh, (struct pl_shader_var) {
666 : : .var = {
667 : : .name = "weights",
668 : : .type = vartype,
669 : 5 : .dim_v = params->comps,
670 : : .dim_m = 1,
671 : : .dim_a = size,
672 : : },
673 : 5 : .data = lut->data,
674 : : });
675 : 5 : break;
676 : :
677 : 0 : case SH_LUT_LITERAL:
678 : 0 : arr_name = sh_fresh(sh, "weights");
679 : 0 : GLSLH("const %s "$"[%d] = %s[](\n ",
680 : : vartypes[vartype][params->comps - 1], arr_name, size,
681 : : vartypes[vartype][params->comps - 1]);
682 : 0 : sh_append_str(sh, SH_BUF_HEADER, lut->str);
683 : 0 : GLSLH(");\n");
684 : : break;
685 : :
686 : : case SH_LUT_AUTO:
687 : : pl_unreachable();
688 : : }
689 : :
690 [ - + ]: 5 : if (arr_name) {
691 [ - + ]: 5 : GLSLH("#define "$"(pos) ("$"[int((pos)%s)\\\n",
692 : : name, arr_name, dims > 1 ? "[0]" : "");
693 : 5 : int shift = params->width;
694 [ + + ]: 10 : for (int i = 1; i < dims; i++) {
695 : 5 : GLSLH(" + %d * int((pos)[%d])\\\n", shift, i);
696 : 5 : shift *= sizes[i];
697 : : }
698 : 5 : GLSLH(" ])\n");
699 : :
700 [ + - ]: 5 : if (is_linear) {
701 [ # # ]: 0 : pl_assert(dims == 1);
702 [ # # ]: 0 : pl_assert(vartype == PL_VAR_FLOAT);
703 : : ident_t arr_lut = name;
704 : 0 : name = sh_fresh(sh, "lut_lin");
705 : 0 : GLSLH("%s "$"(float fpos) { \n"
706 : : " fpos = clamp(fpos, 0.0, 1.0) * %d.0; \n"
707 : : " float fbase = floor(fpos); \n"
708 : : " float fceil = ceil(fpos); \n"
709 : : " float fcoord = fpos - fbase; \n"
710 : : " return mix("$"(fbase), "$"(fceil), fcoord); \n"
711 : : "} \n",
712 : : vartypes[PL_VAR_FLOAT][params->comps - 1], name,
713 : : size - 1,
714 : : arr_lut, arr_lut);
715 : : }
716 : : }
717 : :
718 [ - + ]: 627 : if (method == SH_LUT_CUBIC && dims == 3) {
719 : : ident_t lin_lut = name;
720 : 0 : name = sh_fresh(sh, "lut_tricubic");
721 : 0 : GLSLH("%s "$"(vec3 pos) { \n"
722 : : " vec3 scale = vec3(%d.0, %d.0, %d.0); \n"
723 : : " vec3 scale_inv = 1.0 / scale; \n"
724 : : " pos *= scale; \n"
725 : : " vec3 fpos = fract(pos); \n"
726 : : " vec3 base = pos - fpos; \n"
727 : : " vec3 fpos2 = fpos * fpos; \n"
728 : : " vec3 inv = 1.0 - fpos; \n"
729 : : " vec3 inv2 = inv * inv; \n"
730 : : " vec3 w0 = 1.0/6.0 * inv2 * inv; \n"
731 : : " vec3 w1 = 2.0/3.0 - 0.5 * fpos2 * (2.0 - fpos); \n"
732 : : " vec3 w2 = 2.0/3.0 - 0.5 * inv2 * (2.0 - inv); \n"
733 : : " vec3 w3 = 1.0/6.0 * fpos2 * fpos; \n"
734 : : " vec3 g0 = w0 + w1; \n"
735 : : " vec3 g1 = w2 + w3; \n"
736 : : " vec3 h0 = scale_inv * ((w1 / g0) - 1.0 + base); \n"
737 : : " vec3 h1 = scale_inv * ((w3 / g1) + 1.0 + base); \n"
738 : : " %s c000, c001, c010, c011, c100, c101, c110, c111; \n"
739 : : " c000 = "$"(h0); \n"
740 : : " c100 = "$"(vec3(h1.x, h0.y, h0.z)); \n"
741 : : " c000 = mix(c100, c000, g0.x); \n"
742 : : " c010 = "$"(vec3(h0.x, h1.y, h0.z)); \n"
743 : : " c110 = "$"(vec3(h1.x, h1.y, h0.z)); \n"
744 : : " c010 = mix(c110, c010, g0.x); \n"
745 : : " c000 = mix(c010, c000, g0.y); \n"
746 : : " c001 = "$"(vec3(h0.x, h0.y, h1.z)); \n"
747 : : " c101 = "$"(vec3(h1.x, h0.y, h1.z)); \n"
748 : : " c001 = mix(c101, c001, g0.x); \n"
749 : : " c011 = "$"(vec3(h0.x, h1.y, h1.z)); \n"
750 : : " c111 = "$"(h1); \n"
751 : : " c011 = mix(c111, c011, g0.x); \n"
752 : : " c001 = mix(c011, c001, g0.y); \n"
753 : : " return mix(c001, c000, g0.z); \n"
754 : : "} \n",
755 : : vartypes[PL_VAR_FLOAT][params->comps - 1], name,
756 : : sizes[0] - 1, sizes[1] - 1, sizes[2] - 1,
757 : : vartypes[PL_VAR_FLOAT][params->comps - 1],
758 : : lin_lut, lin_lut, lin_lut, lin_lut,
759 : : lin_lut, lin_lut, lin_lut, lin_lut);
760 : : }
761 : :
762 [ + + ]: 627 : if (method == SH_LUT_TETRAHEDRAL) {
763 : : ident_t int_lut = name;
764 : 65 : name = sh_fresh(sh, "lut_barycentric");
765 : 65 : GLSLH("%s "$"(vec3 pos) { \n"
766 : : // Compute bounding vertices and fractional part
767 : : " pos = clamp(pos, 0.0, 1.0) * vec3(%d.0, %d.0, %d.0); \n"
768 : : " vec3 base = floor(pos); \n"
769 : : " vec3 fpart = pos - base; \n"
770 : : // v0 and v3 are always 'black' and 'white', respectively
771 : : // v1 and v2 are the closest RGB and CMY vertices, respectively
772 : : " ivec3 v0 = ivec3(base), v3 = ivec3(ceil(pos)); \n"
773 : : " ivec3 v1 = v0, v2 = v3; \n"
774 : : // Table of boolean checks to simplify following math
775 : : " bvec3 c = greaterThanEqual(fpart.xyz, fpart.yzx); \n"
776 : : " bool c_xy = c.x, c_yx = !c.x, \n"
777 : : " c_yz = c.y, c_zy = !c.y, \n"
778 : : " c_zx = c.z, c_xz = !c.z; \n"
779 : : " vec3 s = fpart.xyz; \n"
780 : : " bool cond; \n",
781 : : vartypes[PL_VAR_FLOAT][params->comps - 1], name,
782 : : sizes[0] - 1, sizes[1] - 1, sizes[2] - 1);
783 : :
784 : : // Subdivision of the cube into six congruent tetrahedras
785 : : //
786 : : // For each tetrahedron, test if the point is inside, and if so, update
787 : : // the edge vertices. We test all six, even though only one case will
788 : : // ever be true, because this avoids branches.
789 : : static const char *indices[] = { "xyz", "xzy", "zxy", "zyx", "yzx", "yxz"};
790 [ + + ]: 455 : for (int i = 0; i < PL_ARRAY_SIZE(indices); i++) {
791 : 390 : const char x = indices[i][0], y = indices[i][1], z = indices[i][2];
792 : 390 : GLSLH("cond = c_%c%c && c_%c%c; \n"
793 : : "s = cond ? fpart.%c%c%c : s; \n"
794 : : "v1.%c = cond ? v3.%c : v1.%c; \n"
795 : : "v2.%c = cond ? v0.%c : v2.%c; \n",
796 : : x, y, y, z,
797 : : x, y, z,
798 : : x, x, x,
799 : : z, z, z);
800 : : }
801 : :
802 : : // Interpolate in barycentric coordinates, with four texel fetches
803 : 65 : GLSLH(" return (1.0 - s.x) * "$"(v0) + \n"
804 : : " (s.x - s.y) * "$"(v1) + \n"
805 : : " (s.y - s.z) * "$"(v2) + \n"
806 : : " (s.z) * "$"(v3); \n"
807 : : "} \n",
808 : : int_lut, int_lut, int_lut, int_lut);
809 : : }
810 : :
811 [ - + ]: 627 : lut->error = false;
812 : : pl_cache_obj_free(&obj);
813 [ - + ]: 627 : pl_assert(name);
814 : : return name;
815 : :
816 : 1 : error:
817 [ - + ]: 1 : lut->error = true;
818 : : pl_cache_obj_free(&obj);
819 : 1 : return NULL_IDENT;
820 : : }
|