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 "gpu.h"
20 : :
21 : : #define require(expr) pl_require(gpu, expr)
22 : :
23 : 9 : void pl_gpu_destroy(pl_gpu gpu)
24 : : {
25 [ + - ]: 9 : if (!gpu)
26 : : return;
27 : :
28 : 9 : struct pl_gpu_fns *impl = PL_PRIV(gpu);
29 : 9 : pl_dispatch_destroy(&impl->dp);
30 : 9 : impl->destroy(gpu);
31 : : }
32 : :
33 : 26 : pl_dispatch pl_gpu_dispatch(pl_gpu gpu)
34 : : {
35 : 26 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
36 : 26 : return impl->dp;
37 : : }
38 : :
39 : 950 : pl_cache pl_gpu_cache(pl_gpu gpu)
40 : : {
41 [ + + ]: 950 : if (!gpu)
42 : : return NULL;
43 : 949 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
44 : 949 : return atomic_load(&impl->cache);
45 : : }
46 : :
47 : 4 : void pl_gpu_set_cache(pl_gpu gpu, pl_cache cache)
48 : : {
49 : 4 : struct pl_gpu_fns *impl = PL_PRIV(gpu);
50 : 4 : atomic_store(&impl->cache, cache);
51 : 4 : }
52 : :
53 : 4840 : bool pl_fmt_is_ordered(pl_fmt fmt)
54 : : {
55 : 4840 : bool ret = !fmt->opaque;
56 [ + + ]: 15011 : for (int i = 0; i < fmt->num_components; i++)
57 : 10171 : ret &= fmt->sample_order[i] == i;
58 : 4840 : return ret;
59 : : }
60 : :
61 : 174 : bool pl_fmt_is_float(pl_fmt fmt)
62 : : {
63 [ - - + ]: 174 : switch (fmt->type) {
64 : : case PL_FMT_UNKNOWN: // more likely than not
65 : : case PL_FMT_FLOAT:
66 : : case PL_FMT_UNORM:
67 : : case PL_FMT_SNORM:
68 : : return true;
69 : :
70 : 0 : case PL_FMT_UINT:
71 : : case PL_FMT_SINT:
72 : 0 : return false;
73 : :
74 : : case PL_FMT_TYPE_COUNT:
75 : : break;
76 : : }
77 : :
78 : 0 : pl_unreachable();
79 : : }
80 : :
81 : 0 : bool pl_fmt_has_modifier(pl_fmt fmt, uint64_t modifier)
82 : : {
83 [ # # ]: 0 : if (!fmt)
84 : : return false;
85 : :
86 [ # # ]: 0 : for (int i = 0; i < fmt->num_modifiers; i++) {
87 [ # # ]: 0 : if (fmt->modifiers[i] == modifier)
88 : : return true;
89 : : }
90 : :
91 : : return false;
92 : : }
93 : :
94 : 9204 : pl_fmt pl_find_fmt(pl_gpu gpu, enum pl_fmt_type type, int num_components,
95 : : int min_depth, int host_bits, enum pl_fmt_caps caps)
96 : : {
97 [ + + ]: 147801 : for (int n = 0; n < gpu->num_formats; n++) {
98 : 147251 : pl_fmt fmt = gpu->formats[n];
99 [ + + + + ]: 147251 : if (fmt->type != type || fmt->num_components != num_components)
100 : 131974 : continue;
101 [ + + ]: 15277 : if ((fmt->caps & caps) != caps)
102 : 4735 : continue;
103 : :
104 : : // When specifying some particular host representation, ensure the
105 : : // format is non-opaque, ordered and unpadded
106 [ + + - + ]: 10542 : if (host_bits && fmt->opaque)
107 : 0 : continue;
108 [ + + ]: 6723 : if (host_bits && fmt->texel_size * 8 != host_bits * num_components)
109 : 1883 : continue;
110 [ + + - + ]: 8659 : if (host_bits && !pl_fmt_is_ordered(fmt))
111 : 0 : continue;
112 : :
113 [ + + ]: 28087 : for (int i = 0; i < fmt->num_components; i++) {
114 [ + + ]: 19433 : if (fmt->component_depth[i] < min_depth)
115 : 5 : goto next_fmt;
116 [ + + - + ]: 19428 : if (host_bits && fmt->host_bits[i] != host_bits)
117 : 0 : goto next_fmt;
118 : : }
119 : :
120 : : return fmt;
121 : :
122 : 138597 : next_fmt: ; // equivalent to `continue`
123 : : }
124 : :
125 : : // ran out of formats
126 : 550 : PL_TRACE(gpu, "No matching format found");
127 : 550 : return NULL;
128 : : }
129 : :
130 : 4154 : pl_fmt pl_find_vertex_fmt(pl_gpu gpu, enum pl_fmt_type type, int comps)
131 : : {
132 : : static const size_t sizes[] = {
133 : : [PL_FMT_FLOAT] = sizeof(float),
134 : : [PL_FMT_UNORM] = sizeof(unsigned),
135 : : [PL_FMT_UINT] = sizeof(unsigned),
136 : : [PL_FMT_SNORM] = sizeof(int),
137 : : [PL_FMT_SINT] = sizeof(int),
138 : : };
139 : :
140 : 4154 : return pl_find_fmt(gpu, type, comps, 0, 8 * sizes[type], PL_FMT_CAP_VERTEX);
141 : : }
142 : :
143 : 6 : pl_fmt pl_find_named_fmt(pl_gpu gpu, const char *name)
144 : : {
145 [ + - ]: 6 : if (!name)
146 : : return NULL;
147 : :
148 [ + + ]: 208 : for (int i = 0; i < gpu->num_formats; i++) {
149 : 204 : pl_fmt fmt = gpu->formats[i];
150 [ + + ]: 204 : if (strcmp(name, fmt->name) == 0)
151 : 2 : return fmt;
152 : : }
153 : :
154 : : // ran out of formats
155 : : return NULL;
156 : : }
157 : :
158 : 0 : pl_fmt pl_find_fourcc(pl_gpu gpu, uint32_t fourcc)
159 : : {
160 [ # # ]: 0 : if (!fourcc)
161 : : return NULL;
162 : :
163 [ # # ]: 0 : for (int i = 0; i < gpu->num_formats; i++) {
164 : 0 : pl_fmt fmt = gpu->formats[i];
165 [ # # ]: 0 : if (fourcc == fmt->fourcc)
166 : 0 : return fmt;
167 : : }
168 : :
169 : : // ran out of formats
170 : : return NULL;
171 : : }
172 : :
173 : 6 : static inline bool check_mod(pl_gpu gpu, pl_fmt fmt, uint64_t mod)
174 : : {
175 [ + - ]: 22 : for (int i = 0; i < fmt->num_modifiers; i++) {
176 [ + + ]: 22 : if (fmt->modifiers[i] == mod)
177 : : return true;
178 : : }
179 : :
180 : :
181 : 0 : PL_ERR(gpu, "DRM modifier %s not available for format %s. Available modifiers:",
182 : : PRINT_DRM_MOD(mod), fmt->name);
183 [ # # ]: 0 : for (int i = 0; i < fmt->num_modifiers; i++)
184 : 0 : PL_ERR(gpu, " %s", PRINT_DRM_MOD(fmt->modifiers[i]));
185 : :
186 : : return false;
187 : : }
188 : :
189 : 2022 : pl_tex pl_tex_create(pl_gpu gpu, const struct pl_tex_params *params)
190 : : {
191 [ - + ]: 2022 : require(params->format);
192 [ + + - + ]: 2022 : require(!params->import_handle || !params->export_handle);
193 [ + + - + ]: 2022 : require(!params->import_handle || !params->initial_data);
194 [ + + ]: 2022 : if (params->export_handle) {
195 [ - + ]: 10 : require(params->export_handle & gpu->export_caps.tex);
196 [ - + ]: 10 : require(PL_ISPOT(params->export_handle));
197 : : }
198 [ + + ]: 2022 : if (params->import_handle) {
199 [ - + ]: 6 : require(params->import_handle & gpu->import_caps.tex);
200 [ - + ]: 6 : require(PL_ISPOT(params->import_handle));
201 [ + - ]: 6 : if (params->import_handle == PL_HANDLE_DMA_BUF) {
202 [ - + ]: 6 : if (!check_mod(gpu, params->format, params->shared_mem.drm_format_mod))
203 : 0 : goto error;
204 [ + - ]: 6 : if (params->shared_mem.stride_w)
205 [ + - - + ]: 6 : require(params->w && params->shared_mem.stride_w >= params->w);
206 [ - + ]: 6 : if (params->shared_mem.stride_h)
207 [ # # # # ]: 0 : require(params->h && params->shared_mem.stride_h >= params->h);
208 [ # # ]: 0 : } else if (params->import_handle == PL_HANDLE_MTL_TEX) {
209 [ # # ]: 0 : require(params->shared_mem.plane <= 2);
210 : : }
211 : : }
212 : :
213 : : switch (pl_tex_params_dimension(*params)) {
214 : : case 1:
215 [ - + ]: 591 : require(params->w > 0);
216 [ - + ]: 591 : require(params->w <= gpu->limits.max_tex_1d_dim);
217 [ - + ]: 591 : require(!params->renderable);
218 [ + + - + ]: 591 : require(!params->blit_src || gpu->limits.blittable_1d_3d);
219 [ + + - + ]: 591 : require(!params->blit_dst || gpu->limits.blittable_1d_3d);
220 [ - + ]: 591 : require(!params->format->num_planes);
221 : : break;
222 : : case 2:
223 [ + - - + ]: 872 : require(params->w > 0 && params->h > 0);
224 [ - + ]: 872 : require(params->w <= gpu->limits.max_tex_2d_dim);
225 [ - + ]: 872 : require(params->h <= gpu->limits.max_tex_2d_dim);
226 : : break;
227 : : case 3:
228 [ + - + - : 559 : require(params->w > 0 && params->h > 0 && params->d > 0);
- + ]
229 [ - + ]: 559 : require(params->w <= gpu->limits.max_tex_3d_dim);
230 [ - + ]: 559 : require(params->h <= gpu->limits.max_tex_3d_dim);
231 [ - + ]: 559 : require(params->d <= gpu->limits.max_tex_3d_dim);
232 [ - + ]: 559 : require(!params->renderable);
233 [ + + - + ]: 559 : require(!params->blit_src || gpu->limits.blittable_1d_3d);
234 [ + + - + ]: 559 : require(!params->blit_dst || gpu->limits.blittable_1d_3d);
235 [ - + ]: 559 : require(!params->format->num_planes);
236 : : break;
237 : : }
238 : :
239 : 2022 : enum pl_fmt_caps fmt_caps = params->format->caps;
240 : 2022 : bool fmt_opaque = params->format->opaque;
241 [ + + ]: 2025 : for (int i = 0; i < params->format->num_planes; i++) {
242 : 3 : pl_fmt pfmt = params->format->planes[i].format;
243 : 3 : fmt_caps |= pfmt->caps;
244 : 3 : fmt_opaque &= pfmt->opaque;
245 : : }
246 : :
247 [ + + - + ]: 2022 : require(!params->host_readable || fmt_caps & PL_FMT_CAP_HOST_READABLE);
248 [ + + - + ]: 2022 : require(!params->host_writable || !fmt_opaque);
249 [ + + - + ]: 2022 : require(!params->sampleable || fmt_caps & PL_FMT_CAP_SAMPLEABLE);
250 [ + + - + ]: 2022 : require(!params->renderable || fmt_caps & PL_FMT_CAP_RENDERABLE);
251 [ + + - + ]: 2022 : require(!params->storable || fmt_caps & PL_FMT_CAP_STORABLE);
252 [ + + - + ]: 2022 : require(!params->blit_src || fmt_caps & PL_FMT_CAP_BLITTABLE);
253 [ + + - + ]: 2022 : require(!params->blit_dst || fmt_caps & PL_FMT_CAP_BLITTABLE);
254 : :
255 : 2022 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
256 : 2022 : return impl->tex_create(gpu, params);
257 : :
258 : : error:
259 [ # # ]: 0 : if (params->debug_tag)
260 : 0 : PL_ERR(gpu, " for texture: %s", params->debug_tag);
261 : : return NULL;
262 : : }
263 : :
264 : 2190 : void pl_tex_destroy(pl_gpu gpu, pl_tex *tex)
265 : : {
266 [ + + ]: 2190 : if (!*tex)
267 : : return;
268 : :
269 : 1878 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
270 : 1878 : impl->tex_destroy(gpu, *tex);
271 : 1878 : *tex = NULL;
272 : : }
273 : :
274 : 665 : static bool pl_tex_params_superset(struct pl_tex_params a, struct pl_tex_params b)
275 : : {
276 [ + - ]: 649 : return a.w == b.w && a.h == b.h && a.d == b.d &&
277 [ + + ]: 649 : a.format == b.format &&
278 [ - + - - ]: 647 : (a.sampleable || !b.sampleable) &&
279 [ + + + - ]: 647 : (a.renderable || !b.renderable) &&
280 [ + + + - ]: 647 : (a.storable || !b.storable) &&
281 [ + + + - ]: 647 : (a.blit_src || !b.blit_src) &&
282 [ + + + - ]: 647 : (a.blit_dst || !b.blit_dst) &&
283 [ + + + + : 1312 : (a.host_writable || !b.host_writable) &&
+ - ]
284 [ + - + - ]: 647 : (a.host_readable || !b.host_readable);
285 : : }
286 : :
287 : 753 : bool pl_tex_recreate(pl_gpu gpu, pl_tex *tex, const struct pl_tex_params *params)
288 : : {
289 [ - + ]: 753 : if (params->initial_data) {
290 : 0 : PL_ERR(gpu, "pl_tex_recreate may not be used with `initial_data`!");
291 : 0 : return false;
292 : : }
293 : :
294 [ - + ]: 753 : if (params->import_handle) {
295 : 0 : PL_ERR(gpu, "pl_tex_recreate may not be used with `import_handle`!");
296 : 0 : return false;
297 : : }
298 : :
299 [ + + + + ]: 753 : if (*tex && pl_tex_params_superset((*tex)->params, *params)) {
300 : 647 : pl_tex_invalidate(gpu, *tex);
301 : 647 : return true;
302 : : }
303 : :
304 [ + - ]: 212 : PL_DEBUG(gpu, "(Re)creating %dx%dx%d texture with format %s: %s",
305 : : params->w, params->h, params->d, params->format->name,
306 : : PL_DEF(params->debug_tag, "unknown"));
307 : :
308 : 106 : pl_tex_destroy(gpu, tex);
309 : 106 : *tex = pl_tex_create(gpu, params);
310 : :
311 : 106 : return !!*tex;
312 : : }
313 : :
314 : 432 : void pl_tex_clear_ex(pl_gpu gpu, pl_tex dst, const union pl_clear_color color)
315 : : {
316 [ - + ]: 432 : require(dst->params.blit_dst);
317 : :
318 : 432 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
319 [ + - ]: 432 : if (impl->tex_invalidate)
320 : 432 : impl->tex_invalidate(gpu, dst);
321 : 432 : impl->tex_clear_ex(gpu, dst, color);
322 : : return;
323 : :
324 : : error:
325 : : if (dst->params.debug_tag)
326 : : PL_ERR(gpu, " for texture: %s", dst->params.debug_tag);
327 : : }
328 : :
329 : 174 : void pl_tex_clear(pl_gpu gpu, pl_tex dst, const float color[4])
330 : : {
331 [ - + ]: 174 : if (!pl_fmt_is_float(dst->params.format)) {
332 : 0 : PL_ERR(gpu, "Cannot call `pl_tex_clear` on integer textures, please "
333 : : "use `pl_tex_clear_ex` instead.");
334 : 0 : return;
335 : : }
336 : :
337 : 174 : const union pl_clear_color col = {
338 : 174 : .f = { color[0], color[1], color[2], color[3] },
339 : : };
340 : :
341 : 174 : pl_tex_clear_ex(gpu, dst, col);
342 : : }
343 : :
344 : 3357 : void pl_tex_invalidate(pl_gpu gpu, pl_tex tex)
345 : : {
346 : 3357 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
347 [ + - ]: 3357 : if (impl->tex_invalidate)
348 : 3357 : impl->tex_invalidate(gpu, tex);
349 : 3357 : }
350 : :
351 : : static void strip_coords(pl_tex tex, pl_rect3d *rc)
352 : : {
353 [ + + + + : 2926 : if (!tex->params.d) {
+ + + + ]
354 : 1926 : rc->z0 = 0;
355 : 1926 : rc->z1 = 1;
356 : : }
357 : :
358 [ + + + + : 2926 : if (!tex->params.h) {
+ + + + ]
359 : 734 : rc->y0 = 0;
360 : 734 : rc->y1 = 1;
361 : : }
362 : : }
363 : :
364 : 2674 : static void infer_rc(pl_tex tex, pl_rect3d *rc)
365 : : {
366 [ + - + + ]: 2674 : if (!rc->x0 && !rc->x1)
367 : 2002 : rc->x1 = tex->params.w;
368 [ + - + + ]: 2674 : if (!rc->y0 && !rc->y1)
369 : 2024 : rc->y1 = tex->params.h;
370 [ + - + + ]: 2674 : if (!rc->z0 && !rc->z1)
371 : 2078 : rc->z1 = tex->params.d;
372 : 2674 : }
373 : :
374 : 252 : void pl_tex_blit(pl_gpu gpu, const struct pl_tex_blit_params *params)
375 : : {
376 : 252 : pl_tex src = params->src, dst = params->dst;
377 [ - + ]: 252 : require(src && dst);
378 : 252 : pl_fmt src_fmt = src->params.format;
379 : 252 : pl_fmt dst_fmt = dst->params.format;
380 [ - + ]: 252 : require(src_fmt->internal_size == dst_fmt->internal_size);
381 [ - + ]: 252 : require((src_fmt->type == PL_FMT_UINT) == (dst_fmt->type == PL_FMT_UINT));
382 [ - + ]: 252 : require((src_fmt->type == PL_FMT_SINT) == (dst_fmt->type == PL_FMT_SINT));
383 [ - + ]: 252 : require(src->params.blit_src);
384 [ - + ]: 252 : require(dst->params.blit_dst);
385 [ - + - - ]: 252 : require(params->sample_mode != PL_TEX_SAMPLE_LINEAR || (src_fmt->caps & PL_FMT_CAP_LINEAR));
386 : :
387 : 252 : struct pl_tex_blit_params fixed = *params;
388 : 252 : infer_rc(src, &fixed.src_rc);
389 : 252 : infer_rc(dst, &fixed.dst_rc);
390 : : strip_coords(src, &fixed.src_rc);
391 : : strip_coords(dst, &fixed.dst_rc);
392 : :
393 [ + - - + ]: 252 : require(fixed.src_rc.x0 >= 0 && fixed.src_rc.x0 < src->params.w);
394 [ + - - + ]: 252 : require(fixed.src_rc.x1 > 0 && fixed.src_rc.x1 <= src->params.w);
395 [ + - - + ]: 252 : require(fixed.dst_rc.x0 >= 0 && fixed.dst_rc.x0 < dst->params.w);
396 [ + - - + ]: 252 : require(fixed.dst_rc.x1 > 0 && fixed.dst_rc.x1 <= dst->params.w);
397 : :
398 [ + + ]: 252 : if (src->params.h) {
399 [ + - - + ]: 200 : require(fixed.src_rc.y0 >= 0 && fixed.src_rc.y0 < src->params.h);
400 [ + - - + ]: 200 : require(fixed.src_rc.y1 > 0 && fixed.src_rc.y1 <= src->params.h);
401 : : }
402 : :
403 [ + + ]: 252 : if (dst->params.h) {
404 [ + - - + ]: 200 : require(fixed.dst_rc.y0 >= 0 && fixed.dst_rc.y0 < dst->params.h);
405 [ + - - + ]: 200 : require(fixed.dst_rc.y1 > 0 && fixed.dst_rc.y1 <= dst->params.h);
406 : : }
407 : :
408 [ + + ]: 252 : if (src->params.d) {
409 [ + - - + ]: 52 : require(fixed.src_rc.z0 >= 0 && fixed.src_rc.z0 < src->params.d);
410 [ + - - + ]: 52 : require(fixed.src_rc.z1 > 0 && fixed.src_rc.z1 <= src->params.d);
411 : : }
412 : :
413 [ + + ]: 252 : if (dst->params.d) {
414 [ + - - + ]: 52 : require(fixed.dst_rc.z0 >= 0 && fixed.dst_rc.z0 < dst->params.d);
415 [ + - - + ]: 52 : require(fixed.dst_rc.z1 > 0 && fixed.dst_rc.z1 <= dst->params.d);
416 : : }
417 : :
418 : : pl_rect3d full = {0, 0, 0, dst->params.w, dst->params.h, dst->params.d};
419 : : strip_coords(dst, &full);
420 : :
421 : 252 : pl_rect3d rcnorm = fixed.dst_rc;
422 : 252 : pl_rect3d_normalize(&rcnorm);
423 [ + - + - : 252 : if (pl_rect3d_eq(rcnorm, full))
+ - + - +
- + - ]
424 : 252 : pl_tex_invalidate(gpu, dst);
425 : :
426 : 252 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
427 : 252 : impl->tex_blit(gpu, &fixed);
428 : 252 : return;
429 : :
430 : : error:
431 : : if (src->params.debug_tag || dst->params.debug_tag) {
432 : : PL_ERR(gpu, " for textures: src %s, dst %s",
433 : : PL_DEF(src->params.debug_tag, "(unknown)"),
434 : : PL_DEF(dst->params.debug_tag, "(unknown)"));
435 : : }
436 : : }
437 : :
438 : 2170 : static bool fix_tex_transfer(pl_gpu gpu, struct pl_tex_transfer_params *params)
439 : : {
440 : 2170 : pl_tex tex = params->tex;
441 : 2170 : pl_fmt fmt = tex->params.format;
442 : 2170 : pl_rect3d rc = params->rc;
443 : :
444 : : // Infer the default values
445 : 2170 : infer_rc(tex, &rc);
446 : : strip_coords(tex, &rc);
447 : :
448 [ + + - + ]: 2170 : if (!params->row_pitch || !tex->params.w)
449 : 1568 : params->row_pitch = pl_rect_w(rc) * fmt->texel_size;
450 [ + + + + ]: 2170 : if (!params->depth_pitch || !tex->params.d)
451 : 1981 : params->depth_pitch = pl_rect_h(rc) * params->row_pitch;
452 : :
453 [ - + ]: 2170 : require(params->row_pitch);
454 [ - + ]: 2170 : require(params->depth_pitch);
455 [ + + ]: 2170 : params->rc = rc;
456 : :
457 : : // Check the parameters for sanity
458 : : switch (pl_tex_params_dimension(tex->params))
459 : : {
460 : : case 3:
461 [ - + ]: 644 : require(rc.z1 > rc.z0);
462 [ + - - + ]: 644 : require(rc.z0 >= 0 && rc.z0 < tex->params.d);
463 [ + - - + ]: 644 : require(rc.z1 > 0 && rc.z1 <= tex->params.d);
464 [ - + ]: 644 : require(params->depth_pitch >= pl_rect_h(rc) * params->row_pitch);
465 [ - + ]: 644 : require(params->depth_pitch % params->row_pitch == 0);
466 : : // fall through
467 : : case 2:
468 [ - + ]: 1540 : require(rc.y1 > rc.y0);
469 [ + - - + ]: 1540 : require(rc.y0 >= 0 && rc.y0 < tex->params.h);
470 [ + - - + ]: 1540 : require(rc.y1 > 0 && rc.y1 <= tex->params.h);
471 [ - + ]: 1540 : require(params->row_pitch >= pl_rect_w(rc) * fmt->texel_size);
472 [ - + ]: 1540 : require(params->row_pitch % fmt->texel_align == 0);
473 : : // fall through
474 : : case 1:
475 [ - + ]: 2170 : require(rc.x1 > rc.x0);
476 [ + - - + ]: 2170 : require(rc.x0 >= 0 && rc.x0 < tex->params.w);
477 [ + - - + ]: 2170 : require(rc.x1 > 0 && rc.x1 <= tex->params.w);
478 : : break;
479 : : }
480 : :
481 [ - + ]: 2170 : require(!params->buf ^ !params->ptr); // exactly one
482 [ + + ]: 2170 : if (params->buf) {
483 : : pl_buf buf = params->buf;
484 : 568 : size_t size = pl_tex_transfer_size(params);
485 [ - + ]: 568 : require(params->buf_offset + size >= params->buf_offset); // overflow check
486 [ - + ]: 568 : require(params->buf_offset + size <= buf->params.size);
487 [ - + ]: 568 : require(gpu->limits.buf_transfer);
488 : : }
489 : :
490 [ + + - + ]: 2170 : require(!params->callback || gpu->limits.callbacks);
491 : : return true;
492 : :
493 : : error:
494 : : if (tex->params.debug_tag)
495 : : PL_ERR(gpu, " for texture: %s", tex->params.debug_tag);
496 : : return false;
497 : : }
498 : :
499 : 1130 : bool pl_tex_upload(pl_gpu gpu, const struct pl_tex_transfer_params *params)
500 : : {
501 : 1130 : pl_tex tex = params->tex;
502 [ - + ]: 1130 : require(tex->params.host_writable);
503 : :
504 : 1130 : struct pl_tex_transfer_params fixed = *params;
505 [ - + ]: 1130 : if (!fix_tex_transfer(gpu, &fixed))
506 : 0 : goto error;
507 : :
508 : 1130 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
509 : 1130 : return impl->tex_upload(gpu, &fixed);
510 : :
511 : : error:
512 [ # # ]: 0 : if (tex->params.debug_tag)
513 : 0 : PL_ERR(gpu, " for texture: %s", tex->params.debug_tag);
514 : : return false;
515 : : }
516 : :
517 : 1040 : bool pl_tex_download(pl_gpu gpu, const struct pl_tex_transfer_params *params)
518 : : {
519 : 1040 : pl_tex tex = params->tex;
520 [ - + ]: 1040 : require(tex->params.host_readable);
521 : :
522 : 1040 : struct pl_tex_transfer_params fixed = *params;
523 [ - + ]: 1040 : if (!fix_tex_transfer(gpu, &fixed))
524 : 0 : goto error;
525 : :
526 : 1040 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
527 : 1040 : return impl->tex_download(gpu, &fixed);
528 : :
529 : : error:
530 [ # # ]: 0 : if (tex->params.debug_tag)
531 : 0 : PL_ERR(gpu, " for texture: %s", tex->params.debug_tag);
532 : : return false;
533 : : }
534 : :
535 : 4 : bool pl_tex_poll(pl_gpu gpu, pl_tex tex, uint64_t t)
536 : : {
537 : 4 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
538 [ + - + - ]: 4 : return impl->tex_poll ? impl->tex_poll(gpu, tex, t) : false;
539 : : }
540 : :
541 : 1232 : pl_buf pl_buf_create(pl_gpu gpu, const struct pl_buf_params *params)
542 : : {
543 : : struct pl_buf_params params_rounded;
544 : :
545 [ + + - + ]: 1232 : require(!params->import_handle || !params->export_handle);
546 [ + + ]: 1232 : if (params->export_handle) {
547 [ - + ]: 6 : require(PL_ISPOT(params->export_handle));
548 [ - + ]: 6 : require(params->export_handle & gpu->export_caps.buf);
549 : : }
550 [ + + ]: 1232 : if (params->import_handle) {
551 [ - + ]: 22 : require(PL_ISPOT(params->import_handle));
552 [ - + ]: 22 : require(params->import_handle & gpu->import_caps.buf);
553 : : const struct pl_shared_mem *shmem = ¶ms->shared_mem;
554 [ - + ]: 22 : require(shmem->offset + params->size <= shmem->size);
555 [ + + - + ]: 22 : require(params->import_handle != PL_HANDLE_DMA_BUF || !shmem->drm_format_mod);
556 : :
557 : : // Fix misalignment on host pointer imports
558 [ + + ]: 22 : if (params->import_handle == PL_HANDLE_HOST_PTR) {
559 : 20 : uintptr_t page_mask = ~(gpu->limits.align_host_ptr - 1);
560 : 20 : uintptr_t ptr_base = (uintptr_t) shmem->handle.ptr & page_mask;
561 : 20 : size_t ptr_offset = (uintptr_t) shmem->handle.ptr - ptr_base;
562 : 20 : size_t buf_offset = ptr_offset + shmem->offset;
563 : 20 : size_t ptr_size = PL_ALIGN2(ptr_offset + shmem->size,
564 : : gpu->limits.align_host_ptr);
565 : :
566 [ + + - + ]: 20 : if (ptr_base != (uintptr_t) shmem->handle.ptr || ptr_size > shmem->size) {
567 : : static bool warned_rounding = false;
568 [ + + ]: 18 : if (!warned_rounding) {
569 : 1 : warned_rounding = true;
570 : 1 : PL_WARN(gpu, "Imported host pointer is not page-aligned. "
571 : : "This should normally be fine on most platforms, "
572 : : "but may cause issues in some rare circumstances.");
573 : : }
574 : :
575 : 18 : PL_TRACE(gpu, "Rounding imported host pointer %p + %zu -> %zu to "
576 : : "nearest page boundaries: %p + %zu -> %zu",
577 : : shmem->handle.ptr, shmem->offset, shmem->size,
578 : : (void *) ptr_base, buf_offset, ptr_size);
579 : : }
580 : :
581 : 20 : params_rounded = *params;
582 : 20 : params_rounded.shared_mem.handle.ptr = (void *) ptr_base;
583 : 20 : params_rounded.shared_mem.offset = buf_offset;
584 : 20 : params_rounded.shared_mem.size = ptr_size;
585 : : params = ¶ms_rounded;
586 : : }
587 : : }
588 : :
589 [ + - - + ]: 1232 : require(params->size > 0 && params->size <= gpu->limits.max_buf_size);
590 [ + + - + ]: 1232 : require(!params->uniform || params->size <= gpu->limits.max_ubo_size);
591 [ + + - + ]: 1232 : require(!params->storable || params->size <= gpu->limits.max_ssbo_size);
592 [ + + - + ]: 1232 : require(!params->drawable || params->size <= gpu->limits.max_vbo_size);
593 [ + + ]: 1232 : if (params->host_mapped) {
594 [ - + ]: 7 : require(params->size <= gpu->limits.max_mapped_size);
595 [ - + - - ]: 7 : require(params->memory_type != PL_BUF_MEM_DEVICE ||
596 : : params->size <= gpu->limits.max_mapped_vram);
597 : : }
598 : :
599 [ + + ]: 1232 : if (params->format) {
600 : : pl_fmt fmt = params->format;
601 [ - + ]: 18 : require(params->size <= gpu->limits.max_buffer_texels * fmt->texel_size);
602 [ - + - - ]: 18 : require(!params->uniform || (fmt->caps & PL_FMT_CAP_TEXEL_UNIFORM));
603 [ + - - + ]: 18 : require(!params->storable || (fmt->caps & PL_FMT_CAP_TEXEL_STORAGE));
604 : : }
605 : :
606 : 1232 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
607 : 1232 : pl_buf buf = impl->buf_create(gpu, params);
608 [ + + ]: 1232 : if (buf)
609 [ + + - + ]: 1228 : require(!params->host_mapped || buf->data);
610 : :
611 : : return buf;
612 : :
613 : : error:
614 : : if (params->debug_tag)
615 : : PL_ERR(gpu, " for buffer: %s", params->debug_tag);
616 : : return NULL;
617 : : }
618 : :
619 : 2888 : void pl_buf_destroy(pl_gpu gpu, pl_buf *buf)
620 : : {
621 [ + + ]: 2888 : if (!*buf)
622 : : return;
623 : :
624 : 1228 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
625 : 1228 : impl->buf_destroy(gpu, *buf);
626 : 1228 : *buf = NULL;
627 : : }
628 : :
629 : 0 : static bool pl_buf_params_superset(struct pl_buf_params a, struct pl_buf_params b)
630 : : {
631 : 0 : return a.size >= b.size &&
632 [ # # ]: 0 : a.memory_type == b.memory_type &&
633 [ # # ]: 0 : a.format == b.format &&
634 [ # # # # ]: 0 : (a.host_writable || !b.host_writable) &&
635 [ # # # # ]: 0 : (a.host_readable || !b.host_readable) &&
636 [ # # # # ]: 0 : (a.host_mapped || !b.host_mapped) &&
637 [ # # # # ]: 0 : (a.uniform || !b.uniform) &&
638 [ # # # # : 0 : (a.storable || !b.storable) &&
# # ]
639 [ # # # # ]: 0 : (a.drawable || !b.drawable);
640 : : }
641 : :
642 : 0 : bool pl_buf_recreate(pl_gpu gpu, pl_buf *buf, const struct pl_buf_params *params)
643 : : {
644 : :
645 [ # # ]: 0 : if (params->initial_data) {
646 : 0 : PL_ERR(gpu, "pl_buf_recreate may not be used with `initial_data`!");
647 : 0 : return false;
648 : : }
649 : :
650 [ # # # # ]: 0 : if (*buf && pl_buf_params_superset((*buf)->params, *params))
651 : : return true;
652 : :
653 : 0 : PL_INFO(gpu, "(Re)creating %zu buffer", params->size);
654 : 0 : pl_buf_destroy(gpu, buf);
655 : 0 : *buf = pl_buf_create(gpu, params);
656 : :
657 : 0 : return !!*buf;
658 : : }
659 : :
660 : 650 : void pl_buf_write(pl_gpu gpu, pl_buf buf, size_t buf_offset,
661 : : const void *data, size_t size)
662 : : {
663 [ - + ]: 650 : require(buf->params.host_writable);
664 [ - + ]: 650 : require(buf_offset + size <= buf->params.size);
665 [ - + ]: 650 : require(buf_offset == PL_ALIGN2(buf_offset, 4));
666 : :
667 : 650 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
668 : 650 : impl->buf_write(gpu, buf, buf_offset, data, size);
669 : : return;
670 : :
671 : : error:
672 : : if (buf->params.debug_tag)
673 : : PL_ERR(gpu, " for buffer: %s", buf->params.debug_tag);
674 : : }
675 : :
676 : 280 : bool pl_buf_read(pl_gpu gpu, pl_buf buf, size_t buf_offset,
677 : : void *dest, size_t size)
678 : : {
679 [ - + ]: 280 : require(buf->params.host_readable);
680 [ - + ]: 280 : require(buf_offset + size <= buf->params.size);
681 : :
682 : 280 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
683 : 280 : return impl->buf_read(gpu, buf, buf_offset, dest, size);
684 : :
685 : : error:
686 : : if (buf->params.debug_tag)
687 : : PL_ERR(gpu, " for buffer: %s", buf->params.debug_tag);
688 : : return false;
689 : : }
690 : :
691 : 5 : void pl_buf_copy(pl_gpu gpu, pl_buf dst, size_t dst_offset,
692 : : pl_buf src, size_t src_offset, size_t size)
693 : : {
694 [ - + ]: 5 : require(src_offset + size <= src->params.size);
695 [ - + ]: 5 : require(dst_offset + size <= dst->params.size);
696 [ - + ]: 5 : require(src != dst);
697 : :
698 : 5 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
699 : 5 : impl->buf_copy(gpu, dst, dst_offset, src, src_offset, size);
700 : : return;
701 : :
702 : : error:
703 : : if (src->params.debug_tag || dst->params.debug_tag) {
704 : : PL_ERR(gpu, " for buffers: src %s, dst %s",
705 : : src->params.debug_tag, dst->params.debug_tag);
706 : : }
707 : : }
708 : :
709 : 4 : bool pl_buf_export(pl_gpu gpu, pl_buf buf)
710 : : {
711 [ - + ]: 4 : require(buf->params.export_handle || buf->params.import_handle);
712 : :
713 : 4 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
714 : 4 : return impl->buf_export(gpu, buf);
715 : :
716 : : error:
717 : : if (buf->params.debug_tag)
718 : : PL_ERR(gpu, " for buffer: %s", buf->params.debug_tag);
719 : : return false;
720 : : }
721 : :
722 : 52 : bool pl_buf_poll(pl_gpu gpu, pl_buf buf, uint64_t t)
723 : : {
724 : 52 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
725 [ + + + - ]: 52 : return impl->buf_poll ? impl->buf_poll(gpu, buf, t) : false;
726 : : }
727 : :
728 : 23722 : size_t pl_var_type_size(enum pl_var_type type)
729 : : {
730 [ - + ]: 23722 : switch (type) {
731 : : case PL_VAR_SINT: return sizeof(int);
732 : : case PL_VAR_UINT: return sizeof(unsigned int);
733 : : case PL_VAR_FLOAT: return sizeof(float);
734 : : case PL_VAR_INVALID: // fall through
735 : : case PL_VAR_TYPE_COUNT: break;
736 : : }
737 : :
738 : 0 : pl_unreachable();
739 : : }
740 : :
741 : : #define PL_VAR(TYPE, NAME, M, V) \
742 : : struct pl_var pl_var_##NAME(const char *name) { \
743 : : return (struct pl_var) { \
744 : : .name = name, \
745 : : .type = PL_VAR_##TYPE, \
746 : : .dim_m = M, \
747 : : .dim_v = V, \
748 : : .dim_a = 1, \
749 : : }; \
750 : : }
751 : :
752 : 1169 : PL_VAR(FLOAT, float, 1, 1)
753 : 551 : PL_VAR(FLOAT, vec2, 1, 2)
754 : 831 : PL_VAR(FLOAT, vec3, 1, 3)
755 : 4 : PL_VAR(FLOAT, vec4, 1, 4)
756 : 645 : PL_VAR(FLOAT, mat2, 2, 2)
757 : 0 : PL_VAR(FLOAT, mat2x3, 2, 3)
758 : 0 : PL_VAR(FLOAT, mat2x4, 2, 4)
759 : 1025 : PL_VAR(FLOAT, mat3, 3, 3)
760 : 0 : PL_VAR(FLOAT, mat3x4, 3, 4)
761 : 0 : PL_VAR(FLOAT, mat4x2, 4, 2)
762 : 0 : PL_VAR(FLOAT, mat4x3, 4, 3)
763 : 0 : PL_VAR(FLOAT, mat4, 4, 4)
764 : 90 : PL_VAR(SINT, int, 1, 1)
765 : 0 : PL_VAR(SINT, ivec2, 1, 2)
766 : 0 : PL_VAR(SINT, ivec3, 1, 3)
767 : 0 : PL_VAR(SINT, ivec4, 1, 4)
768 : 44 : PL_VAR(UINT, uint, 1, 1)
769 : 0 : PL_VAR(UINT, uvec2, 1, 2)
770 : 0 : PL_VAR(UINT, uvec3, 1, 3)
771 : 0 : PL_VAR(UINT, uvec4, 1, 4)
772 : :
773 : : #undef PL_VAR
774 : :
775 : : const struct pl_named_var pl_var_glsl_types[] = {
776 : : // float vectors
777 : : { "float", { .type = PL_VAR_FLOAT, .dim_m = 1, .dim_v = 1, .dim_a = 1, }},
778 : : { "vec2", { .type = PL_VAR_FLOAT, .dim_m = 1, .dim_v = 2, .dim_a = 1, }},
779 : : { "vec3", { .type = PL_VAR_FLOAT, .dim_m = 1, .dim_v = 3, .dim_a = 1, }},
780 : : { "vec4", { .type = PL_VAR_FLOAT, .dim_m = 1, .dim_v = 4, .dim_a = 1, }},
781 : : // float matrices
782 : : { "mat2", { .type = PL_VAR_FLOAT, .dim_m = 2, .dim_v = 2, .dim_a = 1, }},
783 : : { "mat2x3", { .type = PL_VAR_FLOAT, .dim_m = 2, .dim_v = 3, .dim_a = 1, }},
784 : : { "mat2x4", { .type = PL_VAR_FLOAT, .dim_m = 2, .dim_v = 4, .dim_a = 1, }},
785 : : { "mat3", { .type = PL_VAR_FLOAT, .dim_m = 3, .dim_v = 3, .dim_a = 1, }},
786 : : { "mat3x4", { .type = PL_VAR_FLOAT, .dim_m = 3, .dim_v = 4, .dim_a = 1, }},
787 : : { "mat4x2", { .type = PL_VAR_FLOAT, .dim_m = 4, .dim_v = 2, .dim_a = 1, }},
788 : : { "mat4x3", { .type = PL_VAR_FLOAT, .dim_m = 4, .dim_v = 3, .dim_a = 1, }},
789 : : { "mat4", { .type = PL_VAR_FLOAT, .dim_m = 4, .dim_v = 4, .dim_a = 1, }},
790 : : // integer vectors
791 : : { "int", { .type = PL_VAR_SINT, .dim_m = 1, .dim_v = 1, .dim_a = 1, }},
792 : : { "ivec2", { .type = PL_VAR_SINT, .dim_m = 1, .dim_v = 2, .dim_a = 1, }},
793 : : { "ivec3", { .type = PL_VAR_SINT, .dim_m = 1, .dim_v = 3, .dim_a = 1, }},
794 : : { "ivec4", { .type = PL_VAR_SINT, .dim_m = 1, .dim_v = 4, .dim_a = 1, }},
795 : : // unsigned integer vectors
796 : : { "uint", { .type = PL_VAR_UINT, .dim_m = 1, .dim_v = 1, .dim_a = 1, }},
797 : : { "uvec2", { .type = PL_VAR_UINT, .dim_m = 1, .dim_v = 2, .dim_a = 1, }},
798 : : { "uvec3", { .type = PL_VAR_UINT, .dim_m = 1, .dim_v = 3, .dim_a = 1, }},
799 : : { "uvec4", { .type = PL_VAR_UINT, .dim_m = 1, .dim_v = 4, .dim_a = 1, }},
800 : :
801 : : {0},
802 : : };
803 : :
804 : : #define MAX_DIM 4
805 : :
806 : 6301 : const char *pl_var_glsl_type_name(struct pl_var var)
807 : : {
808 : : static const char *types[PL_VAR_TYPE_COUNT][MAX_DIM+1][MAX_DIM+1] = {
809 : : // float vectors
810 : : [PL_VAR_FLOAT][1][1] = "float",
811 : : [PL_VAR_FLOAT][1][2] = "vec2",
812 : : [PL_VAR_FLOAT][1][3] = "vec3",
813 : : [PL_VAR_FLOAT][1][4] = "vec4",
814 : : // float matrices
815 : : [PL_VAR_FLOAT][2][2] = "mat2",
816 : : [PL_VAR_FLOAT][2][3] = "mat2x3",
817 : : [PL_VAR_FLOAT][2][4] = "mat2x4",
818 : : [PL_VAR_FLOAT][3][2] = "mat3x2",
819 : : [PL_VAR_FLOAT][3][3] = "mat3",
820 : : [PL_VAR_FLOAT][3][4] = "mat3x4",
821 : : [PL_VAR_FLOAT][4][2] = "mat4x2",
822 : : [PL_VAR_FLOAT][4][3] = "mat4x3",
823 : : [PL_VAR_FLOAT][4][4] = "mat4",
824 : : // integer vectors
825 : : [PL_VAR_SINT][1][1] = "int",
826 : : [PL_VAR_SINT][1][2] = "ivec2",
827 : : [PL_VAR_SINT][1][3] = "ivec3",
828 : : [PL_VAR_SINT][1][4] = "ivec4",
829 : : // unsigned integer vectors
830 : : [PL_VAR_UINT][1][1] = "uint",
831 : : [PL_VAR_UINT][1][2] = "uvec2",
832 : : [PL_VAR_UINT][1][3] = "uvec3",
833 : : [PL_VAR_UINT][1][4] = "uvec4",
834 : : };
835 : :
836 [ + - + - ]: 6301 : if (var.dim_v > MAX_DIM || var.dim_m > MAX_DIM)
837 : : return NULL;
838 : :
839 : 6301 : return types[var.type][var.dim_m][var.dim_v];
840 : : }
841 : :
842 : 686 : struct pl_var pl_var_from_fmt(pl_fmt fmt, const char *name)
843 : : {
844 : : static const enum pl_var_type vartypes[] = {
845 : : [PL_FMT_FLOAT] = PL_VAR_FLOAT,
846 : : [PL_FMT_UNORM] = PL_VAR_FLOAT,
847 : : [PL_FMT_SNORM] = PL_VAR_FLOAT,
848 : : [PL_FMT_UINT] = PL_VAR_UINT,
849 : : [PL_FMT_SINT] = PL_VAR_SINT,
850 : : };
851 : :
852 [ - + ]: 686 : pl_assert(fmt->type < PL_ARRAY_SIZE(vartypes));
853 : 686 : return (struct pl_var) {
854 : 686 : .type = vartypes[fmt->type],
855 : : .name = name,
856 : 686 : .dim_v = fmt->num_components,
857 : : .dim_m = 1,
858 : : .dim_a = 1,
859 : : };
860 : : }
861 : :
862 : 11735 : struct pl_var_layout pl_var_host_layout(size_t offset, const struct pl_var *var)
863 : : {
864 : 11735 : size_t col_size = pl_var_type_size(var->type) * var->dim_v;
865 : 11735 : return (struct pl_var_layout) {
866 : : .offset = offset,
867 : : .stride = col_size,
868 : 11735 : .size = col_size * var->dim_m * var->dim_a,
869 : : };
870 : : }
871 : :
872 : 959 : struct pl_var_layout pl_std140_layout(size_t offset, const struct pl_var *var)
873 : : {
874 : 959 : size_t el_size = pl_var_type_size(var->type);
875 : :
876 : : // std140 packing rules:
877 : : // 1. The size of generic values is their size in bytes
878 : : // 2. The size of vectors is the vector length * the base count
879 : : // 3. Matrices are treated like arrays of column vectors
880 : : // 4. The size of array rows is that of the element size rounded up to
881 : : // the nearest multiple of vec4
882 : : // 5. All values are aligned to a multiple of their size (stride for arrays),
883 : : // with the exception of vec3 which is aligned like vec4
884 : 959 : size_t stride = el_size * var->dim_v;
885 : : size_t align = stride;
886 [ + + ]: 959 : if (var->dim_v == 3)
887 : 515 : align += el_size;
888 [ + + ]: 959 : if (var->dim_m * var->dim_a > 1)
889 : 469 : stride = align = PL_ALIGN2(align, sizeof(float[4]));
890 : :
891 : 959 : return (struct pl_var_layout) {
892 : 959 : .offset = PL_ALIGN2(offset, align),
893 : : .stride = stride,
894 : 959 : .size = stride * var->dim_m * var->dim_a,
895 : : };
896 : : }
897 : :
898 : 1369 : struct pl_var_layout pl_std430_layout(size_t offset, const struct pl_var *var)
899 : : {
900 : 1369 : size_t el_size = pl_var_type_size(var->type);
901 : :
902 : : // std430 packing rules: like std140, except arrays/matrices are always
903 : : // "tightly" packed, even arrays/matrices of vec3s
904 : 1369 : size_t stride = el_size * var->dim_v;
905 : : size_t align = stride;
906 [ + + ]: 1369 : if (var->dim_v == 3)
907 : 486 : align += el_size;
908 [ + + ]: 1369 : if (var->dim_m * var->dim_a > 1)
909 : : stride = align;
910 : :
911 : 1369 : return (struct pl_var_layout) {
912 : 1369 : .offset = PL_ALIGN2(offset, align),
913 : : .stride = stride,
914 : 1369 : .size = stride * var->dim_m * var->dim_a,
915 : : };
916 : : }
917 : :
918 : 637 : void memcpy_layout(void *dst_p, struct pl_var_layout dst_layout,
919 : : const void *src_p, struct pl_var_layout src_layout)
920 : : {
921 : 637 : uintptr_t src = (uintptr_t) src_p + src_layout.offset;
922 : 637 : uintptr_t dst = (uintptr_t) dst_p + dst_layout.offset;
923 : :
924 [ + + ]: 637 : if (src_layout.stride == dst_layout.stride) {
925 [ - + ]: 574 : pl_assert(dst_layout.size == src_layout.size);
926 : 574 : memcpy((void *) dst, (const void *) src, src_layout.size);
927 : 574 : return;
928 : : }
929 : :
930 : 63 : size_t stride = PL_MIN(src_layout.stride, dst_layout.stride);
931 : 63 : uintptr_t end = src + src_layout.size;
932 [ + + ]: 252 : while (src < end) {
933 [ - + ]: 189 : pl_assert(dst < dst + dst_layout.size);
934 : 189 : memcpy((void *) dst, (const void *) src, stride);
935 : 189 : src += src_layout.stride;
936 : 189 : dst += dst_layout.stride;
937 : : }
938 : : }
939 : :
940 : 4817 : int pl_desc_namespace(pl_gpu gpu, enum pl_desc_type type)
941 : : {
942 : 4817 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
943 : 4817 : int ret = impl->desc_namespace(gpu, type);
944 [ - + ]: 4817 : pl_assert(ret >= 0 && ret < PL_DESC_TYPE_COUNT);
945 : 4817 : return ret;
946 : : }
947 : :
948 : 154 : const char *pl_desc_access_glsl_name(enum pl_desc_access mode)
949 : : {
950 [ + + - + ]: 154 : switch (mode) {
951 : : case PL_DESC_ACCESS_READWRITE: return "";
952 : 15 : case PL_DESC_ACCESS_READONLY: return "readonly";
953 : 101 : case PL_DESC_ACCESS_WRITEONLY: return "writeonly";
954 : : case PL_DESC_ACCESS_COUNT: break;
955 : : }
956 : :
957 : 0 : pl_unreachable();
958 : : }
959 : :
960 : : const struct pl_blend_params pl_alpha_overlay = {
961 : : .src_rgb = PL_BLEND_SRC_ALPHA,
962 : : .dst_rgb = PL_BLEND_ONE_MINUS_SRC_ALPHA,
963 : : .src_alpha = PL_BLEND_ONE,
964 : : .dst_alpha = PL_BLEND_ONE_MINUS_SRC_ALPHA,
965 : : };
966 : :
967 [ + - ]: 492 : static inline void log_shader_sources(pl_log log, enum pl_log_level level,
968 : : const struct pl_pass_params *params)
969 : : {
970 [ + + + - ]: 492 : if (!pl_msg_test(log, level) || !params->glsl_shader)
971 : : return;
972 : :
973 [ + + - ]: 303 : switch (params->type) {
974 : 215 : case PL_PASS_RASTER:
975 [ + - ]: 215 : if (!params->vertex_shader)
976 : : return;
977 : 215 : pl_msg(log, level, "vertex shader source:");
978 : 215 : pl_msg_source(log, level, params->vertex_shader);
979 : 215 : pl_msg(log, level, "fragment shader source:");
980 : 215 : pl_msg_source(log, level, params->glsl_shader);
981 : 215 : return;
982 : :
983 : 88 : case PL_PASS_COMPUTE:
984 : 88 : pl_msg(log, level, "compute shader source:");
985 : 88 : pl_msg_source(log, level, params->glsl_shader);
986 : 88 : return;
987 : :
988 : : case PL_PASS_INVALID:
989 : : case PL_PASS_TYPE_COUNT:
990 : : break;
991 : : }
992 : :
993 : 0 : pl_unreachable();
994 : : }
995 : :
996 : 492 : static void log_spec_constants(pl_log log, enum pl_log_level lev,
997 : : const struct pl_pass_params *params,
998 : : const void *constant_data)
999 : : {
1000 [ + + + - : 646 : if (!constant_data || !params->num_constants || !pl_msg_test(log, lev))
+ - ]
1001 : : return;
1002 : :
1003 : 154 : pl_msg(log, lev, "Specialization constant values:");
1004 : :
1005 : 154 : uintptr_t data_base = (uintptr_t) constant_data;
1006 [ + + ]: 1187 : for (int i = 0; i < params->num_constants; i++) {
1007 : : union {
1008 : : int i;
1009 : : unsigned u;
1010 : : float f;
1011 : 1033 : } *data = (void *) (data_base + params->constants[i].offset);
1012 : 1033 : int id = params->constants[i].id;
1013 : :
1014 [ + + + - ]: 1033 : switch (params->constants[i].type) {
1015 : 98 : case PL_VAR_SINT: pl_msg(log, lev, " constant_id=%d: %d", id, data->i); break;
1016 : 53 : case PL_VAR_UINT: pl_msg(log, lev, " constant_id=%d: %u", id, data->u); break;
1017 : 882 : case PL_VAR_FLOAT: pl_msg(log, lev, " constant_id=%d: %f", id, data->f); break;
1018 : 0 : default: pl_unreachable();
1019 : : }
1020 : : }
1021 : : }
1022 : :
1023 : 492 : pl_pass pl_pass_create(pl_gpu gpu, const struct pl_pass_params *params)
1024 : : {
1025 [ - + ]: 492 : require(params->glsl_shader);
1026 [ + - + - ]: 492 : switch(params->type) {
1027 : 404 : case PL_PASS_RASTER:
1028 [ - + ]: 404 : require(params->vertex_shader);
1029 [ + - ]: 404 : require(params->vertex_stride % gpu->limits.align_vertex_stride == 0);
1030 [ + + ]: 1240 : for (int i = 0; i < params->num_vertex_attribs; i++) {
1031 : 836 : struct pl_vertex_attrib va = params->vertex_attribs[i];
1032 [ - + ]: 836 : require(va.name);
1033 [ - + ]: 836 : require(va.fmt);
1034 [ - + ]: 836 : require(va.fmt->caps & PL_FMT_CAP_VERTEX);
1035 [ - + ]: 836 : require(va.offset + va.fmt->texel_size <= params->vertex_stride);
1036 : : }
1037 : :
1038 [ - + ]: 404 : require(params->target_format);
1039 [ - + ]: 404 : require(params->target_format->caps & PL_FMT_CAP_RENDERABLE);
1040 [ + + - + ]: 404 : require(!params->blend_params || params->target_format->caps & PL_FMT_CAP_BLENDABLE);
1041 [ + + + - ]: 404 : require(!params->blend_params || params->load_target);
1042 : : break;
1043 : 88 : case PL_PASS_COMPUTE:
1044 [ + - ]: 88 : require(gpu->glsl.compute);
1045 : : break;
1046 : : case PL_PASS_INVALID:
1047 : : case PL_PASS_TYPE_COUNT:
1048 : 0 : pl_unreachable();
1049 : : }
1050 : :
1051 : : size_t num_var_comps = 0;
1052 [ + + ]: 1018 : for (int i = 0; i < params->num_variables; i++) {
1053 : 526 : struct pl_var var = params->variables[i];
1054 : 526 : num_var_comps += var.dim_v * var.dim_m * var.dim_a;
1055 [ - + ]: 526 : require(var.name);
1056 [ - + ]: 526 : require(pl_var_glsl_type_name(var));
1057 : : }
1058 [ - + ]: 492 : require(num_var_comps <= gpu->limits.max_variable_comps);
1059 : :
1060 [ + - ]: 492 : require(params->num_constants <= gpu->limits.max_constants);
1061 [ + + ]: 1525 : for (int i = 0; i < params->num_constants; i++)
1062 [ - + ]: 1033 : require(params->constants[i].type);
1063 : :
1064 [ + + ]: 1553 : for (int i = 0; i < params->num_descriptors; i++) {
1065 : 1061 : struct pl_desc desc = params->descriptors[i];
1066 [ - + ]: 1061 : require(desc.name);
1067 : :
1068 : : // enforce disjoint descriptor bindings for each namespace
1069 : 1061 : int namespace = pl_desc_namespace(gpu, desc.type);
1070 [ + + ]: 2034 : for (int j = i+1; j < params->num_descriptors; j++) {
1071 : 973 : struct pl_desc other = params->descriptors[j];
1072 [ + + - + ]: 973 : require(desc.binding != other.binding ||
1073 : : namespace != pl_desc_namespace(gpu, other.type));
1074 : : }
1075 : : }
1076 : :
1077 [ - + ]: 492 : require(params->push_constants_size <= gpu->limits.max_pushc_size);
1078 [ - + ]: 492 : require(params->push_constants_size == PL_ALIGN2(params->push_constants_size, 4));
1079 : :
1080 : 492 : log_shader_sources(gpu->log, PL_LOG_DEBUG, params);
1081 : 492 : log_spec_constants(gpu->log, PL_LOG_DEBUG, params, params->constant_data);
1082 : :
1083 : 492 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
1084 : 492 : pl_pass pass = impl->pass_create(gpu, params);
1085 [ - + ]: 492 : if (!pass)
1086 : 0 : goto error;
1087 : :
1088 : : return pass;
1089 : :
1090 : : error:
1091 : 0 : log_shader_sources(gpu->log, PL_LOG_ERR, params);
1092 : 0 : pl_log_stack_trace(gpu->log, PL_LOG_ERR);
1093 : 0 : pl_debug_abort();
1094 : : return NULL;
1095 : : }
1096 : :
1097 : 492 : void pl_pass_destroy(pl_gpu gpu, pl_pass *pass)
1098 : : {
1099 [ + - ]: 492 : if (!*pass)
1100 : : return;
1101 : :
1102 : 492 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
1103 : 492 : impl->pass_destroy(gpu, *pass);
1104 : 492 : *pass = NULL;
1105 : : }
1106 : :
1107 : 2420 : void pl_pass_run(pl_gpu gpu, const struct pl_pass_run_params *params)
1108 : : {
1109 : 2420 : pl_pass pass = params->pass;
1110 : 2420 : struct pl_pass_run_params new = *params;
1111 : :
1112 [ + + ]: 6735 : for (int i = 0; i < pass->params.num_descriptors; i++) {
1113 : 4315 : struct pl_desc desc = pass->params.descriptors[i];
1114 : 4315 : struct pl_desc_binding db = params->desc_bindings[i];
1115 [ - + ]: 4315 : require(db.object);
1116 [ + + ]: 6989 : for (int j = 0; j < i; j++)
1117 [ - + ]: 2674 : require(db.object != params->desc_bindings[j].object);
1118 : :
1119 [ + + + + : 4315 : switch (desc.type) {
- + - - ]
1120 : 3663 : case PL_DESC_SAMPLED_TEX: {
1121 : : pl_tex tex = db.object;
1122 : 3663 : pl_fmt fmt = tex->params.format;
1123 [ - + ]: 3663 : require(tex->params.sampleable);
1124 [ + + - + ]: 3663 : require(db.sample_mode != PL_TEX_SAMPLE_LINEAR || (fmt->caps & PL_FMT_CAP_LINEAR));
1125 : : break;
1126 : : }
1127 : 115 : case PL_DESC_STORAGE_IMG: {
1128 : : pl_tex tex = db.object;
1129 : 115 : pl_fmt fmt = tex->params.format;
1130 [ - + ]: 115 : require(tex->params.storable);
1131 [ + + - + ]: 115 : require(desc.access != PL_DESC_ACCESS_READWRITE || (fmt->caps & PL_FMT_CAP_READWRITE));
1132 : : break;
1133 : : }
1134 : 496 : case PL_DESC_BUF_UNIFORM: {
1135 : : pl_buf buf = db.object;
1136 [ - + ]: 496 : require(buf->params.uniform);
1137 : : break;
1138 : : }
1139 : 23 : case PL_DESC_BUF_STORAGE: {
1140 : : pl_buf buf = db.object;
1141 [ - + ]: 23 : require(buf->params.storable);
1142 : : break;
1143 : : }
1144 : 0 : case PL_DESC_BUF_TEXEL_UNIFORM: {
1145 : : pl_buf buf = db.object;
1146 [ # # # # ]: 0 : require(buf->params.uniform && buf->params.format);
1147 : : break;
1148 : : }
1149 : 18 : case PL_DESC_BUF_TEXEL_STORAGE: {
1150 : : pl_buf buf = db.object;
1151 : 18 : pl_fmt fmt = buf->params.format;
1152 [ + - - + ]: 18 : require(buf->params.storable && buf->params.format);
1153 [ + - - + ]: 18 : require(desc.access != PL_DESC_ACCESS_READWRITE || (fmt->caps & PL_FMT_CAP_READWRITE));
1154 : : break;
1155 : : }
1156 : : case PL_DESC_INVALID:
1157 : : case PL_DESC_TYPE_COUNT:
1158 : 0 : pl_unreachable();
1159 : : }
1160 : : }
1161 : :
1162 [ + + ]: 3530 : for (int i = 0; i < params->num_var_updates; i++) {
1163 : 1110 : struct pl_var_update vu = params->var_updates[i];
1164 [ + - - + ]: 1110 : require(vu.index >= 0 && vu.index < pass->params.num_variables);
1165 [ - + ]: 1110 : require(vu.data);
1166 : : }
1167 : :
1168 [ + + - + ]: 2420 : require(params->push_constants || !pass->params.push_constants_size);
1169 : :
1170 [ + + - - ]: 2420 : switch (pass->params.type) {
1171 : 2306 : case PL_PASS_RASTER: {
1172 [ + + - - ]: 2306 : switch (pass->params.vertex_type) {
1173 : 20 : case PL_PRIM_TRIANGLE_LIST:
1174 [ - + ]: 20 : require(params->vertex_count % 3 == 0);
1175 : : // fall through
1176 : : case PL_PRIM_TRIANGLE_STRIP:
1177 [ - + ]: 2306 : require(params->vertex_count >= 3);
1178 : : break;
1179 : : case PL_PRIM_TYPE_COUNT:
1180 : 0 : pl_unreachable();
1181 : : }
1182 : :
1183 [ - + ]: 2306 : require(!params->vertex_data ^ !params->vertex_buf);
1184 [ + + ]: 2306 : if (params->vertex_buf) {
1185 : : pl_buf vertex_buf = params->vertex_buf;
1186 [ - + ]: 471 : require(vertex_buf->params.drawable);
1187 [ + - + + ]: 471 : if (!params->index_data && !params->index_buf) {
1188 : : // Cannot bounds check indexed draws
1189 : 466 : size_t vert_size = params->vertex_count * pass->params.vertex_stride;
1190 [ - + ]: 466 : require(params->buf_offset + vert_size <= vertex_buf->params.size);
1191 : : }
1192 : : }
1193 : :
1194 [ + + - + ]: 2306 : require(!params->index_data || !params->index_buf);
1195 [ + + ]: 2306 : if (params->index_buf) {
1196 : : pl_buf index_buf = params->index_buf;
1197 [ - + ]: 5 : require(!params->vertex_data);
1198 [ - + ]: 5 : require(index_buf->params.drawable);
1199 : 5 : size_t index_size = pl_index_buf_size(params);
1200 [ - + ]: 5 : require(params->index_offset + index_size <= index_buf->params.size);
1201 : : }
1202 : :
1203 : 2306 : pl_tex target = params->target;
1204 [ - + ]: 2306 : require(target);
1205 : 0 : require(pl_tex_params_dimension(target->params) == 2);
1206 [ - + ]: 2306 : require(target->params.format->signature == pass->params.target_format->signature);
1207 [ - + ]: 2306 : require(target->params.renderable);
1208 : : pl_rect2d *vp = &new.viewport;
1209 : : pl_rect2d *sc = &new.scissors;
1210 : :
1211 : : // Sanitize viewport/scissors
1212 [ + - + + ]: 2306 : if (!vp->x0 && !vp->x1)
1213 : 1837 : vp->x1 = target->params.w;
1214 [ + - + + ]: 2306 : if (!vp->y0 && !vp->y1)
1215 : 1837 : vp->y1 = target->params.h;
1216 : :
1217 [ + - + + ]: 2306 : if (!sc->x0 && !sc->x1)
1218 : 24 : sc->x1 = target->params.w;
1219 [ + - + + ]: 2306 : if (!sc->y0 && !sc->y1)
1220 : 24 : sc->y1 = target->params.h;
1221 : :
1222 : : // Constrain the scissors to the target dimension (to sanitize the
1223 : : // underlying graphics API calls)
1224 [ + - ]: 2306 : sc->x0 = PL_CLAMP(sc->x0, 0, target->params.w);
1225 [ + - ]: 2306 : sc->y0 = PL_CLAMP(sc->y0, 0, target->params.h);
1226 [ + - ]: 2306 : sc->x1 = PL_CLAMP(sc->x1, 0, target->params.w);
1227 [ + - ]: 2306 : sc->y1 = PL_CLAMP(sc->y1, 0, target->params.h);
1228 : :
1229 : : // Scissors wholly outside target -> silently drop pass (also needed
1230 : : // to ensure we don't cause UB by specifying invalid scissors)
1231 [ + - + - ]: 2306 : if (!pl_rect_w(*sc) || !pl_rect_h(*sc))
1232 : : return;
1233 : :
1234 [ - + ]: 2306 : require(pl_rect_w(*vp) > 0);
1235 [ - + ]: 2306 : require(pl_rect_h(*vp) > 0);
1236 [ - + ]: 2306 : require(pl_rect_w(*sc) > 0);
1237 [ - + ]: 2306 : require(pl_rect_h(*sc) > 0);
1238 : :
1239 [ + + ]: 2306 : if (!pass->params.load_target)
1240 : 2154 : pl_tex_invalidate(gpu, target);
1241 : : break;
1242 : : }
1243 : : case PL_PASS_COMPUTE:
1244 [ + + ]: 456 : for (int i = 0; i < PL_ARRAY_SIZE(params->compute_groups); i++) {
1245 [ - + ]: 342 : require(params->compute_groups[i] >= 0);
1246 [ - + ]: 342 : require(params->compute_groups[i] <= gpu->limits.max_dispatch[i]);
1247 : : }
1248 : : break;
1249 : : case PL_PASS_INVALID:
1250 : : case PL_PASS_TYPE_COUNT:
1251 : 0 : pl_unreachable();
1252 : : }
1253 : :
1254 : 2420 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
1255 : 2420 : impl->pass_run(gpu, &new);
1256 : :
1257 : : error:
1258 : 2420 : return;
1259 : : }
1260 : :
1261 : 363 : void pl_gpu_flush(pl_gpu gpu)
1262 : : {
1263 : 363 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
1264 [ + - ]: 363 : if (impl->gpu_flush)
1265 : 363 : impl->gpu_flush(gpu);
1266 : 363 : }
1267 : :
1268 : 697 : void pl_gpu_finish(pl_gpu gpu)
1269 : : {
1270 : 697 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
1271 : 697 : impl->gpu_finish(gpu);
1272 : 697 : }
1273 : :
1274 : 11 : bool pl_gpu_is_failed(pl_gpu gpu)
1275 : : {
1276 : 11 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
1277 [ + - ]: 11 : if (!impl->gpu_is_failed)
1278 : : return false;
1279 : :
1280 : 11 : return impl->gpu_is_failed(gpu);
1281 : : }
1282 : :
1283 : 1860 : pl_timer pl_timer_create(pl_gpu gpu)
1284 : : {
1285 : 1860 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
1286 [ + + ]: 1860 : if (!impl->timer_create)
1287 : : return NULL;
1288 : :
1289 : 1404 : return impl->timer_create(gpu);
1290 : : }
1291 : :
1292 : 1860 : void pl_timer_destroy(pl_gpu gpu, pl_timer *timer)
1293 : : {
1294 [ + + ]: 1860 : if (!*timer)
1295 : : return;
1296 : :
1297 : 1369 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
1298 : 1369 : impl->timer_destroy(gpu, *timer);
1299 : 1369 : *timer = NULL;
1300 : : }
1301 : :
1302 : 4746 : uint64_t pl_timer_query(pl_gpu gpu, pl_timer timer)
1303 : : {
1304 [ + + ]: 4746 : if (!timer)
1305 : : return 0;
1306 : :
1307 : 4255 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
1308 : 4255 : return impl->timer_query(gpu, timer);
1309 : : }
|