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 <stdio.h>
19 : : #include <math.h>
20 : :
21 : : #include "common.h"
22 : : #include "log.h"
23 : : #include "shaders.h"
24 : :
25 : 0 : pl_shader_info pl_shader_info_ref(pl_shader_info pinfo)
26 : : {
27 : : struct sh_info *info = (struct sh_info *) pinfo;
28 [ # # ]: 0 : if (!info)
29 : : return NULL;
30 : :
31 : 0 : pl_rc_ref(&info->rc);
32 : 0 : return &info->info;
33 : : }
34 : :
35 : 41 : void pl_shader_info_deref(pl_shader_info *pinfo)
36 : : {
37 : 41 : struct sh_info *info = (struct sh_info *) *pinfo;
38 [ + - ]: 41 : if (!info)
39 : : return;
40 : :
41 [ + - ]: 41 : if (pl_rc_deref(&info->rc))
42 : 41 : pl_free(info);
43 : 41 : *pinfo = NULL;
44 : : }
45 : :
46 : 41 : static struct sh_info *sh_info_alloc(void *alloc)
47 : : {
48 : 41 : struct sh_info *info = pl_zalloc_ptr(alloc, info);
49 : 41 : info->tmp = pl_tmp(info);
50 : 41 : pl_rc_init(&info->rc);
51 : 41 : return info;
52 : : }
53 : :
54 : : // Re-use `sh_info` allocation if possible, allocate new otherwise
55 : 2766 : static struct sh_info *sh_info_recycle(struct sh_info *info)
56 : : {
57 [ - + ]: 2766 : if (!pl_rc_deref(&info->rc))
58 : 0 : return sh_info_alloc(NULL);
59 : :
60 : 2766 : memset(&info->info, 0, sizeof(info->info)); // reset public fields
61 : 2766 : pl_free_children(info->tmp);
62 : 2766 : pl_rc_ref(&info->rc);
63 : 2766 : info->desc.len = 0;
64 : 2766 : info->steps.num = 0;
65 : 2766 : return info;
66 : : }
67 : :
68 : : static uint8_t reverse_bits(uint8_t x)
69 : : {
70 : : static const uint8_t reverse_nibble[16] = {
71 : : 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
72 : : 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf,
73 : : };
74 : :
75 : 2795 : return reverse_nibble[x & 0xF] << 4 | reverse_nibble[x >> 4];
76 : : }
77 : :
78 : 2807 : static void init_shader(pl_shader sh, const struct pl_shader_params *params)
79 : : {
80 [ + + ]: 2807 : if (params) {
81 : 2795 : sh->info->info.params = *params;
82 : :
83 : : // To avoid collisions for shaders with very high number of
84 : : // identifiers, pack the shader ID into the highest bits (MSB -> LSB)
85 : : pl_static_assert(sizeof(sh->prefix) > sizeof(params->id));
86 : : const int shift = 8 * (sizeof(sh->prefix) - sizeof(params->id));
87 : 2795 : sh->prefix = reverse_bits(params->id) << shift;
88 : : }
89 : :
90 : 2807 : sh->name = sh_fresh(sh, "main");
91 : 2807 : }
92 : :
93 : 41 : pl_shader pl_shader_alloc(pl_log log, const struct pl_shader_params *params)
94 : : {
95 : : static const int glsl_ver_req = 130;
96 [ + + - + : 41 : if (params && params->glsl.version && params->glsl.version < 130) {
- - ]
97 : 0 : pl_err(log, "Requested GLSL version %d too low (required: %d)",
98 : : params->glsl.version, glsl_ver_req);
99 : 0 : return NULL;
100 : : }
101 : :
102 : 41 : pl_shader sh = pl_alloc_ptr(NULL, sh);
103 : 41 : *sh = (struct pl_shader_t) {
104 : : .log = log,
105 : 41 : .tmp = pl_tmp(sh),
106 : 41 : .info = sh_info_alloc(NULL),
107 : : .mutable = true,
108 : : };
109 : :
110 [ + + ]: 205 : for (int i = 0; i < PL_ARRAY_SIZE(sh->buffers); i++)
111 : 164 : sh->buffers[i] = pl_str_builder_alloc(sh);
112 : :
113 : 41 : init_shader(sh, params);
114 : 41 : return sh;
115 : : }
116 : :
117 : : static void sh_obj_deref(pl_shader_obj obj);
118 : :
119 : 5561 : void sh_deref(pl_shader sh)
120 : : {
121 : 5561 : pl_free_children(sh->tmp);
122 : :
123 [ + + ]: 7539 : for (int i = 0; i < sh->obj.num; i++)
124 : 1978 : sh_obj_deref(sh->obj.elem[i]);
125 : 5561 : sh->obj.num = 0;
126 : 5561 : }
127 : :
128 : 41 : void pl_shader_free(pl_shader *psh)
129 : : {
130 : 41 : pl_shader sh = *psh;
131 [ + - ]: 41 : if (!sh)
132 : : return;
133 : :
134 : 41 : sh_deref(sh);
135 : 41 : pl_shader_info_deref((pl_shader_info *) &sh->info);
136 : 41 : pl_free_ptr(psh);
137 : : }
138 : :
139 : 2766 : void pl_shader_reset(pl_shader sh, const struct pl_shader_params *params)
140 : : {
141 : 2766 : sh_deref(sh);
142 : :
143 : 5532 : struct pl_shader_t new = {
144 : 2766 : .log = sh->log,
145 : 2766 : .tmp = sh->tmp,
146 : 2766 : .info = sh_info_recycle(sh->info),
147 : 2766 : .data.buf = sh->data.buf,
148 : : .mutable = true,
149 : :
150 : : // Preserve array allocations
151 : 2766 : .obj.elem = sh->obj.elem,
152 : 2766 : .vas.elem = sh->vas.elem,
153 : 2766 : .vars.elem = sh->vars.elem,
154 : 2766 : .descs.elem = sh->descs.elem,
155 : 2766 : .consts.elem = sh->consts.elem,
156 : : };
157 : :
158 : : // Preserve buffer allocations
159 : 2766 : memcpy(new.buffers, sh->buffers, sizeof(new.buffers));
160 [ + + ]: 13830 : for (int i = 0; i < PL_ARRAY_SIZE(new.buffers); i++)
161 : 11064 : pl_str_builder_reset(new.buffers[i]);
162 : :
163 : 2766 : *sh = new;
164 : 2766 : init_shader(sh, params);
165 : 2766 : }
166 : :
167 : 11385 : static void *sh_alloc(pl_shader sh, size_t size, size_t align)
168 : : {
169 : 11385 : const size_t offset = PL_ALIGN2(sh->data.len, align);
170 : 11385 : const size_t req_size = offset + size;
171 [ + + ]: 11385 : if (req_size <= pl_get_size(sh->data.buf)) {
172 : 10540 : sh->data.len = offset + size;
173 : 10540 : return sh->data.buf + offset;
174 : : }
175 : :
176 : : // We can't realloc this buffer because various pointers will be left
177 : : // dangling, so just reparent it onto `sh->tmp` (so it will be cleaned
178 : : // up when the shader is next reset) and allocate a new, larger buffer
179 : : // in its place
180 : 845 : const size_t new_size = PL_MAX(req_size << 1, 256);
181 : 845 : pl_steal(sh->tmp, sh->data.buf);
182 : 845 : sh->data.buf = pl_alloc(sh, new_size);
183 : 845 : sh->data.len = size;
184 : 845 : return sh->data.buf;
185 : : }
186 : :
187 : 7268 : static void *sh_memdup(pl_shader sh, const void *data, size_t size, size_t align)
188 : : {
189 [ + - ]: 7268 : if (!size)
190 : : return NULL;
191 : :
192 : 7268 : void *dst = sh_alloc(sh, size, align);
193 [ - + ]: 7268 : assert(data);
194 : : memcpy(dst, data, size);
195 : 7268 : return dst;
196 : : }
197 : :
198 : 0 : bool pl_shader_is_failed(const pl_shader sh)
199 : : {
200 : 0 : return sh->failed;
201 : : }
202 : :
203 : 18518 : struct pl_glsl_version sh_glsl(const pl_shader sh)
204 : : {
205 [ - + ]: 18518 : if (SH_PARAMS(sh).glsl.version)
206 : 0 : return SH_PARAMS(sh).glsl;
207 : :
208 [ + + ]: 18518 : if (SH_GPU(sh))
209 : 18517 : return SH_GPU(sh)->glsl;
210 : :
211 : 1 : return (struct pl_glsl_version) { .version = 130 };
212 : : }
213 : :
214 : 104 : bool sh_try_compute(pl_shader sh, int bw, int bh, bool flex, size_t mem)
215 : : {
216 [ - + ]: 104 : pl_assert(bw && bh);
217 : : int *sh_bw = &sh->group_size[0];
218 : : int *sh_bh = &sh->group_size[1];
219 : :
220 : 104 : struct pl_glsl_version glsl = sh_glsl(sh);
221 [ + + ]: 104 : if (!glsl.compute) {
222 : 4 : PL_TRACE(sh, "Disabling compute shader due to missing `compute` support");
223 : 4 : return false;
224 : : }
225 : :
226 [ - + ]: 100 : if (sh->shmem + mem > glsl.max_shmem_size) {
227 : 0 : PL_TRACE(sh, "Disabling compute shader due to insufficient shmem");
228 : 0 : return false;
229 : : }
230 : :
231 [ - + ]: 100 : if (sh->type == SH_FRAGMENT) {
232 : 0 : PL_TRACE(sh, "Disabling compute shader because shader is already marked "
233 : : "as fragment shader");
234 : 0 : return false;
235 : : }
236 : :
237 [ + - ]: 100 : if (bw > glsl.max_group_size[0] ||
238 [ + - ]: 100 : bh > glsl.max_group_size[1] ||
239 [ - + ]: 100 : (bw * bh) > glsl.max_group_threads)
240 : : {
241 [ # # ]: 0 : if (!flex) {
242 : 0 : PL_TRACE(sh, "Disabling compute shader due to exceeded group "
243 : : "thread count.");
244 : 0 : return false;
245 : : } else {
246 : : // Pick better group sizes
247 : 0 : bw = PL_MIN(bw, glsl.max_group_size[0]);
248 : 0 : bh = glsl.max_group_threads / bw;
249 : : }
250 : : }
251 : :
252 : 100 : sh->shmem += mem;
253 : :
254 : : // If the current shader is either not a compute shader, or we have no
255 : : // choice but to override the metadata, always do so
256 [ - + - - : 100 : if (sh->type != SH_COMPUTE || (sh->flexible_work_groups && !flex)) {
- - ]
257 : 100 : *sh_bw = bw;
258 : 100 : *sh_bh = bh;
259 : 100 : sh->type = SH_COMPUTE;
260 : 100 : sh->flexible_work_groups = flex;
261 : 100 : return true;
262 : : }
263 : :
264 : : // If both shaders are flexible, pick the larger of the two
265 [ # # # # ]: 0 : if (sh->flexible_work_groups && flex) {
266 : 0 : *sh_bw = PL_MAX(*sh_bw, bw);
267 : 0 : *sh_bh = PL_MAX(*sh_bh, bh);
268 [ # # ]: 0 : pl_assert(*sh_bw * *sh_bh <= glsl.max_group_threads);
269 : : return true;
270 : : }
271 : :
272 : : // At this point we're looking only at a non-flexible compute shader
273 [ # # ]: 0 : pl_assert(sh->type == SH_COMPUTE && !sh->flexible_work_groups);
274 [ # # ]: 0 : if (!flex) {
275 : : // Ensure parameters match
276 [ # # # # ]: 0 : if (bw != *sh_bw || bh != *sh_bh) {
277 : 0 : PL_TRACE(sh, "Disabling compute shader due to incompatible group "
278 : : "sizes %dx%d and %dx%d", *sh_bw, *sh_bh, bw, bh);
279 : 0 : sh->shmem -= mem;
280 : 0 : return false;
281 : : }
282 : : }
283 : :
284 : : return true;
285 : : }
286 : :
287 : 8720 : bool pl_shader_is_compute(const pl_shader sh)
288 : : {
289 : 8720 : return sh->type == SH_COMPUTE;
290 : : }
291 : :
292 : 2662 : bool pl_shader_output_size(const pl_shader sh, int *w, int *h)
293 : : {
294 [ + + + - ]: 2662 : if (!sh->output_w || !sh->output_h)
295 : : return false;
296 : :
297 [ + + ]: 998 : *w = sh->transpose ? sh->output_h : sh->output_w;
298 [ + + ]: 998 : *h = sh->transpose ? sh->output_w : sh->output_h;
299 : 998 : return true;
300 : : }
301 : :
302 : 33188 : ident_t sh_fresh(pl_shader sh, const char *name)
303 : : {
304 : 33188 : unsigned short id = ++sh->fresh;
305 [ - + ]: 33188 : assert(!(sh->prefix & id));
306 : 33188 : id |= sh->prefix;
307 : :
308 [ - + ]: 33188 : assert(name);
309 : 33188 : return sh_mkident(id, name);
310 : : }
311 : :
312 : : static inline ident_t sh_fresh_name(pl_shader sh, const char **pname)
313 : : {
314 : 16794 : ident_t id = sh_fresh(sh, *pname);
315 : 4757 : *pname = sh_ident_pack(id);
316 : : return id;
317 : : }
318 : :
319 : 4673 : ident_t sh_var(pl_shader sh, struct pl_shader_var sv)
320 : : {
321 : : ident_t id = sh_fresh_name(sh, &sv.var.name);
322 : 4673 : struct pl_var_layout layout = pl_var_host_layout(0, &sv.var);
323 : 4673 : sv.data = sh_memdup(sh, sv.data, layout.size, layout.stride);
324 [ + + + + : 4673 : PL_ARRAY_APPEND(sh, sh->vars, sv);
- + ]
325 : 4673 : return id;
326 : : }
327 : :
328 : 72 : ident_t sh_var_int(pl_shader sh, const char *name, int val, bool dynamic)
329 : : {
330 : 72 : return sh_var(sh, (struct pl_shader_var) {
331 : 72 : .var = pl_var_int(name),
332 : : .data = &val,
333 : : .dynamic = dynamic,
334 : : });
335 : : }
336 : :
337 : 36 : ident_t sh_var_uint(pl_shader sh, const char *name, unsigned int val, bool dynamic)
338 : : {
339 : 36 : return sh_var(sh, (struct pl_shader_var) {
340 : 36 : .var = pl_var_uint(name),
341 : : .data = &val,
342 : : .dynamic = dynamic,
343 : : });
344 : : }
345 : :
346 : 1120 : ident_t sh_var_float(pl_shader sh, const char *name, float val, bool dynamic)
347 : : {
348 : 1120 : return sh_var(sh, (struct pl_shader_var) {
349 : 1120 : .var = pl_var_float(name),
350 : : .data = &val,
351 : : .dynamic = dynamic,
352 : : });
353 : : }
354 : :
355 : 188 : ident_t sh_var_mat3(pl_shader sh, const char *name, pl_matrix3x3 val)
356 : : {
357 : 188 : return sh_var(sh, (struct pl_shader_var) {
358 : 188 : .var = pl_var_mat3(name),
359 : 188 : .data = PL_TRANSPOSE_3X3(val.m),
360 : : });
361 : : }
362 : :
363 : 3122 : ident_t sh_desc(pl_shader sh, struct pl_shader_desc sd)
364 : : {
365 [ + + - - ]: 3122 : switch (sd.desc.type) {
366 : 24 : case PL_DESC_BUF_UNIFORM:
367 : : case PL_DESC_BUF_STORAGE:;
368 : 24 : size_t bsize = sizeof(sd.buffer_vars[0]) * sd.num_buffer_vars;
369 : 24 : sd.buffer_vars = sh_memdup(sh, sd.buffer_vars, bsize,
370 : : alignof(struct pl_buffer_var));
371 [ + + ]: 108 : for (int i = 0; i < sd.num_buffer_vars; i++) {
372 : 84 : struct pl_var *bv = &sd.buffer_vars[i].var;
373 : 84 : const char *name = bv->name;
374 : 84 : GLSLP("#define %s "$"\n", name, sh_fresh_name(sh, &bv->name));
375 : : }
376 : : break;
377 : :
378 : 3098 : case PL_DESC_BUF_TEXEL_UNIFORM:
379 : : case PL_DESC_BUF_TEXEL_STORAGE:
380 : : case PL_DESC_SAMPLED_TEX:
381 : : case PL_DESC_STORAGE_IMG:
382 [ - + ]: 3098 : pl_assert(!sd.num_buffer_vars);
383 : : break;
384 : :
385 : : case PL_DESC_INVALID:
386 : : case PL_DESC_TYPE_COUNT:
387 : 0 : pl_unreachable();
388 : : }
389 : :
390 : : ident_t id = sh_fresh_name(sh, &sd.desc.name);
391 [ + + - + : 3122 : PL_ARRAY_APPEND(sh, sh->descs, sd);
- + ]
392 : 3122 : return id;
393 : : }
394 : :
395 : 9555 : ident_t sh_const(pl_shader sh, struct pl_shader_const sc)
396 : : {
397 [ - + - - ]: 9555 : if (SH_PARAMS(sh).dynamic_constants && !sc.compile_time) {
398 : 0 : return sh_var(sh, (struct pl_shader_var) {
399 : : .var = {
400 : : .name = sc.name,
401 : : .type = sc.type,
402 : : .dim_v = 1,
403 : : .dim_m = 1,
404 : : .dim_a = 1,
405 : : },
406 : : .data = sc.data,
407 : : });
408 : : }
409 : :
410 : : ident_t id = sh_fresh_name(sh, &sc.name);
411 : :
412 : 9555 : pl_gpu gpu = SH_GPU(sh);
413 [ + + + + ]: 9555 : if (gpu && gpu->limits.max_constants) {
414 [ + + + + ]: 2575 : if (!sc.compile_time || gpu->limits.array_size_constants) {
415 : 2571 : size_t size = pl_var_type_size(sc.type);
416 : 2571 : sc.data = sh_memdup(sh, sc.data, size, size);
417 [ + + + + : 2571 : PL_ARRAY_APPEND(sh, sh->consts, sc);
- + ]
418 : 2571 : return id;
419 : : }
420 : : }
421 : :
422 : : // Fallback for GPUs without specialization constants
423 [ + + + - ]: 6984 : switch (sc.type) {
424 : : case PL_VAR_SINT:
425 : 74 : GLSLH("const int "$" = %d; \n", id, *(int *) sc.data);
426 : 74 : return id;
427 : : case PL_VAR_UINT:
428 : 196 : GLSLH("const uint "$" = uint(%u); \n", id, *(unsigned int *) sc.data);
429 : 196 : return id;
430 : : case PL_VAR_FLOAT:
431 : 6714 : GLSLH("const float "$" = float(%f); \n", id, *(float *) sc.data);
432 : 6714 : return id;
433 : : case PL_VAR_INVALID:
434 : : case PL_VAR_TYPE_COUNT:
435 : : break;
436 : : }
437 : :
438 : 0 : pl_unreachable();
439 : : }
440 : :
441 : 126 : ident_t sh_const_int(pl_shader sh, const char *name, int val)
442 : : {
443 : 126 : return sh_const(sh, (struct pl_shader_const) {
444 : : .type = PL_VAR_SINT,
445 : : .name = name,
446 : : .data = &val,
447 : : });
448 : : }
449 : :
450 : 256 : ident_t sh_const_uint(pl_shader sh, const char *name, unsigned int val)
451 : : {
452 : 256 : return sh_const(sh, (struct pl_shader_const) {
453 : : .type = PL_VAR_UINT,
454 : : .name = name,
455 : : .data = &val,
456 : : });
457 : : }
458 : :
459 : 9093 : ident_t sh_const_float(pl_shader sh, const char *name, float val)
460 : : {
461 : 9093 : return sh_const(sh, (struct pl_shader_const) {
462 : : .type = PL_VAR_FLOAT,
463 : : .name = name,
464 : : .data = &val,
465 : : });
466 : : }
467 : :
468 : 4117 : ident_t sh_attr(pl_shader sh, struct pl_shader_va sva)
469 : : {
470 : 4117 : const size_t vsize = sva.attr.fmt->texel_size;
471 : 4117 : uint8_t *data = sh_alloc(sh, vsize * 4, vsize);
472 [ + + ]: 20585 : for (int i = 0; i < 4; i++) {
473 : 16468 : memcpy(data, sva.data[i], vsize);
474 : 16468 : sva.data[i] = data;
475 : 16468 : data += vsize;
476 : : }
477 : :
478 : : ident_t id = sh_fresh_name(sh, &sva.attr.name);
479 [ + + - + : 4117 : PL_ARRAY_APPEND(sh, sh->vas, sva);
- + ]
480 : 4117 : return id;
481 : : }
482 : :
483 : 4117 : ident_t sh_attr_vec2(pl_shader sh, const char *name, const pl_rect2df *rc)
484 : : {
485 : 4117 : pl_gpu gpu = SH_GPU(sh);
486 [ - + ]: 4117 : if (!gpu) {
487 : 0 : SH_FAIL(sh, "Failed adding vertex attr '%s': No GPU available!", name);
488 : 0 : return NULL_IDENT;
489 : : }
490 : :
491 : 4117 : pl_fmt fmt = pl_find_vertex_fmt(gpu, PL_FMT_FLOAT, 2);
492 [ - + ]: 4117 : if (!fmt) {
493 : 0 : SH_FAIL(sh, "Failed adding vertex attr '%s': no vertex fmt!", name);
494 : 0 : return NULL_IDENT;
495 : : }
496 : :
497 : 4117 : float verts[4][2] = {
498 : 4117 : { rc->x0, rc->y0 },
499 : 4117 : { rc->x1, rc->y0 },
500 : 4117 : { rc->x0, rc->y1 },
501 : : { rc->x1, rc->y1 },
502 : : };
503 : :
504 : 4117 : return sh_attr(sh, (struct pl_shader_va) {
505 : : .attr = {
506 : : .name = name,
507 : : .fmt = fmt,
508 : : },
509 : : .data = { verts[0], verts[1], verts[2], verts[3] },
510 : : });
511 : : }
512 : :
513 [ - + ]: 2292 : ident_t sh_bind(pl_shader sh, pl_tex tex,
514 : : enum pl_tex_address_mode address_mode,
515 : : enum pl_tex_sample_mode sample_mode,
516 : : const char *name, const pl_rect2df *rect,
517 : : ident_t *out_pos, ident_t *out_pt)
518 : : {
519 : : if (pl_tex_params_dimension(tex->params) != 2) {
520 : 0 : SH_FAIL(sh, "Failed binding texture '%s': not a 2D texture!", name);
521 : 0 : return NULL_IDENT;
522 : : }
523 : :
524 [ - + ]: 2292 : if (!tex->params.sampleable) {
525 : 0 : SH_FAIL(sh, "Failed binding texture '%s': texture not sampleable!", name);
526 : 0 : return NULL_IDENT;
527 : : }
528 : :
529 : 2292 : ident_t itex = sh_desc(sh, (struct pl_shader_desc) {
530 : : .desc = {
531 : : .name = name,
532 : : .type = PL_DESC_SAMPLED_TEX,
533 : : },
534 : : .binding = {
535 : : .object = tex,
536 : : .address_mode = address_mode,
537 : : .sample_mode = sample_mode,
538 : : },
539 : : });
540 : :
541 : : float sx, sy;
542 [ + - ]: 2292 : if (tex->sampler_type == PL_SAMPLER_RECT) {
543 : : sx = 1.0;
544 : : sy = 1.0;
545 : : } else {
546 : 2292 : sx = 1.0 / tex->params.w;
547 : 2292 : sy = 1.0 / tex->params.h;
548 : : }
549 : :
550 [ + + ]: 2292 : if (out_pos) {
551 : 2268 : pl_rect2df full = {
552 : 2268 : .x1 = tex->params.w,
553 : 2268 : .y1 = tex->params.h,
554 : : };
555 : :
556 [ + + ]: 2268 : rect = PL_DEF(rect, &full);
557 : 2268 : *out_pos = sh_attr_vec2(sh, "tex_coord", &(pl_rect2df) {
558 : 2268 : .x0 = sx * rect->x0, .y0 = sy * rect->y0,
559 : 2268 : .x1 = sx * rect->x1, .y1 = sy * rect->y1,
560 : : });
561 : : }
562 : :
563 [ + + ]: 2292 : if (out_pt) {
564 : 379 : *out_pt = sh_var(sh, (struct pl_shader_var) {
565 : 379 : .var = pl_var_vec2("tex_pt"),
566 : 379 : .data = &(float[2]) {sx, sy},
567 : : });
568 : : }
569 : :
570 : : return itex;
571 : : }
572 : :
573 : 957 : bool sh_buf_desc_append(void *alloc, pl_gpu gpu,
574 : : struct pl_shader_desc *buf_desc,
575 : : struct pl_var_layout *out_layout,
576 : : const struct pl_var new_var)
577 : : {
578 : 957 : struct pl_buffer_var bv = { .var = new_var };
579 : : size_t cur_size = sh_buf_desc_size(buf_desc);
580 : :
581 [ + + - - ]: 957 : switch (buf_desc->desc.type) {
582 : 953 : case PL_DESC_BUF_UNIFORM:
583 : 953 : bv.layout = pl_std140_layout(cur_size, &new_var);
584 [ - + ]: 953 : if (bv.layout.offset + bv.layout.size > gpu->limits.max_ubo_size)
585 : : return false;
586 : : break;
587 : 4 : case PL_DESC_BUF_STORAGE:
588 : 4 : bv.layout = pl_std430_layout(cur_size, &new_var);
589 [ - + ]: 4 : if (bv.layout.offset + bv.layout.size > gpu->limits.max_ssbo_size)
590 : : return false;
591 : : break;
592 : : case PL_DESC_INVALID:
593 : : case PL_DESC_SAMPLED_TEX:
594 : : case PL_DESC_STORAGE_IMG:
595 : : case PL_DESC_BUF_TEXEL_UNIFORM:
596 : : case PL_DESC_BUF_TEXEL_STORAGE:
597 : : case PL_DESC_TYPE_COUNT:
598 : 0 : pl_unreachable();
599 : : }
600 : :
601 [ + + ]: 957 : if (out_layout)
602 : 949 : *out_layout = bv.layout;
603 [ + + + + : 957 : PL_ARRAY_APPEND_RAW(alloc, buf_desc->buffer_vars, buf_desc->num_buffer_vars, bv);
- + ]
604 : 957 : return true;
605 : : }
606 : :
607 : 1949 : size_t sh_buf_desc_size(const struct pl_shader_desc *buf_desc)
608 : : {
609 [ + + + + ]: 2906 : if (!buf_desc->num_buffer_vars)
610 : : return 0;
611 : :
612 : : const struct pl_buffer_var *last;
613 : 957 : last = &buf_desc->buffer_vars[buf_desc->num_buffer_vars - 1];
614 : 957 : return last->layout.offset + last->layout.size;
615 : : }
616 : :
617 : 1119 : void sh_describef(pl_shader sh, const char *fmt, ...)
618 : : {
619 : : va_list ap;
620 : 1119 : va_start(ap, fmt);
621 : 1119 : sh_describe(sh, pl_vasprintf(sh->info->tmp, fmt, ap));
622 : 1119 : va_end(ap);
623 : 1119 : }
624 : :
625 : : static const char *insigs[] = {
626 : : [PL_SHADER_SIG_NONE] = "",
627 : : [PL_SHADER_SIG_COLOR] = "vec4 color",
628 : : };
629 : :
630 : : static const char *outsigs[] = {
631 : : [PL_SHADER_SIG_NONE] = "void",
632 : : [PL_SHADER_SIG_COLOR] = "vec4",
633 : : };
634 : :
635 : : static const char *retvals[] = {
636 : : [PL_SHADER_SIG_NONE] = "",
637 : : [PL_SHADER_SIG_COLOR] = "return color;",
638 : : };
639 : :
640 : : // libplacebo currently only allows 2D samplers for shader signatures
641 : : static const char *samplers2D[] = {
642 : : [PL_SAMPLER_NORMAL] = "sampler2D",
643 : : [PL_SAMPLER_RECT] = "sampler2DRect",
644 : : [PL_SAMPLER_EXTERNAL] = "samplerExternalOES",
645 : : };
646 : :
647 : 843 : ident_t sh_subpass(pl_shader sh, pl_shader sub)
648 : : {
649 [ - + ]: 843 : pl_assert(sh->mutable);
650 : :
651 [ - + ]: 843 : if (sh->prefix == sub->prefix) {
652 : 0 : PL_TRACE(sh, "Can't merge shaders: conflicting identifiers!");
653 : 0 : return NULL_IDENT;
654 : : }
655 : :
656 : : // Check for shader compatibility
657 [ + - ]: 843 : int res_w = PL_DEF(sh->output_w, sub->output_w),
658 [ + - ]: 843 : res_h = PL_DEF(sh->output_h, sub->output_h);
659 : :
660 [ + + + - ]: 843 : if ((sub->output_w && res_w != sub->output_w) ||
661 [ + + - + ]: 843 : (sub->output_h && res_h != sub->output_h))
662 : : {
663 : 0 : PL_TRACE(sh, "Can't merge shaders: incompatible sizes: %dx%d and %dx%d",
664 : : sh->output_w, sh->output_h, sub->output_w, sub->output_h);
665 : 0 : return NULL_IDENT;
666 : : }
667 : :
668 [ + + ]: 843 : if (sub->type == SH_COMPUTE) {
669 : 4 : int subw = sub->group_size[0],
670 : 4 : subh = sub->group_size[1];
671 : 4 : bool flex = sub->flexible_work_groups;
672 : :
673 [ - + ]: 4 : if (!sh_try_compute(sh, subw, subh, flex, sub->shmem)) {
674 : 0 : PL_TRACE(sh, "Can't merge shaders: incompatible block sizes or "
675 : : "exceeded shared memory resource capabilities");
676 : 0 : return NULL_IDENT;
677 : : }
678 : : }
679 : :
680 : 843 : sh->output_w = res_w;
681 : 843 : sh->output_h = res_h;
682 : :
683 : : // Append the prelude and header
684 : 843 : pl_str_builder_concat(sh->buffers[SH_BUF_PRELUDE], sub->buffers[SH_BUF_PRELUDE]);
685 : 843 : pl_str_builder_concat(sh->buffers[SH_BUF_HEADER], sub->buffers[SH_BUF_HEADER]);
686 : :
687 : : // Append the body as a new header function
688 [ - + ]: 843 : if (sub->input == PL_SHADER_SIG_SAMPLER) {
689 [ # # ]: 0 : pl_assert(sub->sampler_prefix);
690 : 0 : GLSLH("%s "$"(%c%s src_tex, vec2 tex_coord) {\n",
691 : : outsigs[sub->output], sub->name,
692 : : sub->sampler_prefix, samplers2D[sub->sampler_type]);
693 : : } else {
694 : 843 : GLSLH("%s "$"(%s) {\n",
695 : : outsigs[sub->output], sub->name, insigs[sub->input]);
696 : : }
697 : 843 : pl_str_builder_concat(sh->buffers[SH_BUF_HEADER], sub->buffers[SH_BUF_BODY]);
698 : 843 : GLSLH("%s\n}\n\n", retvals[sub->output]);
699 : :
700 : : // Steal all inputs and objects from the subpass
701 : : #define ARRAY_STEAL(arr) do \
702 : : { \
703 : : PL_ARRAY_CONCAT(sh, sh->arr, sub->arr); \
704 : : sub->arr.num = 0; \
705 : : } while (0)
706 : :
707 [ + + - + ]: 843 : ARRAY_STEAL(obj);
708 [ + + + + ]: 843 : ARRAY_STEAL(vas);
709 [ + + - + ]: 843 : ARRAY_STEAL(vars);
710 [ + + + + ]: 843 : ARRAY_STEAL(descs);
711 [ + + - + ]: 843 : ARRAY_STEAL(consts);
712 : : #undef ARRAY_STEAL
713 : :
714 : : // Steal the scratch buffer (if it holds data)
715 [ + + ]: 843 : if (sub->data.len) {
716 : 817 : pl_steal(sh->tmp, sub->data.buf);
717 : 817 : sub->data = (pl_str) {0};
718 : : }
719 : :
720 : : // Steal all temporary allocations and mark the child as unusable
721 : 843 : pl_steal(sh->tmp, sub->tmp);
722 : 843 : sub->tmp = pl_tmp(sub);
723 : 843 : sub->failed = true;
724 : :
725 : : // Steal the shader steps array (and allocations)
726 [ - + ]: 843 : pl_assert(pl_rc_count(&sub->info->rc) == 1);
727 [ + + - + ]: 843 : PL_ARRAY_CONCAT(sh->info, sh->info->steps, sub->info->steps);
728 : 843 : pl_steal(sh->info->tmp, sub->info->tmp);
729 : 843 : sub->info->tmp = pl_tmp(sub->info);
730 : 843 : sub->info->steps.num = 0; // sanity
731 : :
732 : 843 : return sub->name;
733 : : }
734 : :
735 : 1951 : pl_str_builder sh_finalize_internal(pl_shader sh)
736 : : {
737 [ - + ]: 1951 : pl_assert(sh->mutable); // this function should only ever be called once
738 [ + - ]: 1951 : if (sh->failed)
739 : : return NULL;
740 : :
741 : : // Padding for readability
742 : 1951 : GLSLP("\n");
743 : :
744 : : // Concatenate everything onto the prelude to form the final output
745 : 1951 : pl_str_builder_concat(sh->buffers[SH_BUF_PRELUDE], sh->buffers[SH_BUF_HEADER]);
746 : :
747 [ + + ]: 1951 : if (sh->input == PL_SHADER_SIG_SAMPLER) {
748 [ - + ]: 1 : pl_assert(sh->sampler_prefix);
749 : 1 : GLSLP("%s "$"(%c%s src_tex, vec2 tex_coord) {\n",
750 : : outsigs[sh->output], sh->name,
751 : : sh->sampler_prefix,
752 : : samplers2D[sh->sampler_type]);
753 : : } else {
754 : 1950 : GLSLP("%s "$"(%s) {\n", outsigs[sh->output], sh->name, insigs[sh->input]);
755 : : }
756 : :
757 : 1951 : pl_str_builder_concat(sh->buffers[SH_BUF_PRELUDE], sh->buffers[SH_BUF_BODY]);
758 : 1951 : pl_str_builder_concat(sh->buffers[SH_BUF_PRELUDE], sh->buffers[SH_BUF_FOOTER]);
759 : 1951 : GLSLP("%s\n}\n\n", retvals[sh->output]);
760 : :
761 : : // Generate the shader info
762 : 1951 : struct sh_info *info = sh->info;
763 : 1951 : info->info.steps = info->steps.elem;
764 : 1951 : info->info.num_steps = info->steps.num;
765 : 1951 : info->info.description = "(unknown shader)";
766 : :
767 : : // Generate pretty description
768 [ + + ]: 5318 : for (int i = 0; i < info->steps.num; i++) {
769 : 3367 : const char *step = info->steps.elem[i];
770 : :
771 : : // Prevent duplicates. We're okay using a weak equality check here
772 : : // because most pass descriptions are static strings.
773 [ + + ]: 5186 : for (int j = 0; j < i; j++) {
774 [ + + ]: 1887 : if (info->steps.elem[j] == step)
775 : 68 : goto next_step;
776 : : }
777 : :
778 : : int count = 1;
779 [ + + ]: 5198 : for (int j = i+1; j < info->steps.num; j++) {
780 [ + + ]: 1899 : if (info->steps.elem[j] == step)
781 : 68 : count++;
782 : : }
783 : :
784 [ + + ]: 3299 : const char *prefix = i > 0 ? ", " : "";
785 [ + + ]: 3299 : if (count > 1) {
786 : 36 : pl_str_append_asprintf(info, &info->desc, "%s%s x%d",
787 : : prefix, step, count);
788 : : } else {
789 : 3263 : pl_str_append_asprintf(info, &info->desc, "%s%s", prefix, step);
790 : : }
791 : :
792 : 3367 : next_step: ;
793 : : }
794 : :
795 [ + + ]: 1951 : if (info->desc.len)
796 : 1902 : info->info.description = (char *) info->desc.buf;
797 : :
798 : 1951 : sh->mutable = false;
799 : 1951 : return sh->buffers[SH_BUF_PRELUDE];
800 : : }
801 : :
802 : 6 : const struct pl_shader_res *pl_shader_finalize(pl_shader sh)
803 : : {
804 [ + - ]: 6 : if (sh->failed) {
805 : : return NULL;
806 [ - + ]: 6 : } else if (!sh->mutable) {
807 : 0 : return &sh->result;
808 : : }
809 : :
810 : 6 : pl_shader_info info = &sh->info->info;
811 : 6 : pl_str_builder glsl = sh_finalize_internal(sh);
812 : :
813 : : // Turn ident_t into friendly strings before passing it to users
814 : : #define FIX_IDENT(name) \
815 : : name = sh_ident_tostr(sh_ident_unpack(name))
816 [ + + ]: 7 : for (int i = 0; i < sh->vas.num; i++)
817 [ - + ]: 1 : FIX_IDENT(sh->vas.elem[i].attr.name);
818 [ + + ]: 8 : for (int i = 0; i < sh->vars.num; i++)
819 [ - + ]: 2 : FIX_IDENT(sh->vars.elem[i].var.name);
820 [ + + ]: 24 : for (int i = 0; i < sh->consts.num; i++)
821 [ - + ]: 18 : FIX_IDENT(sh->consts.elem[i].name);
822 [ + + ]: 12 : for (int i = 0; i < sh->descs.num; i++) {
823 : 6 : struct pl_shader_desc *sd = &sh->descs.elem[i];
824 [ - + ]: 6 : FIX_IDENT(sd->desc.name);
825 [ - + ]: 6 : for (int j = 0; j < sd->num_buffer_vars; j++)
826 [ # # ]: 0 : FIX_IDENT(sd->buffer_vars[j].var.name);
827 : : }
828 : : #undef FIX_IDENT
829 : :
830 : 6 : sh->result = (struct pl_shader_res) {
831 : : .info = info,
832 : 6 : .glsl = (char *) pl_str_builder_exec(glsl).buf,
833 : 6 : .name = sh_ident_tostr(sh->name),
834 : 6 : .input = sh->input,
835 : 6 : .output = sh->output,
836 : 6 : .compute_group_size = { sh->group_size[0], sh->group_size[1] },
837 : 6 : .compute_shmem = sh->shmem,
838 : 6 : .vertex_attribs = sh->vas.elem,
839 : 6 : .num_vertex_attribs = sh->vas.num,
840 : 6 : .variables = sh->vars.elem,
841 : 6 : .num_variables = sh->vars.num,
842 : 6 : .descriptors = sh->descs.elem,
843 : 6 : .num_descriptors = sh->descs.num,
844 : 6 : .constants = sh->consts.elem,
845 : 6 : .num_constants = sh->consts.num,
846 : : // deprecated fields
847 : : .params = info->params,
848 : 6 : .steps = info->steps,
849 : 6 : .num_steps = info->num_steps,
850 : 6 : .description = info->description,
851 : : };
852 : :
853 : 6 : return &sh->result;
854 : : }
855 : :
856 : 7047 : bool sh_require(pl_shader sh, enum pl_shader_sig insig, int w, int h)
857 : : {
858 [ - + ]: 7047 : if (sh->failed) {
859 : 0 : SH_FAIL(sh, "Attempting to modify a failed shader!");
860 : 0 : return false;
861 : : }
862 : :
863 [ - + ]: 7047 : if (!sh->mutable) {
864 : 0 : SH_FAIL(sh, "Attempted to modify an immutable shader!");
865 : 0 : return false;
866 : : }
867 : :
868 [ + + - + : 7047 : if ((w && sh->output_w && sh->output_w != w) ||
- - + + ]
869 [ - + - - ]: 400 : (h && sh->output_h && sh->output_h != h))
870 : : {
871 : 0 : SH_FAIL(sh, "Illegal sequence of shader operations: Incompatible "
872 : : "output size requirements %dx%d and %dx%d",
873 : : sh->output_w, sh->output_h, w, h);
874 : 0 : return false;
875 : : }
876 : :
877 : : static const char *names[] = {
878 : : [PL_SHADER_SIG_NONE] = "PL_SHADER_SIG_NONE",
879 : : [PL_SHADER_SIG_COLOR] = "PL_SHADER_SIG_COLOR",
880 : : };
881 : :
882 : : // If we require an input, but there is none available - just get it from
883 : : // the user by turning it into an explicit input signature.
884 [ + + + + ]: 7047 : if (!sh->output && insig) {
885 [ - + ]: 41 : pl_assert(!sh->input);
886 : 41 : sh->input = insig;
887 [ - + ]: 7006 : } else if (sh->output != insig) {
888 : 0 : SH_FAIL(sh, "Illegal sequence of shader operations! Current output "
889 : : "signature is '%s', but called operation expects '%s'!",
890 : : names[sh->output], names[insig]);
891 : 0 : return false;
892 : : }
893 : :
894 : : // All of our shaders end up returning a vec4 color
895 : 7047 : sh->output = PL_SHADER_SIG_COLOR;
896 [ + + ]: 7047 : sh->output_w = PL_DEF(sh->output_w, w);
897 [ + + ]: 7047 : sh->output_h = PL_DEF(sh->output_h, h);
898 : 7047 : return true;
899 : : }
900 : :
901 : 2110 : static void sh_obj_deref(pl_shader_obj obj)
902 : : {
903 [ + + ]: 2110 : if (!pl_rc_deref(&obj->rc))
904 : : return;
905 : :
906 [ + - ]: 132 : if (obj->uninit)
907 : 132 : obj->uninit(obj->gpu, obj->priv);
908 : :
909 : 132 : pl_free(obj);
910 : : }
911 : :
912 : 408 : void pl_shader_obj_destroy(pl_shader_obj *ptr)
913 : : {
914 : 408 : pl_shader_obj obj = *ptr;
915 [ + + ]: 408 : if (!obj)
916 : : return;
917 : :
918 : 132 : sh_obj_deref(obj);
919 : 132 : *ptr = NULL;
920 : : }
921 : :
922 : 1978 : void *sh_require_obj(pl_shader sh, pl_shader_obj *ptr,
923 : : enum pl_shader_obj_type type, size_t priv_size,
924 : : void (*uninit)(pl_gpu gpu, void *priv))
925 : : {
926 [ - + ]: 1978 : if (!ptr)
927 : : return NULL;
928 : :
929 : 1978 : pl_shader_obj obj = *ptr;
930 [ + + - + ]: 1978 : if (obj && obj->gpu != SH_GPU(sh)) {
931 : 0 : SH_FAIL(sh, "Passed pl_shader_obj belongs to different GPU!");
932 : 0 : return NULL;
933 : : }
934 : :
935 [ - + ]: 1846 : if (obj && obj->type != type) {
936 : 0 : SH_FAIL(sh, "Passed pl_shader_obj of wrong type! Shader objects must "
937 : : "always be used with the same type of shader.");
938 : 0 : return NULL;
939 : : }
940 : :
941 : : if (!obj) {
942 : 132 : obj = pl_zalloc_ptr(NULL, obj);
943 : 132 : pl_rc_init(&obj->rc);
944 : 132 : obj->gpu = SH_GPU(sh);
945 : 132 : obj->type = type;
946 : 132 : obj->priv = pl_zalloc(obj, priv_size);
947 : 132 : obj->uninit = uninit;
948 : : }
949 : :
950 [ + + - + : 1978 : PL_ARRAY_APPEND(sh, sh->obj, obj);
- + ]
951 : 1978 : pl_rc_ref(&obj->rc);
952 : :
953 : 1978 : *ptr = obj;
954 : 1978 : return obj->priv;
955 : : }
956 : :
957 : 40 : ident_t sh_prng(pl_shader sh, bool temporal, ident_t *p_state)
958 : : {
959 : 40 : ident_t randfun = sh_fresh(sh, "rand"),
960 : 40 : state = sh_fresh(sh, "state");
961 : :
962 : : // Based on pcg3d (http://jcgt.org/published/0009/03/02/)
963 : 40 : GLSLP("#define prng_t uvec3\n");
964 : 40 : GLSLH("vec3 "$"(inout uvec3 s) { \n"
965 : : " s = 1664525u * s + uvec3(1013904223u); \n"
966 : : " s.x += s.y * s.z; \n"
967 : : " s.y += s.z * s.x; \n"
968 : : " s.z += s.x * s.y; \n"
969 : : " s ^= s >> 16u; \n"
970 : : " s.x += s.y * s.z; \n"
971 : : " s.y += s.z * s.x; \n"
972 : : " s.z += s.x * s.y; \n"
973 : : " return vec3(s) * 1.0/float(0xFFFFFFFFu); \n"
974 : : "} \n",
975 : : randfun);
976 : :
977 [ + + ]: 40 : if (temporal) {
978 : 36 : GLSL("uvec3 "$" = uvec3(gl_FragCoord.xy, "$"); \n",
979 : : state, SH_UINT_DYN(SH_PARAMS(sh).index));
980 : : } else {
981 : 4 : GLSL("uvec3 "$" = uvec3(gl_FragCoord.xy, 0.0); \n", state);
982 : : }
983 : :
984 [ - + ]: 40 : if (p_state)
985 : 0 : *p_state = state;
986 : :
987 : 40 : ident_t res = sh_fresh(sh, "RAND");
988 : 40 : GLSLH("#define "$" ("$"("$"))\n", res, randfun, state);
989 : 40 : return res;
990 : : }
|