LCOV - code coverage report
Current view: top level - src/vulkan - gpu_tex.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 518 661 78.4 %
Date: 2025-03-29 09:04:10 Functions: 17 19 89.5 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 251 434 57.8 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * This file is part of libplacebo.
       3                 :            :  *
       4                 :            :  * libplacebo is free software; you can redistribute it and/or
       5                 :            :  * modify it under the terms of the GNU Lesser General Public
       6                 :            :  * License as published by the Free Software Foundation; either
       7                 :            :  * version 2.1 of the License, or (at your option) any later version.
       8                 :            :  *
       9                 :            :  * libplacebo is distributed in the hope that it will be useful,
      10                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12                 :            :  * GNU Lesser General Public License for more details.
      13                 :            :  *
      14                 :            :  * You should have received a copy of the GNU Lesser General Public
      15                 :            :  * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
      16                 :            :  */
      17                 :            : 
      18                 :            : #include "gpu.h"
      19                 :            : 
      20                 :       2333 : VK_CB_FUNC_DEF(vk_tex_deref);
      21                 :            : 
      22                 :       2333 : void vk_tex_barrier(pl_gpu gpu, struct vk_cmd *cmd, pl_tex tex,
      23                 :            :                     VkPipelineStageFlags2 stage, VkAccessFlags2 access,
      24                 :            :                     VkImageLayout layout, uint32_t qf)
      25                 :            : {
      26                 :       2333 :     struct pl_vk *p = PL_PRIV(gpu);
      27                 :       2333 :     struct vk_ctx *vk = p->vk;
      28                 :       2333 :     struct pl_tex_vk *tex_vk = PL_PRIV(tex);
      29                 :       2333 :     pl_rc_ref(&tex_vk->rc);
      30         [ -  + ]:       2333 :     pl_assert(!tex_vk->held);
      31         [ -  + ]:       2333 :     pl_assert(!tex_vk->num_planes);
      32                 :            : 
      33                 :            :     // CONCURRENT images require transitioning to/from IGNORED, EXCLUSIVE
      34                 :            :     // images require transitioning to/from the concrete QF index
      35         [ -  + ]:       2333 :     if (vk->pools.num == 1) {
      36         [ +  + ]:       2333 :         if (tex_vk->qf == VK_QUEUE_FAMILY_IGNORED)
      37                 :        469 :             tex_vk->qf = cmd->pool->qf;
      38         [ +  + ]:       2333 :         if (qf == VK_QUEUE_FAMILY_IGNORED)
      39                 :       2329 :             qf = cmd->pool->qf;
      40                 :            :     }
      41                 :            : 
      42                 :            :     struct vk_sync_scope last;
      43                 :       2333 :     bool is_trans = layout != tex_vk->layout, is_xfer = qf != tex_vk->qf;
      44                 :       2333 :     last = vk_sem_barrier(cmd, &tex_vk->sem, stage, access, is_trans || is_xfer);
      45                 :            : 
      46                 :       2333 :     VkImageMemoryBarrier2 barr = {
      47                 :            :         .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
      48                 :       2333 :         .srcStageMask = last.stage,
      49                 :            :         .srcAccessMask = last.access,
      50                 :            :         .dstStageMask = stage,
      51                 :            :         .dstAccessMask = access,
      52                 :       2333 :         .oldLayout = tex_vk->layout,
      53                 :            :         .newLayout = layout,
      54                 :       2333 :         .srcQueueFamilyIndex = tex_vk->qf,
      55                 :            :         .dstQueueFamilyIndex = qf,
      56                 :       2333 :         .image = tex_vk->img,
      57                 :            :         .subresourceRange = {
      58                 :       2333 :             .aspectMask = tex_vk->aspect,
      59                 :            :             .levelCount = 1,
      60                 :            :             .layerCount = 1,
      61                 :            :         },
      62                 :            :     };
      63                 :            : 
      64         [ +  + ]:       2333 :     if (tex_vk->may_invalidate) {
      65                 :        807 :         tex_vk->may_invalidate = false;
      66                 :        807 :         barr.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
      67                 :            :     }
      68                 :            : 
      69         [ +  + ]:       2333 :     if (tex_vk->ext_deps.num) {
      70                 :            :         // We need to guarantee that all external dependencies are satisfied
      71                 :            :         // before the barrier begins executing. The easiest way to ensure this
      72                 :            :         // is to add the stage mask at which we wait for the external dependency
      73                 :            :         // to the source stage mask of the image barrier.
      74                 :         14 :         barr.srcStageMask |= stage;
      75                 :            :     }
      76                 :            : 
      77   [ +  +  +  + ]:       2333 :     if (last.access || is_trans || is_xfer) {
      78                 :       1831 :         vk_cmd_barrier(cmd, &(VkDependencyInfo) {
      79                 :            :             .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
      80                 :            :             .imageMemoryBarrierCount = 1,
      81                 :            :             .pImageMemoryBarriers = &barr,
      82                 :            :         });
      83                 :            :     }
      84                 :            : 
      85                 :       2333 :     tex_vk->qf = qf;
      86                 :       2333 :     tex_vk->layout = layout;
      87                 :       2333 :     vk_cmd_callback(cmd, VK_CB_FUNC(vk_tex_deref), gpu, tex);
      88                 :            : 
      89         [ +  + ]:       2347 :     for (int i = 0; i < tex_vk->ext_deps.num; i++)
      90                 :         14 :         vk_cmd_dep(cmd, stage, tex_vk->ext_deps.elem[i]);
      91                 :       2333 :     tex_vk->ext_deps.num = 0;
      92                 :       2333 : }
      93                 :            : 
      94                 :        604 : static void vk_tex_destroy(pl_gpu gpu, struct pl_tex_t *tex)
      95                 :            : {
      96         [ +  - ]:        604 :     if (!tex)
      97                 :            :         return;
      98                 :            : 
      99                 :        604 :     struct pl_vk *p = PL_PRIV(gpu);
     100                 :        604 :     struct vk_ctx *vk = p->vk;
     101                 :        604 :     struct pl_tex_vk *tex_vk = PL_PRIV(tex);
     102                 :            : 
     103                 :        604 :     vk->DestroyFramebuffer(vk->dev, tex_vk->framebuffer, PL_VK_ALLOC);
     104                 :        604 :     vk->DestroyImageView(vk->dev, tex_vk->view, PL_VK_ALLOC);
     105         [ +  + ]:        607 :     for (int i = 0; i < tex_vk->num_planes; i++)
     106                 :          3 :         vk_tex_deref(gpu, tex->planes[i]);
     107         [ +  + ]:        604 :     if (!tex_vk->external_img) {
     108                 :        596 :         vk->DestroyImage(vk->dev, tex_vk->img, PL_VK_ALLOC);
     109                 :        596 :         vk_malloc_free(vk->ma, &tex_vk->mem);
     110                 :            :     }
     111                 :            : 
     112                 :        604 :     pl_free(tex);
     113                 :            : }
     114                 :            : 
     115                 :       2831 : void vk_tex_deref(pl_gpu gpu, pl_tex tex)
     116                 :            : {
     117         [ +  + ]:       2831 :     if (!tex)
     118                 :            :         return;
     119                 :            : 
     120                 :       2828 :     struct pl_tex_vk *tex_vk = PL_PRIV(tex);
     121         [ +  + ]:       2828 :     if (pl_rc_deref(&tex_vk->rc))
     122                 :        495 :         vk_tex_destroy(gpu, (struct pl_tex_t *) tex);
     123                 :            : }
     124                 :            : 
     125                 :            : 
     126                 :            : // Initializes non-VkImage values like the image view, framebuffers, etc.
     127                 :        495 : static bool vk_init_image(pl_gpu gpu, pl_tex tex, pl_debug_tag debug_tag)
     128                 :            : {
     129                 :        495 :     struct pl_vk *p = PL_PRIV(gpu);
     130                 :        495 :     struct vk_ctx *vk = p->vk;
     131                 :            : 
     132                 :            :     const struct pl_tex_params *params = &tex->params;
     133                 :        495 :     struct pl_tex_vk *tex_vk = PL_PRIV(tex);
     134         [ -  + ]:        495 :     pl_assert(tex_vk->img);
     135         [ +  - ]:        495 :     PL_VK_NAME(IMAGE, tex_vk->img, debug_tag);
     136                 :        495 :     pl_rc_init(&tex_vk->rc);
     137         [ +  - ]:        495 :     if (tex_vk->num_planes)
     138                 :            :         return true;
     139                 :        495 :     tex_vk->layout = VK_IMAGE_LAYOUT_UNDEFINED;
     140                 :        495 :     tex_vk->transfer_queue = GRAPHICS;
     141                 :        495 :     tex_vk->qf = VK_QUEUE_FAMILY_IGNORED; // will be set on first use, if needed
     142                 :            : 
     143                 :            :     // Always use the transfer pool if available, for efficiency
     144   [ +  +  +  - ]:        495 :     if ((params->host_writable || params->host_readable) && vk->pool_transfer)
     145                 :        388 :         tex_vk->transfer_queue = TRANSFER;
     146                 :            : 
     147                 :            :     // For emulated formats: force usage of the compute queue, because we
     148                 :            :     // can't properly track cross-queue dependencies for buffers (yet?)
     149         [ +  + ]:        495 :     if (params->format->emulated)
     150                 :         18 :         tex_vk->transfer_queue = COMPUTE;
     151                 :            : 
     152                 :            :     bool ret = false;
     153                 :        495 :     VkRenderPass dummyPass = VK_NULL_HANDLE;
     154                 :            : 
     155         [ +  + ]:        495 :     if (params->sampleable || params->renderable || params->storable) {
     156                 :            :         static const VkImageViewType viewType[] = {
     157                 :            :             [VK_IMAGE_TYPE_1D] = VK_IMAGE_VIEW_TYPE_1D,
     158                 :            :             [VK_IMAGE_TYPE_2D] = VK_IMAGE_VIEW_TYPE_2D,
     159                 :            :             [VK_IMAGE_TYPE_3D] = VK_IMAGE_VIEW_TYPE_3D,
     160                 :            :         };
     161                 :            : 
     162                 :        145 :         const VkImageViewCreateInfo vinfo = {
     163                 :            :             .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
     164                 :        145 :             .image = tex_vk->img,
     165                 :        145 :             .viewType = viewType[tex_vk->type],
     166                 :        145 :             .format = tex_vk->img_fmt,
     167                 :            :             .subresourceRange = {
     168                 :        145 :                 .aspectMask = tex_vk->aspect,
     169                 :            :                 .levelCount = 1,
     170                 :            :                 .layerCount = 1,
     171                 :            :             },
     172                 :            :         };
     173                 :            : 
     174         [ -  + ]:        145 :         VK(vk->CreateImageView(vk->dev, &vinfo, PL_VK_ALLOC, &tex_vk->view));
     175         [ +  - ]:        145 :         PL_VK_NAME(IMAGE_VIEW, tex_vk->view, debug_tag);
     176                 :            :     }
     177                 :            : 
     178         [ +  + ]:        495 :     if (params->renderable) {
     179                 :            :         // Framebuffers need to be created against a specific render pass
     180                 :            :         // layout, so we need to temporarily create a skeleton/dummy render
     181                 :            :         // pass for vulkan to figure out the compatibility
     182                 :         27 :         VkRenderPassCreateInfo rinfo = {
     183                 :            :             .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
     184                 :            :             .attachmentCount = 1,
     185                 :         27 :             .pAttachments = &(VkAttachmentDescription) {
     186                 :         27 :                 .format = tex_vk->img_fmt,
     187                 :            :                 .samples = VK_SAMPLE_COUNT_1_BIT,
     188                 :            :                 .loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
     189                 :            :                 .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
     190                 :            :                 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
     191                 :            :                 .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
     192                 :            :             },
     193                 :            :             .subpassCount = 1,
     194                 :         27 :             .pSubpasses = &(VkSubpassDescription) {
     195                 :            :                 .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
     196                 :            :                 .colorAttachmentCount = 1,
     197                 :         27 :                 .pColorAttachments = &(VkAttachmentReference) {
     198                 :            :                     .attachment = 0,
     199                 :            :                     .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
     200                 :            :                 },
     201                 :            :             },
     202                 :            :         };
     203                 :            : 
     204         [ -  + ]:         27 :         VK(vk->CreateRenderPass(vk->dev, &rinfo, PL_VK_ALLOC, &dummyPass));
     205                 :            : 
     206                 :         27 :         VkFramebufferCreateInfo finfo = {
     207                 :            :             .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
     208                 :            :             .renderPass = dummyPass,
     209                 :            :             .attachmentCount = 1,
     210                 :         27 :             .pAttachments = &tex_vk->view,
     211                 :         27 :             .width = tex->params.w,
     212                 :         27 :             .height = tex->params.h,
     213                 :            :             .layers = 1,
     214                 :            :         };
     215                 :            : 
     216         [ +  - ]:         27 :         if (finfo.width > vk->props.limits.maxFramebufferWidth ||
     217         [ -  + ]:         27 :             finfo.height > vk->props.limits.maxFramebufferHeight)
     218                 :            :         {
     219                 :          0 :             PL_ERR(gpu, "Framebuffer of size %dx%d exceeds the maximum allowed "
     220                 :            :                    "dimensions: %dx%d", finfo.width, finfo.height,
     221                 :            :                    vk->props.limits.maxFramebufferWidth,
     222                 :            :                    vk->props.limits.maxFramebufferHeight);
     223                 :          0 :             goto error;
     224                 :            :         }
     225                 :            : 
     226         [ -  + ]:         27 :         VK(vk->CreateFramebuffer(vk->dev, &finfo, PL_VK_ALLOC,
     227                 :            :                                  &tex_vk->framebuffer));
     228         [ +  - ]:         27 :         PL_VK_NAME(FRAMEBUFFER, tex_vk->framebuffer, debug_tag);
     229                 :            :     }
     230                 :            : 
     231                 :            :     ret = true;
     232                 :            : 
     233                 :        495 : error:
     234                 :        495 :     vk->DestroyRenderPass(vk->dev, dummyPass, PL_VK_ALLOC);
     235                 :        495 :     return ret;
     236                 :            : }
     237                 :            : 
     238                 :        596 : pl_tex vk_tex_create(pl_gpu gpu, const struct pl_tex_params *params)
     239                 :            : {
     240                 :        596 :     struct pl_vk *p = PL_PRIV(gpu);
     241                 :        596 :     struct vk_ctx *vk = p->vk;
     242                 :            : 
     243                 :        596 :     enum pl_handle_type handle_type = params->export_handle |
     244                 :        596 :                                       params->import_handle;
     245                 :        596 :     VkExternalMemoryHandleTypeFlagBitsKHR vk_handle_type = vk_mem_handle_type(handle_type);
     246                 :            : 
     247                 :        596 :     struct pl_tex_t *tex = pl_zalloc_obj(NULL, tex, struct pl_tex_vk);
     248                 :        596 :     pl_fmt fmt = params->format;
     249                 :        596 :     tex->params = *params;
     250                 :        596 :     tex->params.initial_data = NULL;
     251                 :        596 :     tex->sampler_type = PL_SAMPLER_NORMAL;
     252                 :            : 
     253                 :        596 :     struct pl_tex_vk *tex_vk = PL_PRIV(tex);
     254                 :        596 :     struct pl_fmt_vk *fmtp = PL_PRIV(fmt);
     255                 :        596 :     tex_vk->img_fmt = fmtp->vk_fmt->tfmt;
     256                 :        596 :     tex_vk->num_planes = fmt->num_planes;
     257         [ +  + ]:        599 :     for (int i = 0; i < tex_vk->num_planes; i++)
     258                 :          3 :         tex_vk->aspect |= VK_IMAGE_ASPECT_PLANE_0_BIT << i;
     259         [ +  + ]:        596 :     tex_vk->aspect = PL_DEF(tex_vk->aspect, VK_IMAGE_ASPECT_COLOR_BIT);
     260                 :            : 
     261                 :            :     switch (pl_tex_params_dimension(*params)) {
     262                 :        183 :     case 1: tex_vk->type = VK_IMAGE_TYPE_1D; break;
     263                 :        248 :     case 2: tex_vk->type = VK_IMAGE_TYPE_2D; break;
     264                 :        165 :     case 3: tex_vk->type = VK_IMAGE_TYPE_3D; break;
     265                 :            :     }
     266                 :            : 
     267         [ +  + ]:        596 :     if (fmt->emulated) {
     268                 :         48 :         tex_vk->texel_fmt = pl_find_fmt(gpu, fmt->type, 1, 0,
     269                 :         24 :                                         fmt->host_bits[0],
     270                 :            :                                         PL_FMT_CAP_TEXEL_UNIFORM);
     271         [ -  + ]:         24 :         if (!tex_vk->texel_fmt) {
     272                 :          0 :             PL_ERR(gpu, "Failed picking texel format for emulated texture!");
     273                 :          0 :             goto error;
     274                 :            :         }
     275                 :            : 
     276                 :            :         // Our format emulation requires storage image support. In order to
     277                 :            :         // make a bunch of checks happy, just mark it off as storable (and also
     278                 :            :         // enable VK_IMAGE_USAGE_STORAGE_BIT, which we do below)
     279                 :         24 :         tex->params.storable = true;
     280                 :            :     }
     281                 :            : 
     282         [ -  + ]:        596 :     if (fmtp->blit_emulated) {
     283                 :            :         // Enable what's required for sampling
     284                 :          0 :         tex->params.sampleable = fmt->caps & PL_FMT_CAP_SAMPLEABLE;
     285                 :          0 :         tex->params.storable = true;
     286                 :            :     }
     287                 :            : 
     288                 :            :     // Blit emulation on planar textures requires storage
     289   [ +  +  +  + ]:        596 :     if ((params->blit_src || params->blit_dst) && tex_vk->num_planes)
     290                 :          1 :         tex->params.storable = true;
     291                 :            : 
     292                 :            :     VkImageUsageFlags usage = 0;
     293                 :            :     VkImageCreateFlags flags = 0;
     294         [ +  + ]:        596 :     if (tex->params.sampleable)
     295                 :            :         usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
     296         [ +  + ]:        596 :     if (tex->params.renderable)
     297                 :         19 :         usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
     298         [ +  + ]:        596 :     if (tex->params.storable)
     299                 :         46 :         usage |= VK_IMAGE_USAGE_STORAGE_BIT;
     300         [ +  + ]:        596 :     if (tex->params.host_readable || tex->params.blit_src)
     301                 :        488 :         usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
     302   [ +  +  +  + ]:        596 :     if (tex->params.host_writable || tex->params.blit_dst || params->initial_data)
     303                 :        581 :         usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
     304                 :            : 
     305         [ +  + ]:         15 :     if (!usage) {
     306                 :            :         // Vulkan requires images have at least *some* image usage set, but our
     307                 :            :         // API is perfectly happy with a (useless) image. So just put
     308                 :            :         // VK_IMAGE_USAGE_TRANSFER_DST_BIT since this harmless.
     309                 :            :         usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
     310                 :            :     }
     311                 :            : 
     312         [ +  + ]:        596 :     if (tex_vk->num_planes) {
     313                 :            :         flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT |
     314                 :            :                  VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;
     315                 :            :     }
     316                 :            : 
     317                 :            :     // FIXME: Since we can't keep track of queue family ownership properly,
     318                 :            :     // and we don't know in advance what types of queue families this image
     319                 :            :     // will belong to, we're forced to share all of our images between all
     320                 :            :     // command pools.
     321                 :        596 :     uint32_t qfs[3] = {0};
     322         [ +  - ]:        596 :     pl_assert(vk->pools.num <= PL_ARRAY_SIZE(qfs));
     323         [ +  + ]:       1192 :     for (int i = 0; i < vk->pools.num; i++)
     324                 :        596 :         qfs[i] = vk->pools.elem[i]->qf;
     325                 :            : 
     326                 :       1192 :     VkImageDrmFormatModifierExplicitCreateInfoEXT drm_explicit = {
     327                 :            :         .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT,
     328                 :        596 :         .drmFormatModifier = params->shared_mem.drm_format_mod,
     329                 :            :         .drmFormatModifierPlaneCount = 1,
     330                 :       1788 :         .pPlaneLayouts = &(VkSubresourceLayout) {
     331                 :        594 :             .rowPitch = PL_DEF(params->shared_mem.stride_w, params->w),
     332   [ +  +  +  - ]:        596 :             .depthPitch = params->d ? PL_DEF(params->shared_mem.stride_h, params->h) : 0,
     333         [ +  + ]:        596 :             .offset = params->shared_mem.offset,
     334                 :            :         },
     335                 :            :     };
     336                 :            : 
     337                 :            : #ifdef VK_EXT_metal_objects
     338                 :            :     VkImportMetalTextureInfoEXT import_metal_tex = {
     339                 :            :         .sType = VK_STRUCTURE_TYPE_IMPORT_METAL_TEXTURE_INFO_EXT,
     340                 :            :         .plane = VK_IMAGE_ASPECT_PLANE_0_BIT << params->shared_mem.plane,
     341                 :            :     };
     342                 :            : 
     343                 :            :     VkImportMetalIOSurfaceInfoEXT import_iosurface = {
     344                 :            :         .sType = VK_STRUCTURE_TYPE_IMPORT_METAL_IO_SURFACE_INFO_EXT,
     345                 :            :     };
     346                 :            : #endif
     347                 :            : 
     348                 :        596 :     VkImageDrmFormatModifierListCreateInfoEXT drm_list = {
     349                 :            :         .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT,
     350                 :        596 :         .drmFormatModifierCount = fmt->num_modifiers,
     351                 :        596 :         .pDrmFormatModifiers = fmt->modifiers,
     352                 :            :     };
     353                 :            : 
     354                 :        596 :     VkExternalMemoryImageCreateInfoKHR ext_info = {
     355                 :            :         .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR,
     356                 :            :         .handleTypes = vk_handle_type,
     357                 :            :     };
     358                 :            : 
     359                 :       1192 :     VkImageCreateInfo iinfo = {
     360                 :            :         .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
     361         [ +  + ]:        596 :         .pNext = vk_handle_type ? &ext_info : NULL,
     362                 :        596 :         .imageType = tex_vk->type,
     363                 :        596 :         .format = tex_vk->img_fmt,
     364                 :            :         .extent = (VkExtent3D) {
     365                 :        596 :             .width  = params->w,
     366                 :        596 :             .height = PL_MAX(1, params->h),
     367                 :        596 :             .depth  = PL_MAX(1, params->d)
     368                 :            :         },
     369                 :            :         .mipLevels = 1,
     370                 :            :         .arrayLayers = 1,
     371                 :            :         .samples = VK_SAMPLE_COUNT_1_BIT,
     372                 :            :         .tiling = VK_IMAGE_TILING_OPTIMAL,
     373                 :            :         .usage = usage,
     374                 :            :         .flags = flags,
     375                 :            :         .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
     376                 :            :         .sharingMode = vk->pools.num > 1 ? VK_SHARING_MODE_CONCURRENT
     377                 :        596 :                                          : VK_SHARING_MODE_EXCLUSIVE,
     378                 :            :         .queueFamilyIndexCount = vk->pools.num,
     379                 :            :         .pQueueFamilyIndices = qfs,
     380                 :            :     };
     381                 :            : 
     382                 :        596 :     struct vk_malloc_params mparams = {
     383                 :            :         .optimal = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
     384                 :        596 :         .export_handle = params->export_handle,
     385                 :        596 :         .import_handle = params->import_handle,
     386                 :            :         .shared_mem = params->shared_mem,
     387                 :        596 :         .debug_tag = params->debug_tag,
     388                 :            :     };
     389                 :            : 
     390         [ +  + ]:        596 :     if (params->import_handle == PL_HANDLE_DMA_BUF) {
     391                 :          2 :         vk_link_struct(&iinfo, &drm_explicit);
     392                 :          2 :         iinfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
     393                 :          2 :         mparams.shared_mem.offset = 0x0; // handled via plane offsets
     394                 :            :     }
     395                 :            : 
     396                 :            : #ifdef VK_EXT_metal_objects
     397                 :            :     if (params->import_handle == PL_HANDLE_MTL_TEX) {
     398                 :            :         vk_link_struct(&iinfo, &import_metal_tex);
     399                 :            :         import_metal_tex.mtlTexture = params->shared_mem.handle.handle;
     400                 :            :     }
     401                 :            : 
     402                 :            :     if (params->import_handle == PL_HANDLE_IOSURFACE) {
     403                 :            :         vk_link_struct(&iinfo, &import_iosurface);
     404                 :            :         import_iosurface.ioSurface = params->shared_mem.handle.handle;
     405                 :            :     }
     406                 :            : #endif
     407                 :            : 
     408         [ +  + ]:        596 :     if (params->export_handle == PL_HANDLE_DMA_BUF) {
     409         [ -  + ]:          4 :         pl_assert(drm_list.drmFormatModifierCount > 0);
     410                 :          4 :         vk_link_struct(&iinfo, &drm_list);
     411                 :          4 :         iinfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
     412                 :            :     }
     413                 :            : 
     414                 :            :     // Double-check physical image format limits and fail if invalid
     415                 :        596 :     VkPhysicalDeviceImageDrmFormatModifierInfoEXT drm_pinfo = {
     416                 :            :         .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT,
     417                 :        596 :         .sharingMode = iinfo.sharingMode,
     418                 :        596 :         .queueFamilyIndexCount = iinfo.queueFamilyIndexCount,
     419                 :        596 :         .pQueueFamilyIndices = iinfo.pQueueFamilyIndices,
     420                 :            :     };
     421                 :            : 
     422                 :        596 :     VkPhysicalDeviceExternalImageFormatInfoKHR ext_pinfo = {
     423                 :            :         .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR,
     424                 :        596 :         .handleType = ext_info.handleTypes,
     425                 :            :     };
     426                 :            : 
     427         [ +  + ]:        596 :     if (handle_type == PL_HANDLE_DMA_BUF) {
     428         [ +  + ]:          6 :         if (params->import_handle) {
     429                 :            :             // On import, we know exactly which format modifier to test
     430                 :          2 :             drm_pinfo.drmFormatModifier = drm_explicit.drmFormatModifier;
     431                 :            :         } else {
     432                 :            :             // On export, the choice of format modifier is ambiguous, because
     433                 :            :             // we offer the implementation a whole list to choose from. In
     434                 :            :             // principle, we must check *all* supported drm format modifiers,
     435                 :            :             // but in practice it should hopefully suffice to just check one
     436                 :          4 :             drm_pinfo.drmFormatModifier = drm_list.pDrmFormatModifiers[0];
     437                 :            :         }
     438                 :          6 :         vk_link_struct(&ext_pinfo, &drm_pinfo);
     439                 :            :     }
     440                 :            : 
     441                 :       1192 :     VkPhysicalDeviceImageFormatInfo2KHR pinfo = {
     442                 :            :         .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR,
     443         [ +  + ]:        596 :         .pNext = vk_handle_type ? &ext_pinfo : NULL,
     444                 :        596 :         .format = iinfo.format,
     445                 :        596 :         .type = iinfo.imageType,
     446                 :        596 :         .tiling = iinfo.tiling,
     447                 :        596 :         .usage = iinfo.usage,
     448                 :        596 :         .flags = iinfo.flags,
     449                 :            :     };
     450                 :            : 
     451                 :        596 :     VkExternalImageFormatPropertiesKHR ext_props = {
     452                 :            :         .sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR,
     453                 :            :     };
     454                 :            : 
     455                 :       1192 :     VkImageFormatProperties2KHR props = {
     456                 :            :         .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR,
     457         [ +  + ]:        596 :         .pNext = vk_handle_type ? &ext_props : NULL,
     458                 :            :     };
     459                 :            : 
     460                 :            :     VkResult res;
     461                 :        596 :     res = vk->GetPhysicalDeviceImageFormatProperties2KHR(vk->physd, &pinfo, &props);
     462         [ +  + ]:        596 :     if (res == VK_ERROR_FORMAT_NOT_SUPPORTED) {
     463                 :        109 :         PL_DEBUG(gpu, "Texture creation failed: not supported");
     464                 :        109 :         goto error;
     465                 :            :     } else {
     466         [ -  + ]:        487 :         PL_VK_ASSERT(res, "Querying image format properties");
     467                 :            :     }
     468                 :            : 
     469                 :        487 :     VkExtent3D max = props.imageFormatProperties.maxExtent;
     470   [ +  -  +  -  :        487 :     if (params->w > max.width || params->h > max.height || params->d > max.depth)
                   -  + ]
     471                 :            :     {
     472                 :          0 :         PL_ERR(gpu, "Requested image size %dx%dx%d exceeds the maximum allowed "
     473                 :            :                "dimensions %dx%dx%d for vulkan image format %x",
     474                 :            :                params->w, params->h, params->d, max.width, max.height, max.depth,
     475                 :            :                (unsigned) iinfo.format);
     476                 :          0 :         goto error;
     477                 :            :     }
     478                 :            : 
     479                 :            :     // Ensure the handle type is supported
     480         [ +  + ]:        487 :     if (vk_handle_type) {
     481                 :          8 :         bool ok = vk_external_mem_check(vk, &ext_props.externalMemoryProperties,
     482                 :          8 :                                         handle_type, params->import_handle);
     483         [ -  + ]:          8 :         if (!ok) {
     484                 :          0 :             PL_ERR(gpu, "Requested handle type is not compatible with the "
     485                 :            :                    "specified combination of image parameters. Possibly the "
     486                 :            :                    "handle type is unsupported altogether?");
     487                 :          0 :             goto error;
     488                 :            :         }
     489                 :            :     }
     490                 :            : 
     491         [ -  + ]:        487 :     VK(vk->CreateImage(vk->dev, &iinfo, PL_VK_ALLOC, &tex_vk->img));
     492                 :        487 :     tex_vk->usage_flags = iinfo.usage;
     493                 :            : 
     494                 :        487 :     VkMemoryDedicatedRequirements ded_reqs = {
     495                 :            :         .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR,
     496                 :            :     };
     497                 :            : 
     498                 :        487 :     VkMemoryRequirements2 reqs = {
     499                 :            :         .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR,
     500                 :            :         .pNext = &ded_reqs,
     501                 :            :     };
     502                 :            : 
     503                 :        487 :     VkImageMemoryRequirementsInfo2 req_info = {
     504                 :            :         .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR,
     505                 :        487 :         .image = tex_vk->img,
     506                 :            :     };
     507                 :            : 
     508                 :        487 :     vk->GetImageMemoryRequirements2(vk->dev, &req_info, &reqs);
     509                 :        487 :     mparams.reqs = reqs.memoryRequirements;
     510         [ -  + ]:        487 :     if (ded_reqs.prefersDedicatedAllocation) {
     511                 :          0 :         mparams.ded_image = tex_vk->img;
     512         [ #  # ]:          0 :         if (vk_mem_handle_type(params->import_handle))
     513                 :          0 :             mparams.shared_mem.size = reqs.memoryRequirements.size;
     514                 :            :     }
     515                 :            : 
     516         [ +  + ]:        487 :     const char *debug_tag = params->debug_tag ? params->debug_tag :
     517         [ +  + ]:         11 :                             params->import_handle ? "imported" : "created";
     518                 :            : 
     519   [ +  +  +  - ]:        487 :     if (!params->import_handle || vk_mem_handle_type(params->import_handle)) {
     520                 :        487 :         struct vk_memslice *mem = &tex_vk->mem;
     521         [ -  + ]:        487 :         if (!vk_malloc_slice(vk->ma, mem, &mparams))
     522                 :          0 :             goto error;
     523                 :            : 
     524         [ -  + ]:        487 :         VK(vk->BindImageMemory(vk->dev, tex_vk->img, mem->vkmem, mem->offset));
     525                 :            :     }
     526                 :            : 
     527                 :            :     static const char * const plane_names[4] = {
     528                 :            :         "plane 0", "plane 1", "plane 2", "plane 3",
     529                 :            :     };
     530                 :            : 
     531         [ -  + ]:        487 :     if (tex_vk->num_planes) {
     532         [ #  # ]:          0 :         for (int i = 0; i < tex_vk->num_planes; i++) {
     533                 :            :             struct pl_tex_t *plane;
     534                 :            : 
     535         [ #  # ]:          0 :             pl_assert(tex_vk->type == VK_IMAGE_TYPE_2D);
     536         [ #  # ]:          0 :             plane = (struct pl_tex_t *) pl_vulkan_wrap(gpu, pl_vulkan_wrap_params(
     537                 :            :                 .image      = tex_vk->img,
     538                 :            :                 .aspect     = VK_IMAGE_ASPECT_PLANE_0_BIT << i,
     539                 :            :                 .width      = PL_RSHIFT_UP(tex->params.w, fmt->planes[i].shift_x),
     540                 :            :                 .height     = PL_RSHIFT_UP(tex->params.h, fmt->planes[i].shift_y),
     541                 :            :                 .format     = fmtp->vk_fmt->pfmt[i].fmt,
     542                 :            :                 .usage      = usage,
     543                 :            :                 .user_data  = params->user_data,
     544                 :            :                 .debug_tag  = PL_DEF(params->debug_tag, plane_names[i]),
     545                 :            :             ));
     546         [ #  # ]:          0 :             if (!plane)
     547                 :          0 :                 goto error;
     548                 :          0 :             plane->parent = tex;
     549                 :          0 :             tex->planes[i] = plane;
     550                 :          0 :             tex_vk->planes[i] = PL_PRIV(plane);
     551                 :          0 :             tex_vk->planes[i]->held = false;
     552                 :          0 :             tex_vk->planes[i]->layout = tex_vk->layout;
     553                 :            :         }
     554                 :            : 
     555                 :            :         // Explicitly mask out all usage flags from planar parent images
     556         [ #  # ]:          0 :         pl_assert(!fmt->caps);
     557                 :          0 :         tex->params.sampleable      = false;
     558                 :          0 :         tex->params.renderable      = false;
     559                 :          0 :         tex->params.storable        = false;
     560                 :          0 :         tex->params.blit_src        = false;
     561                 :          0 :         tex->params.blit_dst        = false;
     562                 :          0 :         tex->params.host_writable   = false;
     563                 :          0 :         tex->params.host_readable   = false;
     564                 :            :     }
     565                 :            : 
     566         [ -  + ]:        487 :     if (!vk_init_image(gpu, tex, debug_tag))
     567                 :          0 :         goto error;
     568                 :            : 
     569         [ +  + ]:        487 :     if (params->export_handle)
     570                 :          6 :         tex->shared_mem = tex_vk->mem.shared_mem;
     571                 :            : 
     572         [ +  + ]:        487 :     if (params->export_handle == PL_HANDLE_DMA_BUF) {
     573         [ +  - ]:          4 :         if (vk->GetImageDrmFormatModifierPropertiesEXT) {
     574                 :            : 
     575                 :            :             // Query the DRM format modifier and plane layout from the driver
     576                 :          4 :             VkImageDrmFormatModifierPropertiesEXT mod_props = {
     577                 :            :                 .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT,
     578                 :            :             };
     579                 :            : 
     580         [ -  + ]:          4 :             VK(vk->GetImageDrmFormatModifierPropertiesEXT(vk->dev, tex_vk->img, &mod_props));
     581                 :          4 :             tex->shared_mem.drm_format_mod = mod_props.drmFormatModifier;
     582                 :            : 
     583                 :          4 :             VkSubresourceLayout layout = {0};
     584                 :          4 :             VkImageSubresource plane = {
     585                 :            :                 .aspectMask = VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT,
     586                 :            :             };
     587                 :            : 
     588                 :          4 :             vk->GetImageSubresourceLayout(vk->dev, tex_vk->img, &plane, &layout);
     589         [ -  + ]:          4 :             if (layout.offset != 0) {
     590                 :          0 :                 PL_ERR(gpu, "Exported DRM plane 0 has nonzero offset %zu, "
     591                 :            :                        "this should never happen! Erroring for safety...",
     592                 :            :                        (size_t) layout.offset);
     593                 :          0 :                 goto error;
     594                 :            :             }
     595                 :          4 :             tex->shared_mem.stride_w = layout.rowPitch;
     596                 :          4 :             tex->shared_mem.stride_h = layout.depthPitch;
     597                 :            : 
     598                 :            :         } else {
     599                 :            : 
     600                 :            :             // Fallback for no modifiers, just do something stupid.
     601                 :          0 :             tex->shared_mem.drm_format_mod = DRM_FORMAT_MOD_INVALID;
     602                 :          0 :             tex->shared_mem.stride_w = params->w;
     603                 :          0 :             tex->shared_mem.stride_h = params->h;
     604                 :            : 
     605                 :            :         }
     606                 :            :     }
     607                 :            : 
     608         [ +  + ]:        487 :     if (params->initial_data) {
     609                 :         85 :         struct pl_tex_transfer_params ul_params = {
     610                 :            :             .tex = tex,
     611                 :            :             .ptr = (void *) params->initial_data,
     612                 :         85 :             .rc = { 0, 0, 0, params->w, params->h, params->d },
     613                 :            :         };
     614                 :            : 
     615                 :            :         // Since we re-use GPU helpers which require writable images, just fake it
     616                 :         85 :         bool writable = tex->params.host_writable;
     617                 :         85 :         tex->params.host_writable = true;
     618         [ -  + ]:         85 :         if (!pl_tex_upload(gpu, &ul_params))
     619                 :          0 :             goto error;
     620                 :         85 :         tex->params.host_writable = writable;
     621                 :            :     }
     622                 :            : 
     623                 :            :     return tex;
     624                 :            : 
     625                 :        109 : error:
     626                 :        109 :     vk_tex_destroy(gpu, tex);
     627                 :        109 :     return NULL;
     628                 :            : }
     629                 :            : 
     630                 :       1482 : void vk_tex_invalidate(pl_gpu gpu, pl_tex tex)
     631                 :            : {
     632                 :       1482 :     struct pl_tex_vk *tex_vk = PL_PRIV(tex);
     633                 :       1482 :     tex_vk->may_invalidate = true;
     634         [ -  + ]:       1482 :     for (int i = 0; i < tex_vk->num_planes; i++)
     635                 :          0 :         tex_vk->planes[i]->may_invalidate = true;
     636                 :       1482 : }
     637                 :            : 
     638                 :          0 : static bool tex_clear_fallback(pl_gpu gpu, pl_tex tex,
     639                 :            :                                const union pl_clear_color color)
     640                 :            : {
     641                 :          0 :     pl_tex pixel = pl_tex_create(gpu, pl_tex_params(
     642                 :            :         .w = 1,
     643                 :            :         .h = 1,
     644                 :            :         .format = tex->params.format,
     645                 :            :         .storable = true,
     646                 :            :         .blit_src = true,
     647                 :            :         .blit_dst = true,
     648                 :            :     ));
     649         [ #  # ]:          0 :     if (!pixel)
     650                 :            :         return false;
     651                 :            : 
     652                 :          0 :     pl_tex_clear_ex(gpu, pixel, color);
     653                 :            : 
     654         [ #  # ]:          0 :     pl_assert(tex->params.storable);
     655                 :          0 :     pl_tex_blit(gpu, pl_tex_blit_params(
     656                 :            :         .src = pixel,
     657                 :            :         .dst = tex,
     658                 :            :         .sample_mode = PL_TEX_SAMPLE_NEAREST,
     659                 :            :     ));
     660                 :            : 
     661                 :          0 :     pl_tex_destroy(gpu, &pixel);
     662                 :          0 :     return true;
     663                 :            : }
     664                 :            : 
     665                 :        202 : void vk_tex_clear_ex(pl_gpu gpu, pl_tex tex, const union pl_clear_color color)
     666                 :            : {
     667                 :        202 :     struct pl_vk *p = PL_PRIV(gpu);
     668                 :        202 :     struct vk_ctx *vk = p->vk;
     669                 :        202 :     struct pl_tex_vk *tex_vk = PL_PRIV(tex);
     670                 :            : 
     671         [ -  + ]:        202 :     if (tex_vk->aspect != VK_IMAGE_ASPECT_COLOR_BIT) {
     672         [ #  # ]:          0 :         if (!tex_clear_fallback(gpu, tex,  color)) {
     673                 :          0 :             PL_ERR(gpu, "Failed clearing imported planar image: color aspect "
     674                 :            :                    "clears disallowed by spec and no shader fallback "
     675                 :            :                    "available");
     676                 :            :         }
     677                 :          0 :         return;
     678                 :            :     }
     679                 :            : 
     680                 :        202 :     struct vk_cmd *cmd = CMD_BEGIN(GRAPHICS);
     681         [ +  - ]:        202 :     if (!cmd)
     682                 :            :         return;
     683                 :            : 
     684                 :        202 :     vk_tex_barrier(gpu, cmd, tex, VK_PIPELINE_STAGE_2_CLEAR_BIT,
     685                 :            :                    VK_ACCESS_2_TRANSFER_WRITE_BIT,
     686                 :            :                    VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
     687                 :            :                    VK_QUEUE_FAMILY_IGNORED);
     688                 :            : 
     689                 :            :     pl_static_assert(sizeof(VkClearColorValue) == sizeof(union pl_clear_color));
     690                 :            :     const VkClearColorValue *clearColor = (const VkClearColorValue *) &color;
     691                 :            : 
     692         [ -  + ]:        202 :     pl_assert(tex_vk->aspect == VK_IMAGE_ASPECT_COLOR_BIT);
     693                 :            :     static const VkImageSubresourceRange range = {
     694                 :            :         .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
     695                 :            :         .levelCount = 1,
     696                 :            :         .layerCount = 1,
     697                 :            :     };
     698                 :            : 
     699                 :        202 :     vk->CmdClearColorImage(cmd->buf, tex_vk->img, tex_vk->layout,
     700                 :            :                            clearColor, 1, &range);
     701                 :            : 
     702                 :        202 :     CMD_FINISH(&cmd);
     703                 :            : }
     704                 :            : 
     705                 :        156 : void vk_tex_blit(pl_gpu gpu, const struct pl_tex_blit_params *params)
     706                 :            : {
     707                 :        156 :     struct pl_vk *p = PL_PRIV(gpu);
     708                 :        156 :     struct vk_ctx *vk = p->vk;
     709                 :        156 :     struct pl_tex_vk *src_vk = PL_PRIV(params->src);
     710                 :        156 :     struct pl_tex_vk *dst_vk = PL_PRIV(params->dst);
     711                 :        156 :     struct pl_fmt_vk *src_fmtp = PL_PRIV(params->src->params.format);
     712                 :        156 :     struct pl_fmt_vk *dst_fmtp = PL_PRIV(params->dst->params.format);
     713   [ +  -  -  + ]:        156 :     bool blit_emulated = src_fmtp->blit_emulated || dst_fmtp->blit_emulated;
     714         [ +  - ]:        156 :     bool planar_fallback = src_vk->aspect != VK_IMAGE_ASPECT_COLOR_BIT ||
     715         [ -  + ]:        156 :                            dst_vk->aspect != VK_IMAGE_ASPECT_COLOR_BIT;
     716                 :            : 
     717                 :        156 :     pl_rect3d src_rc = params->src_rc, dst_rc = params->dst_rc;
     718   [ +  -  -  +  :        156 :     bool requires_scaling = !pl_rect3d_eq(src_rc, dst_rc);
          -  +  -  +  -  
                +  -  + ]
     719   [ +  -  -  + ]:        156 :     if ((requires_scaling && blit_emulated) || planar_fallback) {
     720         [ #  # ]:          0 :         if (!pl_tex_blit_compute(gpu, params))
     721                 :          0 :             PL_ERR(gpu, "Failed emulating texture blit, incompatible textures?");
     722                 :          0 :         return;
     723                 :            :     }
     724                 :            : 
     725                 :        156 :     struct vk_cmd *cmd = CMD_BEGIN(GRAPHICS);
     726         [ +  - ]:        156 :     if (!cmd)
     727                 :            :         return;
     728                 :            : 
     729                 :            :     // When the blit operation doesn't require scaling, we can use the more
     730                 :            :     // efficient vkCmdCopyImage instead of vkCmdBlitImage
     731         [ +  - ]:        156 :     if (!requires_scaling) {
     732                 :        156 :         vk_tex_barrier(gpu, cmd, params->src, VK_PIPELINE_STAGE_2_COPY_BIT,
     733                 :            :                        VK_ACCESS_2_TRANSFER_READ_BIT,
     734                 :            :                        VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
     735                 :            :                        VK_QUEUE_FAMILY_IGNORED);
     736                 :            : 
     737                 :        156 :         vk_tex_barrier(gpu, cmd, params->dst, VK_PIPELINE_STAGE_2_COPY_BIT,
     738                 :            :                        VK_ACCESS_2_TRANSFER_WRITE_BIT,
     739                 :            :                        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
     740                 :            :                        VK_QUEUE_FAMILY_IGNORED);
     741                 :            : 
     742                 :        156 :         pl_rect3d_normalize(&src_rc);
     743                 :            : 
     744                 :        156 :         VkImageCopy region = {
     745                 :            :             .srcSubresource = {
     746                 :        156 :                 .aspectMask = src_vk->aspect,
     747                 :            :                 .layerCount = 1,
     748                 :            :             },
     749                 :            :             .dstSubresource = {
     750                 :        156 :                 .aspectMask = dst_vk->aspect,
     751                 :            :                 .layerCount = 1,
     752                 :            :             },
     753                 :        156 :             .srcOffset = {src_rc.x0, src_rc.y0, src_rc.z0},
     754                 :            :             .dstOffset = {src_rc.x0, src_rc.y0, src_rc.z0},
     755                 :            :             .extent = {
     756                 :        156 :                 pl_rect_w(src_rc),
     757                 :        156 :                 pl_rect_h(src_rc),
     758                 :        156 :                 pl_rect_d(src_rc),
     759                 :            :             },
     760                 :            :         };
     761                 :            : 
     762                 :        156 :         vk->CmdCopyImage(cmd->buf, src_vk->img, src_vk->layout,
     763                 :            :                          dst_vk->img, dst_vk->layout, 1, &region);
     764                 :            :     } else {
     765                 :          0 :         vk_tex_barrier(gpu, cmd, params->src, VK_PIPELINE_STAGE_2_BLIT_BIT,
     766                 :            :                        VK_ACCESS_2_TRANSFER_READ_BIT,
     767                 :            :                        VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
     768                 :            :                        VK_QUEUE_FAMILY_IGNORED);
     769                 :            : 
     770                 :          0 :         vk_tex_barrier(gpu, cmd, params->dst, VK_PIPELINE_STAGE_2_BLIT_BIT,
     771                 :            :                        VK_ACCESS_2_TRANSFER_WRITE_BIT,
     772                 :            :                        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
     773                 :            :                        VK_QUEUE_FAMILY_IGNORED);
     774                 :            : 
     775                 :          0 :         VkImageBlit region = {
     776                 :            :             .srcSubresource = {
     777                 :          0 :                 .aspectMask = src_vk->aspect,
     778                 :            :                 .layerCount = 1,
     779                 :            :             },
     780                 :            :             .dstSubresource = {
     781                 :          0 :                 .aspectMask = dst_vk->aspect,
     782                 :            :                 .layerCount = 1,
     783                 :            :             },
     784                 :          0 :             .srcOffsets = {{src_rc.x0, src_rc.y0, src_rc.z0},
     785                 :          0 :                            {src_rc.x1, src_rc.y1, src_rc.z1}},
     786                 :            :             .dstOffsets = {{dst_rc.x0, dst_rc.y0, dst_rc.z0},
     787                 :            :                            {dst_rc.x1, dst_rc.y1, dst_rc.z1}},
     788                 :            :         };
     789                 :            : 
     790                 :            :         static const VkFilter filters[PL_TEX_SAMPLE_MODE_COUNT] = {
     791                 :            :             [PL_TEX_SAMPLE_NEAREST] = VK_FILTER_NEAREST,
     792                 :            :             [PL_TEX_SAMPLE_LINEAR]  = VK_FILTER_LINEAR,
     793                 :            :         };
     794                 :            : 
     795                 :          0 :         vk->CmdBlitImage(cmd->buf, src_vk->img, src_vk->layout,
     796                 :            :                          dst_vk->img, dst_vk->layout, 1, &region,
     797                 :          0 :                          filters[params->sample_mode]);
     798                 :            :     }
     799                 :            : 
     800                 :        156 :     CMD_FINISH(&cmd);
     801                 :            : }
     802                 :            : 
     803                 :            : // Determine the best queue type to perform a buffer<->image copy on
     804                 :        490 : static enum queue_type vk_img_copy_queue(pl_gpu gpu, pl_tex tex,
     805                 :            :                                          const struct VkBufferImageCopy *region)
     806                 :            : {
     807                 :        490 :     struct pl_vk *p = PL_PRIV(gpu);
     808                 :        490 :     struct vk_ctx *vk = p->vk;
     809                 :            : 
     810                 :        490 :     const struct pl_tex_vk *tex_vk = PL_PRIV(tex);
     811                 :        490 :     enum queue_type queue = tex_vk->transfer_queue;
     812         [ +  + ]:        490 :     if (queue != TRANSFER)
     813                 :            :         return queue;
     814                 :            : 
     815                 :        405 :     VkExtent3D alignment = vk->pool_transfer->props.minImageTransferGranularity;
     816                 :            : 
     817                 :            :     enum queue_type fallback = GRAPHICS;
     818         [ -  + ]:        405 :     if (gpu->limits.compute_queues > gpu->limits.fragment_queues)
     819                 :            :         fallback = COMPUTE; // prefer async compute queue
     820                 :            : 
     821         [ +  - ]:        405 :     int tex_w = PL_DEF(tex->params.w, 1),
     822         [ +  + ]:        405 :         tex_h = PL_DEF(tex->params.h, 1),
     823         [ +  + ]:        405 :         tex_d = PL_DEF(tex->params.d, 1);
     824                 :            : 
     825                 :        405 :     bool full_w = region->imageOffset.x + region->imageExtent.width  == tex_w,
     826                 :        405 :          full_h = region->imageOffset.y + region->imageExtent.height == tex_h,
     827                 :        405 :          full_d = region->imageOffset.z + region->imageExtent.depth  == tex_d;
     828                 :            : 
     829         [ +  - ]:        405 :     if (alignment.width) {
     830                 :            : 
     831                 :            :         bool unaligned = false;
     832                 :        405 :         unaligned |= region->imageOffset.x % alignment.width;
     833                 :        405 :         unaligned |= region->imageOffset.y % alignment.height;
     834                 :        405 :         unaligned |= region->imageOffset.z % alignment.depth;
     835   [ -  +  -  - ]:        405 :         unaligned |= (region->imageExtent.width  % alignment.width)  && !full_w;
     836   [ -  +  -  - ]:        405 :         unaligned |= (region->imageExtent.height % alignment.height) && !full_h;
     837   [ -  +  -  - ]:        405 :         unaligned |= (region->imageExtent.depth  % alignment.depth)  && !full_d;
     838                 :            : 
     839         [ -  + ]:        405 :         return unaligned ? fallback : queue;
     840                 :            : 
     841                 :            :     } else {
     842                 :            : 
     843                 :            :         // an alignment of {0} means the copy must span the entire image
     844                 :            :         bool unaligned = false;
     845   [ #  #  #  # ]:          0 :         unaligned |= region->imageOffset.x || !full_w;
     846   [ #  #  #  # ]:          0 :         unaligned |= region->imageOffset.y || !full_h;
     847   [ #  #  #  # ]:          0 :         unaligned |= region->imageOffset.z || !full_d;
     848                 :            : 
     849         [ #  # ]:          0 :         return unaligned ? fallback : queue;
     850                 :            : 
     851                 :            :     }
     852                 :            : }
     853                 :            : 
     854                 :        187 : static void tex_xfer_cb(void *ctx, void *arg)
     855                 :            : {
     856                 :        187 :     void (*fun)(void *priv) = ctx;
     857                 :        187 :     fun(arg);
     858                 :        187 : }
     859                 :            : 
     860                 :        569 : bool vk_tex_upload(pl_gpu gpu, const struct pl_tex_transfer_params *params)
     861                 :            : {
     862                 :        569 :     struct pl_vk *p = PL_PRIV(gpu);
     863                 :        569 :     struct vk_ctx *vk = p->vk;
     864                 :        569 :     pl_tex tex = params->tex;
     865                 :        569 :     pl_fmt fmt = tex->params.format;
     866                 :        569 :     struct pl_tex_vk *tex_vk = PL_PRIV(tex);
     867                 :        569 :     struct pl_tex_transfer_params *slices = NULL;
     868                 :            :     int num_slices = 0;
     869                 :            : 
     870         [ +  + ]:        569 :     if (!params->buf)
     871                 :        283 :         return pl_tex_upload_pbo(gpu, params);
     872                 :            : 
     873                 :            :     pl_buf buf = params->buf;
     874                 :        286 :     struct pl_buf_vk *buf_vk = PL_PRIV(buf);
     875                 :        286 :     pl_rect3d rc = params->rc;
     876                 :        286 :     const size_t size = pl_tex_transfer_size(params);
     877                 :        286 :     const size_t buf_offset = buf_vk->mem.offset + params->buf_offset;
     878                 :        286 :     bool unaligned = buf_offset % fmt->texel_size;
     879         [ +  + ]:        286 :     if (unaligned)
     880                 :          3 :         PL_TRACE(gpu, "vk_tex_upload: unaligned transfer (slow path)");
     881                 :            : 
     882   [ +  +  +  + ]:        286 :     if (fmt->emulated || unaligned) {
     883                 :            : 
     884                 :            :         // Create all slice buffers first, to early-fail if OOM, and to avoid
     885                 :            :         // blocking unnecessarily on waiting for these buffers to get read from
     886                 :         12 :         num_slices = pl_tex_transfer_slices(gpu, tex_vk->texel_fmt, params, &slices);
     887         [ +  + ]:         24 :         for (int i = 0; i < num_slices; i++) {
     888                 :         12 :             slices[i].buf = pl_buf_create(gpu, pl_buf_params(
     889                 :            :                 .memory_type = PL_BUF_MEM_DEVICE,
     890                 :            :                 .format      = tex_vk->texel_fmt,
     891                 :            :                 .size        = pl_tex_transfer_size(&slices[i]),
     892                 :            :                 .storable    = fmt->emulated,
     893                 :            :             ));
     894                 :            : 
     895         [ -  + ]:         12 :             if (!slices[i].buf) {
     896                 :          0 :                 PL_ERR(gpu, "Failed creating buffer for tex upload fallback!");
     897                 :            :                 num_slices = i; // only clean up buffers up to here
     898                 :          0 :                 goto error;
     899                 :            :             }
     900                 :            :         }
     901                 :            : 
     902                 :            :         // All temporary buffers successfully created, begin copying source data
     903                 :         12 :         struct vk_cmd *cmd = CMD_BEGIN_TIMED(tex_vk->transfer_queue,
     904                 :            :                                              params->timer);
     905         [ -  + ]:         12 :         if (!cmd)
     906                 :          0 :             goto error;
     907                 :            : 
     908                 :         12 :         vk_buf_barrier(gpu, cmd, buf, VK_PIPELINE_STAGE_2_COPY_BIT,
     909                 :         12 :                        VK_ACCESS_2_TRANSFER_READ_BIT, params->buf_offset, size,
     910                 :            :                        false);
     911                 :            : 
     912         [ +  + ]:         24 :         for (int i = 0; i < num_slices; i++) {
     913                 :         12 :             pl_buf slice = slices[i].buf;
     914                 :         12 :             struct pl_buf_vk *slice_vk = PL_PRIV(slice);
     915                 :         12 :             vk_buf_barrier(gpu, cmd, slice, VK_PIPELINE_STAGE_2_COPY_BIT,
     916                 :         12 :                            VK_ACCESS_2_TRANSFER_WRITE_BIT, 0, slice->params.size,
     917                 :            :                            false);
     918                 :            : 
     919                 :         12 :             vk->CmdCopyBuffer(cmd->buf, buf_vk->mem.buf, slice_vk->mem.buf, 1, &(VkBufferCopy) {
     920                 :         12 :                 .srcOffset = buf_vk->mem.offset + slices[i].buf_offset,
     921                 :         12 :                 .dstOffset = slice_vk->mem.offset,
     922                 :         12 :                 .size      = slice->params.size,
     923                 :            :             });
     924                 :            :         }
     925                 :            : 
     926         [ +  + ]:         12 :         if (params->callback)
     927                 :          4 :             vk_cmd_callback(cmd, tex_xfer_cb, params->callback, params->priv);
     928                 :            : 
     929                 :         12 :         bool ok = CMD_FINISH(&cmd);
     930                 :            : 
     931                 :            :         // Finally, dispatch the (texel) upload asynchronously. We can fire
     932                 :            :         // the callback already at the completion of previous command because
     933                 :            :         // these temporary buffers already hold persistent copies of the data
     934         [ +  + ]:         24 :         for (int i = 0; i < num_slices; i++) {
     935         [ +  - ]:         12 :             if (ok) {
     936                 :         12 :                 slices[i].buf_offset = 0;
     937                 :          9 :                 ok = fmt->emulated ? pl_tex_upload_texel(gpu, &slices[i])
     938         [ +  + ]:         12 :                                    : pl_tex_upload(gpu, &slices[i]);
     939                 :            :             }
     940                 :         12 :             pl_buf_destroy(gpu, &slices[i].buf);
     941                 :            :         }
     942                 :            : 
     943                 :         12 :         pl_free(slices);
     944                 :         12 :         return ok;
     945                 :            : 
     946                 :            :     } else {
     947                 :            : 
     948         [ -  + ]:        274 :         pl_assert(fmt->texel_align == fmt->texel_size);
     949                 :        274 :         const VkBufferImageCopy region = {
     950                 :            :             .bufferOffset = buf_offset,
     951                 :        274 :             .bufferRowLength = params->row_pitch / fmt->texel_size,
     952                 :        274 :             .bufferImageHeight = params->depth_pitch / params->row_pitch,
     953                 :            :             .imageOffset = { rc.x0, rc.y0, rc.z0 },
     954                 :        274 :             .imageExtent = { rc.x1, rc.y1, rc.z1 },
     955                 :            :             .imageSubresource = {
     956                 :        274 :                 .aspectMask = tex_vk->aspect,
     957                 :            :                 .layerCount = 1,
     958                 :            :             },
     959                 :            :         };
     960                 :            : 
     961                 :        274 :         enum queue_type queue = vk_img_copy_queue(gpu, tex, &region);
     962                 :        274 :         struct vk_cmd *cmd = CMD_BEGIN_TIMED(queue, params->timer);
     963         [ -  + ]:        274 :         if (!cmd)
     964                 :          0 :             goto error;
     965                 :            : 
     966                 :        274 :         vk_buf_barrier(gpu, cmd, buf, VK_PIPELINE_STAGE_2_COPY_BIT,
     967                 :        274 :                        VK_ACCESS_2_TRANSFER_READ_BIT, params->buf_offset, size,
     968                 :            :                        false);
     969                 :        274 :         vk_tex_barrier(gpu, cmd, tex, VK_PIPELINE_STAGE_2_COPY_BIT,
     970                 :            :                        VK_ACCESS_2_TRANSFER_WRITE_BIT,
     971                 :            :                        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
     972                 :            :                        VK_QUEUE_FAMILY_IGNORED);
     973                 :        274 :         vk->CmdCopyBufferToImage(cmd->buf, buf_vk->mem.buf, tex_vk->img,
     974                 :            :                                  tex_vk->layout, 1, &region);
     975                 :            : 
     976         [ +  + ]:        274 :         if (params->callback)
     977                 :          3 :             vk_cmd_callback(cmd, tex_xfer_cb, params->callback, params->priv);
     978                 :            : 
     979                 :        274 :         return CMD_FINISH(&cmd);
     980                 :            :     }
     981                 :            : 
     982                 :            :     pl_unreachable();
     983                 :            : 
     984                 :          0 : error:
     985         [ #  # ]:          0 :     for (int i = 0; i < num_slices; i++)
     986                 :          0 :         pl_buf_destroy(gpu, &slices[i].buf);
     987                 :          0 :     pl_free(slices);
     988                 :          0 :     return false;
     989                 :            : }
     990                 :            : 
     991                 :        453 : bool vk_tex_download(pl_gpu gpu, const struct pl_tex_transfer_params *params)
     992                 :            : {
     993                 :        453 :     struct pl_vk *p = PL_PRIV(gpu);
     994                 :        453 :     struct vk_ctx *vk = p->vk;
     995                 :        453 :     pl_tex tex = params->tex;
     996                 :        453 :     pl_fmt fmt = tex->params.format;
     997                 :        453 :     struct pl_tex_vk *tex_vk = PL_PRIV(tex);
     998                 :        453 :     struct pl_tex_transfer_params *slices = NULL;
     999                 :            :     int num_slices = 0;
    1000                 :            : 
    1001         [ +  + ]:        453 :     if (!params->buf)
    1002                 :        225 :         return pl_tex_download_pbo(gpu, params);
    1003                 :            : 
    1004                 :            :     pl_buf buf = params->buf;
    1005                 :        228 :     struct pl_buf_vk *buf_vk = PL_PRIV(buf);
    1006                 :        228 :     pl_rect3d rc = params->rc;
    1007                 :        228 :     const size_t size = pl_tex_transfer_size(params);
    1008                 :        228 :     const size_t buf_offset = buf_vk->mem.offset + params->buf_offset;
    1009                 :        228 :     bool unaligned = buf_offset % fmt->texel_size;
    1010         [ +  + ]:        228 :     if (unaligned)
    1011                 :          3 :         PL_TRACE(gpu, "vk_tex_download: unaligned transfer (slow path)");
    1012                 :            : 
    1013   [ +  +  +  + ]:        228 :     if (fmt->emulated || unaligned) {
    1014                 :            : 
    1015                 :         12 :         num_slices = pl_tex_transfer_slices(gpu, tex_vk->texel_fmt, params, &slices);
    1016         [ +  + ]:         24 :         for (int i = 0; i < num_slices; i++) {
    1017                 :         12 :             slices[i].buf = pl_buf_create(gpu, pl_buf_params(
    1018                 :            :                 .memory_type = PL_BUF_MEM_DEVICE,
    1019                 :            :                 .format      = tex_vk->texel_fmt,
    1020                 :            :                 .size        = pl_tex_transfer_size(&slices[i]),
    1021                 :            :                 .storable    = fmt->emulated,
    1022                 :            :             ));
    1023                 :            : 
    1024         [ -  + ]:         12 :             if (!slices[i].buf) {
    1025                 :          0 :                 PL_ERR(gpu, "Failed creating buffer for tex download fallback!");
    1026                 :            :                 num_slices = i;
    1027                 :          0 :                 goto error;
    1028                 :            :             }
    1029                 :            :         }
    1030                 :            : 
    1031         [ +  + ]:         24 :         for (int i = 0; i < num_slices; i++) {
    1032                 :            :             // Restore buffer offset after downloading into temporary buffer,
    1033                 :            :             // because we still need to copy the data from the temporary buffer
    1034                 :            :             // into this offset in the original buffer
    1035                 :         12 :             const size_t tmp_offset = slices[i].buf_offset;
    1036                 :         12 :             slices[i].buf_offset = 0;
    1037                 :          9 :             bool ok = fmt->emulated ? pl_tex_download_texel(gpu, &slices[i])
    1038         [ +  + ]:         12 :                                     : pl_tex_download(gpu, &slices[i]);
    1039                 :         12 :             slices[i].buf_offset = tmp_offset;
    1040         [ -  + ]:         12 :             if (!ok)
    1041                 :          0 :                 goto error;
    1042                 :            :         }
    1043                 :            : 
    1044                 :            :         // Finally, download into the user buffer
    1045                 :         12 :         struct vk_cmd *cmd = CMD_BEGIN_TIMED(tex_vk->transfer_queue, params->timer);
    1046         [ -  + ]:         12 :         if (!cmd)
    1047                 :          0 :             goto error;
    1048                 :            : 
    1049                 :         12 :         vk_buf_barrier(gpu, cmd, buf, VK_PIPELINE_STAGE_2_COPY_BIT,
    1050                 :         12 :                        VK_ACCESS_2_TRANSFER_WRITE_BIT, params->buf_offset, size,
    1051                 :            :                        false);
    1052                 :            : 
    1053         [ +  + ]:         24 :         for (int i = 0; i < num_slices; i++) {
    1054                 :         12 :             pl_buf slice = slices[i].buf;
    1055                 :         12 :             struct pl_buf_vk *slice_vk = PL_PRIV(slice);
    1056                 :         12 :             vk_buf_barrier(gpu, cmd, slice, VK_PIPELINE_STAGE_2_COPY_BIT,
    1057                 :         12 :                            VK_ACCESS_2_TRANSFER_READ_BIT, 0, slice->params.size,
    1058                 :            :                            false);
    1059                 :            : 
    1060                 :         12 :             vk->CmdCopyBuffer(cmd->buf, slice_vk->mem.buf, buf_vk->mem.buf, 1, &(VkBufferCopy) {
    1061                 :         12 :                 .srcOffset = slice_vk->mem.offset,
    1062                 :         12 :                 .dstOffset = buf_vk->mem.offset + slices[i].buf_offset,
    1063                 :         12 :                 .size      = slice->params.size,
    1064                 :            :             });
    1065                 :            : 
    1066                 :         12 :             pl_buf_destroy(gpu, &slices[i].buf);
    1067                 :            :         }
    1068                 :            : 
    1069                 :         12 :         vk_buf_flush(gpu, cmd, buf, params->buf_offset, size);
    1070                 :            : 
    1071         [ +  - ]:         12 :         if (params->callback)
    1072                 :         12 :             vk_cmd_callback(cmd, tex_xfer_cb, params->callback, params->priv);
    1073                 :            : 
    1074                 :         12 :         pl_free(slices);
    1075                 :         12 :         return CMD_FINISH(&cmd);
    1076                 :            : 
    1077                 :            :     } else {
    1078                 :            : 
    1079         [ -  + ]:        216 :         pl_assert(params->row_pitch % fmt->texel_size == 0);
    1080         [ -  + ]:        216 :         pl_assert(params->depth_pitch % params->row_pitch == 0);
    1081                 :        216 :         const VkBufferImageCopy region = {
    1082                 :            :             .bufferOffset = buf_offset,
    1083                 :        216 :             .bufferRowLength = params->row_pitch / fmt->texel_size,
    1084                 :        216 :             .bufferImageHeight = params->depth_pitch / params->row_pitch,
    1085                 :            :             .imageOffset = { rc.x0, rc.y0, rc.z0 },
    1086                 :        216 :             .imageExtent = { rc.x1, rc.y1, rc.z1 },
    1087                 :            :             .imageSubresource = {
    1088                 :        216 :                 .aspectMask = tex_vk->aspect,
    1089                 :            :                 .layerCount = 1,
    1090                 :            :             },
    1091                 :            :         };
    1092                 :            : 
    1093                 :        216 :         enum queue_type queue = vk_img_copy_queue(gpu, tex, &region);
    1094                 :            : 
    1095                 :        216 :         struct vk_cmd *cmd = CMD_BEGIN_TIMED(queue, params->timer);
    1096         [ -  + ]:        216 :         if (!cmd)
    1097                 :          0 :             goto error;
    1098                 :            : 
    1099                 :        216 :         vk_buf_barrier(gpu, cmd, buf, VK_PIPELINE_STAGE_2_COPY_BIT,
    1100                 :        216 :                        VK_ACCESS_2_TRANSFER_WRITE_BIT, params->buf_offset, size,
    1101                 :            :                        false);
    1102                 :        216 :         vk_tex_barrier(gpu, cmd, tex, VK_PIPELINE_STAGE_2_COPY_BIT,
    1103                 :            :                        VK_ACCESS_2_TRANSFER_READ_BIT,
    1104                 :            :                        VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
    1105                 :            :                        VK_QUEUE_FAMILY_IGNORED);
    1106                 :        216 :         vk->CmdCopyImageToBuffer(cmd->buf, tex_vk->img, tex_vk->layout,
    1107                 :            :                                  buf_vk->mem.buf, 1, &region);
    1108                 :        216 :         vk_buf_flush(gpu, cmd, buf, params->buf_offset, size);
    1109                 :            : 
    1110         [ +  + ]:        216 :         if (params->callback)
    1111                 :        168 :             vk_cmd_callback(cmd, tex_xfer_cb, params->callback, params->priv);
    1112                 :            : 
    1113                 :        216 :         return CMD_FINISH(&cmd);
    1114                 :            :     }
    1115                 :            : 
    1116                 :            :     pl_unreachable();
    1117                 :            : 
    1118                 :          0 : error:
    1119         [ #  # ]:          0 :     for (int i = 0; i < num_slices; i++)
    1120                 :          0 :         pl_buf_destroy(gpu, &slices[i].buf);
    1121                 :          0 :     pl_free(slices);
    1122                 :          0 :     return false;
    1123                 :            : }
    1124                 :            : 
    1125                 :          4 : bool vk_tex_poll(pl_gpu gpu, pl_tex tex, uint64_t timeout)
    1126                 :            : {
    1127                 :          4 :     struct pl_vk *p = PL_PRIV(gpu);
    1128                 :          4 :     struct vk_ctx *vk = p->vk;
    1129                 :          4 :     struct pl_tex_vk *tex_vk = PL_PRIV(tex);
    1130                 :            : 
    1131                 :            :     // Opportunistically check if we can re-use this texture without flush
    1132                 :          4 :     vk_poll_commands(vk, 0);
    1133         [ +  - ]:          4 :     if (pl_rc_count(&tex_vk->rc) == 1)
    1134                 :          4 :         goto skip_blocking;
    1135                 :            : 
    1136                 :            :     // Otherwise, we're force to submit any queued command so that the user is
    1137                 :            :     // guaranteed to see progress eventually, even if they call this in a loop
    1138                 :          0 :     CMD_SUBMIT(NULL);
    1139                 :          0 :     vk_poll_commands(vk, timeout);
    1140         [ #  # ]:          0 :     if (pl_rc_count(&tex_vk->rc) > 1)
    1141                 :            :         return true;
    1142                 :            : 
    1143                 :            :     // fall through
    1144                 :          0 : skip_blocking:
    1145         [ -  + ]:          4 :     for (int i = 0; i < tex_vk->num_planes; i++) {
    1146         [ #  # ]:          0 :         if (vk_tex_poll(gpu, tex->planes[i], timeout))
    1147                 :            :             return true;
    1148                 :            :     }
    1149                 :            : 
    1150                 :            :     return false;
    1151                 :            : }
    1152                 :            : 
    1153                 :          8 : pl_tex pl_vulkan_wrap(pl_gpu gpu, const struct pl_vulkan_wrap_params *params)
    1154                 :            : {
    1155                 :            :     pl_fmt fmt = NULL;
    1156         [ +  - ]:         40 :     for (int i = 0; i < gpu->num_formats; i++) {
    1157                 :         40 :         const struct vk_format **vkfmt = PL_PRIV(gpu->formats[i]);
    1158         [ +  + ]:         40 :         if ((*vkfmt)->tfmt == params->format) {
    1159                 :            :             fmt = gpu->formats[i];
    1160                 :            :             break;
    1161                 :            :         }
    1162                 :            :     }
    1163                 :            : 
    1164         [ -  + ]:          8 :     if (!fmt) {
    1165                 :          0 :         PL_ERR(gpu, "Could not find pl_fmt suitable for wrapped image "
    1166                 :            :                "with format %s", vk_fmt_name(params->format));
    1167                 :          0 :         return NULL;
    1168                 :            :     }
    1169                 :            : 
    1170                 :          8 :     VkImageUsageFlags usage = params->usage;
    1171         [ -  + ]:          8 :     if (fmt->num_planes)
    1172                 :            :         usage = 0; // mask capabilities from the base texture
    1173                 :            : 
    1174                 :          8 :     struct pl_tex_t *tex = pl_zalloc_obj(NULL, tex, struct pl_tex_vk);
    1175                 :          8 :     tex->params = (struct pl_tex_params) {
    1176                 :            :         .format         = fmt,
    1177                 :          8 :         .w              = params->width,
    1178                 :          8 :         .h              = params->height,
    1179                 :          8 :         .d              = params->depth,
    1180                 :          8 :         .sampleable     = !!(usage & VK_IMAGE_USAGE_SAMPLED_BIT),
    1181                 :          8 :         .renderable     = !!(usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT),
    1182                 :          8 :         .storable       = !!(usage & VK_IMAGE_USAGE_STORAGE_BIT),
    1183                 :          8 :         .blit_src       = !!(usage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT),
    1184                 :          8 :         .blit_dst       = !!(usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT),
    1185                 :            :         .host_writable  = !!(usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT),
    1186                 :            :         .host_readable  = !!(usage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT),
    1187                 :          8 :         .user_data      = params->user_data,
    1188                 :          8 :         .debug_tag      = params->debug_tag,
    1189                 :            :     };
    1190                 :            : 
    1191                 :            :     // Mask out capabilities not permitted by the `pl_fmt`
    1192                 :            : #define MASK(field, cap)                                                        \
    1193                 :            :     do {                                                                        \
    1194                 :            :         if (tex->params.field && !(fmt->caps & cap)) {                          \
    1195                 :            :             PL_WARN(gpu, "Masking `" #field "` from wrapped texture because "   \
    1196                 :            :                     "the corresponding format '%s' does not support " #cap,     \
    1197                 :            :                     fmt->name);                                                 \
    1198                 :            :             tex->params.field = false;                                          \
    1199                 :            :         }                                                                       \
    1200                 :            :     } while (0)
    1201                 :            : 
    1202   [ -  +  -  - ]:          8 :     MASK(sampleable,    PL_FMT_CAP_SAMPLEABLE);
    1203   [ +  -  -  + ]:          8 :     MASK(renderable,    PL_FMT_CAP_RENDERABLE);
    1204   [ +  -  -  + ]:          8 :     MASK(storable,      PL_FMT_CAP_STORABLE);
    1205   [ -  +  -  - ]:          8 :     MASK(blit_src,      PL_FMT_CAP_BLITTABLE);
    1206   [ +  -  -  + ]:          8 :     MASK(blit_dst,      PL_FMT_CAP_BLITTABLE);
    1207   [ -  +  -  - ]:          8 :     MASK(host_readable, PL_FMT_CAP_HOST_READABLE);
    1208                 :            : #undef MASK
    1209                 :            : 
    1210                 :            :     // For simplicity, explicitly mask out blit emulation for wrapped textures
    1211                 :          8 :     struct pl_fmt_vk *fmtp = PL_PRIV(fmt);
    1212         [ -  + ]:          8 :     if (fmtp->blit_emulated) {
    1213                 :          0 :         tex->params.blit_src = false;
    1214                 :          0 :         tex->params.blit_dst = false;
    1215                 :            :     }
    1216                 :            : 
    1217         [ -  + ]:          8 :     struct pl_tex_vk *tex_vk = PL_PRIV(tex);
    1218                 :            :     switch (pl_tex_params_dimension(tex->params)) {
    1219                 :          0 :     case 1: tex_vk->type = VK_IMAGE_TYPE_1D; break;
    1220                 :          8 :     case 2: tex_vk->type = VK_IMAGE_TYPE_2D; break;
    1221                 :          0 :     case 3: tex_vk->type = VK_IMAGE_TYPE_3D; break;
    1222                 :            :     }
    1223                 :          8 :     tex_vk->external_img = true;
    1224                 :          8 :     tex_vk->held = !fmt->num_planes;
    1225                 :          8 :     tex_vk->img = params->image;
    1226                 :          8 :     tex_vk->img_fmt = params->format;
    1227                 :          8 :     tex_vk->num_planes = fmt->num_planes;
    1228                 :          8 :     tex_vk->usage_flags = usage;
    1229                 :          8 :     tex_vk->aspect = params->aspect;
    1230                 :            : 
    1231         [ +  - ]:          8 :     if (!tex_vk->aspect) {
    1232         [ -  + ]:          8 :         for (int i = 0; i < tex_vk->num_planes; i++)
    1233                 :          0 :             tex_vk->aspect |= VK_IMAGE_ASPECT_PLANE_0_BIT << i;
    1234                 :          8 :         tex_vk->aspect = PL_DEF(tex_vk->aspect, VK_IMAGE_ASPECT_COLOR_BIT);
    1235                 :            :     }
    1236                 :            : 
    1237                 :            :     // Blitting to planar images requires fallback via compute shaders
    1238         [ -  + ]:          8 :     if (tex_vk->aspect != VK_IMAGE_ASPECT_COLOR_BIT) {
    1239                 :          0 :         tex->params.blit_src &= tex->params.storable;
    1240                 :          0 :         tex->params.blit_dst &= tex->params.storable;
    1241                 :            :     }
    1242                 :            : 
    1243                 :            :     static const char * const wrapped_plane_names[4] = {
    1244                 :            :         "wrapped plane 0", "wrapped plane 1", "wrapped plane 2", "wrapped plane 3",
    1245                 :            :     };
    1246                 :            : 
    1247         [ -  + ]:          8 :     for (int i = 0; i < tex_vk->num_planes; i++) {
    1248                 :            :         struct pl_tex_t *plane;
    1249                 :          0 :         VkImageAspectFlags aspect = VK_IMAGE_ASPECT_PLANE_0_BIT << i;
    1250         [ #  # ]:          0 :         if (!(aspect & tex_vk->aspect)) {
    1251                 :          0 :             PL_INFO(gpu, "Not wrapping plane %d due to aspect bit 0x%x not "
    1252                 :            :                     "being contained in supplied params->aspect 0x%x!",
    1253                 :            :                     i, (unsigned) aspect, (unsigned) tex_vk->aspect);
    1254                 :          0 :             continue;
    1255                 :            :         }
    1256                 :            : 
    1257         [ #  # ]:          0 :         pl_assert(tex_vk->type == VK_IMAGE_TYPE_2D);
    1258         [ #  # ]:          0 :         plane = (struct pl_tex_t *) pl_vulkan_wrap(gpu, pl_vulkan_wrap_params(
    1259                 :            :             .image      = tex_vk->img,
    1260                 :            :             .aspect     = aspect,
    1261                 :            :             .width      = PL_RSHIFT_UP(tex->params.w, fmt->planes[i].shift_x),
    1262                 :            :             .height     = PL_RSHIFT_UP(tex->params.h, fmt->planes[i].shift_y),
    1263                 :            :             .format     = fmtp->vk_fmt->pfmt[i].fmt,
    1264                 :            :             .usage      = params->usage,
    1265                 :            :             .user_data  = params->user_data,
    1266                 :            :             .debug_tag  = PL_DEF(params->debug_tag, wrapped_plane_names[i]),
    1267                 :            :         ));
    1268         [ #  # ]:          0 :         if (!plane)
    1269                 :          0 :             goto error;
    1270                 :          0 :         plane->parent = tex;
    1271                 :          0 :         tex->planes[i] = plane;
    1272                 :          0 :         tex_vk->planes[i] = PL_PRIV(plane);
    1273                 :            :     }
    1274                 :            : 
    1275   [ +  -  -  + ]:         16 :     if (!vk_init_image(gpu, tex, PL_DEF(params->debug_tag, "wrapped")))
    1276                 :          0 :         goto error;
    1277                 :            : 
    1278                 :            :     return tex;
    1279                 :            : 
    1280                 :          0 : error:
    1281                 :          0 :     vk_tex_destroy(gpu, tex);
    1282                 :          0 :     return NULL;
    1283                 :            : }
    1284                 :            : 
    1285                 :          0 : VkImage pl_vulkan_unwrap(pl_gpu gpu, pl_tex tex, VkFormat *out_format,
    1286                 :            :                          VkImageUsageFlags *out_flags)
    1287                 :            : {
    1288                 :          0 :     struct pl_tex_vk *tex_vk = PL_PRIV(tex);
    1289                 :            : 
    1290         [ #  # ]:          0 :     if (out_format)
    1291                 :          0 :         *out_format = tex_vk->img_fmt;
    1292         [ #  # ]:          0 :     if (out_flags)
    1293                 :          0 :         *out_flags = tex_vk->usage_flags;
    1294                 :            : 
    1295                 :          0 :     return tex_vk->img;
    1296                 :            : }
    1297                 :            : 
    1298                 :         14 : bool pl_vulkan_hold_ex(pl_gpu gpu, const struct pl_vulkan_hold_params *params)
    1299                 :            : {
    1300                 :         14 :     struct pl_tex_vk *tex_vk = PL_PRIV(params->tex);
    1301         [ -  + ]:         14 :     pl_assert(params->semaphore.sem);
    1302                 :            : 
    1303                 :         14 :     bool held = tex_vk->held;
    1304         [ -  + ]:         14 :     for (int i = 0; i < tex_vk->num_planes; i++)
    1305                 :          0 :         held |= tex_vk->planes[i]->held;
    1306                 :            : 
    1307         [ -  + ]:         14 :     if (held) {
    1308                 :          0 :         PL_ERR(gpu, "Attempting to hold an already held image!");
    1309                 :          0 :         return false;
    1310                 :            :     }
    1311                 :            : 
    1312                 :         14 :     struct vk_cmd *cmd = CMD_BEGIN(GRAPHICS);
    1313         [ -  + ]:         14 :     if (!cmd) {
    1314                 :          0 :         PL_ERR(gpu, "Failed holding external image!");
    1315                 :          0 :         return false;
    1316                 :            :     }
    1317                 :            : 
    1318                 :         14 :     VkImageLayout layout = params->layout;
    1319         [ -  + ]:         14 :     if (params->out_layout) {
    1320                 :            :         // For planar images, arbitrarily pick the current image layout of the
    1321                 :            :         // first plane. This should be fine in practice, since all planes will
    1322                 :            :         // share the same usage capabilities.
    1323         [ #  # ]:          0 :         if (tex_vk->num_planes) {
    1324                 :          0 :             layout = tex_vk->planes[0]->layout;
    1325                 :            :         } else {
    1326                 :          0 :             layout = tex_vk->layout;
    1327                 :            :         }
    1328                 :            :     }
    1329                 :            : 
    1330                 :            :     bool may_invalidate = true;
    1331         [ +  - ]:         14 :     if (!tex_vk->num_planes) {
    1332                 :         14 :         may_invalidate &= tex_vk->may_invalidate;
    1333                 :         14 :         vk_tex_barrier(gpu, cmd, params->tex, VK_PIPELINE_STAGE_2_NONE,
    1334                 :         14 :                        0, layout, params->qf);
    1335                 :            :     }
    1336                 :            : 
    1337         [ -  + ]:         14 :     for (int i = 0; i < tex_vk->num_planes; i++) {
    1338                 :          0 :         may_invalidate &= tex_vk->planes[i]->may_invalidate;
    1339                 :          0 :         vk_tex_barrier(gpu, cmd, params->tex->planes[i],
    1340                 :          0 :                        VK_PIPELINE_STAGE_2_NONE, 0, layout, params->qf);
    1341                 :            :     }
    1342                 :            : 
    1343                 :         14 :     vk_cmd_sig(cmd, VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, params->semaphore);
    1344                 :         14 :     bool ok = CMD_SUBMIT(&cmd);
    1345                 :            : 
    1346         [ +  - ]:         14 :     if (!tex_vk->num_planes) {
    1347                 :         14 :         tex_vk->sem.write.queue = tex_vk->sem.read.queue = NULL;
    1348                 :         14 :         tex_vk->held = ok;
    1349                 :            :     }
    1350                 :            : 
    1351         [ -  + ]:         14 :     for (int i = 0; i < tex_vk->num_planes; i++) {
    1352                 :          0 :         struct pl_tex_vk *plane_vk = tex_vk->planes[i];
    1353                 :          0 :         plane_vk->sem.write.queue = plane_vk->sem.read.queue = NULL;
    1354                 :          0 :         plane_vk->held = ok;
    1355                 :            :     }
    1356                 :            : 
    1357   [ +  -  +  - ]:         14 :     if (ok && params->out_layout)
    1358         [ #  # ]:          0 :         *params->out_layout = may_invalidate ? VK_IMAGE_LAYOUT_UNDEFINED : layout;
    1359                 :            : 
    1360                 :            :     return ok;
    1361                 :            : }
    1362                 :            : 
    1363                 :         14 : void pl_vulkan_release_ex(pl_gpu gpu, const struct pl_vulkan_release_params *params)
    1364                 :            : {
    1365                 :         14 :     struct pl_tex_vk *tex_vk = PL_PRIV(params->tex);
    1366         [ -  + ]:         14 :     if (tex_vk->num_planes) {
    1367                 :          0 :         struct pl_vulkan_release_params plane_pars = *params;
    1368         [ #  # ]:          0 :         for (int i = 0; i < tex_vk->num_planes; i++) {
    1369                 :          0 :             plane_pars.tex = params->tex->planes[i];
    1370                 :          0 :             pl_vulkan_release_ex(gpu, &plane_pars);
    1371                 :            :         }
    1372                 :            :         return;
    1373                 :            :     }
    1374                 :            : 
    1375         [ -  + ]:         14 :     if (!tex_vk->held) {
    1376                 :          0 :         PL_ERR(gpu, "Attempting to release an unheld image?");
    1377                 :          0 :         return;
    1378                 :            :     }
    1379                 :            : 
    1380         [ +  - ]:         14 :     if (params->semaphore.sem)
    1381   [ +  +  -  +  :         14 :         PL_ARRAY_APPEND(params->tex, tex_vk->ext_deps, params->semaphore);
                   -  + ]
    1382                 :            : 
    1383                 :         14 :     tex_vk->qf = params->qf;
    1384                 :         14 :     tex_vk->layout = params->layout;
    1385                 :         14 :     tex_vk->held = false;
    1386                 :            : }

Generated by: LCOV version 1.16