LCOV - code coverage report
Current view: top level - src/opengl - gpu_tex.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 413 536 77.1 %
Date: 2025-03-29 09:04:10 Functions: 13 14 92.9 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 263 417 63.1 %

           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, &params->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                 :            : }

Generated by: LCOV version 1.16