LCOV - code coverage report
Current view: top level - src/gpu - utils.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 395 464 85.1 %
Date: 2025-03-29 09:04:10 Functions: 19 20 95.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 250 376 66.5 %

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

Generated by: LCOV version 1.16