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 "gpu.h"
19 : : #include "formats.h"
20 : : #include "utils.h"
21 : :
22 : : #ifdef PL_HAVE_UNIX
23 : : #include <unistd.h>
24 : : #include <errno.h>
25 : : #endif
26 : :
27 : 974 : void gl_tex_destroy(pl_gpu gpu, pl_tex tex)
28 : : {
29 : : const gl_funcs *gl = gl_funcs_get(gpu);
30 : : if (!MAKE_CURRENT()) {
31 : 0 : PL_ERR(gpu, "Failed uninitializing texture, leaking resources!");
32 : 0 : return;
33 : : }
34 : :
35 : 974 : struct pl_tex_gl *tex_gl = PL_PRIV(tex);
36 [ + + + - ]: 974 : if (tex_gl->fbo && !tex_gl->wrapped_fb)
37 : 455 : gl->DeleteFramebuffers(1, &tex_gl->fbo);
38 [ + + ]: 974 : if (tex_gl->image) {
39 : : struct pl_gl *p = PL_PRIV(gpu);
40 : 8 : eglDestroyImageKHR(p->egl_dpy, tex_gl->image);
41 : : }
42 [ + + ]: 974 : if (!tex_gl->wrapped_tex)
43 : 970 : gl->DeleteTextures(1, &tex_gl->texture);
44 : :
45 : : #ifdef PL_HAVE_UNIX
46 [ + + ]: 974 : if (tex_gl->fd != -1)
47 : 8 : close(tex_gl->fd);
48 : : #endif
49 : :
50 : 974 : gl_check_err(gpu, "gl_tex_destroy");
51 : : RELEASE_CURRENT();
52 : 974 : pl_free((void *) tex);
53 : : }
54 : :
55 : : static GLbitfield tex_barrier(pl_tex tex)
56 : : {
57 : : GLbitfield barrier = 0;
58 : : const struct pl_tex_params *params = &tex->params;
59 : :
60 [ + + ]: 8 : if (params->sampleable)
61 : : barrier |= GL_TEXTURE_FETCH_BARRIER_BIT;
62 [ + - + + ]: 974 : if (params->renderable || params->blit_src || params->blit_dst)
63 : 297 : barrier |= GL_FRAMEBUFFER_BARRIER_BIT;
64 [ + + + + ]: 974 : if (params->storable)
65 : 22 : barrier |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
66 [ + - + + ]: 974 : if (params->host_writable || params->host_readable)
67 : 658 : barrier |= GL_TEXTURE_UPDATE_BARRIER_BIT;
68 : :
69 : : return barrier;
70 : : }
71 : :
72 : : #define ADD_ATTRIB(name, value) \
73 : : do { \
74 : : assert(num_attribs + 3 < PL_ARRAY_SIZE(attribs)); \
75 : : attribs[num_attribs++] = (name); \
76 : : attribs[num_attribs++] = (value); \
77 : : } while (0)
78 : :
79 : : #define ADD_DMABUF_PLANE_ATTRIBS(plane, fd, offset, stride) \
80 : : do { \
81 : : ADD_ATTRIB(EGL_DMA_BUF_PLANE ## plane ## _FD_EXT, \
82 : : fd); \
83 : : ADD_ATTRIB(EGL_DMA_BUF_PLANE ## plane ## _OFFSET_EXT, \
84 : : offset); \
85 : : ADD_ATTRIB(EGL_DMA_BUF_PLANE ## plane ## _PITCH_EXT, \
86 : : stride); \
87 : : } while (0)
88 : :
89 : : #define ADD_DMABUF_PLANE_MODIFIERS(plane, mod) \
90 : : do { \
91 : : ADD_ATTRIB(EGL_DMA_BUF_PLANE ## plane ## _MODIFIER_LO_EXT, \
92 : : (uint32_t) ((mod) & 0xFFFFFFFFlu)); \
93 : : ADD_ATTRIB(EGL_DMA_BUF_PLANE ## plane ## _MODIFIER_HI_EXT, \
94 : : (uint32_t) (((mod) >> 32u) & 0xFFFFFFFFlu)); \
95 : : } while (0)
96 : :
97 : 4 : static bool gl_tex_import(pl_gpu gpu,
98 : : enum pl_handle_type handle_type,
99 : : const struct pl_shared_mem *shared_mem,
100 : : struct pl_tex_t *tex)
101 : : {
102 : : const gl_funcs *gl = gl_funcs_get(gpu);
103 : : struct pl_gl *p = PL_PRIV(gpu);
104 : : if (!MAKE_CURRENT())
105 : 0 : return false;
106 : :
107 : 4 : struct pl_tex_gl *tex_gl = PL_PRIV(tex);
108 : : const struct pl_tex_params *params = &tex->params;
109 : :
110 : 4 : int attribs[20] = {};
111 : : int num_attribs = 0;
112 : 4 : ADD_ATTRIB(EGL_WIDTH, params->w);
113 : 4 : ADD_ATTRIB(EGL_HEIGHT, params->h);
114 : :
115 [ + - - ]: 4 : switch (handle_type) {
116 : :
117 : : #ifdef PL_HAVE_UNIX
118 : 4 : case PL_HANDLE_DMA_BUF:
119 [ - + ]: 4 : if (shared_mem->handle.fd == -1) {
120 : 0 : PL_ERR(gpu, "%s: invalid fd", __func__);
121 : 0 : goto error;
122 : : }
123 : :
124 : 4 : tex_gl->fd = dup(shared_mem->handle.fd);
125 [ - + ]: 4 : if (tex_gl->fd == -1) {
126 : 0 : PL_ERR(gpu, "%s: cannot duplicate fd %d for importing: %s",
127 : : __func__, shared_mem->handle.fd, strerror(errno));
128 : 0 : goto error;
129 : : }
130 : :
131 : 4 : ADD_ATTRIB(EGL_LINUX_DRM_FOURCC_EXT, params->format->fourcc);
132 [ + - ]: 4 : ADD_DMABUF_PLANE_ATTRIBS(0, tex_gl->fd, shared_mem->offset,
133 : : PL_DEF(shared_mem->stride_w, params->w));
134 [ + - ]: 4 : if (p->has_modifiers)
135 : 4 : ADD_DMABUF_PLANE_MODIFIERS(0, shared_mem->drm_format_mod);
136 : :
137 : 4 : attribs[num_attribs] = EGL_NONE;
138 : :
139 : : // EGL_LINUX_DMA_BUF_EXT requires EGL_NO_CONTEXT
140 : 4 : tex_gl->image = eglCreateImageKHR(p->egl_dpy,
141 : : EGL_NO_CONTEXT,
142 : : EGL_LINUX_DMA_BUF_EXT,
143 : : (EGLClientBuffer) NULL,
144 : : attribs);
145 : :
146 : 4 : break;
147 : : #else // !PL_HAVE_UNIX
148 : : case PL_HANDLE_DMA_BUF:
149 : : pl_unreachable();
150 : : #endif
151 : :
152 : : case PL_HANDLE_WIN32:
153 : : case PL_HANDLE_WIN32_KMT:
154 : : case PL_HANDLE_HOST_PTR:
155 : : case PL_HANDLE_FD:
156 : : case PL_HANDLE_MTL_TEX:
157 : : case PL_HANDLE_IOSURFACE:
158 : 0 : pl_unreachable();
159 : :
160 : : }
161 : :
162 [ + - - + ]: 4 : if (!egl_check_err(gpu, "eglCreateImageKHR") || !tex_gl->image)
163 : 0 : goto error;
164 : :
165 : : // tex_gl->image should be already bound
166 [ + - ]: 4 : if (p->has_egl_storage) {
167 : 4 : gl->EGLImageTargetTexStorageEXT(GL_TEXTURE_2D, tex_gl->image, NULL);
168 : : } else {
169 : 0 : gl->EGLImageTargetTexture2DOES(GL_TEXTURE_2D, tex_gl->image);
170 : : }
171 [ - + ]: 4 : if (!egl_check_err(gpu, "EGLImageTargetTexture2DOES"))
172 : 0 : goto error;
173 : :
174 : : RELEASE_CURRENT();
175 : 4 : return true;
176 : :
177 : 0 : error:
178 : 0 : PL_ERR(gpu, "Failed importing GL texture!");
179 : : RELEASE_CURRENT();
180 : 0 : return false;
181 : : }
182 : :
183 : 4 : static EGLenum egl_from_gl_target(pl_gpu gpu, int target)
184 : : {
185 [ - - + ]: 4 : switch(target) {
186 : : case GL_TEXTURE_2D: return EGL_GL_TEXTURE_2D;
187 : 0 : case GL_TEXTURE_3D: return EGL_GL_TEXTURE_3D;
188 : 0 : default:
189 : 0 : PL_ERR(gpu, "%s: unsupported texture target 0x%x", __func__, target);
190 : 0 : return 0;
191 : : }
192 : : }
193 : :
194 : 4 : static bool gl_tex_export(pl_gpu gpu, enum pl_handle_type handle_type,
195 : : bool preserved, struct pl_tex_t *tex)
196 : : {
197 : 4 : struct pl_tex_gl *tex_gl = PL_PRIV(tex);
198 : 4 : struct pl_gl *p = PL_PRIV(gpu);
199 : :
200 : 4 : EGLenum egltarget = egl_from_gl_target(gpu, tex_gl->target);
201 [ - + ]: 4 : if (!egltarget)
202 : 0 : goto error;
203 : :
204 : 4 : int attribs[] = {
205 : : EGL_IMAGE_PRESERVED, preserved,
206 : : EGL_NONE,
207 : : };
208 : :
209 : : // We assume that tex_gl->texture is already bound
210 : 8 : tex_gl->image = eglCreateImageKHR(p->egl_dpy,
211 : : p->egl_ctx,
212 : : egltarget,
213 : 4 : (EGLClientBuffer) (uintptr_t) tex_gl->texture,
214 : : attribs);
215 [ + - - + ]: 4 : if (!egl_check_err(gpu, "eglCreateImageKHR") || !tex_gl->image)
216 : 0 : goto error;
217 : :
218 [ + - - ]: 4 : switch (handle_type) {
219 : :
220 : : #ifdef PL_HAVE_UNIX
221 : 4 : case PL_HANDLE_DMA_BUF: {
222 : 4 : int fourcc = 0;
223 : 4 : int num_planes = 0;
224 : 4 : EGLuint64KHR modifier = 0;
225 : : bool ok;
226 : 4 : ok = eglExportDMABUFImageQueryMESA(p->egl_dpy,
227 : : tex_gl->image,
228 : : &fourcc,
229 : : &num_planes,
230 : : &modifier);
231 [ + - - + ]: 4 : if (!egl_check_err(gpu, "eglExportDMABUFImageQueryMESA") || !ok)
232 : 0 : goto error;
233 : :
234 [ - + ]: 4 : if (fourcc != tex->params.format->fourcc) {
235 [ # # # # ]: 0 : PL_ERR(gpu, "Exported DRM format %s does not match fourcc of "
236 : : "specified pl_fmt %s? Please open a bug.",
237 : : PRINT_FOURCC(fourcc), PRINT_FOURCC(tex->params.format->fourcc));
238 : 0 : goto error;
239 : : }
240 : :
241 [ - + ]: 4 : if (num_planes != 1) {
242 : 0 : PL_ERR(gpu, "Unsupported number of planes: %d", num_planes);
243 : 0 : goto error;
244 : : }
245 : :
246 : 4 : int offset = 0, stride = 0;
247 : 4 : ok = eglExportDMABUFImageMESA(p->egl_dpy,
248 : : tex_gl->image,
249 : : &tex_gl->fd,
250 : : &stride,
251 : : &offset);
252 [ + - - + ]: 4 : if (!egl_check_err(gpu, "eglExportDMABUFImageMesa") || !ok)
253 : 0 : goto error;
254 : :
255 : 4 : off_t fdsize = lseek(tex_gl->fd, 0, SEEK_END);
256 [ + - ]: 4 : off_t err = fdsize > 0 && lseek(tex_gl->fd, 0, SEEK_SET);
257 [ - + ]: 4 : if (fdsize <= 0 || err < 0) {
258 : 0 : PL_ERR(gpu, "Failed querying FD size: %s", strerror(errno));
259 : 0 : goto error;
260 : : }
261 : :
262 : 4 : tex->shared_mem = (struct pl_shared_mem) {
263 : 4 : .handle.fd = tex_gl->fd,
264 : : .size = fdsize,
265 : : .offset = offset,
266 : : .drm_format_mod = modifier,
267 : : .stride_w = stride,
268 : : };
269 : 4 : break;
270 : : }
271 : : #else // !PL_HAVE_UNIX
272 : : case PL_HANDLE_DMA_BUF:
273 : : pl_unreachable();
274 : : #endif
275 : :
276 : : case PL_HANDLE_WIN32:
277 : : case PL_HANDLE_WIN32_KMT:
278 : : case PL_HANDLE_HOST_PTR:
279 : : case PL_HANDLE_FD:
280 : : case PL_HANDLE_MTL_TEX:
281 : : case PL_HANDLE_IOSURFACE:
282 : 0 : pl_unreachable();
283 : :
284 : : }
285 : :
286 : : return true;
287 : :
288 : 0 : error:
289 : 0 : PL_ERR(gpu, "Failed exporting GL texture!");
290 : 0 : return false;
291 : : }
292 : :
293 : 0 : static const char *fb_err_str(GLenum err)
294 : : {
295 [ # # # # : 0 : switch (err) {
# # # # #
# # ]
296 : : #define CASE(name) case name: return #name
297 : : CASE(GL_FRAMEBUFFER_COMPLETE);
298 : 0 : CASE(GL_FRAMEBUFFER_UNDEFINED);
299 : 0 : CASE(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
300 : 0 : CASE(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
301 : 0 : CASE(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
302 : 0 : CASE(GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER);
303 : 0 : CASE(GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER);
304 : 0 : CASE(GL_FRAMEBUFFER_UNSUPPORTED);
305 : 0 : CASE(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE);
306 : 0 : CASE(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS);
307 : : #undef CASE
308 : :
309 : 0 : default: return "unknown error";
310 : : }
311 : : }
312 : :
313 : 966 : pl_tex gl_tex_create(pl_gpu gpu, const struct pl_tex_params *params)
314 : : {
315 : : const gl_funcs *gl = gl_funcs_get(gpu);
316 : : if (!MAKE_CURRENT())
317 : 0 : return NULL;
318 : :
319 : : struct pl_gl *p = PL_PRIV(gpu);
320 : 966 : struct pl_tex_t *tex = pl_zalloc_obj(NULL, tex, struct pl_tex_gl);
321 : 966 : tex->params = *params;
322 : 966 : tex->params.initial_data = NULL;
323 : 966 : tex->sampler_type = PL_SAMPLER_NORMAL;
324 : :
325 : 966 : struct pl_tex_gl *tex_gl = PL_PRIV(tex);
326 : :
327 : 966 : const struct gl_format **fmtp = PL_PRIV(params->format);
328 [ + + ]: 966 : const struct gl_format *fmt = *fmtp;
329 : 966 : *tex_gl = (struct pl_tex_gl) {
330 : 966 : .format = fmt->fmt,
331 : 966 : .iformat = fmt->ifmt,
332 [ + + ]: 966 : .type = fmt->type,
333 : : .barrier = tex_barrier(tex),
334 : : .fd = -1,
335 : : };
336 : :
337 : : static const GLint targets[] = {
338 : : [1] = GL_TEXTURE_1D,
339 : : [2] = GL_TEXTURE_2D,
340 : : [3] = GL_TEXTURE_3D,
341 : : };
342 : :
343 : : int dims = pl_tex_params_dimension(*params);
344 : : pl_assert(dims >= 1 && dims <= 3);
345 : 966 : tex_gl->target = targets[dims];
346 : :
347 : 966 : gl->GenTextures(1, &tex_gl->texture);
348 : 966 : gl->BindTexture(tex_gl->target, tex_gl->texture);
349 : :
350 [ + + ]: 966 : if (params->import_handle) {
351 [ - + ]: 4 : if (!gl_tex_import(gpu, params->import_handle, ¶ms->shared_mem, tex))
352 : 0 : goto error;
353 : : } else {
354 : 962 : gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1);
355 : :
356 [ + + + ]: 962 : switch (dims) {
357 : 253 : case 1:
358 : 253 : gl->TexImage1D(tex_gl->target, 0, tex_gl->iformat, params->w, 0,
359 : 253 : tex_gl->format, tex_gl->type, params->initial_data);
360 : 253 : break;
361 : 468 : case 2:
362 : 468 : gl->TexImage2D(tex_gl->target, 0, tex_gl->iformat, params->w, params->h,
363 : 468 : 0, tex_gl->format, tex_gl->type, params->initial_data);
364 : 468 : break;
365 : 241 : case 3:
366 : 241 : gl->TexImage3D(tex_gl->target, 0, tex_gl->iformat, params->w, params->h,
367 : 241 : params->d, 0, tex_gl->format, tex_gl->type,
368 : 241 : params->initial_data);
369 : 241 : break;
370 : : }
371 : :
372 : 962 : gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
373 : : }
374 : :
375 [ + + ]: 966 : if (params->export_handle) {
376 [ - + ]: 4 : if (!gl_tex_export(gpu, params->export_handle, params->initial_data, tex))
377 : 0 : goto error;
378 : : }
379 : :
380 : 966 : gl->BindTexture(tex_gl->target, 0);
381 : :
382 [ - + ]: 966 : if (!gl_check_err(gpu, "gl_tex_create: texture"))
383 : 0 : goto error;
384 : :
385 : 966 : bool need_fbo = tex->params.renderable;
386 [ + + ]: 966 : if (tex->params.blit_src || tex->params.blit_dst) {
387 [ - + ]: 285 : if (dims != 2) {
388 : 0 : PL_ERR(gpu, "Blittable textures may only be 2D!");
389 : 0 : goto error;
390 : : }
391 : :
392 : : need_fbo = true;
393 : : }
394 : :
395 [ + + ]: 966 : bool can_fbo = tex->params.format->caps & PL_FMT_CAP_RENDERABLE &&
396 [ + + ]: 930 : tex->params.d == 0;
397 : :
398 : : // Try creating an FBO for host-readable textures, since this allows
399 : : // reading back with glReadPixels instead of glGetTexImage. (Additionally,
400 : : // GLES does not support glGetTexImage)
401 [ + + + + : 966 : if (tex->params.host_readable && (can_fbo || p->gles_ver))
+ + ]
402 : : need_fbo = true;
403 : :
404 [ + + ]: 541 : if (need_fbo) {
405 [ + + ]: 491 : if (!can_fbo) {
406 : 40 : PL_ERR(gpu, "Trying to create a renderable/blittable/readable "
407 : : "texture with an incompatible (non-renderable) format!");
408 : 40 : goto error;
409 : : }
410 : :
411 : 451 : const GLenum target = p->gles_ver && p->gles_ver < 30 ?
412 [ + - ]: 451 : GL_FRAMEBUFFER : GL_READ_FRAMEBUFFER;
413 : :
414 : 451 : gl->GenFramebuffers(1, &tex_gl->fbo);
415 : 451 : gl->BindFramebuffer(target, tex_gl->fbo);
416 [ + + - ]: 451 : switch (dims) {
417 : 162 : case 1:
418 : 162 : gl->FramebufferTexture1D(target, GL_COLOR_ATTACHMENT0,
419 : : GL_TEXTURE_1D, tex_gl->texture, 0);
420 : 162 : break;
421 : 289 : case 2:
422 : 289 : gl->FramebufferTexture2D(target, GL_COLOR_ATTACHMENT0,
423 : : GL_TEXTURE_2D, tex_gl->texture, 0);
424 : 289 : break;
425 : 0 : case 3: pl_unreachable();
426 : : }
427 : :
428 : 451 : GLenum err = gl->CheckFramebufferStatus(target);
429 [ - + ]: 451 : if (err != GL_FRAMEBUFFER_COMPLETE) {
430 : 0 : gl->BindFramebuffer(target, 0);
431 : 0 : PL_ERR(gpu, "Failed creating framebuffer: %s", fb_err_str(err));
432 : 0 : goto error;
433 : : }
434 : :
435 [ + + + + ]: 451 : if (params->host_readable && p->gles_ver) {
436 : 45 : GLint read_type = 0, read_fmt = 0;
437 : 45 : gl->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &read_type);
438 : 45 : gl->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &read_fmt);
439 [ + + + + ]: 45 : if (read_type != tex_gl->type || read_fmt != tex_gl->format) {
440 : 12 : gl->BindFramebuffer(target, 0);
441 : 12 : PL_ERR(gpu, "Trying to create host_readable texture whose "
442 : : "implementation-defined pixel read format "
443 : : "(type=0x%X, fmt=0x%X) does not match the texture's "
444 : : "internal format (type=0x%X, fmt=0x%X)! This is a "
445 : : "GLES/driver limitation, there's little we can do "
446 : : "about it.",
447 : : read_type, read_fmt, tex_gl->type, tex_gl->format);
448 : 12 : goto error;
449 : : }
450 : : }
451 : :
452 : 439 : gl->BindFramebuffer(target, 0);
453 [ - + ]: 439 : if (!gl_check_err(gpu, "gl_tex_create: fbo"))
454 : 0 : goto error;
455 : : }
456 : :
457 : : RELEASE_CURRENT();
458 : 914 : return tex;
459 : :
460 : 52 : error:
461 : 52 : gl_tex_destroy(gpu, tex);
462 : : RELEASE_CURRENT();
463 : 52 : return NULL;
464 : : }
465 : :
466 : 4 : static bool gl_fb_query(pl_gpu gpu, int fbo, struct pl_fmt_t *fmt,
467 : : struct gl_format *glfmt)
468 : : {
469 : : const gl_funcs *gl = gl_funcs_get(gpu);
470 : : struct pl_gl *p = PL_PRIV(gpu);
471 : 4 : *fmt = (struct pl_fmt_t) {
472 : : .name = "fbo",
473 : : .type = PL_FMT_UNKNOWN,
474 : : .caps = PL_FMT_CAP_RENDERABLE | PL_FMT_CAP_BLENDABLE,
475 : : .num_components = 4,
476 : : .component_depth = {8, 8, 8, 8}, // default to rgba8
477 : : .sample_order = {0, 1, 2, 3},
478 : : };
479 : :
480 : 4 : *glfmt = (struct gl_format) {
481 : : .fmt = GL_RGBA,
482 : : };
483 : :
484 : 4 : bool can_query = gl_test_ext(gpu, "GL_ARB_framebuffer_object", 30, 20);
485 [ + - + + : 4 : if (!fbo && p->gles_ver && p->gles_ver < 30)
+ - ]
486 : : can_query = false; // can't query default framebuffer on GLES 2.0
487 : :
488 [ + - ]: 4 : if (can_query) {
489 : 4 : gl->BindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
490 : :
491 [ + + ]: 4 : GLenum obj = p->gles_ver ? GL_BACK : GL_BACK_LEFT;
492 [ - + ]: 4 : if (fbo != 0)
493 : : obj = GL_COLOR_ATTACHMENT0;
494 : :
495 : 4 : GLint type = 0;
496 : 4 : gl->GetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, obj,
497 : : GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE, &type);
498 [ - - - - : 4 : switch (type) {
+ - ]
499 : 0 : case GL_FLOAT: fmt->type = PL_FMT_FLOAT; break;
500 : 0 : case GL_INT: fmt->type = PL_FMT_SINT; break;
501 : 0 : case GL_UNSIGNED_INT: fmt->type = PL_FMT_UINT; break;
502 : 0 : case GL_SIGNED_NORMALIZED: fmt->type = PL_FMT_SNORM; break;
503 : 4 : case GL_UNSIGNED_NORMALIZED: fmt->type = PL_FMT_UNORM; break;
504 : 0 : default: fmt->type = PL_FMT_UNKNOWN; break;
505 : : }
506 : :
507 : 4 : gl->GetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, obj,
508 : 4 : GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, &fmt->component_depth[0]);
509 : 4 : gl->GetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, obj,
510 : 4 : GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, &fmt->component_depth[1]);
511 : 4 : gl->GetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, obj,
512 : 4 : GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE, &fmt->component_depth[2]);
513 : 4 : gl->GetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, obj,
514 : 4 : GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, &fmt->component_depth[3]);
515 : :
516 : 4 : gl->BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
517 : 4 : gl_check_err(gpu, "gl_fb_query");
518 : :
519 [ - + ]: 4 : if (!fmt->component_depth[0]) {
520 : 0 : PL_INFO(gpu, "OpenGL framebuffer did not export depth information,"
521 : : "assuming 8-bit framebuffer");
522 [ # # ]: 0 : for (int i = 0; i < PL_ARRAY_SIZE(fmt->component_depth); i++)
523 : 0 : fmt->component_depth[i] = 8;
524 : : }
525 : :
526 : : // Strip missing components from component map
527 [ + + ]: 8 : while (!fmt->component_depth[fmt->num_components - 1]) {
528 : 4 : fmt->num_components--;
529 [ + - ]: 4 : pl_assert(fmt->num_components);
530 : : }
531 : : }
532 : :
533 : : int gpu_bits = 0;
534 [ + + ]: 20 : for (int i = 0; i < 4; i++)
535 : 16 : gpu_bits += fmt->component_depth[i];
536 : 4 : fmt->internal_size = (gpu_bits + 7) / 8;
537 : :
538 : : size_t host_size = 0;
539 [ - - + - : 4 : switch (fmt->type) {
- - ]
540 : 0 : case PL_FMT_UNKNOWN:
541 : 0 : fmt->opaque = true;
542 : 0 : return true;
543 : 0 : case PL_FMT_FLOAT:
544 : 0 : glfmt->type = GL_FLOAT;
545 : : host_size = sizeof(float);
546 : 0 : break;
547 : 4 : case PL_FMT_UNORM:
548 : : case PL_FMT_UINT:
549 [ - + ]: 4 : if (gpu_bits > 32) {
550 : 0 : glfmt->type = GL_UNSIGNED_SHORT;
551 : : host_size = sizeof(uint16_t);
552 : : } else {
553 : 4 : glfmt->type = GL_UNSIGNED_BYTE;
554 : : host_size = sizeof(uint8_t);
555 : : }
556 : : break;
557 : 0 : case PL_FMT_SNORM:
558 : : case PL_FMT_SINT:
559 [ # # ]: 0 : if (gpu_bits > 32) {
560 : 0 : glfmt->type = GL_SHORT;
561 : : host_size = sizeof(int16_t);
562 : : } else {
563 : 0 : glfmt->type = GL_BYTE;
564 : : host_size = sizeof(int8_t);
565 : : }
566 : : break;
567 : : case PL_FMT_TYPE_COUNT:
568 : 0 : pl_unreachable();
569 : : }
570 : :
571 : 4 : fmt->texel_size = fmt->num_components * host_size;
572 [ + + ]: 16 : for (int i = 0; i < fmt->num_components; i++)
573 : 12 : fmt->host_bits[i] = 8 * host_size;
574 : 4 : fmt->caps |= PL_FMT_CAP_HOST_READABLE;
575 [ + - ]: 4 : if (!p->gles_ver || p->gles_ver >= 30)
576 : 4 : fmt->caps |= PL_FMT_CAP_BLITTABLE;
577 : :
578 : : return true;
579 : : }
580 : :
581 : 8 : pl_tex pl_opengl_wrap(pl_gpu gpu, const struct pl_opengl_wrap_params *params)
582 : : {
583 : : const gl_funcs *gl = gl_funcs_get(gpu);
584 : : if (!MAKE_CURRENT())
585 : 0 : return NULL;
586 : :
587 : : struct pl_gl *p = PL_PRIV(gpu);
588 : 8 : struct pl_tex_t *tex = pl_alloc_obj(NULL, tex, struct pl_tex_gl);
589 : 8 : struct pl_tex_gl *tex_gl = PL_PRIV(tex);
590 : 8 : *tex = (struct pl_tex_t) {
591 : : .params = {
592 : 8 : .w = params->width,
593 : 8 : .h = params->height,
594 : 8 : .d = params->depth,
595 : : },
596 : : };
597 : :
598 : : pl_fmt fmt = NULL;
599 : : const struct gl_format *glfmt = NULL;
600 : :
601 [ + + ]: 8 : if (params->texture) {
602 : : // Wrapping texture: Require matching iformat
603 [ - + ]: 4 : pl_assert(params->iformat);
604 [ + - ]: 4 : for (int i = 0; i < gpu->num_formats; i++) {
605 : 4 : const struct gl_format **glfmtp = PL_PRIV(gpu->formats[i]);
606 [ - + ]: 4 : if ((*glfmtp)->ifmt == params->iformat) {
607 : : fmt = gpu->formats[i];
608 : : glfmt = *glfmtp;
609 : : break;
610 : : }
611 : : }
612 : :
613 [ - + ]: 4 : if (!fmt) {
614 : 0 : PL_ERR(gpu, "Failed mapping iformat %d to any equivalent `pl_fmt`",
615 : : params->iformat);
616 : 0 : goto error;
617 : : }
618 : : } else {
619 : : // Wrapping framebuffer: Allocate/infer generic FBO format
620 : 4 : fmt = pl_alloc_obj((void *) gpu, fmt, const struct gl_format *);
621 : 4 : glfmt = pl_alloc_ptr((void *) fmt, glfmt);
622 : 4 : const struct gl_format **glfmtp = PL_PRIV(fmt);
623 : 4 : *glfmtp = glfmt;
624 [ - + ]: 4 : if (!gl_fb_query(gpu, params->framebuffer,
625 : : (struct pl_fmt_t *) fmt,
626 : : (struct gl_format *) glfmt))
627 : : {
628 : 0 : PL_ERR(gpu, "Failed querying framebuffer specifics!");
629 : 0 : pl_free((void *) fmt);
630 : 0 : goto error;
631 : : }
632 : : }
633 : :
634 : 8 : *tex_gl = (struct pl_tex_gl) {
635 : 8 : .target = params->target,
636 : 8 : .texture = params->texture,
637 : 8 : .fbo = params->framebuffer,
638 : 8 : .wrapped_tex = !!params->texture,
639 [ + - + + ]: 8 : .wrapped_fb = params->framebuffer || !params->texture,
640 : 8 : .iformat = glfmt->ifmt,
641 : 8 : .format = glfmt->fmt,
642 [ + - ]: 8 : .type = glfmt->type,
643 : : .fd = -1,
644 : : };
645 : :
646 : : int dims = pl_tex_params_dimension(tex->params);
647 [ + + ]: 8 : if (!tex_gl->target) {
648 [ - + - ]: 4 : switch (dims) {
649 : 0 : case 1: tex_gl->target = GL_TEXTURE_1D; break;
650 : 4 : case 2: tex_gl->target = GL_TEXTURE_2D; break;
651 : 0 : case 3: tex_gl->target = GL_TEXTURE_3D; break;
652 : : }
653 : : }
654 : :
655 : : // Map texture-specific sampling metadata
656 [ + + ]: 8 : if (params->texture) {
657 [ - + - - : 4 : switch (params->target) {
- - ]
658 : 0 : case GL_TEXTURE_1D:
659 [ # # # # ]: 0 : if (params->width || params->depth) {
660 : 0 : PL_ERR(gpu, "Invalid texture dimensions for GL_TEXTURE_1D");
661 : 0 : goto error;
662 : : }
663 : : // fall through
664 : : case GL_TEXTURE_2D:
665 [ - + ]: 4 : if (params->depth) {
666 : 0 : PL_ERR(gpu, "Invalid texture dimensions for GL_TEXTURE_2D");
667 : 0 : goto error;
668 : : }
669 : : // fall through
670 : : case 0:
671 : : case GL_TEXTURE_3D:
672 : 4 : tex->sampler_type = PL_SAMPLER_NORMAL;
673 : 4 : break;
674 : :
675 : 0 : case GL_TEXTURE_RECTANGLE: tex->sampler_type = PL_SAMPLER_RECT; break;
676 : 0 : case GL_TEXTURE_EXTERNAL_OES: tex->sampler_type = PL_SAMPLER_EXTERNAL; break;
677 : :
678 : 0 : default:
679 : 0 : PL_ERR(gpu, "Failed mapping texture target %u to any equivalent "
680 : : "`pl_sampler_type`", params->target);
681 : 0 : goto error;
682 : : }
683 : : }
684 : :
685 : : // Create optional extra fbo if needed/possible
686 : 4 : bool can_fbo = tex_gl->texture &&
687 [ + - ]: 4 : (fmt->caps & PL_FMT_CAP_RENDERABLE) &&
688 [ + + + - : 12 : tex->sampler_type != PL_SAMPLER_EXTERNAL &&
+ - ]
689 : : dims < 3;
690 : :
691 [ + - ]: 4 : if (can_fbo && !tex_gl->fbo) {
692 : 4 : const GLenum target = p->gles_ver && p->gles_ver < 30 ?
693 [ + - ]: 4 : GL_FRAMEBUFFER : GL_READ_FRAMEBUFFER;
694 : :
695 : 4 : gl->GenFramebuffers(1, &tex_gl->fbo);
696 : 4 : gl->BindFramebuffer(target, tex_gl->fbo);
697 [ - + ]: 4 : switch (dims) {
698 : 0 : case 1:
699 : 0 : gl->FramebufferTexture1D(target, GL_COLOR_ATTACHMENT0,
700 : : tex_gl->target, tex_gl->texture, 0);
701 : 0 : break;
702 : 4 : case 2:
703 : 4 : gl->FramebufferTexture2D(target, GL_COLOR_ATTACHMENT0,
704 : : tex_gl->target, tex_gl->texture, 0);
705 : 4 : break;
706 : : }
707 : :
708 : 4 : GLenum err = gl->CheckFramebufferStatus(target);
709 [ - + ]: 4 : if (err != GL_FRAMEBUFFER_COMPLETE) {
710 : 0 : gl->BindFramebuffer(target, 0);
711 : 0 : PL_ERR(gpu, "Failed creating framebuffer: error code %d", err);
712 : 0 : goto error;
713 : : }
714 : :
715 [ + + ]: 4 : if (p->gles_ver) {
716 : 1 : GLint read_type = 0, read_fmt = 0;
717 : 1 : gl->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &read_type);
718 : 1 : gl->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &read_fmt);
719 [ + - ]: 2 : tex->params.host_readable = read_type == tex_gl->type &&
720 [ - + ]: 1 : read_fmt == tex_gl->format;
721 : : } else {
722 : 3 : tex->params.host_readable = true;
723 : : }
724 : :
725 : 4 : gl->BindFramebuffer(target, 0);
726 [ - + ]: 4 : if (!gl_check_err(gpu, "pl_opengl_wrap: fbo"))
727 : 0 : goto error;
728 : : }
729 : :
730 : : // Complete the process of inferring the texture capabilities
731 : 8 : tex->params.format = fmt;
732 [ + + ]: 8 : if (tex_gl->texture) {
733 : 4 : tex->params.sampleable = fmt->caps & PL_FMT_CAP_SAMPLEABLE;
734 : 4 : tex->params.storable = fmt->caps & PL_FMT_CAP_STORABLE;
735 : 4 : tex->params.host_writable = !fmt->opaque;
736 : 4 : tex->params.host_readable |= fmt->caps & PL_FMT_CAP_HOST_READABLE;
737 : : }
738 [ + + + - ]: 8 : if (tex_gl->fbo || tex_gl->wrapped_fb) {
739 : 8 : tex->params.renderable = fmt->caps & PL_FMT_CAP_RENDERABLE;
740 : 8 : tex->params.host_readable |= fmt->caps & PL_FMT_CAP_HOST_READABLE;
741 [ + - + - ]: 8 : if (dims == 2 && (fmt->caps & PL_FMT_CAP_BLITTABLE)) {
742 : 8 : tex->params.blit_src = true;
743 : 8 : tex->params.blit_dst = true;
744 : : }
745 : : }
746 : :
747 : 8 : tex_gl->barrier = tex_barrier(tex);
748 : : RELEASE_CURRENT();
749 : 8 : return tex;
750 : :
751 : 0 : error:
752 : 0 : gl_tex_destroy(gpu, tex);
753 : : RELEASE_CURRENT();
754 : 0 : return NULL;
755 : : }
756 : :
757 : 4 : unsigned int pl_opengl_unwrap(pl_gpu gpu, pl_tex tex,
758 : : unsigned int *out_target, int *out_iformat,
759 : : unsigned int *out_fbo)
760 : : {
761 : 4 : struct pl_tex_gl *tex_gl = PL_PRIV(tex);
762 [ - + ]: 4 : if (!tex_gl->texture) {
763 : 0 : PL_ERR(gpu, "Trying to call `pl_opengl_unwrap` on a pseudo-texture "
764 : : "(perhaps obtained by `pl_swapchain_start_frame`?)");
765 : 0 : return 0;
766 : : }
767 : :
768 [ + - ]: 4 : if (out_target)
769 : 4 : *out_target = tex_gl->target;
770 [ + - ]: 4 : if (out_iformat)
771 : 4 : *out_iformat = tex_gl->iformat;
772 [ - + ]: 4 : if (out_fbo)
773 : 0 : *out_fbo = tex_gl->fbo;
774 : :
775 : 4 : return tex_gl->texture;
776 : : }
777 : :
778 : 2307 : void gl_tex_invalidate(pl_gpu gpu, pl_tex tex)
779 : : {
780 : : const gl_funcs *gl = gl_funcs_get(gpu);
781 : : struct pl_gl *p = PL_PRIV(gpu);
782 : 2307 : struct pl_tex_gl *tex_gl = PL_PRIV(tex);
783 : : if (!MAKE_CURRENT())
784 : 0 : return;
785 : :
786 [ + + + + ]: 2307 : if (tex_gl->texture && p->has_invalidate_tex)
787 : 2232 : gl->InvalidateTexImage(tex_gl->texture, 0);
788 : :
789 [ + + + + : 2307 : if ((tex_gl->wrapped_fb || tex_gl->fbo) && p->has_invalidate_fb) {
+ - ]
790 [ + + ]: 2304 : GLenum attachment = tex_gl->fbo ? GL_COLOR_ATTACHMENT0 : GL_COLOR;
791 : 2304 : gl->BindFramebuffer(GL_DRAW_FRAMEBUFFER, tex_gl->fbo);
792 : 2304 : gl->InvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, &attachment);
793 : 2304 : gl->BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
794 : : }
795 : :
796 : 2307 : gl_check_err(gpu, "gl_tex_invalidate");
797 : : RELEASE_CURRENT();
798 : : }
799 : :
800 : 230 : void gl_tex_clear_ex(pl_gpu gpu, pl_tex tex, const union pl_clear_color color)
801 : : {
802 : : const gl_funcs *gl = gl_funcs_get(gpu);
803 : : if (!MAKE_CURRENT())
804 : 0 : return;
805 : :
806 : : struct pl_gl *p = PL_PRIV(gpu);
807 : 230 : struct pl_tex_gl *tex_gl = PL_PRIV(tex);
808 [ + + - + ]: 230 : pl_assert(tex_gl->fbo || tex_gl->wrapped_fb);
809 : :
810 : 230 : const GLenum target = p->gles_ver && p->gles_ver < 30 ?
811 [ + - ]: 230 : GL_FRAMEBUFFER : GL_DRAW_FRAMEBUFFER;
812 : :
813 : 230 : gl->BindFramebuffer(target, tex_gl->fbo);
814 : :
815 [ + + - - : 230 : switch (tex->params.format->type) {
- ]
816 : 206 : case PL_FMT_UNKNOWN:
817 : : case PL_FMT_FLOAT:
818 : : case PL_FMT_UNORM:
819 : : case PL_FMT_SNORM:
820 : 206 : gl->ClearColor(color.f[0], color.f[1], color.f[2], color.f[3]);
821 : 206 : gl->Clear(GL_COLOR_BUFFER_BIT);
822 : 206 : break;
823 : :
824 : 24 : case PL_FMT_UINT: {
825 : 24 : GLuint gl_color[] = { color.u[0], color.u[1], color.u[2], color.u[3] };
826 : 24 : gl->ClearBufferuiv(GL_COLOR, 0, gl_color);
827 : : break;
828 : : }
829 : :
830 : 0 : case PL_FMT_SINT: {
831 : 0 : GLint gl_color[] = { color.i[0], color.i[1], color.i[2], color.i[3] };
832 : 0 : gl->ClearBufferiv(GL_COLOR, 0, gl_color);
833 : : break;
834 : : }
835 : :
836 : : case PL_FMT_TYPE_COUNT:
837 : 0 : pl_unreachable();
838 : : }
839 : :
840 : 230 : gl->BindFramebuffer(target, 0);
841 : 230 : gl_check_err(gpu, "gl_tex_clear");
842 : : RELEASE_CURRENT();
843 : : }
844 : :
845 : 96 : void gl_tex_blit(pl_gpu gpu, const struct pl_tex_blit_params *params)
846 : : {
847 : : const gl_funcs *gl = gl_funcs_get(gpu);
848 : : if (!MAKE_CURRENT())
849 : : return;
850 : :
851 : 96 : struct pl_tex_gl *src_gl = PL_PRIV(params->src);
852 : 96 : struct pl_tex_gl *dst_gl = PL_PRIV(params->dst);
853 : :
854 [ - + - - ]: 96 : pl_assert(src_gl->fbo || src_gl->wrapped_fb);
855 [ - + - - ]: 96 : pl_assert(dst_gl->fbo || dst_gl->wrapped_fb);
856 : 96 : gl->BindFramebuffer(GL_READ_FRAMEBUFFER, src_gl->fbo);
857 : 96 : gl->BindFramebuffer(GL_DRAW_FRAMEBUFFER, dst_gl->fbo);
858 : :
859 : : static const GLint filters[PL_TEX_SAMPLE_MODE_COUNT] = {
860 : : [PL_TEX_SAMPLE_NEAREST] = GL_NEAREST,
861 : : [PL_TEX_SAMPLE_LINEAR] = GL_LINEAR,
862 : : };
863 : :
864 : 96 : pl_rect3d src_rc = params->src_rc, dst_rc = params->dst_rc;
865 : 96 : gl->BlitFramebuffer(src_rc.x0, src_rc.y0, src_rc.x1, src_rc.y1,
866 : : dst_rc.x0, dst_rc.y0, dst_rc.x1, dst_rc.y1,
867 : 96 : GL_COLOR_BUFFER_BIT, filters[params->sample_mode]);
868 : :
869 : 96 : gl->BindFramebuffer(GL_READ_FRAMEBUFFER, 0);
870 : 96 : gl->BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
871 : 96 : gl_check_err(gpu, "gl_tex_blit");
872 : : RELEASE_CURRENT();
873 : : }
874 : :
875 : : static int get_alignment(size_t pitch)
876 : : {
877 : 363 : if (pitch % 8 == 0)
878 : : return 8;
879 [ - + - + ]: 16 : if (pitch % 4 == 0)
880 : : return 4;
881 [ # # # # ]: 0 : if (pitch % 2 == 0)
882 : 0 : return 2;
883 : : return 1;
884 : : }
885 : :
886 [ + + ]: 333 : bool gl_tex_upload(pl_gpu gpu, const struct pl_tex_transfer_params *params)
887 : : {
888 : : const gl_funcs *gl = gl_funcs_get(gpu);
889 : : struct pl_gl *p = PL_PRIV(gpu);
890 : 333 : pl_tex tex = params->tex;
891 : 333 : pl_fmt fmt = tex->params.format;
892 : 333 : pl_buf buf = params->buf;
893 : 333 : struct pl_tex_gl *tex_gl = PL_PRIV(tex);
894 [ + + ]: 333 : struct pl_buf_gl *buf_gl = buf ? PL_PRIV(buf) : NULL;
895 : :
896 : : // If the user requests asynchronous uploads, it's more efficient to do
897 : : // them via a PBO - this allows us to skip blocking the caller, especially
898 : : // when the host pointer can be imported directly.
899 [ + + + - ]: 333 : if (params->callback && !buf) {
900 : 276 : size_t buf_size = pl_tex_transfer_size(params);
901 : : const size_t min_size = 32*1024; // 32 KiB
902 [ + + + - ]: 276 : if (buf_size >= min_size && buf_size <= gpu->limits.max_buf_size)
903 : 27 : return pl_tex_upload_pbo(gpu, params);
904 : : }
905 : :
906 : : if (!MAKE_CURRENT())
907 : 0 : return false;
908 : :
909 : 306 : uintptr_t src = (uintptr_t) params->ptr;
910 [ + + ]: 306 : if (buf) {
911 : 27 : gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, buf_gl->buffer);
912 : 27 : src = buf_gl->offset + params->buf_offset;
913 : : }
914 : :
915 : 306 : bool misaligned = params->row_pitch % fmt->texel_size;
916 : 306 : int stride_w = params->row_pitch / fmt->texel_size;
917 [ + + ]: 306 : int stride_h = params->depth_pitch / params->row_pitch;
918 : :
919 : : int dims = pl_tex_params_dimension(tex->params);
920 : : if (dims > 1)
921 [ + + ]: 219 : gl->PixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(params->row_pitch));
922 : :
923 : 306 : int rows = pl_rect_h(params->rc);
924 [ + - ]: 306 : if (misaligned) {
925 : : rows = 1;
926 [ + + ]: 306 : } else if (stride_w != pl_rect_w(params->rc)) {
927 : 4 : gl->PixelStorei(GL_UNPACK_ROW_LENGTH, stride_w);
928 : : }
929 : :
930 : 306 : int imgs = pl_rect_d(params->rc);
931 [ + - - + ]: 306 : if (stride_h != pl_rect_h(params->rc) || rows < stride_h)
932 : 0 : gl->PixelStorei(GL_UNPACK_IMAGE_HEIGHT, stride_h);
933 : :
934 : 306 : gl->BindTexture(tex_gl->target, tex_gl->texture);
935 : 306 : gl_timer_begin(gpu, params->timer);
936 : :
937 [ + + + ]: 306 : switch (dims) {
938 : 95 : case 1:
939 : 95 : gl->TexSubImage1D(tex_gl->target, 0, params->rc.x0, pl_rect_w(params->rc),
940 : : tex_gl->format, tex_gl->type, (void *) src);
941 : 95 : break;
942 : 124 : case 2:
943 [ + + ]: 248 : for (int y = params->rc.y0; y < params->rc.y1; y += rows) {
944 : 124 : gl->TexSubImage2D(tex_gl->target, 0, params->rc.x0, y,
945 : 124 : pl_rect_w(params->rc), rows, tex_gl->format,
946 : : tex_gl->type, (void *) src);
947 : 124 : src += params->row_pitch * rows;
948 : : }
949 : : break;
950 : 87 : case 3:
951 [ + + ]: 174 : for (int z = params->rc.z0; z < params->rc.z1; z += imgs) {
952 : : uintptr_t row_src = src;
953 [ + + ]: 174 : for (int y = params->rc.y0; y < params->rc.y1; y += rows) {
954 : 87 : gl->TexSubImage3D(tex_gl->target, 0, params->rc.x0, y, z,
955 : 87 : pl_rect_w(params->rc), rows, imgs,
956 : : tex_gl->format, tex_gl->type, (void *) row_src);
957 : 87 : row_src = (uintptr_t) row_src + params->row_pitch * rows;
958 : : }
959 : 87 : src += params->depth_pitch * imgs;
960 : : }
961 : : break;
962 : : }
963 : :
964 : 306 : gl_timer_end(gpu, params->timer);
965 : 306 : gl->BindTexture(tex_gl->target, 0);
966 : 306 : gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
967 : 306 : gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
968 : 306 : gl->PixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
969 : :
970 [ + + ]: 306 : if (buf) {
971 : 27 : gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
972 [ - + ]: 27 : if (buf->params.host_mapped) {
973 : : // Make sure the PBO is not reused until GL is done with it. If a
974 : : // previous operation is pending, "update" it by creating a new
975 : : // fence that will cover the previous operation as well.
976 : 0 : gl->DeleteSync(buf_gl->fence);
977 : 0 : buf_gl->fence = gl->FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
978 : : }
979 : : }
980 : :
981 [ + + ]: 306 : if (params->callback) {
982 [ + + - + : 249 : PL_ARRAY_APPEND(gpu, p->callbacks, (struct gl_cb) {
- + ]
983 : : .sync = gl->FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0),
984 : : .callback = params->callback,
985 : : .priv = params->priv,
986 : : });
987 : : }
988 : :
989 : 306 : bool ok = gl_check_err(gpu, "gl_tex_upload");
990 : : RELEASE_CURRENT();
991 : 306 : return ok;
992 : : }
993 : :
994 [ + + ]: 359 : bool gl_tex_download(pl_gpu gpu, const struct pl_tex_transfer_params *params)
995 : : {
996 : : const gl_funcs *gl = gl_funcs_get(gpu);
997 : : struct pl_gl *p = PL_PRIV(gpu);
998 : 359 : pl_tex tex = params->tex;
999 : 359 : pl_fmt fmt = tex->params.format;
1000 : 359 : pl_buf buf = params->buf;
1001 : 359 : struct pl_tex_gl *tex_gl = PL_PRIV(tex);
1002 [ + + ]: 359 : struct pl_buf_gl *buf_gl = buf ? PL_PRIV(buf) : NULL;
1003 : : bool ok = true;
1004 : :
1005 [ + + + + ]: 359 : if (params->callback && !buf) {
1006 : 276 : size_t buf_size = pl_tex_transfer_size(params);
1007 : : const size_t min_size = 32*1024; // 32 KiB
1008 [ + + + - ]: 276 : if (buf_size >= min_size && buf_size <= gpu->limits.max_buf_size)
1009 : 27 : return pl_tex_download_pbo(gpu, params);
1010 : : }
1011 : :
1012 : : if (!MAKE_CURRENT())
1013 : 0 : return false;
1014 : :
1015 : 332 : uintptr_t dst = (uintptr_t) params->ptr;
1016 [ + + ]: 332 : if (buf) {
1017 : 27 : gl->BindBuffer(GL_PIXEL_PACK_BUFFER, buf_gl->buffer);
1018 : 27 : dst = buf_gl->offset + params->buf_offset;
1019 : : }
1020 : :
1021 : : pl_rect3d full = {
1022 : : 0, 0, 0,
1023 : 332 : tex->params.w,
1024 [ + + ]: 332 : PL_DEF(tex->params.h, 1),
1025 [ + + ]: 332 : PL_DEF(tex->params.d, 1),
1026 : : };
1027 : :
1028 : 332 : bool misaligned = params->row_pitch % fmt->texel_size;
1029 : 332 : int stride_w = params->row_pitch / fmt->texel_size;
1030 [ + + ]: 332 : int stride_h = params->depth_pitch / params->row_pitch;
1031 : :
1032 : : int dims = pl_tex_params_dimension(tex->params);
1033 [ + - + - : 332 : bool is_copy = pl_rect3d_eq(params->rc, full) &&
+ - + - +
- + + ]
1034 [ + - ]: 328 : stride_w == tex->params.w &&
1035 [ + - - + ]: 660 : stride_h == PL_DEF(tex->params.h, 1) &&
1036 : : !misaligned;
1037 : :
1038 : 332 : gl_timer_begin(gpu, params->timer);
1039 : :
1040 [ + + - + ]: 565 : if (tex_gl->fbo || tex_gl->wrapped_fb) {
1041 : : // We can use a more efficient path when we have an FBO available
1042 [ + + ]: 233 : if (dims > 1)
1043 [ + + ]: 160 : gl->PixelStorei(GL_PACK_ALIGNMENT, get_alignment(params->row_pitch));
1044 : :
1045 : 233 : int rows = pl_rect_h(params->rc);
1046 [ + - ]: 233 : if (misaligned) {
1047 : : rows = 1;
1048 [ + + ]: 233 : } else if (stride_w != tex->params.w) {
1049 : 4 : gl->PixelStorei(GL_PACK_ROW_LENGTH, stride_w);
1050 : : }
1051 : :
1052 : : // No 3D framebuffers
1053 [ - + ]: 233 : pl_assert(pl_rect_d(params->rc) == 1);
1054 : :
1055 : 233 : const GLenum target = p->gles_ver && p->gles_ver < 30 ?
1056 [ + - ]: 233 : GL_FRAMEBUFFER : GL_READ_FRAMEBUFFER;
1057 : :
1058 : 233 : gl->BindFramebuffer(target, tex_gl->fbo);
1059 [ + + ]: 466 : for (int y = params->rc.y0; y < params->rc.y1; y += rows) {
1060 : 233 : gl->ReadPixels(params->rc.x0, y, pl_rect_w(params->rc), rows,
1061 : : tex_gl->format, tex_gl->type, (void *) dst);
1062 : 233 : dst += params->row_pitch * rows;
1063 : : }
1064 : 233 : gl->BindFramebuffer(target, 0);
1065 : 233 : gl->PixelStorei(GL_PACK_ALIGNMENT, 4);
1066 : 233 : gl->PixelStorei(GL_PACK_ROW_LENGTH, 0);
1067 [ + - ]: 99 : } else if (is_copy) {
1068 : : // We're downloading the entire texture
1069 : 99 : gl->BindTexture(tex_gl->target, tex_gl->texture);
1070 : 99 : gl->GetTexImage(tex_gl->target, 0, tex_gl->format, tex_gl->type, (void *) dst);
1071 : 99 : gl->BindTexture(tex_gl->target, 0);
1072 : : } else {
1073 : 0 : PL_ERR(gpu, "Partial downloads of 3D textures not implemented!");
1074 : : ok = false;
1075 : : }
1076 : :
1077 : 332 : gl_timer_end(gpu, params->timer);
1078 : :
1079 [ + + ]: 332 : if (buf) {
1080 : 27 : gl->BindBuffer(GL_PIXEL_PACK_BUFFER, 0);
1081 [ + - - + ]: 27 : if (ok && buf->params.host_mapped) {
1082 : 0 : gl->DeleteSync(buf_gl->fence);
1083 : 0 : buf_gl->fence = gl->FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
1084 : : }
1085 : : }
1086 : :
1087 [ + + ]: 332 : if (params->callback) {
1088 [ - + - + : 276 : PL_ARRAY_APPEND(gpu, p->callbacks, (struct gl_cb) {
- + ]
1089 : : .sync = gl->FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0),
1090 : : .callback = params->callback,
1091 : : .priv = params->priv,
1092 : : });
1093 : : }
1094 : :
1095 : 332 : ok &= gl_check_err(gpu, "gl_tex_download");
1096 : : RELEASE_CURRENT();
1097 : 332 : return ok;
1098 : : }
|