Branch data Line data Source code
1 : : /*
2 : : * This file is part of libplacebo.
3 : : *
4 : : * libplacebo is free software; you can redistribute it and/or
5 : : * modify it under the terms of the GNU Lesser General Public
6 : : * License as published by the Free Software Foundation; either
7 : : * version 2.1 of the License, or (at your option) any later version.
8 : : *
9 : : * libplacebo is distributed in the hope that it will be useful,
10 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : : * GNU Lesser General Public License for more details.
13 : : *
14 : : * You should have received a copy of the GNU Lesser General Public
15 : : * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
16 : : */
17 : :
18 : : #include <math.h>
19 : :
20 : : #include "common.h"
21 : : #include "shaders.h"
22 : : #include "gpu.h"
23 : :
24 : : // GPU-internal helpers
25 : :
26 : 2635 : static int cmp_fmt(const void *pa, const void *pb)
27 : : {
28 : 2635 : pl_fmt a = *(pl_fmt *)pa;
29 : 2635 : pl_fmt b = *(pl_fmt *)pb;
30 : :
31 : : // Always prefer non-opaque formats
32 [ + + ]: 2635 : if (a->opaque != b->opaque)
33 : 33 : return PL_CMP(a->opaque, b->opaque);
34 : :
35 : : // Always prefer non-emulated formats
36 [ + + ]: 2602 : if (a->emulated != b->emulated)
37 : 232 : return PL_CMP(a->emulated, b->emulated);
38 : :
39 : : // Prefer formats with many optional rendering capabilities
40 : : const enum pl_fmt_caps caps_whitelist =
41 : : PL_FMT_CAP_SAMPLEABLE |
42 : : PL_FMT_CAP_STORABLE |
43 : : PL_FMT_CAP_LINEAR |
44 : : PL_FMT_CAP_RENDERABLE |
45 : : PL_FMT_CAP_BLENDABLE |
46 : : PL_FMT_CAP_BLITTABLE;
47 : :
48 : 2370 : enum pl_fmt_caps a_caps = a->caps & caps_whitelist,
49 : 2370 : b_caps = b->caps & caps_whitelist;
50 : :
51 : 2370 : int ca = __builtin_popcount(a_caps),
52 : 2370 : cb = __builtin_popcount(b_caps);
53 [ + + ]: 2370 : if (ca != cb)
54 : 711 : return -PL_CMP(ca, cb); // invert to sort higher values first
55 : :
56 : : // If the population count is the same but the caps are different, prefer
57 : : // the caps with a "lower" value (which tend to be more fundamental caps)
58 [ + + ]: 1659 : if (a_caps != b_caps)
59 : 6 : return PL_CMP(a_caps, b_caps);
60 : :
61 : : // If the capabilities are equal, sort based on the component attributes
62 [ + + ]: 3932 : for (int i = 0; i < PL_ARRAY_SIZE(a->component_depth); i++) {
63 : 3553 : int da = a->component_depth[i],
64 : 3553 : db = b->component_depth[i];
65 [ + + ]: 3553 : if (da != db)
66 : 1212 : return PL_CMP(da, db);
67 : :
68 : 2341 : int ha = a->host_bits[i],
69 : 2341 : hb = b->host_bits[i];
70 [ + + ]: 2341 : if (ha != hb)
71 : 20 : return PL_CMP(ha, hb);
72 : :
73 : 2321 : int oa = a->sample_order[i],
74 : 2321 : ob = b->sample_order[i];
75 [ + + ]: 2321 : if (oa != ob)
76 : 42 : return PL_CMP(oa, ob);
77 : : }
78 : :
79 : : // Fall back to sorting by the name (for stability)
80 : 379 : return strcmp(a->name, b->name);
81 : : }
82 : :
83 : : #define FMT_BOOL(letter, cap) ((cap) ? (letter) : '-')
84 : : #define FMT_IDX4(f) (f)[0], (f)[1], (f)[2], (f)[3]
85 : :
86 : 9 : static void print_formats(pl_gpu gpu)
87 : : {
88 [ + - + + ]: 18 : if (!pl_msg_test(gpu->log, PL_LOG_DEBUG))
89 : : return;
90 : :
91 : : #define CAP_HEADER "%-12s"
92 : : #define CAP_FIELDS "%c%c%c%c%c%c%c%c%c%c%c%c"
93 : : #define CAP_VALUES \
94 : : FMT_BOOL('S', fmt->caps & PL_FMT_CAP_SAMPLEABLE), \
95 : : FMT_BOOL('s', fmt->caps & PL_FMT_CAP_STORABLE), \
96 : : FMT_BOOL('L', fmt->caps & PL_FMT_CAP_LINEAR), \
97 : : FMT_BOOL('R', fmt->caps & PL_FMT_CAP_RENDERABLE), \
98 : : FMT_BOOL('b', fmt->caps & PL_FMT_CAP_BLENDABLE), \
99 : : FMT_BOOL('B', fmt->caps & PL_FMT_CAP_BLITTABLE), \
100 : : FMT_BOOL('V', fmt->caps & PL_FMT_CAP_VERTEX), \
101 : : FMT_BOOL('u', fmt->caps & PL_FMT_CAP_TEXEL_UNIFORM), \
102 : : FMT_BOOL('t', fmt->caps & PL_FMT_CAP_TEXEL_STORAGE), \
103 : : FMT_BOOL('H', fmt->caps & PL_FMT_CAP_HOST_READABLE), \
104 : : FMT_BOOL('W', fmt->caps & PL_FMT_CAP_READWRITE), \
105 : : FMT_BOOL('G', fmt->gatherable)
106 : :
107 : 6 : PL_DEBUG(gpu, "GPU texture formats:");
108 : 6 : PL_DEBUG(gpu, " %-20s %-6s %-4s %-4s " CAP_HEADER " %-3s %-13s %-13s %-10s %-10s %-6s",
109 : : "NAME", "TYPE", "SIZE", "COMP", "CAPS", "EMU", "DEPTH", "HOST_BITS",
110 : : "GLSL_TYPE", "GLSL_FMT", "FOURCC");
111 [ + + ]: 493 : for (int n = 0; n < gpu->num_formats; n++) {
112 : 487 : pl_fmt fmt = gpu->formats[n];
113 : :
114 : : static const char *types[] = {
115 : : [PL_FMT_UNKNOWN] = "UNKNOWN",
116 : : [PL_FMT_UNORM] = "UNORM",
117 : : [PL_FMT_SNORM] = "SNORM",
118 : : [PL_FMT_UINT] = "UINT",
119 : : [PL_FMT_SINT] = "SINT",
120 : : [PL_FMT_FLOAT] = "FLOAT",
121 : : };
122 : :
123 : : static const char idx_map[4] = {'R', 'G', 'B', 'A'};
124 : 487 : char indices[4] = {' ', ' ', ' ', ' '};
125 [ + + ]: 487 : if (!fmt->opaque) {
126 [ + + ]: 1520 : for (int i = 0; i < fmt->num_components; i++)
127 : 1105 : indices[i] = idx_map[fmt->sample_order[i]];
128 : : }
129 : :
130 : :
131 [ + + + + : 3963 : PL_DEBUG(gpu, " %-20s %-6s %-4zu %c%c%c%c " CAP_FIELDS " %-3s "
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + ]
132 : : "{%-2d %-2d %-2d %-2d} {%-2d %-2d %-2d %-2d} %-10s %-10s %-6s",
133 : : fmt->name, types[fmt->type], fmt->texel_size,
134 : : FMT_IDX4(indices), CAP_VALUES, fmt->emulated ? "y" : "n",
135 : : FMT_IDX4(fmt->component_depth), FMT_IDX4(fmt->host_bits),
136 : : PL_DEF(fmt->glsl_type, ""), PL_DEF(fmt->glsl_format, ""),
137 : : PRINT_FOURCC(fmt->fourcc));
138 : :
139 : : #undef CAP_HEADER
140 : : #undef CAP_FIELDS
141 : : #undef CAP_VALUES
142 : :
143 [ + + ]: 773 : for (int i = 0; i < fmt->num_modifiers; i++) {
144 : 286 : PL_TRACE(gpu, " modifiers[%d]: %s",
145 : : i, PRINT_DRM_MOD(fmt->modifiers[i]));
146 : : }
147 : : }
148 : : }
149 : :
150 : 9 : pl_gpu pl_gpu_finalize(struct pl_gpu_t *gpu)
151 : : {
152 : : // Sort formats
153 : 9 : qsort(gpu->formats, gpu->num_formats, sizeof(pl_fmt), cmp_fmt);
154 : :
155 : : // Verification
156 [ - + ]: 9 : pl_assert(gpu->limits.max_tex_2d_dim);
157 [ + + - + ]: 9 : pl_assert(gpu->limits.max_variable_comps || gpu->limits.max_ubo_size);
158 [ - + ]: 9 : pl_assert(gpu->limits.max_ubo_size <= gpu->limits.max_buf_size);
159 [ - + ]: 9 : pl_assert(gpu->limits.max_ssbo_size <= gpu->limits.max_buf_size);
160 [ - + ]: 9 : pl_assert(gpu->limits.max_vbo_size <= gpu->limits.max_buf_size);
161 [ - + ]: 9 : pl_assert(gpu->limits.max_mapped_size <= gpu->limits.max_buf_size);
162 [ - + ]: 9 : pl_assert(gpu->limits.max_mapped_vram <= gpu->limits.max_mapped_size);
163 : :
164 [ + + ]: 583 : for (int n = 0; n < gpu->num_formats; n++) {
165 : 574 : pl_fmt fmt = gpu->formats[n];
166 [ - + ]: 574 : pl_assert(fmt->name);
167 [ - + ]: 574 : pl_assert(fmt->type);
168 [ - + ]: 574 : pl_assert(fmt->num_components);
169 [ - + ]: 574 : pl_assert(fmt->internal_size);
170 [ + + - + ]: 574 : pl_assert(fmt->opaque ? !fmt->texel_size : fmt->texel_size);
171 [ + + + - ]: 574 : pl_assert(!fmt->gatherable || (fmt->caps & PL_FMT_CAP_SAMPLEABLE));
172 [ + + ]: 2117 : for (int i = 0; i < fmt->num_components; i++) {
173 [ - + ]: 1543 : pl_assert(fmt->component_depth[i]);
174 [ + + - + ]: 1543 : pl_assert(fmt->opaque ? !fmt->host_bits[i] : fmt->host_bits[i]);
175 : : }
176 [ + + ]: 754 : for (int i = 0; i < fmt->num_planes; i++)
177 [ - + ]: 180 : pl_assert(fmt->planes[i].format);
178 : :
179 : : enum pl_fmt_caps texel_caps = PL_FMT_CAP_VERTEX |
180 : : PL_FMT_CAP_TEXEL_UNIFORM |
181 : : PL_FMT_CAP_TEXEL_STORAGE;
182 : :
183 [ + + ]: 574 : if (fmt->caps & texel_caps) {
184 [ - + ]: 422 : pl_assert(fmt->glsl_type);
185 [ - + ]: 422 : pl_assert(!fmt->opaque);
186 : : }
187 [ + + ]: 574 : if (!fmt->opaque) {
188 [ + - - + ]: 502 : pl_assert(fmt->texel_size && fmt->texel_align);
189 [ - + ]: 502 : pl_assert((fmt->texel_size % fmt->texel_align) == 0);
190 [ + + - + ]: 502 : pl_assert(fmt->internal_size == fmt->texel_size || fmt->emulated);
191 : : } else {
192 [ + - - + ]: 72 : pl_assert(!fmt->texel_size && !fmt->texel_align);
193 [ - + ]: 72 : pl_assert(!(fmt->caps & PL_FMT_CAP_HOST_READABLE));
194 : : }
195 : :
196 : : // Assert uniqueness of name
197 [ + + ]: 23351 : for (int o = n + 1; o < gpu->num_formats; o++)
198 [ - + ]: 22777 : pl_assert(strcmp(fmt->name, gpu->formats[o]->name) != 0);
199 : : }
200 : :
201 : : // Print info
202 : 9 : PL_INFO(gpu, "GPU information:");
203 : :
204 : : #define LOG(fmt, field) \
205 : : PL_INFO(gpu, " %-26s %" fmt, #field ":", gpu->LOG_STRUCT.field)
206 : :
207 : : #define LOG_STRUCT glsl
208 [ + + + + ]: 14 : PL_INFO(gpu, " GLSL version: %d%s", gpu->glsl.version,
209 : : gpu->glsl.vulkan ? " (vulkan)" : gpu->glsl.gles ? " es" : "");
210 [ + + ]: 9 : if (gpu->glsl.compute) {
211 : 6 : LOG("zu", max_shmem_size);
212 : 6 : LOG(PRIu32, max_group_threads);
213 : 6 : LOG(PRIu32, max_group_size[0]);
214 : 6 : LOG(PRIu32, max_group_size[1]);
215 : 6 : LOG(PRIu32, max_group_size[2]);
216 : : }
217 : 9 : LOG(PRIu32, subgroup_size);
218 : 9 : LOG(PRIi16, min_gather_offset);
219 : 9 : LOG(PRIi16, max_gather_offset);
220 : : #undef LOG_STRUCT
221 : :
222 : : #define LOG_STRUCT limits
223 : 9 : PL_INFO(gpu, " Limits:");
224 : : // pl_gpu
225 : 9 : LOG("d", thread_safe);
226 : 9 : LOG("d", callbacks);
227 : : // pl_buf
228 : 9 : LOG("zu", max_buf_size);
229 : 9 : LOG("zu", max_ubo_size);
230 : 9 : LOG("zu", max_ssbo_size);
231 : 9 : LOG("zu", max_vbo_size);
232 : 9 : LOG("zu", max_mapped_size);
233 : 9 : LOG(PRIu64, max_buffer_texels);
234 : 9 : LOG("zu", align_host_ptr);
235 : 9 : LOG("d", host_cached);
236 : : // pl_tex
237 : 9 : LOG(PRIu32, max_tex_1d_dim);
238 : 9 : LOG(PRIu32, max_tex_2d_dim);
239 : 9 : LOG(PRIu32, max_tex_3d_dim);
240 : 9 : LOG("d", blittable_1d_3d);
241 : 9 : LOG("d", buf_transfer);
242 : 9 : LOG("zu", align_tex_xfer_pitch);
243 : 9 : LOG("zu", align_tex_xfer_offset);
244 : : // pl_pass
245 : 9 : LOG("zu", max_variable_comps);
246 : 9 : LOG("zu", max_constants);
247 : 9 : LOG("zu", max_pushc_size);
248 : 9 : LOG("zu", align_vertex_stride);
249 [ + + ]: 9 : if (gpu->glsl.compute) {
250 : 6 : LOG(PRIu32, max_dispatch[0]);
251 : 6 : LOG(PRIu32, max_dispatch[1]);
252 : 6 : LOG(PRIu32, max_dispatch[2]);
253 : : }
254 : 9 : LOG(PRIu32, fragment_queues);
255 : 9 : LOG(PRIu32, compute_queues);
256 : : #undef LOG_STRUCT
257 : : #undef LOG
258 : :
259 : : if (pl_gpu_supports_interop(gpu)) {
260 : 7 : PL_INFO(gpu, " External API interop:");
261 : :
262 : 7 : PL_INFO(gpu, " UUID: %s", PRINT_UUID(gpu->uuid));
263 : 7 : PL_INFO(gpu, " PCI: %04x:%02x:%02x:%x",
264 : : gpu->pci.domain, gpu->pci.bus, gpu->pci.device, gpu->pci.function);
265 : 7 : PL_INFO(gpu, " buf export caps: 0x%x",
266 : : (unsigned int) gpu->export_caps.buf);
267 : 7 : PL_INFO(gpu, " buf import caps: 0x%x",
268 : : (unsigned int) gpu->import_caps.buf);
269 : 7 : PL_INFO(gpu, " tex export caps: 0x%x",
270 : : (unsigned int) gpu->export_caps.tex);
271 : 7 : PL_INFO(gpu, " tex import caps: 0x%x",
272 : : (unsigned int) gpu->import_caps.tex);
273 : 7 : PL_INFO(gpu, " sync export caps: 0x%x",
274 : : (unsigned int) gpu->export_caps.sync);
275 : 7 : PL_INFO(gpu, " sync import caps: 0x%x",
276 : : (unsigned int) gpu->import_caps.sync);
277 : : }
278 : :
279 : 9 : print_formats(gpu);
280 : :
281 : : // Finally, create a `pl_dispatch` object for internal operations
282 : 9 : struct pl_gpu_fns *impl = PL_PRIV(gpu);
283 : 9 : atomic_init(&impl->cache, NULL);
284 : 9 : impl->dp = pl_dispatch_create(gpu->log, gpu);
285 : 9 : return gpu;
286 : : }
287 : :
288 : : struct glsl_fmt {
289 : : enum pl_fmt_type type;
290 : : int num_components;
291 : : int depth[4];
292 : : const char *glsl_format;
293 : : };
294 : :
295 : : // List taken from the GLSL specification. (Yes, GLSL supports only exactly
296 : : // these formats with exactly these names)
297 : : static const struct glsl_fmt pl_glsl_fmts[] = {
298 : : {PL_FMT_FLOAT, 1, {16}, "r16f"},
299 : : {PL_FMT_FLOAT, 1, {32}, "r32f"},
300 : : {PL_FMT_FLOAT, 2, {16, 16}, "rg16f"},
301 : : {PL_FMT_FLOAT, 2, {32, 32}, "rg32f"},
302 : : {PL_FMT_FLOAT, 4, {16, 16, 16, 16}, "rgba16f"},
303 : : {PL_FMT_FLOAT, 4, {32, 32, 32, 32}, "rgba32f"},
304 : : {PL_FMT_FLOAT, 3, {11, 11, 10}, "r11f_g11f_b10f"},
305 : :
306 : : {PL_FMT_UNORM, 1, {8}, "r8"},
307 : : {PL_FMT_UNORM, 1, {16}, "r16"},
308 : : {PL_FMT_UNORM, 2, {8, 8}, "rg8"},
309 : : {PL_FMT_UNORM, 2, {16, 16}, "rg16"},
310 : : {PL_FMT_UNORM, 4, {8, 8, 8, 8}, "rgba8"},
311 : : {PL_FMT_UNORM, 4, {16, 16, 16, 16}, "rgba16"},
312 : : {PL_FMT_UNORM, 4, {10, 10, 10, 2}, "rgb10_a2"},
313 : :
314 : : {PL_FMT_SNORM, 1, {8}, "r8_snorm"},
315 : : {PL_FMT_SNORM, 1, {16}, "r16_snorm"},
316 : : {PL_FMT_SNORM, 2, {8, 8}, "rg8_snorm"},
317 : : {PL_FMT_SNORM, 2, {16, 16}, "rg16_snorm"},
318 : : {PL_FMT_SNORM, 4, {8, 8, 8, 8}, "rgba8_snorm"},
319 : : {PL_FMT_SNORM, 4, {16, 16, 16, 16}, "rgba16_snorm"},
320 : :
321 : : {PL_FMT_UINT, 1, {8}, "r8ui"},
322 : : {PL_FMT_UINT, 1, {16}, "r16ui"},
323 : : {PL_FMT_UINT, 1, {32}, "r32ui"},
324 : : {PL_FMT_UINT, 2, {8, 8}, "rg8ui"},
325 : : {PL_FMT_UINT, 2, {16, 16}, "rg16ui"},
326 : : {PL_FMT_UINT, 2, {32, 32}, "rg32ui"},
327 : : {PL_FMT_UINT, 4, {8, 8, 8, 8}, "rgba8ui"},
328 : : {PL_FMT_UINT, 4, {16, 16, 16, 16}, "rgba16ui"},
329 : : {PL_FMT_UINT, 4, {32, 32, 32, 32}, "rgba32ui"},
330 : : {PL_FMT_UINT, 4, {10, 10, 10, 2}, "rgb10_a2ui"},
331 : :
332 : : {PL_FMT_SINT, 1, {8}, "r8i"},
333 : : {PL_FMT_SINT, 1, {16}, "r16i"},
334 : : {PL_FMT_SINT, 1, {32}, "r32i"},
335 : : {PL_FMT_SINT, 2, {8, 8}, "rg8i"},
336 : : {PL_FMT_SINT, 2, {16, 16}, "rg16i"},
337 : : {PL_FMT_SINT, 2, {32, 32}, "rg32i"},
338 : : {PL_FMT_SINT, 4, {8, 8, 8, 8}, "rgba8i"},
339 : : {PL_FMT_SINT, 4, {16, 16, 16, 16}, "rgba16i"},
340 : : {PL_FMT_SINT, 4, {32, 32, 32, 32}, "rgba32i"},
341 : : };
342 : :
343 : 406 : const char *pl_fmt_glsl_format(pl_fmt fmt, int components)
344 : : {
345 [ - + ]: 406 : if (fmt->opaque)
346 : : return NULL;
347 : :
348 [ + + ]: 9570 : for (int n = 0; n < PL_ARRAY_SIZE(pl_glsl_fmts); n++) {
349 : : const struct glsl_fmt *gfmt = &pl_glsl_fmts[n];
350 : :
351 [ + + ]: 9453 : if (fmt->type != gfmt->type)
352 : 8843 : continue;
353 [ + + ]: 2104 : if (components != gfmt->num_components)
354 : 1494 : continue;
355 : :
356 : : // The component order is irrelevant, so we need to sort the depth
357 : : // based on the component's index
358 : 610 : int depth[4] = {0};
359 [ + + ]: 2132 : for (int i = 0; i < fmt->num_components; i++)
360 : 1522 : depth[fmt->sample_order[i]] = fmt->component_depth[i];
361 : :
362 : : // Copy over any emulated components
363 [ - + ]: 610 : for (int i = fmt->num_components; i < components; i++)
364 : 0 : depth[i] = gfmt->depth[i];
365 : :
366 [ + + ]: 1766 : for (int i = 0; i < PL_ARRAY_SIZE(depth); i++) {
367 [ + + ]: 1477 : if (depth[i] != gfmt->depth[i])
368 : 321 : goto next_fmt;
369 : : }
370 : :
371 : 289 : return gfmt->glsl_format;
372 : :
373 : : next_fmt: ; // equivalent to `continue`
374 : : }
375 : :
376 : : return NULL;
377 : : }
378 : :
379 : : #define FOURCC(a,b,c,d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \
380 : : ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
381 : :
382 : : struct pl_fmt_fourcc {
383 : : const char *name;
384 : : uint32_t fourcc;
385 : : };
386 : :
387 : : static const struct pl_fmt_fourcc pl_fmt_fourccs[] = {
388 : : // 8 bpp red
389 : : {"r8", FOURCC('R','8',' ',' ')},
390 : : // 16 bpp red
391 : : {"r16", FOURCC('R','1','6',' ')},
392 : : // 16 bpp rg
393 : : {"rg8", FOURCC('G','R','8','8')},
394 : : {"gr8", FOURCC('R','G','8','8')},
395 : : // 32 bpp rg
396 : : {"rg16", FOURCC('G','R','3','2')},
397 : : {"gr16", FOURCC('R','G','3','2')},
398 : : // 8 bpp rgb: N/A
399 : : // 16 bpp rgb
400 : : {"argb4", FOURCC('B','A','1','2')},
401 : : {"abgr4", FOURCC('R','A','1','2')},
402 : : {"rgba4", FOURCC('A','B','1','2')},
403 : : {"bgra4", FOURCC('A','R','1','2')},
404 : :
405 : : {"a1rgb5", FOURCC('B','A','1','5')},
406 : : {"a1bgr5", FOURCC('R','A','1','5')},
407 : : {"rgb5a1", FOURCC('A','B','1','5')},
408 : : {"bgr5a1", FOURCC('A','R','1','5')},
409 : :
410 : : {"rgb565", FOURCC('B','G','1','6')},
411 : : {"bgr565", FOURCC('R','G','1','6')},
412 : : // 24 bpp rgb
413 : : {"rgb8", FOURCC('B','G','2','4')},
414 : : {"bgr8", FOURCC('R','G','2','4')},
415 : : // 32 bpp rgb
416 : : {"argb8", FOURCC('B','A','2','4')},
417 : : {"abgr8", FOURCC('R','A','2','4')},
418 : : {"rgba8", FOURCC('A','B','2','4')},
419 : : {"bgra8", FOURCC('A','R','2','4')},
420 : :
421 : : {"a2rgb10", FOURCC('B','A','3','0')},
422 : : {"a2bgr10", FOURCC('R','A','3','0')},
423 : : {"rgb10a2", FOURCC('A','B','3','0')},
424 : : {"bgr10a2", FOURCC('A','R','3','0')},
425 : : // 64bpp rgb
426 : : {"rgba16", FOURCC('A','B','4','8')},
427 : : {"bgra16", FOURCC('A','R','4','8')},
428 : : {"rgba16hf", FOURCC('A','B','4','H')},
429 : : {"bgra16hf", FOURCC('A','R','4','H')},
430 : :
431 : : // packed 16-bit formats
432 : : // rx10: N/A
433 : : // rxgx10: N/A
434 : : {"rxgxbxax10", FOURCC('A','B','1','0')},
435 : : // rx12: N/A
436 : : // rxgx12: N/A
437 : : // rxgxbxax12: N/A
438 : :
439 : : // planar formats
440 : : {"g8_b8_r8_420", FOURCC('Y','U','1','2')},
441 : : {"g8_b8_r8_422", FOURCC('Y','U','1','6')},
442 : : {"g8_b8_r8_444", FOURCC('Y','U','2','4')},
443 : : // g16_b18_r8_*: N/A
444 : : // gx10_bx10_rx10_42*: N/A
445 : : {"gx10_bx10_rx10_444", FOURCC('Q','4','1','0')},
446 : : // gx12_bx12_rx12_*:N/A
447 : : {"g8_br8_420", FOURCC('N','V','1','2')},
448 : : {"g8_br8_422", FOURCC('N','V','1','6')},
449 : : {"g8_br8_444", FOURCC('N','V','2','4')},
450 : : {"g16_br16_420", FOURCC('P','0','1','6')},
451 : : // g16_br16_422: N/A
452 : : // g16_br16_444: N/A
453 : : {"gx10_bxrx10_420", FOURCC('P','0','1','0')},
454 : : {"gx10_bxrx10_422", FOURCC('P','2','1','0')},
455 : : // gx10_bxrx10_444: N/A
456 : : {"gx12_bxrx12_420", FOURCC('P','0','1','2')},
457 : : // gx12_bxrx12_422: N/A
458 : : // gx12_bxrx12_444: N/A
459 : : };
460 : :
461 : 574 : uint32_t pl_fmt_fourcc(pl_fmt fmt)
462 : : {
463 [ + + ]: 21352 : for (int n = 0; n < PL_ARRAY_SIZE(pl_fmt_fourccs); n++) {
464 : : const struct pl_fmt_fourcc *fourcc = &pl_fmt_fourccs[n];
465 [ + + ]: 20920 : if (strcmp(fmt->name, fourcc->name) == 0)
466 : 142 : return fourcc->fourcc;
467 : : }
468 : :
469 : : return 0; // no matching format
470 : : }
471 : :
472 : 1658 : size_t pl_tex_transfer_size(const struct pl_tex_transfer_params *par)
473 : : {
474 : 2220 : int w = pl_rect_w(par->rc), h = pl_rect_h(par->rc), d = pl_rect_d(par->rc);
475 : 2220 : size_t pixel_pitch = par->tex->params.format->texel_size;
476 : :
477 : : // This generates the absolute bare minimum size of a buffer required to
478 : : // hold the data of a texture upload/download, by including stride padding
479 : : // only where strictly necessary.
480 : 2220 : return (d - 1) * par->depth_pitch + (h - 1) * par->row_pitch + w * pixel_pitch;
481 : : }
482 : :
483 : 24 : int pl_tex_transfer_slices(pl_gpu gpu, pl_fmt texel_fmt,
484 : : const struct pl_tex_transfer_params *params,
485 : : struct pl_tex_transfer_params **out_slices)
486 : : {
487 : : PL_ARRAY(struct pl_tex_transfer_params) slices = {0};
488 [ + - ]: 24 : size_t max_size = params->buf ? gpu->limits.max_buf_size : SIZE_MAX;
489 : :
490 : 24 : pl_fmt fmt = params->tex->params.format;
491 [ + + + - ]: 24 : if (fmt->emulated && texel_fmt) {
492 : 18 : size_t max_texel = gpu->limits.max_buffer_texels * texel_fmt->texel_size;
493 : 18 : max_size = PL_MIN(gpu->limits.max_ssbo_size, max_texel);
494 : : }
495 : :
496 : 24 : int slice_w = pl_rect_w(params->rc);
497 : 24 : int slice_h = pl_rect_h(params->rc);
498 : 24 : int slice_d = pl_rect_d(params->rc);
499 : :
500 : 24 : slice_d = PL_MIN(slice_d, max_size / params->depth_pitch);
501 [ - + ]: 24 : if (!slice_d) {
502 : : slice_d = 1;
503 : 0 : slice_h = PL_MIN(slice_h, max_size / params->row_pitch);
504 [ # # ]: 0 : if (!slice_h) {
505 : : slice_h = 1;
506 : 0 : slice_w = PL_MIN(slice_w, max_size / fmt->texel_size);
507 [ # # ]: 0 : pl_assert(slice_w);
508 : : }
509 : : }
510 : :
511 [ + + ]: 48 : for (int z = 0; z < pl_rect_d(params->rc); z += slice_d) {
512 [ + + ]: 48 : for (int y = 0; y < pl_rect_h(params->rc); y += slice_h) {
513 [ + + ]: 48 : for (int x = 0; x < pl_rect_w(params->rc); x += slice_w) {
514 : 24 : struct pl_tex_transfer_params slice = *params;
515 : : slice.callback = NULL;
516 : 24 : slice.rc.x0 = params->rc.x0 + x;
517 : 24 : slice.rc.y0 = params->rc.y0 + y;
518 : 24 : slice.rc.z0 = params->rc.z0 + z;
519 : 24 : slice.rc.x1 = PL_MIN(slice.rc.x0 + slice_w, params->rc.x1);
520 : 24 : slice.rc.y1 = PL_MIN(slice.rc.y0 + slice_h, params->rc.y1);
521 : 24 : slice.rc.z1 = PL_MIN(slice.rc.z0 + slice_d, params->rc.z1);
522 : :
523 : 24 : const size_t offset = z * params->depth_pitch +
524 : 24 : y * params->row_pitch +
525 : 24 : x * fmt->texel_size;
526 [ - + ]: 24 : if (slice.ptr) {
527 : 0 : slice.ptr = (uint8_t *) slice.ptr + offset;
528 : : } else {
529 : 24 : slice.buf_offset += offset;
530 : : }
531 : :
532 [ + - - - : 24 : PL_ARRAY_APPEND(NULL, slices, slice);
- - ]
533 : : }
534 : : }
535 : : }
536 : :
537 : 24 : *out_slices = slices.elem;
538 : 24 : return slices.num;
539 : : }
540 : :
541 : 310 : bool pl_tex_upload_pbo(pl_gpu gpu, const struct pl_tex_transfer_params *params)
542 : : {
543 [ - + ]: 310 : if (params->buf)
544 : 0 : return pl_tex_upload(gpu, params);
545 : :
546 : : const size_t size = pl_tex_transfer_size(params);
547 : 310 : struct pl_tex_transfer_params fixed = *params;
548 : 310 : fixed.ptr = NULL;
549 : :
550 : : // If we can import host pointers directly, and the function is being used
551 : : // asynchronously, then we can use host pointer import to skip a memcpy. In
552 : : // the synchronous case, we still force a host memcpy to avoid stalling the
553 : : // host until the GPU memcpy completes.
554 : 310 : bool can_import = gpu->import_caps.buf & PL_HANDLE_HOST_PTR;
555 : 310 : can_import &= !params->no_import;
556 : 310 : can_import &= params->callback != NULL;
557 : 310 : can_import &= size > (32 << 10); // 32 KiB
558 [ + + ]: 310 : if (can_import) {
559 : : // Suppress errors for this test because it may fail, in which case we
560 : : // want to silently fall back.
561 : 7 : pl_log_level_cap(gpu->log, PL_LOG_DEBUG);
562 : 7 : fixed.buf = pl_buf_create(gpu, pl_buf_params(
563 : : .size = size,
564 : : .import_handle = PL_HANDLE_HOST_PTR,
565 : : .shared_mem = (struct pl_shared_mem) {
566 : : .handle.ptr = params->ptr,
567 : : .size = size,
568 : : .offset = 0,
569 : : },
570 : : ));
571 : 7 : pl_log_level_cap(gpu->log, PL_LOG_NONE);
572 : : }
573 : :
574 [ + + ]: 310 : if (!fixed.buf) {
575 : 303 : fixed.buf = pl_buf_create(gpu, pl_buf_params(
576 : : .size = size,
577 : : .host_writable = true,
578 : : ));
579 [ - + ]: 303 : if (!fixed.buf)
580 : 0 : return false;
581 : 303 : pl_buf_write(gpu, fixed.buf, 0, params->ptr, size);
582 [ + + ]: 303 : if (params->callback)
583 : 200 : params->callback(params->priv);
584 : 303 : fixed.callback = NULL;
585 : : }
586 : :
587 : 310 : bool ok = pl_tex_upload(gpu, &fixed);
588 : 310 : pl_buf_destroy(gpu, &fixed.buf);
589 : 310 : return ok;
590 : : }
591 : :
592 : : struct pbo_cb_ctx {
593 : : pl_gpu gpu;
594 : : pl_buf buf;
595 : : void *ptr;
596 : : void (*callback)(void *priv);
597 : : void *priv;
598 : : };
599 : :
600 : 200 : static void pbo_download_cb(void *priv)
601 : : {
602 : : struct pbo_cb_ctx *p = priv;
603 : 200 : pl_buf_read(p->gpu, p->buf, 0, p->ptr, p->buf->params.size);
604 : 200 : pl_buf_destroy(p->gpu, &p->buf);
605 : :
606 : : // Run the original callback
607 : 200 : p->callback(p->priv);
608 : 200 : pl_free(priv);
609 : 200 : };
610 : :
611 : 252 : bool pl_tex_download_pbo(pl_gpu gpu, const struct pl_tex_transfer_params *params)
612 : : {
613 [ - + ]: 252 : if (params->buf)
614 : 0 : return pl_tex_download(gpu, params);
615 : :
616 : : const size_t size = pl_tex_transfer_size(params);
617 : 252 : pl_buf buf = NULL;
618 : :
619 : : // If we can import host pointers directly, we can avoid an extra memcpy
620 : : // (sometimes). In the cases where it isn't avoidable, the extra memcpy
621 : : // will happen inside VRAM, which is typically faster anyway.
622 : 252 : bool can_import = gpu->import_caps.buf & PL_HANDLE_HOST_PTR;
623 : 252 : can_import &= !params->no_import;
624 : 252 : can_import &= size > (32 << 10); // 32 KiB
625 [ + + ]: 252 : if (can_import) {
626 : : // Suppress errors for this test because it may fail, in which case we
627 : : // want to silently fall back.
628 : 11 : pl_log_level_cap(gpu->log, PL_LOG_DEBUG);
629 : 11 : buf = pl_buf_create(gpu, pl_buf_params(
630 : : .size = size,
631 : : .import_handle = PL_HANDLE_HOST_PTR,
632 : : .shared_mem = (struct pl_shared_mem) {
633 : : .handle.ptr = params->ptr,
634 : : .size = size,
635 : : .offset = 0,
636 : : },
637 : : ));
638 : 11 : pl_log_level_cap(gpu->log, PL_LOG_NONE);
639 : : }
640 : :
641 [ + + ]: 252 : if (!buf) {
642 : : // Fallback when host pointer import is not supported
643 : 245 : buf = pl_buf_create(gpu, pl_buf_params(
644 : : .size = size,
645 : : .host_readable = true,
646 : : ));
647 : : }
648 : :
649 [ - + ]: 252 : if (!buf)
650 : : return false;
651 : :
652 : 252 : struct pl_tex_transfer_params newparams = *params;
653 : 252 : newparams.ptr = NULL;
654 : 252 : newparams.buf = buf;
655 : :
656 : 252 : bool import_handle = buf->params.import_handle;
657 : : // If the transfer is asynchronous, propagate our host read asynchronously
658 [ + + + + ]: 252 : if (params->callback && !import_handle) {
659 : 200 : newparams.callback = pbo_download_cb;
660 : 200 : newparams.priv = pl_alloc_struct(NULL, struct pbo_cb_ctx, {
661 : : .gpu = gpu,
662 : : .buf = buf,
663 : : .ptr = params->ptr,
664 : : .callback = params->callback,
665 : : .priv = params->priv,
666 : : });
667 : : }
668 : :
669 [ - + ]: 252 : if (!pl_tex_download(gpu, &newparams)) {
670 : 0 : pl_buf_destroy(gpu, &buf);
671 : 0 : return false;
672 : : }
673 : :
674 [ + + ]: 252 : if (!params->callback) {
675 [ - + ]: 45 : while (pl_buf_poll(gpu, buf, 10000000)) // 10 ms
676 : 0 : PL_TRACE(gpu, "pl_tex_download: synchronous/blocking (slow path)");
677 : : }
678 : :
679 : : bool ok;
680 [ + + ]: 252 : if (import_handle) {
681 : : // Buffer download completion already means the host pointer contains
682 : : // the valid data, no more need to copy. (Note: this applies even for
683 : : // asynchronous downloads)
684 : : ok = true;
685 : 7 : pl_buf_destroy(gpu, &buf);
686 [ + + ]: 245 : } else if (!params->callback) {
687 : : // Synchronous read back to the host pointer
688 : 45 : ok = pl_buf_read(gpu, buf, 0, params->ptr, size);
689 : 45 : pl_buf_destroy(gpu, &buf);
690 : : } else {
691 : : // Nothing left to do here, the rest will be done by pbo_download_cb
692 : : ok = true;
693 : : }
694 : :
695 : : return ok;
696 : : }
697 : :
698 : 9 : bool pl_tex_upload_texel(pl_gpu gpu, const struct pl_tex_transfer_params *params)
699 : : {
700 : 9 : const int threads = PL_MIN(256, pl_rect_w(params->rc));
701 : 9 : pl_tex tex = params->tex;
702 : 9 : pl_fmt fmt = tex->params.format;
703 [ - + ]: 9 : pl_require(gpu, params->buf);
704 : :
705 : 9 : pl_dispatch dp = pl_gpu_dispatch(gpu);
706 : 9 : pl_shader sh = pl_dispatch_begin(dp);
707 [ - + ]: 9 : if (!sh_try_compute(sh, threads, 1, false, 0)) {
708 : 0 : PL_ERR(gpu, "Failed emulating texture transfer!");
709 : 0 : pl_dispatch_abort(dp, &sh);
710 : 0 : return false;
711 : : }
712 : :
713 : 9 : ident_t buf = sh_desc(sh, (struct pl_shader_desc) {
714 : 9 : .binding.object = params->buf,
715 : : .desc = {
716 : : .name = "data",
717 : : .type = PL_DESC_BUF_TEXEL_STORAGE,
718 : : },
719 : : });
720 : :
721 : 9 : ident_t img = sh_desc(sh, (struct pl_shader_desc) {
722 : 9 : .binding.object = params->tex,
723 : : .desc = {
724 : : .name = "image",
725 : : .type = PL_DESC_STORAGE_IMG,
726 : : .access = PL_DESC_ACCESS_WRITEONLY,
727 : : },
728 : : });
729 : :
730 : : // If the transfer width is a natural multiple of the thread size, we
731 : : // can skip the bounds check. Otherwise, make sure we aren't blitting out
732 : : // of the range since this would read out of bounds.
733 : 9 : int groups_x = PL_DIV_UP(pl_rect_w(params->rc), threads);
734 [ - + ]: 9 : if (groups_x * threads != pl_rect_w(params->rc)) {
735 : 0 : GLSL("if (gl_GlobalInvocationID.x >= %d) \n"
736 : : " return; \n",
737 : : pl_rect_w(params->rc));
738 : : }
739 : :
740 : : // fmt->texel_align contains the size of an individual color value
741 [ - + ]: 9 : assert(fmt->texel_size == fmt->num_components * fmt->texel_align);
742 : 9 : GLSL("vec4 color = vec4(0.0, 0.0, 0.0, 1.0); \n"
743 : : "ivec3 pos = ivec3(gl_GlobalInvocationID); \n"
744 : : "ivec3 tex_pos = pos + ivec3("$", "$", "$"); \n"
745 : : "int base = "$" + pos.z * "$" + pos.y * "$" + pos.x * "$"; \n",
746 : : SH_INT_DYN(params->rc.x0), SH_INT_DYN(params->rc.y0), SH_INT_DYN(params->rc.z0),
747 : : SH_INT_DYN(params->buf_offset),
748 : : SH_INT(params->depth_pitch / fmt->texel_align),
749 : : SH_INT(params->row_pitch / fmt->texel_align),
750 : : SH_INT(fmt->texel_size / fmt->texel_align));
751 : :
752 [ + + ]: 30 : for (int i = 0; i < fmt->num_components; i++)
753 : 21 : GLSL("color[%d] = imageLoad("$", base + %d).r; \n", i, buf, i);
754 : :
755 : : int dims = pl_tex_params_dimension(tex->params);
756 : : static const char *coord_types[] = {
757 : : [1] = "int",
758 : : [2] = "ivec2",
759 : : [3] = "ivec3",
760 : : };
761 : :
762 : 9 : GLSL("imageStore("$", %s(tex_pos), color);\n", img, coord_types[dims]);
763 : 9 : return pl_dispatch_compute(dp, pl_dispatch_compute_params(
764 : : .shader = &sh,
765 : : .dispatch_size = {
766 : : groups_x,
767 : : pl_rect_h(params->rc),
768 : : pl_rect_d(params->rc),
769 : : },
770 : : ));
771 : :
772 : : error:
773 : : return false;
774 : : }
775 : :
776 : 9 : bool pl_tex_download_texel(pl_gpu gpu, const struct pl_tex_transfer_params *params)
777 : : {
778 : 9 : const int threads = PL_MIN(256, pl_rect_w(params->rc));
779 : 9 : pl_tex tex = params->tex;
780 : 9 : pl_fmt fmt = tex->params.format;
781 [ - + ]: 9 : pl_require(gpu, params->buf);
782 : :
783 : 9 : pl_dispatch dp = pl_gpu_dispatch(gpu);
784 : 9 : pl_shader sh = pl_dispatch_begin(dp);
785 [ - + ]: 9 : if (!sh_try_compute(sh, threads, 1, false, 0)) {
786 : 0 : PL_ERR(gpu, "Failed emulating texture transfer!");
787 : 0 : pl_dispatch_abort(dp, &sh);
788 : 0 : return false;
789 : : }
790 : :
791 : 9 : ident_t buf = sh_desc(sh, (struct pl_shader_desc) {
792 : 9 : .binding.object = params->buf,
793 : : .desc = {
794 : : .name = "data",
795 : : .type = PL_DESC_BUF_TEXEL_STORAGE,
796 : : },
797 : : });
798 : :
799 : 9 : ident_t img = sh_desc(sh, (struct pl_shader_desc) {
800 : 9 : .binding.object = params->tex,
801 : : .desc = {
802 : : .name = "image",
803 : : .type = PL_DESC_STORAGE_IMG,
804 : : .access = PL_DESC_ACCESS_READONLY,
805 : : },
806 : : });
807 : :
808 : 9 : int groups_x = PL_DIV_UP(pl_rect_w(params->rc), threads);
809 [ - + ]: 9 : if (groups_x * threads != pl_rect_w(params->rc)) {
810 : 0 : GLSL("if (gl_GlobalInvocationID.x >= %d) \n"
811 : : " return; \n",
812 : : pl_rect_w(params->rc));
813 : : }
814 : :
815 : : int dims = pl_tex_params_dimension(tex->params);
816 : : static const char *coord_types[] = {
817 : : [1] = "int",
818 : : [2] = "ivec2",
819 : : [3] = "ivec3",
820 : : };
821 : :
822 [ - + ]: 9 : assert(fmt->texel_size == fmt->num_components * fmt->texel_align);
823 : 9 : GLSL("ivec3 pos = ivec3(gl_GlobalInvocationID); \n"
824 : : "ivec3 tex_pos = pos + ivec3("$", "$", "$"); \n"
825 : : "int base = "$" + pos.z * "$" + pos.y * "$" + pos.x * "$"; \n"
826 : : "vec4 color = imageLoad("$", %s(tex_pos)); \n",
827 : : SH_INT_DYN(params->rc.x0), SH_INT_DYN(params->rc.y0), SH_INT_DYN(params->rc.z0),
828 : : SH_INT_DYN(params->buf_offset),
829 : : SH_INT(params->depth_pitch / fmt->texel_align),
830 : : SH_INT(params->row_pitch / fmt->texel_align),
831 : : SH_INT(fmt->texel_size / fmt->texel_align),
832 : : img, coord_types[dims]);
833 : :
834 [ + + ]: 30 : for (int i = 0; i < fmt->num_components; i++)
835 : 21 : GLSL("imageStore("$", base + %d, vec4(color[%d])); \n", buf, i, i);
836 : :
837 : 9 : return pl_dispatch_compute(dp, pl_dispatch_compute_params(
838 : : .shader = &sh,
839 : : .dispatch_size = {
840 : : groups_x,
841 : : pl_rect_h(params->rc),
842 : : pl_rect_d(params->rc),
843 : : },
844 : : ));
845 : :
846 : : error:
847 : : return false;
848 : : }
849 : :
850 : 4 : bool pl_tex_blit_compute(pl_gpu gpu, const struct pl_tex_blit_params *params)
851 : : {
852 [ - + ]: 4 : if (!params->dst->params.storable)
853 : : return false;
854 : :
855 : : // Normalize `dst_rc`, moving all flipping to `src_rc` instead.
856 : 4 : pl_rect3d src_rc = params->src_rc;
857 : 4 : pl_rect3d dst_rc = params->dst_rc;
858 [ - + ]: 4 : if (pl_rect_w(dst_rc) < 0) {
859 : : PL_SWAP(src_rc.x0, src_rc.x1);
860 : : PL_SWAP(dst_rc.x0, dst_rc.x1);
861 : : }
862 [ - + ]: 4 : if (pl_rect_h(dst_rc) < 0) {
863 : : PL_SWAP(src_rc.y0, src_rc.y1);
864 : : PL_SWAP(dst_rc.y0, dst_rc.y1);
865 : : }
866 [ - + ]: 4 : if (pl_rect_d(dst_rc) < 0) {
867 : : PL_SWAP(src_rc.z0, src_rc.z1);
868 : : PL_SWAP(dst_rc.z0, dst_rc.z1);
869 : : }
870 : :
871 : : bool needs_scaling = false;
872 : 4 : needs_scaling |= pl_rect_w(dst_rc) != abs(pl_rect_w(src_rc));
873 : 4 : needs_scaling |= pl_rect_h(dst_rc) != abs(pl_rect_h(src_rc));
874 : 4 : needs_scaling |= pl_rect_d(dst_rc) != abs(pl_rect_d(src_rc));
875 : :
876 : : // Exception: fast path for 1-pixel blits, which don't require scaling
877 [ + + - + ]: 4 : bool is_1pixel = abs(pl_rect_w(src_rc)) == 1 && abs(pl_rect_h(src_rc)) == 1;
878 : 4 : needs_scaling &= !is_1pixel;
879 : :
880 : : // Manual trilinear interpolation would be too slow to justify
881 [ - + - - ]: 4 : bool needs_sampling = needs_scaling && params->sample_mode != PL_TEX_SAMPLE_NEAREST;
882 : 4 : needs_sampling |= !params->src->params.storable;
883 [ - + - - ]: 4 : if (needs_sampling && !params->src->params.sampleable)
884 : : return false;
885 : :
886 : : const int threads = 256;
887 : 4 : int bw = PL_MIN(32, pl_rect_w(dst_rc));
888 : 4 : int bh = PL_MIN(threads / bw, pl_rect_h(dst_rc));
889 : 4 : pl_dispatch dp = pl_gpu_dispatch(gpu);
890 : 4 : pl_shader sh = pl_dispatch_begin(dp);
891 [ - + ]: 4 : if (!sh_try_compute(sh, bw, bh, false, 0)) {
892 : 0 : pl_dispatch_abort(dp, &sh);
893 : 0 : return false;
894 : : }
895 : :
896 : : // Avoid over-writing into `dst`
897 : 4 : int groups_x = PL_DIV_UP(pl_rect_w(dst_rc), bw);
898 [ - + ]: 4 : if (groups_x * bw != pl_rect_w(dst_rc)) {
899 : 0 : GLSL("if (gl_GlobalInvocationID.x >= %d) \n"
900 : : " return; \n",
901 : : pl_rect_w(dst_rc));
902 : : }
903 : :
904 : 4 : int groups_y = PL_DIV_UP(pl_rect_h(dst_rc), bh);
905 [ - + ]: 4 : if (groups_y * bh != pl_rect_h(dst_rc)) {
906 : 0 : GLSL("if (gl_GlobalInvocationID.y >= %d) \n"
907 : : " return; \n",
908 : : pl_rect_h(dst_rc));
909 : : }
910 : :
911 : 4 : ident_t dst = sh_desc(sh, (struct pl_shader_desc) {
912 : 4 : .binding.object = params->dst,
913 : : .desc = {
914 : : .name = "dst",
915 : : .type = PL_DESC_STORAGE_IMG,
916 : : .access = PL_DESC_ACCESS_WRITEONLY,
917 : : },
918 : : });
919 : :
920 : : static const char *vecs[] = {
921 : : [1] = "float",
922 : : [2] = "vec2",
923 : : [3] = "vec3",
924 : : [4] = "vec4",
925 : : };
926 : :
927 : : static const char *ivecs[] = {
928 : : [1] = "int",
929 : : [2] = "ivec2",
930 : : [3] = "ivec3",
931 : : [4] = "ivec4",
932 : : };
933 : :
934 [ + - ]: 4 : int src_dims = pl_tex_params_dimension(params->src->params);
935 [ + - ]: 4 : int dst_dims = pl_tex_params_dimension(params->dst->params);
936 : 4 : GLSL("ivec3 pos = ivec3(gl_GlobalInvocationID); \n"
937 : : "%s dst_pos = %s(pos + ivec3(%d, %d, %d)); \n",
938 : : ivecs[dst_dims], ivecs[dst_dims],
939 : : params->dst_rc.x0, params->dst_rc.y0, params->dst_rc.z0);
940 : :
941 [ + - - + : 4 : if (needs_sampling || (needs_scaling && params->src->params.sampleable)) {
- - ]
942 : :
943 : 0 : ident_t src = sh_desc(sh, (struct pl_shader_desc) {
944 : : .desc = {
945 : : .name = "src",
946 : : .type = PL_DESC_SAMPLED_TEX,
947 : : },
948 : : .binding = {
949 : 0 : .object = params->src,
950 : : .address_mode = PL_TEX_ADDRESS_CLAMP,
951 : 0 : .sample_mode = params->sample_mode,
952 : : }
953 : : });
954 : :
955 [ # # ]: 0 : if (is_1pixel) {
956 : 0 : GLSL("%s fpos = %s(0.5); \n", vecs[src_dims], vecs[src_dims]);
957 : : } else {
958 : 0 : GLSL("vec3 fpos = (vec3(pos) + vec3(0.5)) / vec3(%d.0, %d.0, %d.0); \n",
959 : : pl_rect_w(dst_rc), pl_rect_h(dst_rc), pl_rect_d(dst_rc));
960 : : }
961 : :
962 : 0 : GLSL("%s src_pos = %s(0.5); \n"
963 : : "src_pos.x = mix(%f, %f, fpos.x); \n",
964 : : vecs[src_dims], vecs[src_dims],
965 : : (float) src_rc.x0 / params->src->params.w,
966 : : (float) src_rc.x1 / params->src->params.w);
967 : :
968 [ # # ]: 0 : if (params->src->params.h) {
969 : 0 : GLSL("src_pos.y = mix(%f, %f, fpos.y); \n",
970 : : (float) src_rc.y0 / params->src->params.h,
971 : : (float) src_rc.y1 / params->src->params.h);
972 : : }
973 : :
974 [ # # ]: 0 : if (params->src->params.d) {
975 : 0 : GLSL("src_pos.z = mix(%f, %f, fpos.z); \n",
976 : : (float) src_rc.z0 / params->src->params.d,
977 : : (float) src_rc.z1 / params->src->params.d);
978 : : }
979 : :
980 : 0 : GLSL("imageStore("$", dst_pos, textureLod("$", src_pos, 0.0)); \n",
981 : : dst, src);
982 : :
983 : : } else {
984 : :
985 : 4 : ident_t src = sh_desc(sh, (struct pl_shader_desc) {
986 : 4 : .binding.object = params->src,
987 : : .desc = {
988 : : .name = "src",
989 : : .type = PL_DESC_STORAGE_IMG,
990 : : .access = PL_DESC_ACCESS_READONLY,
991 : : },
992 : : });
993 : :
994 [ + + ]: 4 : if (is_1pixel) {
995 : 2 : GLSL("ivec3 src_pos = ivec3(0); \n");
996 [ - + ]: 2 : } else if (needs_scaling) {
997 : 0 : GLSL("ivec3 src_pos = ivec3(vec3(%f, %f, %f) * vec3(pos)); \n",
998 : : fabs((float) pl_rect_w(src_rc) / pl_rect_w(dst_rc)),
999 : : fabs((float) pl_rect_h(src_rc) / pl_rect_h(dst_rc)),
1000 : : fabs((float) pl_rect_d(src_rc) / pl_rect_d(dst_rc)));
1001 : : } else {
1002 : 2 : GLSL("ivec3 src_pos = pos; \n");
1003 : : }
1004 : :
1005 [ + - + - : 16 : GLSL("src_pos = ivec3(%d, %d, %d) * src_pos + ivec3(%d, %d, %d); \n"
+ - ]
1006 : : "imageStore("$", dst_pos, imageLoad("$", %s(src_pos))); \n",
1007 : : src_rc.x1 < src_rc.x0 ? -1 : 1,
1008 : : src_rc.y1 < src_rc.y0 ? -1 : 1,
1009 : : src_rc.z1 < src_rc.z0 ? -1 : 1,
1010 : : src_rc.x0, src_rc.y0, src_rc.z0,
1011 : : dst, src, ivecs[src_dims]);
1012 : :
1013 : : }
1014 : :
1015 : 4 : return pl_dispatch_compute(dp, pl_dispatch_compute_params(
1016 : : .shader = &sh,
1017 : : .dispatch_size = {
1018 : : groups_x,
1019 : : groups_y,
1020 : : pl_rect_d(dst_rc),
1021 : : },
1022 : : ));
1023 : : }
1024 : :
1025 : 0 : void pl_tex_blit_raster(pl_gpu gpu, const struct pl_tex_blit_params *params)
1026 : : {
1027 : 0 : enum pl_fmt_type src_type = params->src->params.format->type;
1028 : 0 : enum pl_fmt_type dst_type = params->dst->params.format->type;
1029 : :
1030 : : // Only for 2D textures
1031 [ # # # # ]: 0 : pl_assert(params->src->params.h && !params->src->params.d);
1032 [ # # # # ]: 0 : pl_assert(params->dst->params.h && !params->dst->params.d);
1033 : :
1034 : : // Integer textures are not supported
1035 [ # # ]: 0 : pl_assert(src_type != PL_FMT_UINT && src_type != PL_FMT_SINT);
1036 [ # # ]: 0 : pl_assert(dst_type != PL_FMT_UINT && dst_type != PL_FMT_SINT);
1037 : :
1038 : 0 : pl_rect2df src_rc = {
1039 : 0 : .x0 = params->src_rc.x0, .x1 = params->src_rc.x1,
1040 : 0 : .y0 = params->src_rc.y0, .y1 = params->src_rc.y1,
1041 : : };
1042 : : pl_rect2d dst_rc = {
1043 : 0 : .x0 = params->dst_rc.x0, .x1 = params->dst_rc.x1,
1044 : 0 : .y0 = params->dst_rc.y0, .y1 = params->dst_rc.y1,
1045 : : };
1046 : :
1047 : 0 : pl_dispatch dp = pl_gpu_dispatch(gpu);
1048 : 0 : pl_shader sh = pl_dispatch_begin(dp);
1049 : 0 : sh->output = PL_SHADER_SIG_COLOR;
1050 : :
1051 : 0 : ident_t pos, src = sh_bind(sh, params->src, PL_TEX_ADDRESS_CLAMP,
1052 : 0 : params->sample_mode, "src_tex", &src_rc, &pos, NULL);
1053 : :
1054 : 0 : GLSL("vec4 color = textureLod("$", "$", 0.0); \n", src, pos);
1055 : :
1056 : 0 : pl_dispatch_finish(dp, pl_dispatch_params(
1057 : : .shader = &sh,
1058 : : .target = params->dst,
1059 : : .rect = dst_rc,
1060 : : ));
1061 : 0 : }
1062 : :
1063 : 4 : bool pl_buf_copy_swap(pl_gpu gpu, const struct pl_buf_copy_swap_params *params)
1064 : : {
1065 : 4 : pl_buf src = params->src, dst = params->dst;
1066 [ + - - + ]: 4 : pl_require(gpu, src->params.storable && dst->params.storable);
1067 [ - + ]: 4 : pl_require(gpu, params->src_offset % sizeof(unsigned) == 0);
1068 [ - + ]: 4 : pl_require(gpu, params->dst_offset % sizeof(unsigned) == 0);
1069 [ - + ]: 4 : pl_require(gpu, params->src_offset + params->size <= src->params.size);
1070 [ - + ]: 4 : pl_require(gpu, params->dst_offset + params->size <= dst->params.size);
1071 [ + + - + ]: 4 : pl_require(gpu, src != dst || params->src_offset == params->dst_offset);
1072 [ - + ]: 4 : pl_require(gpu, params->size % sizeof(unsigned) == 0);
1073 [ - + ]: 4 : pl_require(gpu, params->wordsize == sizeof(uint16_t) ||
1074 : : params->wordsize == sizeof(uint32_t));
1075 : :
1076 : 4 : const size_t words = params->size / sizeof(unsigned);
1077 : 4 : const size_t src_off = params->src_offset / sizeof(unsigned);
1078 : 4 : const size_t dst_off = params->dst_offset / sizeof(unsigned);
1079 : :
1080 : 4 : const int threads = PL_MIN(256, words);
1081 : 4 : pl_dispatch dp = pl_gpu_dispatch(gpu);
1082 : 4 : pl_shader sh = pl_dispatch_begin(dp);
1083 [ - + ]: 4 : if (!sh_try_compute(sh, threads, 1, false, 0)) {
1084 : 0 : pl_dispatch_abort(dp, &sh);
1085 : 0 : return false;
1086 : : }
1087 : :
1088 : 4 : const size_t groups = PL_DIV_UP(words, threads);
1089 [ - + ]: 4 : if (groups * threads > words) {
1090 : 0 : GLSL("if (gl_GlobalInvocationID.x >= %zu) \n"
1091 : : " return; \n",
1092 : : words);
1093 : : }
1094 : :
1095 : 4 : sh_desc(sh, (struct pl_shader_desc) {
1096 : : .binding.object = src,
1097 : : .desc = {
1098 : : .name = "SrcBuf",
1099 : : .type = PL_DESC_BUF_STORAGE,
1100 : 4 : .access = src == dst ? PL_DESC_ACCESS_READWRITE : PL_DESC_ACCESS_READONLY,
1101 : : },
1102 : : .num_buffer_vars = 1,
1103 : 4 : .buffer_vars = &(struct pl_buffer_var) {
1104 : : .var = {
1105 : : .name = "src",
1106 : : .type = PL_VAR_UINT,
1107 : : .dim_v = 1,
1108 : : .dim_m = 1,
1109 : 4 : .dim_a = src_off + words,
1110 : : },
1111 : : },
1112 : : });
1113 : :
1114 [ + + ]: 4 : if (src != dst) {
1115 : 2 : sh_desc(sh, (struct pl_shader_desc) {
1116 : : .binding.object = dst,
1117 : : .desc = {
1118 : : .name = "DstBuf",
1119 : : .type = PL_DESC_BUF_STORAGE,
1120 : : .access = PL_DESC_ACCESS_WRITEONLY,
1121 : : },
1122 : : .num_buffer_vars = 1,
1123 : 2 : .buffer_vars = &(struct pl_buffer_var) {
1124 : : .var = {
1125 : : .name = "dst",
1126 : : .type = PL_VAR_UINT,
1127 : : .dim_v = 1,
1128 : : .dim_m = 1,
1129 : 2 : .dim_a = dst_off + words,
1130 : : },
1131 : : },
1132 : : });
1133 : : } else {
1134 : 2 : GLSL("#define dst src \n");
1135 : : }
1136 : :
1137 : 4 : GLSL("// pl_buf_copy_swap \n"
1138 : : "{ \n"
1139 : : "uint word = src["$" + gl_GlobalInvocationID.x]; \n"
1140 : : "word = (word & 0xFF00FF00u) >> 8 | \n"
1141 : : " (word & 0x00FF00FFu) << 8; \n",
1142 : : SH_UINT(src_off));
1143 [ + + ]: 4 : if (params->wordsize > 2) {
1144 : 2 : GLSL("word = (word & 0xFFFF0000u) >> 16 | \n"
1145 : : " (word & 0x0000FFFFu) << 16; \n");
1146 : : }
1147 : 4 : GLSL("dst["$" + gl_GlobalInvocationID.x] = word; \n"
1148 : : "} \n",
1149 : : SH_UINT(dst_off));
1150 : :
1151 : 4 : return pl_dispatch_compute(dp, pl_dispatch_compute_params(
1152 : : .shader = &sh,
1153 : : .dispatch_size = {groups, 1, 1},
1154 : : ));
1155 : :
1156 : : error:
1157 : : if (src->params.debug_tag || dst->params.debug_tag) {
1158 : : PL_ERR(gpu, " for buffers: src %s, dst %s",
1159 : : src->params.debug_tag, dst->params.debug_tag);
1160 : : }
1161 : : return false;
1162 : : }
1163 : :
1164 : 469 : void pl_pass_run_vbo(pl_gpu gpu, const struct pl_pass_run_params *params)
1165 : : {
1166 [ - + - - ]: 469 : if (!params->vertex_data && !params->index_data)
1167 : 0 : return pl_pass_run(gpu, params);
1168 : :
1169 : 469 : struct pl_pass_run_params newparams = *params;
1170 : 469 : pl_buf vert = NULL, index = NULL;
1171 : :
1172 [ + - ]: 469 : if (params->vertex_data) {
1173 : 469 : vert = pl_buf_create(gpu, pl_buf_params(
1174 : : .size = pl_vertex_buf_size(params),
1175 : : .initial_data = params->vertex_data,
1176 : : .drawable = true,
1177 : : ));
1178 : :
1179 [ - + ]: 469 : if (!vert) {
1180 : 0 : PL_ERR(gpu, "Failed allocating vertex buffer!");
1181 : 0 : return;
1182 : : }
1183 : :
1184 : 469 : newparams.vertex_buf = vert;
1185 : 469 : newparams.vertex_data = NULL;
1186 : : }
1187 : :
1188 [ + + ]: 469 : if (params->index_data) {
1189 : 5 : index = pl_buf_create(gpu, pl_buf_params(
1190 : : .size = pl_index_buf_size(params),
1191 : : .initial_data = params->index_data,
1192 : : .drawable = true,
1193 : : ));
1194 : :
1195 [ - + ]: 5 : if (!index) {
1196 : 0 : PL_ERR(gpu, "Failed allocating index buffer!");
1197 : 0 : return;
1198 : : }
1199 : :
1200 : 5 : newparams.index_buf = index;
1201 : 5 : newparams.index_data = NULL;
1202 : : }
1203 : :
1204 : 469 : pl_pass_run(gpu, &newparams);
1205 : 469 : pl_buf_destroy(gpu, &vert);
1206 : 469 : pl_buf_destroy(gpu, &index);
1207 : : }
1208 : :
1209 : 492 : struct pl_pass_params pl_pass_params_copy(void *alloc, const struct pl_pass_params *params)
1210 : : {
1211 : 492 : struct pl_pass_params new = *params;
1212 : :
1213 : 492 : new.glsl_shader = pl_str0dup0(alloc, new.glsl_shader);
1214 : 492 : new.vertex_shader = pl_str0dup0(alloc, new.vertex_shader);
1215 [ + + ]: 492 : if (new.blend_params)
1216 : 8 : new.blend_params = pl_memdup_ptr(alloc, new.blend_params);
1217 : :
1218 : : #define DUPNAMES(field) \
1219 : : do { \
1220 : : size_t _size = new.num_##field * sizeof(new.field[0]); \
1221 : : new.field = pl_memdup(alloc, new.field, _size); \
1222 : : for (int j = 0; j < new.num_##field; j++) \
1223 : : new.field[j].name = pl_str0dup0(alloc, new.field[j].name); \
1224 : : } while (0)
1225 : :
1226 [ + + ]: 1018 : DUPNAMES(variables);
1227 [ + + ]: 1553 : DUPNAMES(descriptors);
1228 [ + + ]: 1328 : DUPNAMES(vertex_attribs);
1229 : :
1230 : : #undef DUPNAMES
1231 : :
1232 : : new.constant_data = NULL;
1233 : 492 : new.constants = pl_memdup(alloc, new.constants,
1234 : 492 : new.num_constants * sizeof(new.constants[0]));
1235 : :
1236 : 492 : return new;
1237 : : }
1238 : :
1239 : 1835 : size_t pl_vertex_buf_size(const struct pl_pass_run_params *params)
1240 : : {
1241 [ + + ]: 1835 : if (!params->index_data)
1242 : 1817 : return params->vertex_count * params->pass->params.vertex_stride;
1243 : :
1244 : : int num_vertices = 0;
1245 : : const void *idx = params->index_data;
1246 [ + - - - ]: 18 : switch (params->index_fmt) {
1247 : : case PL_INDEX_UINT16:
1248 [ + + ]: 170 : for (int i = 0; i < params->vertex_count; i++)
1249 : 152 : num_vertices = PL_MAX(num_vertices, ((const uint16_t *) idx)[i]);
1250 : : break;
1251 : : case PL_INDEX_UINT32:
1252 [ # # ]: 0 : for (int i = 0; i < params->vertex_count; i++)
1253 : 0 : num_vertices = PL_MAX(num_vertices, ((const uint32_t *) idx)[i]);
1254 : : break;
1255 : 0 : case PL_INDEX_FORMAT_COUNT: pl_unreachable();
1256 : : }
1257 : :
1258 : 18 : return (num_vertices + 1) * params->pass->params.vertex_stride;
1259 : : }
1260 : :
1261 : 14 : const char *print_uuid(char buf[3 * UUID_SIZE], const uint8_t uuid[UUID_SIZE])
1262 : : {
1263 : : static const char *hexdigits = "0123456789ABCDEF";
1264 [ + + ]: 238 : for (int i = 0; i < UUID_SIZE; i++) {
1265 : 224 : uint8_t x = uuid[i];
1266 : 224 : buf[3 * i + 0] = hexdigits[x >> 4];
1267 : 224 : buf[3 * i + 1] = hexdigits[x & 0xF];
1268 [ + + ]: 434 : buf[3 * i + 2] = i == UUID_SIZE - 1 ? '\0' : ':';
1269 : : }
1270 : :
1271 : 14 : return buf;
1272 : : }
1273 : :
1274 : 337 : const char *print_drm_mod(char buf[DRM_MOD_SIZE], uint64_t mod)
1275 : : {
1276 [ + + + ]: 337 : switch (mod) {
1277 : : case DRM_FORMAT_MOD_LINEAR: return "LINEAR";
1278 : 29 : case DRM_FORMAT_MOD_INVALID: return "INVALID";
1279 : : }
1280 : :
1281 : 204 : uint8_t vendor = mod >> 56;
1282 : 204 : uint64_t val = mod & ((1ULL << 56) - 1);
1283 : :
1284 : : const char *name = NULL;
1285 [ - - - - : 204 : switch (vendor) {
+ - - ]
1286 : : case 0x00: name = "NONE"; break;
1287 : : case 0x01: name = "INTEL"; break;
1288 : : case 0x02: name = "AMD"; break;
1289 : : case 0x03: name = "NVIDIA"; break;
1290 : : case 0x04: name = "SAMSUNG"; break;
1291 : : case 0x08: name = "ARM"; break;
1292 : : }
1293 : :
1294 : : if (name) {
1295 : : snprintf(buf, DRM_MOD_SIZE, "%s 0x%"PRIx64, name, val);
1296 : : } else {
1297 : 0 : snprintf(buf, DRM_MOD_SIZE, "0x%02x 0x%"PRIx64, vendor, val);
1298 : : }
1299 : :
1300 : : return buf;
1301 : : }
|