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 "common.h"
19 : : #include "log.h"
20 : : #include "shaders.h"
21 : : #include "dispatch.h"
22 : : #include "gpu.h"
23 : : #include "pl_thread.h"
24 : :
25 : : // Maximum number of passes to keep around at once. If full, passes older than
26 : : // MIN_AGE are evicted to make room. (Failing that, the passes array doubles)
27 : : #define MAX_PASSES 100
28 : : #define MIN_AGE 10
29 : :
30 : : enum {
31 : : TMP_PRELUDE, // GLSL version, global definitions, etc.
32 : : TMP_MAIN, // main GLSL shader body
33 : : TMP_VERT_HEAD, // vertex shader inputs/outputs
34 : : TMP_VERT_BODY, // vertex shader body
35 : : TMP_COUNT,
36 : : };
37 : :
38 : : struct pl_dispatch_t {
39 : : pl_mutex lock;
40 : : pl_log log;
41 : : pl_gpu gpu;
42 : : uint8_t current_ident;
43 : : uint8_t current_index;
44 : : bool dynamic_constants;
45 : : int max_passes;
46 : :
47 : : void (*info_callback)(void *, const struct pl_dispatch_info *);
48 : : void *info_priv;
49 : :
50 : : PL_ARRAY(pl_shader) shaders; // to avoid re-allocations
51 : : PL_ARRAY(struct pass *) passes; // compiled passes
52 : :
53 : : // temporary buffers to help avoid re_allocations during pass creation
54 : : PL_ARRAY(const struct pl_buffer_var *) buf_tmp;
55 : : pl_str_builder tmp[TMP_COUNT];
56 : : uint8_t *ubo_tmp;
57 : : };
58 : :
59 : : enum pass_var_type {
60 : : PASS_VAR_NONE = 0,
61 : : PASS_VAR_GLOBAL, // regular/global uniforms
62 : : PASS_VAR_UBO, // uniform buffers
63 : : PASS_VAR_PUSHC // push constants
64 : : };
65 : :
66 : : // Cached metadata about a variable's effective placement / update method
67 : : struct pass_var {
68 : : int index; // for pl_var_update
69 : : enum pass_var_type type;
70 : : struct pl_var_layout layout;
71 : : void *cached_data;
72 : : };
73 : :
74 : : struct pass {
75 : : uint64_t signature;
76 : : pl_pass pass;
77 : : int last_index;
78 : :
79 : : // contains cached data and update metadata, same order as pl_shader
80 : : struct pass_var *vars;
81 : : int num_var_locs;
82 : :
83 : : // for uniform buffer updates
84 : : struct pl_shader_desc ubo_desc; // temporary
85 : : int ubo_index;
86 : : pl_buf ubo;
87 : :
88 : : // Cached pl_pass_run_params. This will also contain mutable allocations
89 : : // for the push constants, descriptor bindings (including the binding for
90 : : // the UBO pre-filled), vertex array and variable updates
91 : : struct pl_pass_run_params run_params;
92 : :
93 : : // for pl_dispatch_info
94 : : pl_timer timer;
95 : : uint64_t ts_last;
96 : : uint64_t ts_peak;
97 : : uint64_t ts_sum;
98 : : uint64_t samples[PL_ARRAY_SIZE(((struct pl_dispatch_info *) NULL)->samples)];
99 : : int ts_idx;
100 : : };
101 : :
102 : 490 : static void pass_destroy(pl_dispatch dp, struct pass *pass)
103 : : {
104 [ + - ]: 490 : if (!pass)
105 : : return;
106 : :
107 : 490 : pl_buf_destroy(dp->gpu, &pass->ubo);
108 : 490 : pl_pass_destroy(dp->gpu, &pass->pass);
109 : 490 : pl_timer_destroy(dp->gpu, &pass->timer);
110 : 490 : pl_free(pass);
111 : : }
112 : :
113 : 27 : pl_dispatch pl_dispatch_create(pl_log log, pl_gpu gpu)
114 : : {
115 : 27 : struct pl_dispatch_t *dp = pl_zalloc_ptr(NULL, dp);
116 [ - + ]: 27 : pl_mutex_init(&dp->lock);
117 : 27 : dp->log = log;
118 : 27 : dp->gpu = gpu;
119 : 27 : dp->max_passes = MAX_PASSES;
120 [ + + ]: 135 : for (int i = 0; i < PL_ARRAY_SIZE(dp->tmp); i++)
121 : 108 : dp->tmp[i] = pl_str_builder_alloc(dp);
122 : :
123 : 27 : return dp;
124 : : }
125 : :
126 : 27 : void pl_dispatch_destroy(pl_dispatch *ptr)
127 : : {
128 : 27 : pl_dispatch dp = *ptr;
129 [ + - ]: 27 : if (!dp)
130 : : return;
131 : :
132 [ + + ]: 517 : for (int i = 0; i < dp->passes.num; i++)
133 : 490 : pass_destroy(dp, dp->passes.elem[i]);
134 [ + + ]: 55 : for (int i = 0; i < dp->shaders.num; i++)
135 : 28 : pl_shader_free(&dp->shaders.elem[i]);
136 : :
137 : 27 : pl_mutex_destroy(&dp->lock);
138 : 27 : pl_free(dp);
139 : 27 : *ptr = NULL;
140 : : }
141 : :
142 : 2754 : pl_shader pl_dispatch_begin_ex(pl_dispatch dp, bool unique)
143 : : {
144 : 2754 : pl_mutex_lock(&dp->lock);
145 : :
146 [ + + ]: 2754 : struct pl_shader_params params = {
147 : 1811 : .id = unique ? dp->current_ident++ : 0,
148 : 2754 : .gpu = dp->gpu,
149 : 2754 : .index = dp->current_index,
150 : 2754 : .dynamic_constants = dp->dynamic_constants,
151 : : };
152 : :
153 : : pl_shader sh = NULL;
154 [ + + ]: 2754 : PL_ARRAY_POP(dp->shaders, &sh);
155 : 2754 : pl_mutex_unlock(&dp->lock);
156 : :
157 [ + + ]: 2754 : if (sh) {
158 : 2726 : pl_shader_reset(sh, ¶ms);
159 : 2726 : return sh;
160 : : }
161 : :
162 : 28 : return pl_shader_alloc(dp->log, ¶ms);
163 : : }
164 : :
165 : 1097 : void pl_dispatch_mark_dynamic(pl_dispatch dp, bool dynamic)
166 : : {
167 : 1097 : dp->dynamic_constants = dynamic;
168 : 1097 : }
169 : :
170 : 1401 : void pl_dispatch_callback(pl_dispatch dp, void *priv,
171 : : void (*cb)(void *priv, const struct pl_dispatch_info *))
172 : : {
173 : 1401 : dp->info_callback = cb;
174 : 1401 : dp->info_priv = priv;
175 : 1401 : }
176 : :
177 : 919 : pl_shader pl_dispatch_begin(pl_dispatch dp)
178 : : {
179 : 919 : return pl_dispatch_begin_ex(dp, false);
180 : : }
181 : :
182 : 9342 : static bool add_pass_var(pl_dispatch dp, void *tmp, struct pass *pass,
183 : : struct pl_pass_params *params,
184 : : const struct pl_shader_var *sv, struct pass_var *pv,
185 : : bool greedy)
186 : : {
187 : 9342 : pl_gpu gpu = dp->gpu;
188 [ + + ]: 9342 : if (pv->type)
189 : : return true;
190 : :
191 : : // Try not to use push constants for "large" values like matrices in the
192 : : // first pass, since this is likely to exceed the VGPR/pushc size budgets
193 [ + + + + : 8412 : bool try_pushc = greedy || (sv->var.dim_m == 1 && sv->var.dim_a == 1) || sv->dynamic;
+ + ]
194 [ + + + - ]: 6735 : if (try_pushc && gpu->glsl.vulkan && gpu->limits.max_pushc_size) {
195 : 1362 : pv->layout = pl_std430_layout(params->push_constants_size, &sv->var);
196 : 1362 : size_t new_size = pv->layout.offset + pv->layout.size;
197 [ + + ]: 1362 : if (new_size <= gpu->limits.max_pushc_size) {
198 : 1331 : params->push_constants_size = new_size;
199 : 1331 : pv->type = PASS_VAR_PUSHC;
200 : 1331 : return true;
201 : : }
202 : : }
203 : :
204 : : // If we haven't placed all PCs yet, don't place anything else, since
205 : : // we want to try and fit more stuff into PCs before "giving up"
206 [ + + ]: 7081 : if (!greedy)
207 : : return true;
208 : :
209 : 3340 : int num_locs = sv->var.dim_v * sv->var.dim_m * sv->var.dim_a;
210 : 3340 : bool can_var = pass->num_var_locs + num_locs <= gpu->limits.max_variable_comps;
211 : :
212 : : // Attempt using uniform buffer next. The GLSL version 440 check is due
213 : : // to explicit offsets on UBO entries. In theory we could leave away
214 : : // the offsets and support UBOs for older GL as well, but this is a nice
215 : : // safety net for driver bugs (and also rules out potentially buggy drivers)
216 : : // Also avoid UBOs for highly dynamic stuff since that requires synchronizing
217 : : // the UBO writes every frame
218 [ + + + + ]: 3340 : bool try_ubo = !can_var || !sv->dynamic;
219 [ + + + - ]: 2377 : if (try_ubo && gpu->glsl.version >= 440 && gpu->limits.max_ubo_size) {
220 [ + - ]: 949 : if (sh_buf_desc_append(tmp, gpu, &pass->ubo_desc, &pv->layout, sv->var)) {
221 : 949 : pv->type = PASS_VAR_UBO;
222 : 949 : return true;
223 : : }
224 : : }
225 : :
226 : : // Otherwise, use global uniforms
227 [ + - ]: 2391 : if (can_var) {
228 : 2391 : pv->type = PASS_VAR_GLOBAL;
229 : 2391 : pv->index = params->num_variables;
230 : 2391 : pv->layout = pl_var_host_layout(0, &sv->var);
231 [ + + + + : 2391 : PL_ARRAY_APPEND_RAW(tmp, params->variables, params->num_variables, sv->var);
- + ]
232 : 2391 : pass->num_var_locs += num_locs;
233 : 2391 : return true;
234 : : }
235 : :
236 : : // Ran out of variable binding methods. The most likely scenario in which
237 : : // this can happen is if we're using a GPU that does not support global
238 : : // input vars and we've exhausted the UBO size limits.
239 : 0 : PL_ERR(dp, "Unable to add input variable: possibly exhausted "
240 : : "variable count / UBO size limits?");
241 : 0 : return false;
242 : : }
243 : :
244 : : #define ADD(b, ...) pl_str_builder_addf(b, __VA_ARGS__)
245 : : #define ADD_CAT(b, cat) pl_str_builder_concat(b, cat)
246 : : #define ADD_CONST(b, s) pl_str_builder_const_str(b, s)
247 : :
248 : 4755 : static void add_var(pl_str_builder body, const struct pl_var *var)
249 : : {
250 : 4755 : const char *type = pl_var_glsl_type_name(*var);
251 [ + + ]: 4755 : if (var->dim_a > 1) {
252 [ - + ]: 91 : ADD(body, "%s "$"[%d];\n", type, sh_ident_unpack(var->name), var->dim_a);
253 : : } else {
254 [ - + ]: 4664 : ADD(body, "%s "$";\n", type, sh_ident_unpack(var->name));
255 : : }
256 : 4755 : }
257 : :
258 : 1733 : static int cmp_buffer_var(const void *pa, const void *pb)
259 : : {
260 : : const struct pl_buffer_var * const *a = pa, * const *b = pb;
261 : 1733 : return PL_CMP((*a)->layout.offset, (*b)->layout.offset);
262 : : }
263 : :
264 : 994 : static void add_buffer_vars(pl_dispatch dp, void *tmp, pl_str_builder body,
265 : : const struct pl_buffer_var *vars, int num)
266 : : {
267 : : // Sort buffer vars by offset
268 [ + + ]: 994 : PL_ARRAY_RESIZE(dp, dp->buf_tmp, num);
269 [ + + ]: 3358 : for (int i = 0; i < num; i++)
270 : 2364 : dp->buf_tmp.elem[i] = &vars[i];
271 : 994 : qsort(dp->buf_tmp.elem, num, sizeof(&vars[0]), cmp_buffer_var);
272 : :
273 : 994 : ADD(body, "{\n");
274 [ + + ]: 3358 : for (int i = 0; i < num; i++) {
275 : 2364 : const struct pl_buffer_var *bv = dp->buf_tmp.elem[i];
276 : : // Add an explicit offset wherever possible
277 [ + - ]: 2364 : if (dp->gpu->glsl.version >= 440)
278 : 2364 : ADD(body, " layout(offset=%zu) ", bv->layout.offset);
279 : 2364 : add_var(body, &bv->var);
280 : : }
281 : 994 : ADD(body, "};\n");
282 : 994 : }
283 : :
284 : : struct generate_params {
285 : : void *tmp;
286 : : pl_shader sh;
287 : : struct pass *pass;
288 : : struct pl_pass_params *pass_params;
289 : : ident_t out_mat;
290 : : ident_t out_off;
291 : : int vert_idx;
292 : : };
293 : :
294 : 1945 : static void generate_shaders(pl_dispatch dp,
295 : : const struct generate_params *params,
296 : : pl_str_builder *out_vert_builder,
297 : : pl_str_builder *out_glsl_builder)
298 : : {
299 : 1945 : pl_gpu gpu = dp->gpu;
300 : 1945 : pl_shader sh = params->sh;
301 : 1945 : void *tmp = params->tmp;
302 : 1945 : struct pass *pass = params->pass;
303 : 1945 : struct pl_pass_params *pass_params = params->pass_params;
304 : 1945 : pl_str_builder shader_body = sh_finalize_internal(sh);
305 : :
306 : 1945 : pl_str_builder pre = dp->tmp[TMP_PRELUDE];
307 [ + + - + ]: 3885 : ADD(pre, "#version %d%s\n", gpu->glsl.version,
308 : : (gpu->glsl.gles && gpu->glsl.version > 100) ? " es" : "");
309 [ + + ]: 1945 : if (pass_params->type == PL_PASS_COMPUTE)
310 : 114 : ADD(pre, "#extension GL_ARB_compute_shader : enable\n");
311 : :
312 : : // Enable this unconditionally if the GPU supports it, since we have no way
313 : : // of knowing whether subgroups are being used or not
314 [ + + ]: 1945 : if (gpu->glsl.subgroup_size) {
315 : 533 : ADD(pre, "#extension GL_KHR_shader_subgroup_basic : enable \n"
316 : : "#extension GL_KHR_shader_subgroup_vote : enable \n"
317 : : "#extension GL_KHR_shader_subgroup_arithmetic : enable \n"
318 : : "#extension GL_KHR_shader_subgroup_ballot : enable \n"
319 : : "#extension GL_KHR_shader_subgroup_shuffle : enable \n"
320 : : "#extension GL_KHR_shader_subgroup_clustered : enable \n"
321 : : "#extension GL_KHR_shader_subgroup_quad : enable \n");
322 : : }
323 : :
324 : : // Enable all extensions needed for different types of input
325 : : bool has_ssbo = false, has_ubo = false, has_img = false, has_texel = false,
326 : : has_ext = false, has_nofmt = false, has_gather = false;
327 [ + + ]: 5543 : for (int i = 0; i < sh->descs.num; i++) {
328 [ + + - + : 3598 : switch (sh->descs.elem[i].desc.type) {
+ + - - ]
329 : 484 : case PL_DESC_BUF_UNIFORM: has_ubo = true; break;
330 : 22 : case PL_DESC_BUF_STORAGE: has_ssbo = true; break;
331 : 0 : case PL_DESC_BUF_TEXEL_UNIFORM: has_texel = true; break;
332 : 18 : case PL_DESC_BUF_TEXEL_STORAGE: {
333 : 18 : pl_buf buf = sh->descs.elem[i].binding.object;
334 : 18 : has_nofmt |= !buf->params.format->glsl_format;
335 : : has_texel = true;
336 : 18 : break;
337 : : }
338 : 114 : case PL_DESC_STORAGE_IMG: {
339 : 114 : pl_tex tex = sh->descs.elem[i].binding.object;
340 : 114 : has_nofmt |= !tex->params.format->glsl_format;
341 : : has_img = true;
342 : 114 : break;
343 : : }
344 : 2960 : case PL_DESC_SAMPLED_TEX: {
345 : 2960 : pl_tex tex = sh->descs.elem[i].binding.object;
346 : 2960 : has_gather |= tex->params.format->gatherable;
347 [ - - + ]: 2960 : switch (tex->sampler_type) {
348 : : case PL_SAMPLER_NORMAL: break;
349 : : case PL_SAMPLER_RECT: break;
350 : 0 : case PL_SAMPLER_EXTERNAL: has_ext = true; break;
351 : 0 : case PL_SAMPLER_TYPE_COUNT: pl_unreachable();
352 : : }
353 : : break;
354 : : }
355 : :
356 : : case PL_DESC_INVALID:
357 : : case PL_DESC_TYPE_COUNT:
358 : 0 : pl_unreachable();
359 : : }
360 : : }
361 : :
362 [ + + + - ]: 1945 : if (has_img && !gpu->glsl.gles)
363 : 110 : ADD(pre, "#extension GL_ARB_shader_image_load_store : enable\n");
364 [ + + ]: 1945 : if (has_ubo)
365 : 483 : ADD(pre, "#extension GL_ARB_uniform_buffer_object : enable\n");
366 [ + + ]: 1945 : if (has_ssbo)
367 : 20 : ADD(pre, "#extension GL_ARB_shader_storage_buffer_object : enable\n");
368 [ + + ]: 1945 : if (has_texel)
369 : 18 : ADD(pre, "#extension GL_ARB_texture_buffer_object : enable\n");
370 [ - + ]: 1945 : if (has_ext) {
371 [ # # ]: 0 : if (gpu->glsl.version >= 300) {
372 : 0 : ADD(pre, "#extension GL_OES_EGL_image_external_essl3 : enable\n");
373 : : } else {
374 : 0 : ADD(pre, "#extension GL_OES_EGL_image_external : enable\n");
375 : : }
376 : : }
377 [ - + ]: 1945 : if (has_nofmt)
378 : 0 : ADD(pre, "#extension GL_EXT_shader_image_load_formatted : enable\n");
379 [ + + + - ]: 1945 : if (has_gather && !gpu->glsl.gles)
380 : 1014 : ADD(pre, "#extension GL_ARB_texture_gather : enable\n");
381 : :
382 [ + + ]: 1945 : if (gpu->glsl.gles) {
383 : : // Use 32-bit precision for floats if possible
384 : 5 : ADD(pre, "#ifdef GL_FRAGMENT_PRECISION_HIGH \n"
385 : : "precision highp float; \n"
386 : : "#else \n"
387 : : "precision mediump float; \n"
388 : : "#endif \n");
389 : :
390 : : // Always use 16-bit precision for samplers
391 : 5 : ADD(pre, "precision mediump sampler2D; \n");
392 [ - + ]: 5 : if (gpu->limits.max_tex_1d_dim)
393 : 0 : ADD(pre, "precision mediump sampler1D; \n");
394 [ + - + - ]: 5 : if (gpu->limits.max_tex_3d_dim && gpu->glsl.version > 100)
395 : 5 : ADD(pre, "precision mediump sampler3D; \n");
396 : :
397 : : // Integer math has a good chance of caring about precision
398 : 5 : ADD(pre, "precision highp int; \n");
399 : : }
400 : :
401 : : // textureLod() doesn't work on external/rect samplers, simply disable
402 : : // LOD sampling in this case. We don't currently support mipmaps anyway.
403 [ + + ]: 5543 : for (int i = 0; i < sh->descs.num; i++) {
404 [ + + ]: 3598 : if (pass_params->descriptors[i].type != PL_DESC_SAMPLED_TEX)
405 : 638 : continue;
406 : 2960 : pl_tex tex = sh->descs.elem[i].binding.object;
407 [ - + ]: 2960 : if (tex->sampler_type != PL_SAMPLER_NORMAL) {
408 : 0 : ADD(pre, "#define textureLod(t, p, b) texture(t, p) \n"
409 : : "#define textureLodOffset(t, p, b, o) \\\n"
410 : : " textureOffset(t, p, o) \n");
411 : 0 : break;
412 : : }
413 : : }
414 : :
415 : : // Add all of the push constants as their own element
416 [ + + ]: 1945 : if (pass_params->push_constants_size) {
417 : : // We re-use add_buffer_vars to make sure variables are sorted, this
418 : : // is important because the push constants can be out-of-order in
419 : : // `pass->vars`
420 : : PL_ARRAY(struct pl_buffer_var) pc_bvars = {0};
421 [ + + ]: 1850 : for (int i = 0; i < sh->vars.num; i++) {
422 [ + + ]: 1362 : if (pass->vars[i].type != PASS_VAR_PUSHC)
423 : 31 : continue;
424 : :
425 [ + + - + : 1331 : PL_ARRAY_APPEND(tmp, pc_bvars, (struct pl_buffer_var) {
- + ]
426 : : .var = sh->vars.elem[i].var,
427 : : .layout = pass->vars[i].layout,
428 : : });
429 : : }
430 : :
431 : 488 : ADD(pre, "layout(std430, push_constant) uniform PushC ");
432 : 488 : add_buffer_vars(dp, tmp, pre, pc_bvars.elem, pc_bvars.num);
433 : : }
434 : :
435 : : // Add all of the specialization constants
436 [ + + ]: 4498 : for (int i = 0; i < sh->consts.num; i++) {
437 : : static const char *types[PL_VAR_TYPE_COUNT] = {
438 : : [PL_VAR_SINT] = "int",
439 : : [PL_VAR_UINT] = "uint",
440 : : [PL_VAR_FLOAT] = "float",
441 : : };
442 : :
443 : 2553 : const struct pl_shader_const *sc = &sh->consts.elem[i];
444 [ - + ]: 2553 : ADD(pre, "layout(constant_id=%"PRIu32") const %s "$" = 1; \n",
445 : : pass_params->constants[i].id, types[sc->type],
446 : : sh_ident_unpack(sc->name));
447 : : }
448 : :
449 : : static const char sampler_prefixes[PL_FMT_TYPE_COUNT] = {
450 : : [PL_FMT_FLOAT] = ' ',
451 : : [PL_FMT_UNORM] = ' ',
452 : : [PL_FMT_SNORM] = ' ',
453 : : [PL_FMT_UINT] = 'u',
454 : : [PL_FMT_SINT] = 'i',
455 : : };
456 : :
457 : : // Add all of the required descriptors
458 [ + + ]: 5543 : for (int i = 0; i < sh->descs.num; i++) {
459 : 3598 : const struct pl_shader_desc *sd = &sh->descs.elem[i];
460 : 3598 : const struct pl_desc *desc = &pass_params->descriptors[i];
461 : :
462 [ + + + + : 3598 : switch (desc->type) {
- + - - ]
463 : 2960 : case PL_DESC_SAMPLED_TEX: {
464 : : static const char *types[][4] = {
465 : : [PL_SAMPLER_NORMAL][1] = "sampler1D",
466 : : [PL_SAMPLER_NORMAL][2] = "sampler2D",
467 : : [PL_SAMPLER_NORMAL][3] = "sampler3D",
468 : : [PL_SAMPLER_RECT][2] = "sampler2DRect",
469 : : [PL_SAMPLER_EXTERNAL][2] = "samplerExternalOES",
470 : : };
471 : :
472 [ + + ]: 2960 : pl_tex tex = sd->binding.object;
473 : : int dims = pl_tex_params_dimension(tex->params);
474 : 2960 : const char *type = types[tex->sampler_type][dims];
475 : 2960 : char prefix = sampler_prefixes[tex->params.format->type];
476 [ - + ]: 2960 : ident_t id = sh_ident_unpack(desc->name);
477 [ - + ]: 2960 : pl_assert(type && prefix);
478 : :
479 : : // Vulkan requires explicit bindings; GL always sets the
480 : : // bindings manually to avoid relying on the user doing so
481 [ + + ]: 2960 : if (gpu->glsl.vulkan) {
482 : 779 : ADD(pre, "layout(binding=%d) uniform %c%s "$";\n",
483 : : desc->binding, prefix, type, id);
484 [ + + - + ]: 2181 : } else if (gpu->glsl.gles && prefix != ' ') {
485 : 0 : ADD(pre, "uniform highp %c%s "$";\n", prefix, type, id);
486 : : } else {
487 : 2181 : ADD(pre, "uniform %c%s "$";\n", prefix, type, id);
488 : : }
489 : : break;
490 : : }
491 : :
492 : 114 : case PL_DESC_STORAGE_IMG: {
493 : : static const char *types[] = {
494 : : [1] = "image1D",
495 : : [2] = "image2D",
496 : : [3] = "image3D",
497 : : };
498 : :
499 : : // For better compatibility, we have to explicitly label the
500 : : // type of data we will be reading/writing to this image.
501 : 114 : pl_tex tex = sd->binding.object;
502 [ + + ]: 114 : const char *format = tex->params.format->glsl_format;
503 : : int dims = pl_tex_params_dimension(tex->params);
504 [ + + ]: 114 : if (gpu->glsl.vulkan) {
505 [ + - ]: 66 : if (format) {
506 : 66 : ADD(pre, "layout(binding=%d, %s) ", desc->binding, format);
507 : : } else {
508 : 0 : ADD(pre, "layout(binding=%d) ", desc->binding);
509 : : }
510 [ + - ]: 48 : } else if (format) {
511 : 48 : ADD(pre, "layout(%s) ", format);
512 : : }
513 : :
514 : 114 : ADD_CONST(pre, pl_desc_access_glsl_name(desc->access));
515 [ + + ]: 114 : if (sd->memory & PL_MEMORY_COHERENT)
516 : 2 : ADD(pre, " coherent");
517 [ - + ]: 114 : if (sd->memory & PL_MEMORY_VOLATILE)
518 : 0 : ADD(pre, " volatile");
519 [ - + ]: 114 : ADD(pre, " restrict uniform %s "$";\n",
520 : : types[dims], sh_ident_unpack(desc->name));
521 : 114 : break;
522 : : }
523 : :
524 : 484 : case PL_DESC_BUF_UNIFORM:
525 [ + + ]: 484 : if (gpu->glsl.vulkan) {
526 : 13 : ADD(pre, "layout(std140, binding=%d) ", desc->binding);
527 : : } else {
528 : 471 : ADD(pre, "layout(std140) ");
529 : : }
530 [ - + ]: 484 : ADD(pre, "uniform "$" ", sh_ident_unpack(desc->name));
531 : 484 : add_buffer_vars(dp, tmp, pre, sd->buffer_vars, sd->num_buffer_vars);
532 : 484 : break;
533 : :
534 : 22 : case PL_DESC_BUF_STORAGE:
535 [ + - ]: 22 : if (gpu->glsl.version >= 140)
536 : 22 : ADD(pre, "layout(std430, binding=%d) ", desc->binding);
537 : 22 : ADD_CONST(pre, pl_desc_access_glsl_name(desc->access));
538 [ + + ]: 22 : if (sd->memory & PL_MEMORY_COHERENT)
539 : 2 : ADD(pre, " coherent");
540 [ - + ]: 22 : if (sd->memory & PL_MEMORY_VOLATILE)
541 : 0 : ADD(pre, " volatile");
542 [ - + ]: 22 : ADD(pre, " restrict buffer "$" ", sh_ident_unpack(desc->name));
543 : 22 : add_buffer_vars(dp, tmp, pre, sd->buffer_vars, sd->num_buffer_vars);
544 : 22 : break;
545 : :
546 : 0 : case PL_DESC_BUF_TEXEL_UNIFORM: {
547 : 0 : pl_buf buf = sd->binding.object;
548 : 0 : char prefix = sampler_prefixes[buf->params.format->type];
549 [ # # ]: 0 : if (gpu->glsl.vulkan)
550 : 0 : ADD(pre, "layout(binding=%d) ", desc->binding);
551 [ # # ]: 0 : ADD(pre, "uniform %csamplerBuffer "$";\n", prefix,
552 : : sh_ident_unpack(desc->name));
553 : 0 : break;
554 : : }
555 : :
556 : 18 : case PL_DESC_BUF_TEXEL_STORAGE: {
557 : 18 : pl_buf buf = sd->binding.object;
558 : 18 : const char *format = buf->params.format->glsl_format;
559 : 18 : char prefix = sampler_prefixes[buf->params.format->type];
560 [ + - ]: 18 : if (gpu->glsl.vulkan) {
561 [ + - ]: 18 : if (format) {
562 : 18 : ADD(pre, "layout(binding=%d, %s) ", desc->binding, format);
563 : : } else {
564 : 0 : ADD(pre, "layout(binding=%d) ", desc->binding);
565 : : }
566 [ # # ]: 0 : } else if (format) {
567 : 0 : ADD(pre, "layout(%s) ", format);
568 : : }
569 : :
570 : 18 : ADD_CONST(pre, pl_desc_access_glsl_name(desc->access));
571 [ - + ]: 18 : if (sd->memory & PL_MEMORY_COHERENT)
572 : 0 : ADD(pre, " coherent");
573 [ - + ]: 18 : if (sd->memory & PL_MEMORY_VOLATILE)
574 : 0 : ADD(pre, " volatile");
575 [ - + ]: 18 : ADD(pre, " restrict uniform %cimageBuffer "$";\n",
576 : : prefix, sh_ident_unpack(desc->name));
577 : 18 : break;
578 : : }
579 : :
580 : : case PL_DESC_INVALID:
581 : : case PL_DESC_TYPE_COUNT:
582 : 0 : pl_unreachable();
583 : : }
584 : : }
585 : :
586 : : // Add all of the remaining variables
587 [ + + ]: 6616 : for (int i = 0; i < sh->vars.num; i++) {
588 : 4671 : const struct pl_var *var = &sh->vars.elem[i].var;
589 : 4671 : const struct pass_var *pv = &pass->vars[i];
590 [ + + ]: 4671 : if (pv->type != PASS_VAR_GLOBAL)
591 : 2280 : continue;
592 : 2391 : ADD(pre, "uniform ");
593 : 2391 : add_var(pre, var);
594 : : }
595 : :
596 : 1945 : pl_str_builder glsl = dp->tmp[TMP_MAIN];
597 : 1945 : ADD_CAT(glsl, pre);
598 : :
599 [ + + - - ]: 1945 : switch(pass_params->type) {
600 : 1831 : case PL_PASS_RASTER: {
601 [ - + ]: 1831 : pl_assert(params->vert_idx >= 0);
602 : 1831 : pl_str_builder vert_head = dp->tmp[TMP_VERT_HEAD];
603 : 1831 : pl_str_builder vert_body = dp->tmp[TMP_VERT_BODY];
604 : :
605 : : // Older GLSL doesn't support the use of explicit locations
606 : 1831 : bool has_loc = gpu->glsl.version >= 430;
607 : :
608 : : // Set up a trivial vertex shader
609 : 1831 : ADD_CAT(vert_head, pre);
610 : 1831 : ADD(vert_body, "void main() {\n");
611 [ + + ]: 5933 : for (int i = 0; i < sh->vas.num; i++) {
612 : 4102 : const struct pl_vertex_attrib *va = &pass_params->vertex_attribs[i];
613 : 4102 : const struct pl_shader_va *sva = &sh->vas.elem[i];
614 : 4102 : const char *type = va->fmt->glsl_type;
615 : :
616 : : // Use the pl_shader_va for the name in the fragment shader since
617 : : // the pl_vertex_attrib is already mangled for the vertex shader
618 [ - + ]: 4102 : ident_t id = sh_ident_unpack(sva->attr.name);
619 : :
620 [ + + ]: 4102 : if (has_loc) {
621 [ - + ]: 2086 : ADD(vert_head, "layout(location=%d) in %s "$";\n",
622 : : va->location, type, sh_ident_unpack(va->name));
623 : : } else {
624 [ - + ]: 2016 : ADD(vert_head, "in %s "$";\n", type, sh_ident_unpack(va->name));
625 : : }
626 : :
627 [ + + ]: 4102 : if (i == params->vert_idx) {
628 [ - + ]: 1831 : pl_assert(va->fmt->num_components == 2);
629 [ - + ]: 1831 : ADD(vert_body, "vec2 va_pos = "$"; \n", sh_ident_unpack(va->name));
630 [ + + ]: 1831 : if (params->out_mat)
631 : 616 : ADD(vert_body, "va_pos = "$" * va_pos; \n", params->out_mat);
632 [ - + ]: 1831 : if (params->out_off)
633 : 0 : ADD(vert_body, "va_pos += "$"; \n", params->out_off);
634 : 1831 : ADD(vert_body, "gl_Position = vec4(va_pos, 0.0, 1.0); \n");
635 : : } else {
636 : : // Everything else is just blindly passed through
637 [ + + ]: 2271 : if (has_loc) {
638 : 1152 : ADD(vert_head, "layout(location=%d) out %s "$";\n",
639 : : va->location, type, id);
640 : 1152 : ADD(glsl, "layout(location=%d) in %s "$";\n",
641 : : va->location, type, id);
642 : : } else {
643 : 1119 : ADD(vert_head, "out %s "$";\n", type, id);
644 : 1119 : ADD(glsl, "in %s "$";\n", type, id);
645 : : }
646 [ - + ]: 2271 : ADD(vert_body, $" = "$";\n", id, sh_ident_unpack(va->name));
647 : : }
648 : : }
649 : :
650 : 1831 : ADD(vert_body, "}");
651 : 1831 : ADD_CAT(vert_head, vert_body);
652 : 1831 : pl_hash_merge(&pass->signature, pl_str_builder_hash(vert_head));
653 : 1831 : *out_vert_builder = vert_head;
654 : :
655 [ + + ]: 1831 : if (has_loc) {
656 : 934 : ADD(glsl, "layout(location=0) out vec4 out_color;\n");
657 : : } else {
658 : 897 : ADD(glsl, "out vec4 out_color;\n");
659 : : }
660 : : break;
661 : : }
662 : : case PL_PASS_COMPUTE:
663 : 114 : ADD(glsl, "layout (local_size_x = %d, local_size_y = %d) in;\n",
664 : : sh->group_size[0], sh->group_size[1]);
665 : 114 : break;
666 : : case PL_PASS_INVALID:
667 : : case PL_PASS_TYPE_COUNT:
668 : 0 : pl_unreachable();
669 : : }
670 : :
671 : : // Set up the main shader body
672 : 1945 : ADD_CAT(glsl, shader_body);
673 : 1945 : ADD(glsl, "void main() {\n");
674 : :
675 [ - + ]: 1945 : pl_assert(sh->input == PL_SHADER_SIG_NONE);
676 [ + + - - ]: 1945 : switch (pass_params->type) {
677 : 1831 : case PL_PASS_RASTER:
678 [ - + ]: 1831 : pl_assert(sh->output == PL_SHADER_SIG_COLOR);
679 : 1831 : ADD(glsl, "out_color = "$"();\n", sh->name);
680 : 1831 : break;
681 : : case PL_PASS_COMPUTE:
682 : 114 : ADD(glsl, $"();\n", sh->name);
683 : 114 : break;
684 : : case PL_PASS_INVALID:
685 : : case PL_PASS_TYPE_COUNT:
686 : 0 : pl_unreachable();
687 : : }
688 : :
689 : 1945 : ADD(glsl, "}");
690 : :
691 : 1945 : pl_hash_merge(&pass->signature, pl_str_builder_hash(glsl));
692 : 1945 : *out_glsl_builder = glsl;
693 : 1945 : }
694 : :
695 : : #undef ADD
696 : : #undef ADD_CAT
697 : :
698 : : #define pass_age(pass) (dp->current_index - (pass)->last_index)
699 : :
700 : 0 : static int cmp_pass_age(const void *ptra, const void *ptrb)
701 : : {
702 : 0 : const struct pass *a = *(const struct pass **) ptra;
703 : 0 : const struct pass *b = *(const struct pass **) ptrb;
704 : 0 : return b->last_index - a->last_index;
705 : : }
706 : :
707 : 1401 : static void garbage_collect_passes(pl_dispatch dp)
708 : : {
709 [ - + ]: 1401 : if (dp->passes.num <= dp->max_passes)
710 : : return;
711 : :
712 : : // Garbage collect oldest passes, starting at the middle
713 : 0 : qsort(dp->passes.elem, dp->passes.num, sizeof(struct pass *), cmp_pass_age);
714 : 0 : int idx = dp->passes.num / 2;
715 [ # # # # ]: 0 : while (idx < dp->passes.num && pass_age(dp->passes.elem[idx]) < MIN_AGE)
716 : 0 : idx++;
717 : :
718 [ # # ]: 0 : for (int i = idx; i < dp->passes.num; i++)
719 : 0 : pass_destroy(dp, dp->passes.elem[i]);
720 : :
721 : 0 : int num_evicted = dp->passes.num - idx;
722 : 0 : dp->passes.num = idx;
723 : :
724 [ # # ]: 0 : if (num_evicted) {
725 : 0 : PL_DEBUG(dp, "Evicted %d passes from dispatch cache, consider "
726 : : "using more dynamic shaders", num_evicted);
727 : : } else {
728 : 0 : dp->max_passes *= 2;
729 : : }
730 : : }
731 : :
732 : 1945 : static struct pass *finalize_pass(pl_dispatch dp, pl_shader sh,
733 : : pl_tex target, int vert_idx,
734 : : const struct pl_blend_params *blend, bool load,
735 : : const struct pl_dispatch_vertex_params *vparams,
736 : : const pl_transform2x2 *proj)
737 : : {
738 : 1945 : struct pass *pass = pl_alloc_ptr(dp, pass);
739 : 1945 : *pass = (struct pass) {
740 : : .signature = 0x0, // updated incrementally below
741 : 1945 : .last_index = dp->current_index,
742 : : .ubo_desc = {
743 : : .desc = {
744 : 1945 : .name = sh_ident_pack(sh_fresh(sh, "UBO")),
745 : : .type = PL_DESC_BUF_UNIFORM,
746 : : },
747 : : },
748 : : };
749 : :
750 : : // For identifiers tied to the lifetime of this shader
751 : 1945 : void *tmp = sh->tmp;
752 : :
753 : 7780 : struct pl_pass_params params = {
754 [ + + ]: 1945 : .type = pl_shader_is_compute(sh) ? PL_PASS_COMPUTE : PL_PASS_RASTER,
755 : 1945 : .num_descriptors = sh->descs.num,
756 [ + + ]: 1945 : .vertex_type = vparams ? vparams->vertex_type : PL_PRIM_TRIANGLE_STRIP,
757 [ + + ]: 1945 : .vertex_stride = vparams ? vparams->vertex_stride : 0,
758 : : .blend_params = blend,
759 : : };
760 : :
761 : 1945 : struct generate_params gen_params = {
762 : : .tmp = tmp,
763 : : .pass = pass,
764 : : .pass_params = ¶ms,
765 : : .sh = sh,
766 : : .vert_idx = vert_idx,
767 : : };
768 : :
769 [ + + ]: 1945 : if (params.type == PL_PASS_RASTER) {
770 [ - + ]: 1831 : assert(target);
771 : 1831 : params.target_format = target->params.format;
772 : 1831 : params.load_target = load;
773 : :
774 : : // Fill in the vertex attributes array
775 : 1831 : params.num_vertex_attribs = sh->vas.num;
776 : 1831 : params.vertex_attribs = pl_calloc_ptr(tmp, sh->vas.num, params.vertex_attribs);
777 : :
778 : : int va_loc = 0;
779 [ + + ]: 5933 : for (int i = 0; i < sh->vas.num; i++) {
780 : 4102 : struct pl_vertex_attrib *va = ¶ms.vertex_attribs[i];
781 : 4102 : *va = sh->vas.elem[i].attr;
782 : :
783 : : // Mangle the name to make sure it doesn't conflict with the
784 : : // fragment shader input, this will be converted back to a legal
785 : : // string by the shader compilation code
786 : 4102 : va->name = sh_ident_pack(sh_fresh(sh, "va"));
787 : :
788 : : // Place the vertex attribute
789 : 4102 : va->location = va_loc;
790 [ + + ]: 4102 : if (!vparams) {
791 : 4058 : va->offset = params.vertex_stride;
792 : 4058 : params.vertex_stride += va->fmt->texel_size;
793 : : }
794 : :
795 : : // The number of vertex attribute locations consumed by a vertex
796 : : // attribute is the number of vec4s it consumes, rounded up
797 : : const size_t va_loc_size = sizeof(float[4]);
798 : 4102 : va_loc += PL_DIV_UP(va->fmt->texel_size, va_loc_size);
799 : : }
800 : :
801 : : // Hash in the raster state configuration
802 [ + + ]: 1831 : pl_hash_merge(&pass->signature, (uint64_t) params.vertex_type);
803 : 1831 : pl_hash_merge(&pass->signature, (uint64_t) params.vertex_stride);
804 : 1831 : pl_hash_merge(&pass->signature, (uint64_t) params.load_target);
805 : 1831 : pl_hash_merge(&pass->signature, target->params.format->signature);
806 [ + + ]: 1831 : if (blend) {
807 : : pl_static_assert(sizeof(*blend) == sizeof(enum pl_blend_mode) * 4);
808 : : pl_hash_merge(&pass->signature, pl_var_hash(*blend));
809 : : }
810 : :
811 : : // Load projection matrix if required
812 [ + + + + ]: 1831 : if (proj && memcmp(&proj->mat, &pl_matrix2x2_identity, sizeof(proj->mat)) != 0) {
813 : 616 : gen_params.out_mat = sh_var(sh, (struct pl_shader_var) {
814 : 616 : .var = pl_var_mat2("proj"),
815 : 616 : .data = PL_TRANSPOSE_2X2(proj->mat.m),
816 : : });
817 : : }
818 : :
819 [ + - - + ]: 634 : if (proj && (proj->c[0] || proj->c[1])) {
820 : 0 : gen_params.out_off = sh_var(sh, (struct pl_shader_var) {
821 : 0 : .var = pl_var_vec2("offset"),
822 : 0 : .data = proj->c,
823 : : });
824 : : }
825 : : }
826 : :
827 : : // Place all of the compile-time constants
828 : : uint8_t *constant_data = NULL;
829 [ + + ]: 1945 : if (sh->consts.num) {
830 : 528 : params.num_constants = sh->consts.num;
831 : 528 : params.constants = pl_alloc(tmp, sh->consts.num * sizeof(struct pl_constant));
832 : :
833 : : // Compute offsets
834 : : size_t total_size = 0;
835 : : uint32_t const_id = 0;
836 [ + + ]: 3081 : for (int i = 0; i < sh->consts.num; i++) {
837 : 2553 : params.constants[i] = (struct pl_constant) {
838 : 2553 : .type = sh->consts.elem[i].type,
839 : 2553 : .id = const_id++,
840 : : .offset = total_size,
841 : : };
842 : 2553 : total_size += pl_var_type_size(sh->consts.elem[i].type);
843 : : }
844 : :
845 : : // Write values into the constants buffer
846 : 528 : params.constant_data = constant_data = pl_alloc(pass, total_size);
847 [ + + ]: 3081 : for (int i = 0; i < sh->consts.num; i++) {
848 : 2553 : const struct pl_shader_const *sc = &sh->consts.elem[i];
849 : 2553 : void *data = constant_data + params.constants[i].offset;
850 : 2553 : memcpy(data, sc->data, pl_var_type_size(sc->type));
851 : : }
852 : : }
853 : :
854 : : // Place all the variables; these will dynamically end up in different
855 : : // locations based on what the underlying GPU supports (UBOs, pushc, etc.)
856 : : //
857 : : // We go through the list twice, once to place stuff that we definitely
858 : : // want inside PCs, and then a second time to opportunistically place the rest.
859 : 1945 : pass->vars = pl_calloc_ptr(pass, sh->vars.num, pass->vars);
860 [ + + ]: 6616 : for (int i = 0; i < sh->vars.num; i++) {
861 [ - + ]: 4671 : if (!add_pass_var(dp, tmp, pass, ¶ms, &sh->vars.elem[i], &pass->vars[i], false))
862 : 0 : goto error;
863 : : }
864 [ + + ]: 6616 : for (int i = 0; i < sh->vars.num; i++) {
865 [ - + ]: 4671 : if (!add_pass_var(dp, tmp, pass, ¶ms, &sh->vars.elem[i], &pass->vars[i], true))
866 : 0 : goto error;
867 : : }
868 : :
869 : : // Now that we know the variable placement, finalize pushc/UBO sizes
870 : 1945 : params.push_constants_size = PL_ALIGN2(params.push_constants_size, 4);
871 : 1945 : size_t ubo_size = sh_buf_desc_size(&pass->ubo_desc);
872 [ + + ]: 1945 : if (ubo_size) {
873 : 482 : pass->ubo_index = sh->descs.num;
874 [ + + - + : 482 : PL_ARRAY_APPEND(sh, sh->descs, pass->ubo_desc); // don't mangle names
- + ]
875 : : };
876 : :
877 : : // Place and fill in the descriptors
878 : 1945 : const int num_descs = sh->descs.num;
879 : 1945 : int binding[PL_DESC_TYPE_COUNT] = {0};
880 : 1945 : params.num_descriptors = num_descs;
881 : 1945 : params.descriptors = pl_calloc_ptr(tmp, num_descs, params.descriptors);
882 [ + + ]: 5543 : for (int i = 0; i < num_descs; i++) {
883 : 3598 : struct pl_desc *desc = ¶ms.descriptors[i];
884 : 3598 : *desc = sh->descs.elem[i].desc;
885 : 3598 : desc->binding = binding[pl_desc_namespace(dp->gpu, desc->type)]++;
886 : : }
887 : :
888 : : // Finalize the shader and look it up in the pass cache
889 : 1945 : pl_str_builder vert_builder = NULL, glsl_builder = NULL;
890 : 1945 : generate_shaders(dp, &gen_params, &vert_builder, &glsl_builder);
891 [ + + ]: 98805 : for (int i = 0; i < dp->passes.num; i++) {
892 : 98315 : struct pass *p = dp->passes.elem[i];
893 [ + + ]: 98315 : if (p->signature != pass->signature)
894 : : continue;
895 : :
896 : : // Found existing shader, re-use directly
897 [ + + ]: 1455 : if (p->ubo)
898 : 373 : sh->descs.elem[p->ubo_index].binding.object = p->ubo;
899 : 1455 : pl_free(p->run_params.constant_data);
900 : 1455 : p->run_params.constant_data = pl_steal(p, constant_data);
901 : 1455 : p->last_index = dp->current_index;
902 : 1455 : pl_free(pass);
903 : 1455 : return p;
904 : : }
905 : :
906 : : // Need to compile new shader, execute templates now
907 [ + + ]: 490 : if (vert_builder) {
908 : 402 : pl_str vert = pl_str_builder_exec(vert_builder);
909 : 402 : params.vertex_shader = (char *) vert.buf;
910 : : }
911 : 490 : pl_str glsl = pl_str_builder_exec(glsl_builder);
912 : 490 : params.glsl_shader = (char *) glsl.buf;
913 : :
914 : : // Turn all shader identifiers into actual strings before passing it
915 : : // to the `pl_gpu`
916 : : #define FIX_IDENT(name) \
917 : : name = sh_ident_tostr(sh_ident_unpack(name))
918 [ + + ]: 1016 : for (int i = 0; i < params.num_variables; i++)
919 [ - + ]: 526 : FIX_IDENT(params.variables[i].name);
920 [ + + ]: 1551 : for (int i = 0; i < params.num_descriptors; i++)
921 [ - + ]: 1061 : FIX_IDENT(params.descriptors[i].name);
922 [ + + ]: 1322 : for (int i = 0; i < params.num_vertex_attribs; i++)
923 [ - + ]: 832 : FIX_IDENT(params.vertex_attribs[i].name);
924 : : #undef FIX_IDENT
925 : :
926 : 490 : pass->pass = pl_pass_create(dp->gpu, ¶ms);
927 [ - + ]: 490 : if (!pass->pass) {
928 : 0 : PL_ERR(dp, "Failed creating render pass for dispatch");
929 : : // Add it anyway
930 : : }
931 : :
932 : : struct pl_pass_run_params *rparams = &pass->run_params;
933 : 490 : rparams->pass = pass->pass;
934 : 490 : rparams->constant_data = constant_data;
935 : 490 : rparams->push_constants = pl_zalloc(pass, params.push_constants_size);
936 : 490 : rparams->desc_bindings = pl_calloc_ptr(pass, params.num_descriptors,
937 : : rparams->desc_bindings);
938 : :
939 [ + + + - ]: 490 : if (ubo_size && pass->pass) {
940 : : // Create the UBO
941 : 109 : pass->ubo = pl_buf_create(dp->gpu, pl_buf_params(
942 : : .size = ubo_size,
943 : : .uniform = true,
944 : : .host_writable = true,
945 : : ));
946 : :
947 [ - + ]: 109 : if (!pass->ubo) {
948 : 0 : PL_ERR(dp, "Failed creating uniform buffer for dispatch");
949 : 0 : goto error;
950 : : }
951 : :
952 : 109 : sh->descs.elem[pass->ubo_index].binding.object = pass->ubo;
953 : : }
954 : :
955 [ + + + + ]: 490 : if (params.type == PL_PASS_RASTER && !vparams) {
956 : : // Generate the vertex array placeholder
957 : 392 : rparams->vertex_count = 4; // single quad
958 : 392 : size_t vert_size = rparams->vertex_count * params.vertex_stride;
959 : 392 : rparams->vertex_data = pl_zalloc(pass, vert_size);
960 : : }
961 : :
962 : 490 : pass->timer = pl_timer_create(dp->gpu);
963 : :
964 [ + + + + : 490 : PL_ARRAY_APPEND(dp, dp->passes, pass);
- + ]
965 : 490 : return pass;
966 : :
967 : 0 : error:
968 : 0 : pass_destroy(dp, pass);
969 : 0 : return NULL;
970 : : }
971 : :
972 : 4671 : static void update_pass_var(pl_dispatch dp, struct pass *pass,
973 : : const struct pl_shader_var *sv, struct pass_var *pv)
974 : : {
975 : 4671 : struct pl_var_layout host_layout = pl_var_host_layout(0, &sv->var);
976 [ - + ]: 4671 : pl_assert(host_layout.size);
977 : :
978 : : // Use the cache to skip updates if possible
979 [ + + + + ]: 4671 : if (pv->cached_data && !memcmp(sv->data, pv->cached_data, host_layout.size))
980 : 2582 : return;
981 [ + + ]: 2089 : if (!pv->cached_data)
982 : 1277 : pv->cached_data = pl_alloc(pass, host_layout.size);
983 [ - + + + : 2089 : memcpy(pv->cached_data, sv->data, host_layout.size);
- ]
984 : :
985 : : struct pl_pass_run_params *rparams = &pass->run_params;
986 [ - + + + : 2089 : switch (pv->type) {
- ]
987 : : case PASS_VAR_NONE:
988 : 0 : pl_unreachable();
989 : 1110 : case PASS_VAR_GLOBAL: {
990 : : struct pl_var_update vu = {
991 : 1110 : .index = pv->index,
992 : 1110 : .data = sv->data,
993 : : };
994 [ + + + + : 1110 : PL_ARRAY_APPEND_RAW(pass, rparams->var_updates, rparams->num_var_updates, vu);
- + ]
995 : : break;
996 : : }
997 : 342 : case PASS_VAR_UBO: {
998 [ - + ]: 342 : pl_assert(pass->ubo);
999 : 342 : const size_t offset = pv->layout.offset;
1000 [ + + ]: 342 : if (host_layout.stride == pv->layout.stride) {
1001 [ - + ]: 220 : pl_assert(host_layout.size == pv->layout.size);
1002 : 220 : pl_buf_write(dp->gpu, pass->ubo, offset, sv->data, host_layout.size);
1003 : : } else {
1004 : : // Coalesce strided UBO write into a single pl_buf_write to avoid
1005 : : // unnecessary synchronization overhead by assembling the correctly
1006 : : // strided upload in RAM
1007 [ + + ]: 122 : pl_grow(dp, &dp->ubo_tmp, pv->layout.size);
1008 : 122 : uint8_t * const tmp = dp->ubo_tmp;
1009 : 122 : const uint8_t *src = sv->data;
1010 : 122 : const uint8_t *end = src + host_layout.size;
1011 : : uint8_t *dst = tmp;
1012 [ + + ]: 524 : while (src < end) {
1013 : : memcpy(dst, src, host_layout.stride);
1014 : 402 : src += host_layout.stride;
1015 : 402 : dst += pv->layout.stride;
1016 : : }
1017 : 122 : pl_buf_write(dp->gpu, pass->ubo, offset, tmp, pv->layout.size);
1018 : : }
1019 : : break;
1020 : : }
1021 : 637 : case PASS_VAR_PUSHC:
1022 [ - + ]: 637 : pl_assert(rparams->push_constants);
1023 : 637 : memcpy_layout(rparams->push_constants, pv->layout, sv->data, host_layout);
1024 : 637 : break;
1025 : : };
1026 : : }
1027 : :
1028 : 68 : static void compute_vertex_attribs(pl_dispatch dp, pl_shader sh,
1029 : : int width, int height, ident_t *out_scale)
1030 : : {
1031 : : // Simulate vertex attributes using global definitions
1032 : 136 : *out_scale = sh_var(sh, (struct pl_shader_var) {
1033 : 68 : .var = pl_var_vec2("out_scale"),
1034 : 68 : .data = &(float[2]){ 1.0 / width, 1.0 / height },
1035 : : .dynamic = true,
1036 : : });
1037 : :
1038 : 68 : GLSLP("#define frag_pos(id) (vec2(id) + vec2(0.5)) \n"
1039 : : "#define frag_map(id) ("$" * frag_pos(id)) \n"
1040 : : "#define gl_FragCoord vec4(frag_pos(gl_GlobalInvocationID), 0.0, 1.0) \n",
1041 : : *out_scale);
1042 : :
1043 [ + + ]: 126 : for (int n = 0; n < sh->vas.num; n++) {
1044 : 58 : const struct pl_shader_va *sva = &sh->vas.elem[n];
1045 : :
1046 : : ident_t points[4];
1047 [ + + ]: 290 : for (int i = 0; i < PL_ARRAY_SIZE(points); i++) {
1048 : 232 : points[i] = sh_var(sh, (struct pl_shader_var) {
1049 : 232 : .var = pl_var_from_fmt(sva->attr.fmt, "pt"),
1050 : 232 : .data = sva->data[i],
1051 : : });
1052 : : }
1053 : :
1054 [ - + ]: 58 : GLSLP("#define "$"_map(id) "
1055 : : "(mix(mix("$", "$", frag_map(id).x), "
1056 : : " mix("$", "$", frag_map(id).x), "
1057 : : "frag_map(id).y)) \n"
1058 : : "#define "$" ("$"_map(gl_GlobalInvocationID)) \n",
1059 : : sh_ident_unpack(sva->attr.name),
1060 : : points[0], points[1], points[2], points[3],
1061 : : sh_ident_unpack(sva->attr.name),
1062 : : sh_ident_unpack(sva->attr.name));
1063 : : }
1064 : 68 : }
1065 : :
1066 : 0 : static const char *map_blend_mode(enum pl_blend_mode mode)
1067 : : {
1068 [ # # # # : 0 : switch (mode) {
# ]
1069 : : case PL_BLEND_ZERO: return "0.0";
1070 : 0 : case PL_BLEND_ONE: return "1.0";
1071 : 0 : case PL_BLEND_SRC_ALPHA: return "color.a";
1072 : 0 : case PL_BLEND_ONE_MINUS_SRC_ALPHA: return "(1.0 - color.a)";
1073 : : case PL_BLEND_MODE_COUNT: break;
1074 : : }
1075 : :
1076 : 0 : pl_unreachable();
1077 : : }
1078 : :
1079 : 66 : static void translate_compute_shader(pl_dispatch dp, pl_shader sh,
1080 : : const pl_rect2d *rc,
1081 : : const struct pl_dispatch_params *params)
1082 : : {
1083 : 66 : int width = abs(pl_rect_w(*rc)), height = abs(pl_rect_h(*rc));
1084 [ - + ]: 66 : if (sh->transpose)
1085 : : PL_SWAP(width, height);
1086 : : ident_t out_scale;
1087 : 66 : compute_vertex_attribs(dp, sh, width, height, &out_scale);
1088 : :
1089 : : // Simulate a framebuffer using storage images
1090 [ - + ]: 66 : pl_assert(params->target->params.storable);
1091 [ - + ]: 66 : pl_assert(sh->output == PL_SHADER_SIG_COLOR);
1092 : 66 : ident_t fbo = sh_desc(sh, (struct pl_shader_desc) {
1093 : : .binding.object = params->target,
1094 : : .desc = {
1095 : : .name = "out_image",
1096 : : .type = PL_DESC_STORAGE_IMG,
1097 : 66 : .access = params->blend_params ? PL_DESC_ACCESS_READWRITE
1098 [ + - ]: 66 : : PL_DESC_ACCESS_WRITEONLY,
1099 : : },
1100 : : });
1101 : :
1102 : 66 : ident_t base = sh_var(sh, (struct pl_shader_var) {
1103 : 66 : .data = &(int[2]){ rc->x0, rc->y0 },
1104 : : .dynamic = true,
1105 : : .var = {
1106 : : .name = "base",
1107 : : .type = PL_VAR_SINT,
1108 : : .dim_v = 2,
1109 : : .dim_m = 1,
1110 : : .dim_a = 1,
1111 : : },
1112 : : });
1113 : :
1114 [ + - + - ]: 132 : int dx = rc->x0 > rc->x1 ? -1 : 1, dy = rc->y0 > rc->y1 ? -1 : 1;
1115 : 66 : GLSL("ivec2 dir = ivec2(%d, %d);\n", dx, dy); // hard-code, not worth var
1116 [ + - + - ]: 198 : GLSL("ivec2 pos = "$" + dir * ivec2(gl_GlobalInvocationID).%c%c;\n",
1117 : : base, sh->transpose ? 'y' : 'x', sh->transpose ? 'x' : 'y');
1118 : 66 : GLSL("vec2 fpos = "$" * vec2(gl_GlobalInvocationID);\n", out_scale);
1119 : 66 : GLSL("if (fpos.x < 1.0 && fpos.y < 1.0) {\n");
1120 [ - + ]: 66 : if (params->blend_params) {
1121 : 0 : GLSL("vec4 orig = imageLoad("$", pos);\n", fbo);
1122 : 0 : GLSL("color = vec4(color.rgb * vec3(%s), color.a * %s) \n"
1123 : : " + vec4(orig.rgb * vec3(%s), orig.a * %s);\n",
1124 : : map_blend_mode(params->blend_params->src_rgb),
1125 : : map_blend_mode(params->blend_params->src_alpha),
1126 : : map_blend_mode(params->blend_params->dst_rgb),
1127 : : map_blend_mode(params->blend_params->dst_alpha));
1128 : : }
1129 : 66 : GLSL("imageStore("$", pos, color);\n", fbo);
1130 : 66 : GLSL("}\n");
1131 : 66 : sh->output = PL_SHADER_SIG_NONE;
1132 : 66 : }
1133 : :
1134 : 1945 : static void run_pass(pl_dispatch dp, pl_shader sh, struct pass *pass)
1135 : : {
1136 : 1945 : pl_shader_info shader = &sh->info->info;
1137 : 1945 : pl_pass_run(dp->gpu, &pass->run_params);
1138 : :
1139 [ + + ]: 5321 : for (uint64_t ts; (ts = pl_timer_query(dp->gpu, pass->timer));) {
1140 : 1431 : PL_TRACE(dp, "Spent %.3f ms on shader: %s", ts / 1e6, shader->description);
1141 : :
1142 : 1431 : uint64_t old = pass->samples[pass->ts_idx];
1143 : 1431 : pass->samples[pass->ts_idx] = ts;
1144 : 1431 : pass->ts_last = ts;
1145 : 1431 : pass->ts_peak = PL_MAX(pass->ts_peak, ts);
1146 : 1431 : pass->ts_sum += ts;
1147 : 1431 : pass->ts_idx = (pass->ts_idx + 1) % PL_ARRAY_SIZE(pass->samples);
1148 : :
1149 [ - + ]: 1431 : if (old) {
1150 : 0 : pass->ts_sum -= old;
1151 [ # # ]: 0 : if (old == pass->ts_peak) {
1152 : : uint64_t new_peak = 0;
1153 [ # # ]: 0 : for (int i = 0; i < PL_ARRAY_SIZE(pass->samples); i++)
1154 : 0 : new_peak = PL_MAX(new_peak, pass->samples[i]);
1155 : 0 : pass->ts_peak = new_peak;
1156 : : }
1157 : : }
1158 : : }
1159 : :
1160 [ + + ]: 1945 : if (!dp->info_callback)
1161 : 142 : return;
1162 : :
1163 : : struct pl_dispatch_info info;
1164 : 1803 : info.signature = pass->signature;
1165 : 1803 : info.shader = shader;
1166 : :
1167 : : // Test to see if the ring buffer already wrapped around once
1168 [ - + ]: 1803 : if (pass->samples[pass->ts_idx]) {
1169 : 0 : info.num_samples = PL_ARRAY_SIZE(pass->samples);
1170 : 0 : int num_wrapped = info.num_samples - pass->ts_idx;
1171 : 0 : memcpy(info.samples, &pass->samples[pass->ts_idx],
1172 : : num_wrapped * sizeof(info.samples[0]));
1173 : 0 : memcpy(&info.samples[num_wrapped], pass->samples,
1174 : 0 : pass->ts_idx * sizeof(info.samples[0]));
1175 : : } else {
1176 : 1803 : info.num_samples = pass->ts_idx;
1177 : 1803 : memcpy(info.samples, pass->samples,
1178 : 1803 : pass->ts_idx * sizeof(info.samples[0]));
1179 : : }
1180 : :
1181 : 1803 : info.last = pass->ts_last;
1182 : 1803 : info.peak = pass->ts_peak;
1183 : 1803 : info.average = pass->ts_sum / PL_MAX(info.num_samples, 1);
1184 : 1803 : dp->info_callback(dp->info_priv, &info);
1185 : : }
1186 : :
1187 : 1879 : bool pl_dispatch_finish(pl_dispatch dp, const struct pl_dispatch_params *params)
1188 : : {
1189 : 1879 : pl_shader sh = *params->shader;
1190 : : bool ret = false;
1191 : 1879 : pl_mutex_lock(&dp->lock);
1192 : :
1193 [ - + ]: 1879 : if (sh->failed) {
1194 : 0 : PL_ERR(sh, "Trying to dispatch a failed shader.");
1195 : 0 : goto error;
1196 : : }
1197 : :
1198 [ - + ]: 1879 : if (!sh->mutable) {
1199 : 0 : PL_ERR(dp, "Trying to dispatch non-mutable shader?");
1200 : 0 : goto error;
1201 : : }
1202 : :
1203 [ + - - + ]: 1879 : if (sh->input != PL_SHADER_SIG_NONE || sh->output != PL_SHADER_SIG_COLOR) {
1204 : 0 : PL_ERR(dp, "Trying to dispatch shader with incompatible signature!");
1205 : 0 : goto error;
1206 : : }
1207 : :
1208 [ + - ]: 1879 : const struct pl_tex_params *tpars = ¶ms->target->params;
1209 [ - + ]: 1879 : if (pl_tex_params_dimension(*tpars) != 2 || !tpars->renderable) {
1210 : 0 : PL_ERR(dp, "Trying to dispatch a shader using an invalid target "
1211 : : "texture. The target must be a renderable 2D texture.");
1212 : 0 : goto error;
1213 : : }
1214 : :
1215 : 1879 : const struct pl_gpu_limits *limits = &dp->gpu->limits;
1216 : 1879 : bool can_compute = tpars->storable;
1217 [ + + - + ]: 1879 : if (can_compute && params->blend_params)
1218 : 0 : can_compute = tpars->format->caps & PL_FMT_CAP_READWRITE;
1219 : :
1220 [ + + - + ]: 1879 : if (pl_shader_is_compute(sh) && !can_compute) {
1221 : 0 : PL_ERR(dp, "Trying to dispatch using a compute shader with a "
1222 : : "non-storable or incompatible target texture.");
1223 : 0 : goto error;
1224 [ + + - + ]: 1879 : } else if (can_compute && limits->compute_queues > limits->fragment_queues) {
1225 [ # # ]: 0 : if (sh_try_compute(sh, 16, 16, true, 0))
1226 : 0 : PL_TRACE(dp, "Upgrading fragment shader to compute shader.");
1227 : : }
1228 : :
1229 : 1879 : pl_rect2d rc = params->rect;
1230 [ + + ]: 1879 : if (!pl_rect_w(rc)) {
1231 : 792 : rc.x0 = 0;
1232 : 792 : rc.x1 = tpars->w;
1233 : : }
1234 [ + + ]: 1879 : if (!pl_rect_h(rc)) {
1235 : 792 : rc.y0 = 0;
1236 : 792 : rc.y1 = tpars->h;
1237 : : }
1238 : :
1239 : 1879 : int w, h, tw = abs(pl_rect_w(rc)), th = abs(pl_rect_h(rc));
1240 [ + + + - : 1879 : if (pl_shader_output_size(sh, &w, &h) && (w != tw || h != th))
- + ]
1241 : : {
1242 [ # # ]: 0 : PL_ERR(dp, "Trying to dispatch a shader with explicit output size "
1243 : : "requirements %dx%d%s using a target rect of size %dx%d.",
1244 : : w, h, sh->transpose ? " (transposed)" : "", tw, th);
1245 : 0 : goto error;
1246 : : }
1247 : :
1248 : : int vert_idx = -1;
1249 : : const pl_transform2x2 *proj = NULL;
1250 [ + + ]: 1879 : if (pl_shader_is_compute(sh)) {
1251 : : // Translate the compute shader to simulate vertices etc.
1252 : 66 : translate_compute_shader(dp, sh, &rc, params);
1253 : : } else {
1254 : : // Add the vertex information encoding the position
1255 : 1813 : pl_rect2df vert_rect = {
1256 : 1813 : .x0 = 2.0 * rc.x0 / tpars->w - 1.0,
1257 : 1813 : .y0 = 2.0 * rc.y0 / tpars->h - 1.0,
1258 : 1813 : .x1 = 2.0 * rc.x1 / tpars->w - 1.0,
1259 : 1813 : .y1 = 2.0 * rc.y1 / tpars->h - 1.0,
1260 : : };
1261 : :
1262 [ + + ]: 1813 : if (sh->transpose) {
1263 : : static const pl_transform2x2 transpose_proj = {{{
1264 : : { 0, 1 },
1265 : : { 1, 0 },
1266 : : }}};
1267 : : proj = &transpose_proj;
1268 : 616 : PL_SWAP(vert_rect.x0, vert_rect.y0);
1269 : 616 : PL_SWAP(vert_rect.x1, vert_rect.y1);
1270 : : }
1271 : :
1272 : 1813 : sh_attr_vec2(sh, "position", &vert_rect);
1273 : 1813 : vert_idx = sh->vas.num - 1;
1274 : : }
1275 : :
1276 : : // We need to set pl_pass_params.load_target when either blending is
1277 : : // enabled or we're drawing to some scissored sub-rect of the texture
1278 : 1879 : pl_rect2d full = { 0, 0, tpars->w, tpars->h };
1279 : 1879 : pl_rect2d rc_norm = rc;
1280 : 1879 : pl_rect2d_normalize(&rc_norm);
1281 : 1879 : rc_norm.x0 = PL_MAX(rc_norm.x0, 0);
1282 : 1879 : rc_norm.y0 = PL_MAX(rc_norm.y0, 0);
1283 : 1879 : rc_norm.x1 = PL_MIN(rc_norm.x1, tpars->w);
1284 : 1879 : rc_norm.y1 = PL_MIN(rc_norm.y1, tpars->h);
1285 [ + - - + : 1879 : bool load = params->blend_params || !pl_rect2d_eq(rc_norm, full);
+ + - + -
+ ]
1286 : :
1287 : 1879 : struct pass *pass = finalize_pass(dp, sh, params->target, vert_idx,
1288 : : params->blend_params, load, NULL, proj);
1289 : :
1290 : : // Silently return on failed passes
1291 [ + - - + ]: 1879 : if (!pass || !pass->pass)
1292 : 0 : goto error;
1293 : :
1294 : : struct pl_pass_run_params *rparams = &pass->run_params;
1295 : :
1296 : : // Update the descriptor bindings
1297 [ + + ]: 5362 : for (int i = 0; i < sh->descs.num; i++)
1298 : 3483 : rparams->desc_bindings[i] = sh->descs.elem[i].binding;
1299 : :
1300 : : // Update all of the variables (if needed)
1301 : 1879 : rparams->num_var_updates = 0;
1302 [ + + ]: 6436 : for (int i = 0; i < sh->vars.num; i++)
1303 : 4557 : update_pass_var(dp, pass, &sh->vars.elem[i], &pass->vars[i]);
1304 : :
1305 : : // Update the vertex data
1306 [ + + ]: 1879 : if (rparams->vertex_data) {
1307 : 1813 : uintptr_t vert_base = (uintptr_t) rparams->vertex_data;
1308 : 1813 : size_t stride = rparams->pass->params.vertex_stride;
1309 [ + + ]: 5871 : for (int i = 0; i < sh->vas.num; i++) {
1310 : 4058 : const struct pl_shader_va *sva = &sh->vas.elem[i];
1311 : 4058 : struct pl_vertex_attrib *va = &rparams->pass->params.vertex_attribs[i];
1312 : :
1313 : 4058 : size_t size = sva->attr.fmt->texel_size;
1314 : 4058 : uintptr_t va_base = vert_base + va->offset; // use placed offset
1315 [ + + ]: 20290 : for (int n = 0; n < 4; n++)
1316 : 16232 : memcpy((void *) (va_base + n * stride), sva->data[n], size);
1317 : : }
1318 : : }
1319 : :
1320 : : // For compute shaders: also update the dispatch dimensions
1321 [ + + ]: 1879 : if (pl_shader_is_compute(sh)) {
1322 : : int width = abs(pl_rect_w(rc)),
1323 : : height = abs(pl_rect_h(rc));
1324 [ - + ]: 66 : if (sh->transpose)
1325 : : PL_SWAP(width, height);
1326 : : // Round up to make sure we don't leave off a part of the target
1327 : 66 : int block_w = sh->group_size[0],
1328 : 66 : block_h = sh->group_size[1],
1329 : 66 : num_x = PL_DIV_UP(width, block_w),
1330 : 66 : num_y = PL_DIV_UP(height, block_h);
1331 : :
1332 : 66 : rparams->compute_groups[0] = num_x;
1333 : 66 : rparams->compute_groups[1] = num_y;
1334 : 66 : rparams->compute_groups[2] = 1;
1335 : : } else {
1336 : : // Update the scissors for performance
1337 : 1813 : rparams->scissors = rc_norm;
1338 : : }
1339 : :
1340 : : // Dispatch the actual shader
1341 : 1879 : rparams->target = params->target;
1342 [ + - ]: 1879 : rparams->timer = PL_DEF(params->timer, pass->timer);
1343 : 1879 : run_pass(dp, sh, pass);
1344 : :
1345 : : ret = true;
1346 : : // fall through
1347 : :
1348 : 1879 : error:
1349 : : // Reset the temporary buffers which we use to build the shader
1350 [ + + ]: 9395 : for (int i = 0; i < PL_ARRAY_SIZE(dp->tmp); i++)
1351 : 7516 : pl_str_builder_reset(dp->tmp[i]);
1352 : :
1353 : 1879 : pl_mutex_unlock(&dp->lock);
1354 : 1879 : pl_dispatch_abort(dp, params->shader);
1355 : 1879 : return ret;
1356 : : }
1357 : :
1358 : 48 : bool pl_dispatch_compute(pl_dispatch dp, const struct pl_dispatch_compute_params *params)
1359 : : {
1360 : 48 : pl_shader sh = *params->shader;
1361 : : bool ret = false;
1362 : 48 : pl_mutex_lock(&dp->lock);
1363 : :
1364 [ - + ]: 48 : if (sh->failed) {
1365 : 0 : PL_ERR(sh, "Trying to dispatch a failed shader.");
1366 : 0 : goto error;
1367 : : }
1368 : :
1369 [ - + ]: 48 : if (!sh->mutable) {
1370 : 0 : PL_ERR(dp, "Trying to dispatch non-mutable shader?");
1371 : 0 : goto error;
1372 : : }
1373 : :
1374 [ - + ]: 48 : if (sh->input != PL_SHADER_SIG_NONE) {
1375 : 0 : PL_ERR(dp, "Trying to dispatch shader with incompatible signature!");
1376 : 0 : goto error;
1377 : : }
1378 : :
1379 [ - + ]: 48 : if (!pl_shader_is_compute(sh)) {
1380 : 0 : PL_ERR(dp, "Trying to dispatch a non-compute shader using "
1381 : : "`pl_dispatch_compute`!");
1382 : 0 : goto error;
1383 : : }
1384 : :
1385 [ + + ]: 48 : if (sh->vas.num) {
1386 [ + - - + ]: 2 : if (!params->width || !params->height) {
1387 : 0 : PL_ERR(dp, "Trying to dispatch a targetless compute shader that "
1388 : : "uses vertex attributes, this requires specifying the size "
1389 : : "of the effective rendering area!");
1390 : 0 : goto error;
1391 : : }
1392 : :
1393 : 2 : compute_vertex_attribs(dp, sh, params->width, params->height,
1394 : : &(ident_t){0});
1395 : : }
1396 : :
1397 : 48 : struct pass *pass = finalize_pass(dp, sh, NULL, -1, NULL, false, NULL, NULL);
1398 : :
1399 : : // Silently return on failed passes
1400 [ + - + - ]: 48 : if (!pass || !pass->pass)
1401 : 0 : goto error;
1402 : :
1403 : : struct pl_pass_run_params *rparams = &pass->run_params;
1404 : :
1405 : : // Update the descriptor bindings
1406 [ + + ]: 143 : for (int i = 0; i < sh->descs.num; i++)
1407 : 95 : rparams->desc_bindings[i] = sh->descs.elem[i].binding;
1408 : :
1409 : : // Update all of the variables (if needed)
1410 : 48 : rparams->num_var_updates = 0;
1411 [ + + ]: 130 : for (int i = 0; i < sh->vars.num; i++)
1412 : 82 : update_pass_var(dp, pass, &sh->vars.elem[i], &pass->vars[i]);
1413 : :
1414 : : // Update the dispatch size
1415 : : int groups = 1;
1416 [ + + ]: 192 : for (int i = 0; i < 3; i++) {
1417 : 144 : groups *= params->dispatch_size[i];
1418 : 144 : rparams->compute_groups[i] = params->dispatch_size[i];
1419 : : }
1420 : :
1421 [ + + ]: 48 : if (!groups) {
1422 [ + - - + ]: 2 : pl_assert(params->width && params->height);
1423 : 2 : int block_w = sh->group_size[0],
1424 : 2 : block_h = sh->group_size[1],
1425 : 2 : num_x = PL_DIV_UP(params->width, block_w),
1426 : 2 : num_y = PL_DIV_UP(params->height, block_h);
1427 : :
1428 : 2 : rparams->compute_groups[0] = num_x;
1429 : 2 : rparams->compute_groups[1] = num_y;
1430 : 2 : rparams->compute_groups[2] = 1;
1431 : : }
1432 : :
1433 : : // Dispatch the actual shader
1434 [ + - ]: 48 : rparams->timer = PL_DEF(params->timer, pass->timer);
1435 : 48 : run_pass(dp, sh, pass);
1436 : :
1437 : : ret = true;
1438 : : // fall through
1439 : :
1440 : 48 : error:
1441 : : // Reset the temporary buffers which we use to build the shader
1442 [ + + ]: 240 : for (int i = 0; i < PL_ARRAY_SIZE(dp->tmp); i++)
1443 : 192 : pl_str_builder_reset(dp->tmp[i]);
1444 : :
1445 : 48 : pl_mutex_unlock(&dp->lock);
1446 : 48 : pl_dispatch_abort(dp, params->shader);
1447 : 48 : return ret;
1448 : : }
1449 : :
1450 : 18 : bool pl_dispatch_vertex(pl_dispatch dp, const struct pl_dispatch_vertex_params *params)
1451 : : {
1452 : 18 : pl_shader sh = *params->shader;
1453 : : bool ret = false;
1454 : 18 : pl_mutex_lock(&dp->lock);
1455 : :
1456 [ - + ]: 18 : if (sh->failed) {
1457 : 0 : PL_ERR(sh, "Trying to dispatch a failed shader.");
1458 : 0 : goto error;
1459 : : }
1460 : :
1461 [ - + ]: 18 : if (!sh->mutable) {
1462 : 0 : PL_ERR(dp, "Trying to dispatch non-mutable shader?");
1463 : 0 : goto error;
1464 : : }
1465 : :
1466 [ + - - + ]: 18 : if (sh->input != PL_SHADER_SIG_NONE || sh->output != PL_SHADER_SIG_COLOR) {
1467 : 0 : PL_ERR(dp, "Trying to dispatch shader with incompatible signature!");
1468 : 0 : goto error;
1469 : : }
1470 : :
1471 [ - + ]: 18 : const struct pl_tex_params *tpars = ¶ms->target->params;
1472 [ - + ]: 18 : if (pl_tex_params_dimension(*tpars) != 2 || !tpars->renderable) {
1473 : 0 : PL_ERR(dp, "Trying to dispatch a shader using an invalid target "
1474 : : "texture. The target must be a renderable 2D texture.");
1475 : 0 : goto error;
1476 : : }
1477 : :
1478 [ - + ]: 18 : if (pl_shader_is_compute(sh)) {
1479 : 0 : PL_ERR(dp, "Trying to dispatch a compute shader using pl_dispatch_vertex.");
1480 : 0 : goto error;
1481 : : }
1482 : :
1483 [ - + ]: 18 : if (sh->vas.num) {
1484 : 0 : PL_ERR(dp, "Trying to dispatch a custom vertex shader with already "
1485 : : "attached vertex attributes.");
1486 : 0 : goto error;
1487 : : }
1488 : :
1489 [ - + ]: 18 : if (sh->transpose) {
1490 : 0 : PL_ERR(dp, "Trying to dispatch a transposed shader using "
1491 : : "pl_dispatch_vertex, unlikely to be correct. Erroring as a "
1492 : : "safety precaution!");
1493 : 0 : goto error;
1494 : : }
1495 : :
1496 : 18 : int pos_idx = params->vertex_position_idx;
1497 [ + - - + ]: 18 : if (pos_idx < 0 || pos_idx >= params->num_vertex_attribs) {
1498 : 0 : PL_ERR(dp, "Vertex position index out of range?");
1499 : 0 : goto error;
1500 : : }
1501 : :
1502 : : // Attach all of the vertex attributes to the shader manually
1503 : 18 : sh->vas.num = params->num_vertex_attribs;
1504 [ + + ]: 18 : PL_ARRAY_RESIZE(sh, sh->vas, sh->vas.num);
1505 [ + + ]: 62 : for (int i = 0; i < params->num_vertex_attribs; i++) {
1506 : 44 : ident_t id = sh_fresh(sh, params->vertex_attribs[i].name);
1507 : 44 : sh->vas.elem[i].attr = params->vertex_attribs[i];
1508 : 44 : sh->vas.elem[i].attr.name = sh_ident_pack(id);
1509 : 44 : GLSLP("#define %s "$"\n", params->vertex_attribs[i].name, id);
1510 : : }
1511 : :
1512 : : // Compute the coordinate projection matrix
1513 : 18 : pl_transform2x2 proj = pl_transform2x2_identity;
1514 [ - - + - ]: 18 : switch (params->vertex_coords) {
1515 : 0 : case PL_COORDS_ABSOLUTE:
1516 : 0 : proj.mat.m[0][0] /= tpars->w;
1517 : 0 : proj.mat.m[1][1] /= tpars->h;
1518 : : // fall through
1519 : 0 : case PL_COORDS_RELATIVE:
1520 : 0 : proj.mat.m[0][0] *= 2.0;
1521 : 0 : proj.mat.m[1][1] *= 2.0;
1522 : 0 : proj.c[0] -= 1.0;
1523 : 0 : proj.c[1] -= 1.0;
1524 : : // fall through
1525 : 18 : case PL_COORDS_NORMALIZED:
1526 [ - + ]: 18 : if (params->vertex_flipped) {
1527 : 0 : proj.mat.m[1][1] = -proj.mat.m[1][1];
1528 : 0 : proj.c[1] += 2.0;
1529 : : }
1530 : : break;
1531 : : }
1532 : :
1533 : 18 : struct pass *pass = finalize_pass(dp, sh, params->target, pos_idx,
1534 : 18 : params->blend_params, true, params, &proj);
1535 : :
1536 : : // Silently return on failed passes
1537 [ + - + - ]: 18 : if (!pass || !pass->pass)
1538 : 0 : goto error;
1539 : :
1540 : : struct pl_pass_run_params *rparams = &pass->run_params;
1541 : :
1542 : : // Update the descriptor bindings
1543 [ + + ]: 38 : for (int i = 0; i < sh->descs.num; i++)
1544 : 20 : rparams->desc_bindings[i] = sh->descs.elem[i].binding;
1545 : :
1546 : : // Update all of the variables (if needed)
1547 : 18 : rparams->num_var_updates = 0;
1548 [ + + ]: 50 : for (int i = 0; i < sh->vars.num; i++)
1549 : 32 : update_pass_var(dp, pass, &sh->vars.elem[i], &pass->vars[i]);
1550 : :
1551 : : // Update the scissors
1552 : 18 : rparams->scissors = params->scissors;
1553 [ - + ]: 18 : if (params->vertex_flipped) {
1554 : 0 : rparams->scissors.y0 = tpars->h - rparams->scissors.y0;
1555 : 0 : rparams->scissors.y1 = tpars->h - rparams->scissors.y1;
1556 : : }
1557 : 18 : pl_rect2d_normalize(&rparams->scissors);
1558 : :
1559 : : // Dispatch the actual shader
1560 : 18 : rparams->target = params->target;
1561 : 18 : rparams->vertex_count = params->vertex_count;
1562 : 18 : rparams->vertex_data = params->vertex_data;
1563 : 18 : rparams->vertex_buf = params->vertex_buf;
1564 : 18 : rparams->buf_offset = params->buf_offset;
1565 : 18 : rparams->index_data = params->index_data;
1566 : 18 : rparams->index_fmt = params->index_fmt;
1567 : 18 : rparams->index_buf = params->index_buf;
1568 : 18 : rparams->index_offset = params->index_offset;
1569 [ + - ]: 18 : rparams->timer = PL_DEF(params->timer, pass->timer);
1570 : 18 : run_pass(dp, sh, pass);
1571 : :
1572 : : ret = true;
1573 : : // fall through
1574 : :
1575 : 18 : error:
1576 : : // Reset the temporary buffers which we use to build the shader
1577 [ + + ]: 90 : for (int i = 0; i < PL_ARRAY_SIZE(dp->tmp); i++)
1578 : 72 : pl_str_builder_reset(dp->tmp[i]);
1579 : :
1580 : 18 : pl_mutex_unlock(&dp->lock);
1581 : 18 : pl_dispatch_abort(dp, params->shader);
1582 : 18 : return ret;
1583 : : }
1584 : :
1585 : 4170 : void pl_dispatch_abort(pl_dispatch dp, pl_shader *psh)
1586 : : {
1587 : 4170 : pl_shader sh = *psh;
1588 [ + + ]: 4170 : if (!sh)
1589 : : return;
1590 : :
1591 : : // Free unused memory as early as possible
1592 : 2754 : sh_deref(sh);
1593 : :
1594 : : // Re-add the shader to the internal pool of shaders
1595 : 2754 : pl_mutex_lock(&dp->lock);
1596 [ + + - + : 2754 : PL_ARRAY_APPEND(dp, dp->shaders, sh);
- + ]
1597 : 2754 : pl_mutex_unlock(&dp->lock);
1598 : 2754 : *psh = NULL;
1599 : : }
1600 : :
1601 : 1401 : void pl_dispatch_reset_frame(pl_dispatch dp)
1602 : : {
1603 : 1401 : pl_mutex_lock(&dp->lock);
1604 : :
1605 : 1401 : dp->current_ident = 0;
1606 : 1401 : dp->current_index++;
1607 : 1401 : garbage_collect_passes(dp);
1608 : :
1609 : 1401 : pl_mutex_unlock(&dp->lock);
1610 : 1401 : }
1611 : :
1612 : 8 : size_t pl_dispatch_save(pl_dispatch dp, uint8_t *out)
1613 : : {
1614 [ + + ]: 12 : return pl_cache_save(pl_gpu_cache(dp->gpu), out, out ? SIZE_MAX : 0);
1615 : : }
1616 : :
1617 : 2 : void pl_dispatch_load(pl_dispatch dp, const uint8_t *cache)
1618 : : {
1619 : 2 : pl_cache_load(pl_gpu_cache(dp->gpu), cache, SIZE_MAX);
1620 : 2 : }
|