LCOV - code coverage report
Current view: top level - src/vulkan - gpu.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 281 323 87.0 %
Date: 2025-03-29 09:04:10 Functions: 22 23 95.7 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 86 146 58.9 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * This file is part of libplacebo.
       3                 :            :  *
       4                 :            :  * libplacebo is free software; you can redistribute it and/or
       5                 :            :  * modify it under the terms of the GNU Lesser General Public
       6                 :            :  * License as published by the Free Software Foundation; either
       7                 :            :  * version 2.1 of the License, or (at your option) any later version.
       8                 :            :  *
       9                 :            :  * libplacebo is distributed in the hope that it will be useful,
      10                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12                 :            :  * GNU Lesser General Public License for more details.
      13                 :            :  *
      14                 :            :  * You should have received a copy of the GNU Lesser General Public
      15                 :            :  * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
      16                 :            :  */
      17                 :            : 
      18                 :            : #include "gpu.h"
      19                 :            : #include "formats.h"
      20                 :            : #include "glsl/spirv.h"
      21                 :            : 
      22                 :            : #ifdef PL_HAVE_UNIX
      23                 :            : #include <unistd.h>
      24                 :            : #endif
      25                 :            : 
      26                 :            : // Gives us enough queries for 8 results
      27                 :            : #define QUERY_POOL_SIZE 16
      28                 :            : 
      29                 :            : struct pl_timer_t {
      30                 :            :     VkQueryPool qpool; // even=start, odd=stop
      31                 :            :     int index_write; // next index to write to
      32                 :            :     int index_read; // next index to read from
      33                 :            :     uint64_t pending; // bitmask of queries that are still running
      34                 :            : };
      35                 :            : 
      36                 :            : static inline uint64_t timer_bit(int index)
      37                 :            : {
      38                 :       3962 :     return 1llu << (index / 2);
      39                 :            : }
      40                 :            : 
      41                 :        520 : static void timer_destroy_cb(pl_gpu gpu, pl_timer timer)
      42                 :            : {
      43                 :        520 :     struct pl_vk *p = PL_PRIV(gpu);
      44                 :        520 :     struct vk_ctx *vk = p->vk;
      45                 :            : 
      46         [ -  + ]:        520 :     pl_assert(!timer->pending);
      47                 :        520 :     vk->DestroyQueryPool(vk->dev, timer->qpool, PL_VK_ALLOC);
      48                 :        520 :     pl_free(timer);
      49                 :        520 : }
      50                 :            : 
      51                 :        520 : VK_CB_FUNC_DEF(timer_destroy_cb);
      52                 :            : 
      53                 :        520 : static pl_timer vk_timer_create(pl_gpu gpu)
      54                 :            : {
      55                 :        520 :     struct pl_vk *p = PL_PRIV(gpu);
      56                 :        520 :     struct vk_ctx *vk = p->vk;
      57                 :            : 
      58                 :        520 :     pl_timer timer = pl_alloc_ptr(NULL, timer);
      59                 :        520 :     *timer = (struct pl_timer_t) {0};
      60                 :            : 
      61                 :        520 :     struct VkQueryPoolCreateInfo qinfo = {
      62                 :            :         .sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
      63                 :            :         .queryType = VK_QUERY_TYPE_TIMESTAMP,
      64                 :            :         .queryCount = QUERY_POOL_SIZE,
      65                 :            :     };
      66                 :            : 
      67         [ -  + ]:        520 :     VK(vk->CreateQueryPool(vk->dev, &qinfo, PL_VK_ALLOC, &timer->qpool));
      68                 :            :     return timer;
      69                 :            : 
      70                 :            : error:
      71                 :          0 :     timer_destroy_cb(gpu, timer);
      72                 :          0 :     return NULL;
      73                 :            : }
      74                 :            : 
      75                 :        520 : static void vk_timer_destroy(pl_gpu gpu, pl_timer timer)
      76                 :            : {
      77                 :        520 :     vk_gpu_idle_callback(gpu, VK_CB_FUNC(timer_destroy_cb), gpu, timer);
      78                 :        520 : }
      79                 :            : 
      80                 :       1268 : static uint64_t vk_timer_query(pl_gpu gpu, pl_timer timer)
      81                 :            : {
      82                 :       1268 :     struct pl_vk *p = PL_PRIV(gpu);
      83                 :       1268 :     struct vk_ctx *vk = p->vk;
      84                 :            : 
      85         [ +  + ]:       1268 :     if (timer->index_read == timer->index_write)
      86                 :            :         return 0; // no more unprocessed results
      87                 :            : 
      88                 :       1262 :     vk_poll_commands(vk, 0);
      89         [ +  + ]:       1262 :     if (timer->pending & timer_bit(timer->index_read))
      90                 :            :         return 0; // still waiting for results
      91                 :            : 
      92                 :            :     VkResult res;
      93                 :        735 :     uint64_t ts[2] = {0};
      94                 :        735 :     res = vk->GetQueryPoolResults(vk->dev, timer->qpool, timer->index_read, 2,
      95                 :            :                                   sizeof(ts), &ts[0], sizeof(uint64_t),
      96                 :            :                                   VK_QUERY_RESULT_64_BIT);
      97                 :            : 
      98      [ -  +  - ]:        735 :     switch (res) {
      99                 :        735 :     case VK_SUCCESS:
     100                 :        735 :         timer->index_read = (timer->index_read + 2) % QUERY_POOL_SIZE;
     101                 :        735 :         return (ts[1] - ts[0]) * vk->props.limits.timestampPeriod;
     102                 :            :     case VK_NOT_READY:
     103                 :            :         return 0;
     104                 :            :     default:
     105                 :          0 :         PL_VK_ASSERT(res, "Retrieving query pool results");
     106                 :            :     }
     107                 :            : 
     108                 :            : error:
     109                 :          0 :     return 0;
     110                 :            : }
     111                 :            : 
     112                 :       1423 : static void timer_begin(pl_gpu gpu, struct vk_cmd *cmd, pl_timer timer)
     113                 :            : {
     114                 :       1423 :     struct pl_vk *p = PL_PRIV(gpu);
     115                 :       1423 :     struct vk_ctx *vk = p->vk;
     116                 :            : 
     117         [ +  + ]:       1423 :     if (!timer)
     118                 :            :         return;
     119                 :            : 
     120         [ -  + ]:        900 :     if (!cmd->pool->props.timestampValidBits) {
     121                 :          0 :         PL_TRACE(gpu, "QF %d does not support timestamp queries", cmd->pool->qf);
     122                 :          0 :         return;
     123                 :            :     }
     124                 :            : 
     125                 :        900 :     vk_poll_commands(vk, 0);
     126         [ +  - ]:        900 :     if (timer->pending & timer_bit(timer->index_write))
     127                 :            :         return; // next query is still running, skip this timer
     128                 :            : 
     129                 :            :     VkQueueFlags reset_flags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT;
     130         [ +  - ]:        900 :     if (cmd->pool->props.queueFlags & reset_flags) {
     131                 :            :         // Use direct command buffer resets
     132                 :        900 :         vk->CmdResetQueryPool(cmd->buf, timer->qpool, timer->index_write, 2);
     133                 :            :     } else {
     134                 :            :         // Use host query reset
     135                 :          0 :         vk->ResetQueryPool(vk->dev, timer->qpool, timer->index_write, 2);
     136                 :            :     }
     137                 :            : 
     138                 :        900 :     vk->CmdWriteTimestamp(cmd->buf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
     139                 :        900 :                           timer->qpool, timer->index_write);
     140                 :            : 
     141                 :        900 :     p->cmd_timer = timer;
     142                 :            : }
     143                 :            : 
     144                 :            : static inline bool supports_marks(struct vk_cmd *cmd) {
     145                 :            :     // Spec says debug markers are only available on graphics/compute queues
     146                 :       2846 :     VkQueueFlags flags = cmd->pool->props.queueFlags;
     147                 :       2846 :     return flags & (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT);
     148                 :            : }
     149                 :            : 
     150                 :       1423 : struct vk_cmd *_begin_cmd(pl_gpu gpu, enum queue_type type, const char *label,
     151                 :            :                           pl_timer timer)
     152                 :            : {
     153                 :       1423 :     struct pl_vk *p = PL_PRIV(gpu);
     154                 :       1423 :     struct vk_ctx *vk = p->vk;
     155                 :       1423 :     pl_mutex_lock(&p->recording);
     156                 :            : 
     157                 :            :     struct vk_cmdpool *pool;
     158   [ -  +  +  +  :       1423 :     switch (type) {
                      - ]
     159         [ #  # ]:          0 :     case ANY:      pool = p->cmd ? p->cmd->pool : vk->pool_graphics; break;
     160                 :        928 :     case GRAPHICS: pool = vk->pool_graphics; break;
     161                 :         84 :     case COMPUTE:  pool = vk->pool_compute;  break;
     162                 :        411 :     case TRANSFER: pool = vk->pool_transfer; break;
     163                 :          0 :     default: pl_unreachable();
     164                 :            :     }
     165                 :            : 
     166   [ +  +  -  + ]:       1423 :     if (!p->cmd || p->cmd->pool != pool) {
     167                 :        780 :         vk_cmd_submit(&p->cmd);
     168                 :        780 :         p->cmd = vk_cmd_begin(pool, label);
     169         [ -  + ]:        780 :         if (!p->cmd) {
     170                 :          0 :             pl_mutex_unlock(&p->recording);
     171                 :          0 :             return NULL;
     172                 :            :         }
     173                 :            :     }
     174                 :            : 
     175   [ +  -  +  - ]:       1423 :     if (vk->CmdBeginDebugUtilsLabelEXT && supports_marks(p->cmd)) {
     176                 :       1423 :         vk->CmdBeginDebugUtilsLabelEXT(p->cmd->buf, &(VkDebugUtilsLabelEXT) {
     177                 :            :             .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
     178                 :            :             .pLabelName = label,
     179                 :            :         });
     180                 :            :     }
     181                 :            : 
     182                 :       1423 :     timer_begin(gpu, p->cmd, timer);
     183                 :       1423 :     return p->cmd;
     184                 :            : }
     185                 :            : 
     186                 :        900 : static void timer_end_cb(void *ptimer, void *pindex)
     187                 :            : {
     188                 :            :     pl_timer timer = ptimer;
     189                 :        900 :     int index = (uintptr_t) pindex;
     190                 :        900 :     timer->pending &= ~timer_bit(index);
     191                 :        900 : }
     192                 :            : 
     193                 :       1757 : bool _end_cmd(pl_gpu gpu, struct vk_cmd **pcmd, bool submit)
     194                 :            : {
     195                 :       1757 :     struct pl_vk *p = PL_PRIV(gpu);
     196                 :       1757 :     struct vk_ctx *vk = p->vk;
     197                 :            :     bool ret = true;
     198         [ +  + ]:       1757 :     if (!pcmd) {
     199         [ +  - ]:        334 :         if (submit) {
     200                 :        334 :             pl_mutex_lock(&p->recording);
     201                 :        334 :             ret = vk_cmd_submit(&p->cmd);
     202                 :        334 :             pl_mutex_unlock(&p->recording);
     203                 :            :         }
     204                 :        334 :         return ret;
     205                 :            :     }
     206                 :            : 
     207                 :       1423 :     struct vk_cmd *cmd = *pcmd;
     208         [ -  + ]:       1423 :     pl_assert(p->cmd == cmd);
     209                 :            : 
     210         [ +  + ]:       1423 :     if (p->cmd_timer) {
     211                 :            :         pl_timer timer = p->cmd_timer;
     212                 :        900 :         vk->CmdWriteTimestamp(cmd->buf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
     213                 :        900 :                               timer->qpool, timer->index_write + 1);
     214                 :            : 
     215                 :        900 :         timer->pending |= timer_bit(timer->index_write);
     216                 :        900 :         vk_cmd_callback(cmd, timer_end_cb, timer,
     217                 :        900 :                         (void *) (uintptr_t) timer->index_write);
     218                 :            : 
     219                 :        900 :         timer->index_write = (timer->index_write + 2) % QUERY_POOL_SIZE;
     220         [ -  + ]:        900 :         if (timer->index_write == timer->index_read) {
     221                 :            :             // forcibly drop the least recent result to make space
     222                 :          0 :             timer->index_read = (timer->index_read + 2) % QUERY_POOL_SIZE;
     223                 :            :         }
     224                 :            : 
     225                 :        900 :         p->cmd_timer = NULL;
     226                 :            :     }
     227                 :            : 
     228   [ +  -  +  - ]:       1423 :     if (vk->CmdEndDebugUtilsLabelEXT && supports_marks(cmd))
     229                 :       1423 :         vk->CmdEndDebugUtilsLabelEXT(cmd->buf);
     230                 :            : 
     231         [ +  + ]:       1423 :     if (submit)
     232                 :        550 :         ret = vk_cmd_submit(&p->cmd);
     233                 :            : 
     234                 :       1423 :     pl_mutex_unlock(&p->recording);
     235                 :       1423 :     return ret;
     236                 :            : }
     237                 :            : 
     238                 :        680 : void vk_gpu_idle_callback(pl_gpu gpu, vk_cb cb, const void *priv, const void *arg)
     239                 :            : {
     240                 :        680 :     struct pl_vk *p = PL_PRIV(gpu);
     241                 :        680 :     struct vk_ctx *vk = p->vk;
     242                 :            : 
     243                 :        680 :     pl_mutex_lock(&p->recording);
     244         [ -  + ]:        680 :     if (p->cmd) {
     245                 :          0 :         vk_cmd_callback(p->cmd, cb, priv, arg);
     246                 :            :     } else {
     247                 :        680 :         vk_dev_callback(vk, cb, priv, arg);
     248                 :            :     }
     249                 :        680 :     pl_mutex_unlock(&p->recording);
     250                 :        680 : }
     251                 :            : 
     252                 :          3 : static void vk_gpu_destroy(pl_gpu gpu)
     253                 :            : {
     254                 :          3 :     struct pl_vk *p = PL_PRIV(gpu);
     255                 :          3 :     struct vk_ctx *vk = p->vk;
     256                 :            : 
     257                 :          3 :     vk_cmd_submit(&p->cmd);
     258                 :          3 :     vk_wait_idle(vk);
     259                 :            : 
     260         [ +  + ]:          9 :     for (enum pl_tex_sample_mode s = 0; s < PL_TEX_SAMPLE_MODE_COUNT; s++) {
     261         [ +  + ]:         24 :         for (enum pl_tex_address_mode a = 0; a < PL_TEX_ADDRESS_MODE_COUNT; a++)
     262                 :         18 :             vk->DestroySampler(vk->dev, p->samplers[s][a], PL_VK_ALLOC);
     263                 :            :     }
     264                 :            : 
     265                 :          3 :     pl_spirv_destroy(&p->spirv);
     266                 :          3 :     pl_mutex_destroy(&p->recording);
     267                 :          3 :     pl_free((void *) gpu);
     268                 :          3 : }
     269                 :            : 
     270                 :          0 : pl_vulkan pl_vulkan_get(pl_gpu gpu)
     271                 :            : {
     272                 :          0 :     const struct pl_gpu_fns *impl = PL_PRIV(gpu);
     273         [ #  # ]:          0 :     if (impl->destroy == vk_gpu_destroy) {
     274                 :            :         struct pl_vk *p = (struct pl_vk *) impl;
     275                 :          0 :         return p->vk->vulkan;
     276                 :            :     }
     277                 :            : 
     278                 :            :     return NULL;
     279                 :            : }
     280                 :            : 
     281                 :          3 : static pl_handle_caps vk_sync_handle_caps(struct vk_ctx *vk)
     282                 :            : {
     283                 :            :     pl_handle_caps caps = 0;
     284                 :            : 
     285         [ +  + ]:          6 :     for (int i = 0; vk_sync_handle_list[i]; i++) {
     286                 :            :         enum pl_handle_type type = vk_sync_handle_list[i];
     287                 :            : 
     288                 :          6 :         VkPhysicalDeviceExternalSemaphoreInfo info = {
     289                 :            :             .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHR,
     290                 :          3 :             .handleType = vk_sync_handle_type(type),
     291                 :            :         };
     292                 :            : 
     293                 :          3 :         VkExternalSemaphoreProperties props = {
     294                 :            :             .sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHR,
     295                 :            :         };
     296                 :            : 
     297                 :          3 :         vk->GetPhysicalDeviceExternalSemaphoreProperties(vk->physd, &info, &props);
     298                 :          3 :         VkExternalSemaphoreFeatureFlags flags = props.externalSemaphoreFeatures;
     299         [ +  - ]:          3 :         if ((props.compatibleHandleTypes & info.handleType) &&
     300         [ +  - ]:          3 :             (flags & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHR))
     301                 :            :         {
     302                 :          3 :             caps |= type;
     303                 :            :         }
     304                 :            :     }
     305                 :            : 
     306                 :          3 :     return caps;
     307                 :            : }
     308                 :            : 
     309                 :          6 : static pl_handle_caps vk_tex_handle_caps(struct vk_ctx *vk, bool import)
     310                 :            : {
     311                 :            :     pl_handle_caps caps = 0;
     312                 :            : 
     313         [ +  + ]:         24 :     for (int i = 0; vk_mem_handle_list[i]; i++) {
     314                 :            :         enum pl_handle_type handle_type = vk_mem_handle_list[i];
     315   [ +  +  -  + ]:         18 :         if (handle_type == PL_HANDLE_DMA_BUF && !vk->GetImageDrmFormatModifierPropertiesEXT) {
     316                 :          0 :             PL_DEBUG(vk, "Tex caps for %s (0x%x) unsupported: no DRM modifiers",
     317                 :            :                      vk_handle_name(vk_mem_handle_type(PL_HANDLE_DMA_BUF)),
     318                 :            :                      (unsigned int) PL_HANDLE_DMA_BUF);
     319                 :          6 :             continue;
     320                 :            :         }
     321                 :            : 
     322                 :            :         // Query whether creation of a "basic" dummy texture would work
     323                 :         18 :         VkPhysicalDeviceImageDrmFormatModifierInfoEXT drm_pinfo = {
     324                 :            :             .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT,
     325                 :            :             .drmFormatModifier = DRM_FORMAT_MOD_LINEAR,
     326                 :            :             .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
     327                 :            :         };
     328                 :            : 
     329                 :         36 :         VkPhysicalDeviceExternalImageFormatInfoKHR ext_pinfo = {
     330                 :            :             .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR,
     331                 :         18 :             .handleType = vk_mem_handle_type(handle_type),
     332                 :            :         };
     333                 :            : 
     334                 :         18 :         VkPhysicalDeviceImageFormatInfo2KHR pinfo = {
     335                 :            :             .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR,
     336                 :            :             .pNext = &ext_pinfo,
     337                 :            :             .format = VK_FORMAT_R8_UNORM,
     338                 :            :             .type = VK_IMAGE_TYPE_2D,
     339                 :            :             .tiling = VK_IMAGE_TILING_OPTIMAL,
     340                 :            :             .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT,
     341                 :            :         };
     342                 :            : 
     343         [ +  + ]:         18 :         if (handle_type == PL_HANDLE_DMA_BUF) {
     344                 :          6 :             vk_link_struct(&pinfo, &drm_pinfo);
     345                 :          6 :             pinfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
     346                 :            :         }
     347                 :            : 
     348                 :         18 :         VkExternalImageFormatPropertiesKHR ext_props = {
     349                 :            :             .sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR,
     350                 :            :         };
     351                 :            : 
     352                 :         18 :         VkImageFormatProperties2KHR props = {
     353                 :            :             .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR,
     354                 :            :             .pNext = &ext_props,
     355                 :            :         };
     356                 :            : 
     357                 :            :         VkResult res;
     358                 :         18 :         res = vk->GetPhysicalDeviceImageFormatProperties2KHR(vk->physd, &pinfo, &props);
     359         [ +  + ]:         18 :         if (res != VK_SUCCESS) {
     360                 :          6 :             PL_DEBUG(vk, "Tex caps for %s (0x%x) unsupported: %s",
     361                 :            :                      vk_handle_name(ext_pinfo.handleType),
     362                 :            :                      (unsigned int) handle_type,
     363                 :            :                      vk_res_str(res));
     364                 :          6 :             continue;
     365                 :            :         }
     366                 :            : 
     367         [ +  - ]:         12 :         if (vk_external_mem_check(vk, &ext_props.externalMemoryProperties,
     368                 :            :                                   handle_type, import))
     369                 :            :         {
     370                 :         12 :             caps |= handle_type;
     371                 :            :         }
     372                 :            :     }
     373                 :            : 
     374                 :            : #ifdef VK_EXT_metal_objects
     375                 :            :     if (vk->ExportMetalObjectsEXT && import)
     376                 :            :         caps |= PL_HANDLE_MTL_TEX | PL_HANDLE_IOSURFACE;
     377                 :            : #endif
     378                 :            : 
     379                 :          6 :     return caps;
     380                 :            : }
     381                 :            : 
     382                 :            : static const VkFilter filters[PL_TEX_SAMPLE_MODE_COUNT] = {
     383                 :            :     [PL_TEX_SAMPLE_NEAREST] = VK_FILTER_NEAREST,
     384                 :            :     [PL_TEX_SAMPLE_LINEAR]  = VK_FILTER_LINEAR,
     385                 :            : };
     386                 :            : 
     387                 :          3 : static inline struct pl_spirv_version get_spirv_version(const struct vk_ctx *vk)
     388                 :            : {
     389         [ +  - ]:          3 :     if (vk->api_ver >= VK_API_VERSION_1_3) {
     390                 :            :         const VkPhysicalDeviceMaintenance4Features *device_maintenance4;
     391                 :          3 :         device_maintenance4 = vk_find_struct(&vk->features,
     392                 :            :             VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES);
     393                 :            : 
     394   [ +  +  +  - ]:          3 :         if (device_maintenance4 && device_maintenance4->maintenance4) {
     395                 :          1 :             return (struct pl_spirv_version) {
     396                 :            :                 .env_version = VK_API_VERSION_1_3,
     397                 :            :                 .spv_version = PL_SPV_VERSION(1, 6),
     398                 :            :             };
     399                 :            :         }
     400                 :            :     }
     401                 :            : 
     402         [ -  + ]:          2 :     pl_assert(vk->api_ver >= VK_API_VERSION_1_2);
     403                 :          2 :     return (struct pl_spirv_version) {
     404                 :            :         .env_version = VK_API_VERSION_1_2,
     405                 :            :         .spv_version = PL_SPV_VERSION(1, 5),
     406                 :            :     };
     407                 :            : }
     408                 :            : 
     409                 :            : static const struct pl_gpu_fns pl_fns_vk;
     410                 :            : 
     411                 :          3 : pl_gpu pl_gpu_create_vk(struct vk_ctx *vk)
     412                 :            : {
     413         [ -  + ]:          3 :     pl_assert(vk->dev);
     414                 :            : 
     415                 :          3 :     struct pl_gpu_t *gpu = pl_zalloc_obj(NULL, gpu, struct pl_vk);
     416                 :          3 :     gpu->log = vk->log;
     417                 :            : 
     418                 :          3 :     struct pl_vk *p = PL_PRIV(gpu);
     419         [ -  + ]:          3 :     pl_mutex_init(&p->recording);
     420                 :          3 :     p->vk = vk;
     421                 :          3 :     p->impl = pl_fns_vk;
     422                 :          3 :     p->spirv = pl_spirv_create(vk->log, get_spirv_version(vk));
     423         [ -  + ]:          3 :     if (!p->spirv)
     424                 :          0 :         goto error;
     425                 :            : 
     426                 :            :     // Query all device properties
     427                 :          3 :     VkPhysicalDevicePCIBusInfoPropertiesEXT pci_props = {
     428                 :            :         .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT,
     429                 :            :     };
     430                 :            : 
     431                 :          3 :     VkPhysicalDeviceIDPropertiesKHR id_props = {
     432                 :            :         .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHR,
     433                 :            :         .pNext = &pci_props,
     434                 :            :     };
     435                 :            : 
     436                 :          3 :     VkPhysicalDevicePushDescriptorPropertiesKHR pushd_props = {
     437                 :            :         .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR,
     438                 :            :         .pNext = &id_props,
     439                 :            :     };
     440                 :            : 
     441                 :          3 :     VkPhysicalDeviceSubgroupProperties group_props = {
     442                 :            :         .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES,
     443                 :            :         .pNext = &pushd_props,
     444                 :            :     };
     445                 :            : 
     446                 :          3 :     VkPhysicalDeviceExternalMemoryHostPropertiesEXT host_props = {
     447                 :            :         .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT,
     448                 :            :         .pNext = &group_props,
     449                 :            :     };
     450                 :            : 
     451                 :          3 :     VkPhysicalDeviceProperties2KHR props = {
     452                 :            :         .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
     453                 :            :         .pNext = &host_props,
     454                 :            :     };
     455                 :            : 
     456                 :            :     bool is_portability = false;
     457                 :            : 
     458                 :            : #ifdef VK_KHR_portability_subset
     459                 :          3 :     VkPhysicalDevicePortabilitySubsetPropertiesKHR port_props = {
     460                 :            :         .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_PROPERTIES_KHR,
     461                 :            :         .minVertexInputBindingStrideAlignment = 1,
     462                 :            :     };
     463                 :            : 
     464         [ +  + ]:         21 :     for (int i = 0; i < vk->exts.num; i++) {
     465         [ -  + ]:         18 :         if (!strcmp(vk->exts.elem[i], VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) {
     466                 :          0 :             vk_link_struct(&props, &port_props);
     467                 :            :             is_portability = true;
     468                 :          0 :             break;
     469                 :            :         }
     470                 :            :     }
     471                 :            : #endif
     472                 :            : 
     473                 :          3 :     vk->GetPhysicalDeviceProperties2(vk->physd, &props);
     474                 :          3 :     VkPhysicalDeviceLimits limits = props.properties.limits;
     475                 :            : 
     476                 :            :     // Determine GLSL features and limits
     477                 :          3 :     gpu->glsl = (struct pl_glsl_version) {
     478                 :            :         .version = 450,
     479                 :            :         .vulkan = true,
     480                 :            :         .compute = true,
     481                 :          3 :         .max_shmem_size = limits.maxComputeSharedMemorySize,
     482                 :            :         .max_group_threads = limits.maxComputeWorkGroupInvocations,
     483                 :            :         .max_group_size = {
     484                 :            :             limits.maxComputeWorkGroupSize[0],
     485                 :            :             limits.maxComputeWorkGroupSize[1],
     486                 :            :             limits.maxComputeWorkGroupSize[2],
     487                 :            :         },
     488                 :            :     };
     489                 :            : 
     490                 :            :     VkShaderStageFlags req_stages = VK_SHADER_STAGE_FRAGMENT_BIT |
     491                 :            :                                     VK_SHADER_STAGE_COMPUTE_BIT;
     492                 :            :     VkSubgroupFeatureFlags req_flags = VK_SUBGROUP_FEATURE_BASIC_BIT |
     493                 :            :                                        VK_SUBGROUP_FEATURE_VOTE_BIT |
     494                 :            :                                        VK_SUBGROUP_FEATURE_ARITHMETIC_BIT |
     495                 :            :                                        VK_SUBGROUP_FEATURE_BALLOT_BIT |
     496                 :            :                                        VK_SUBGROUP_FEATURE_SHUFFLE_BIT;
     497                 :            : 
     498         [ +  - ]:          3 :     if ((group_props.supportedStages & req_stages) == req_stages &&
     499         [ +  - ]:          3 :         (group_props.supportedOperations & req_flags) == req_flags)
     500                 :            :     {
     501                 :          3 :         gpu->glsl.subgroup_size = group_props.subgroupSize;
     502                 :            :     }
     503                 :            : 
     504         [ +  - ]:          3 :     if (vk->features.features.shaderImageGatherExtended) {
     505                 :          3 :         gpu->glsl.min_gather_offset = limits.minTexelGatherOffset;
     506                 :          3 :         gpu->glsl.max_gather_offset = limits.maxTexelGatherOffset;
     507                 :            :     }
     508                 :            : 
     509                 :          3 :     const size_t max_size = vk_malloc_avail(vk->ma, 0);
     510                 :          3 :     gpu->limits = (struct pl_gpu_limits) {
     511                 :            :         // pl_gpu
     512                 :            :         .thread_safe        = true,
     513                 :            :         .callbacks          = true,
     514                 :            :         // pl_buf
     515                 :            :         .max_buf_size       = max_size,
     516                 :          3 :         .max_ubo_size       = PL_MIN(limits.maxUniformBufferRange, max_size),
     517                 :          3 :         .max_ssbo_size      = PL_MIN(limits.maxStorageBufferRange, max_size),
     518                 :          3 :         .max_vbo_size       = vk_malloc_avail(vk->ma, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT),
     519                 :          3 :         .max_mapped_size    = vk_malloc_avail(vk->ma, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT),
     520                 :          3 :         .max_mapped_vram    = vk_malloc_avail(vk->ma, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
     521                 :            :                                                       VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT),
     522                 :          3 :         .max_buffer_texels  = PL_MIN(limits.maxTexelBufferElements, max_size),
     523                 :          3 :         .align_host_ptr     = host_props.minImportedHostPointerAlignment,
     524                 :          3 :         .host_cached        = vk_malloc_avail(vk->ma, VK_MEMORY_PROPERTY_HOST_CACHED_BIT),
     525                 :            :         // pl_tex
     526                 :            :         .max_tex_1d_dim     = limits.maxImageDimension1D,
     527                 :            :         .max_tex_2d_dim     = limits.maxImageDimension2D,
     528                 :            :         .max_tex_3d_dim     = limits.maxImageDimension3D,
     529                 :            :         .blittable_1d_3d    = true,
     530                 :            :         .buf_transfer       = true,
     531                 :            :         .align_tex_xfer_pitch  = limits.optimalBufferCopyRowPitchAlignment,
     532                 :          3 :         .align_tex_xfer_offset = pl_lcm(limits.optimalBufferCopyOffsetAlignment, 4),
     533                 :            :         // pl_pass
     534                 :            :         .max_variable_comps = 0, // vulkan doesn't support these at all
     535                 :            :         .max_constants      = SIZE_MAX,
     536                 :          3 :         .array_size_constants = !is_portability,
     537                 :          3 :         .max_pushc_size     = limits.maxPushConstantsSize,
     538                 :            : #ifdef VK_KHR_portability_subset
     539                 :          3 :         .align_vertex_stride = port_props.minVertexInputBindingStrideAlignment,
     540                 :            : #else
     541                 :            :         .align_vertex_stride = 1,
     542                 :            : #endif
     543                 :            :         .max_dispatch = {
     544                 :            :             limits.maxComputeWorkGroupCount[0],
     545                 :            :             limits.maxComputeWorkGroupCount[1],
     546                 :            :             limits.maxComputeWorkGroupCount[2],
     547                 :            :         },
     548                 :          3 :         .fragment_queues    = vk->pool_graphics->num_queues,
     549                 :          3 :         .compute_queues     = vk->pool_compute->num_queues,
     550                 :            :     };
     551                 :            : 
     552                 :          3 :     gpu->export_caps.buf = vk_malloc_handle_caps(vk->ma, false);
     553                 :          3 :     gpu->import_caps.buf = vk_malloc_handle_caps(vk->ma, true);
     554                 :          3 :     gpu->export_caps.tex = vk_tex_handle_caps(vk, false);
     555                 :          3 :     gpu->import_caps.tex = vk_tex_handle_caps(vk, true);
     556                 :          3 :     gpu->export_caps.sync = vk_sync_handle_caps(vk);
     557         [ -  + ]:          3 :     gpu->import_caps.sync = 0; // Not supported yet
     558                 :            : 
     559                 :            :     if (pl_gpu_supports_interop(gpu)) {
     560                 :            :         pl_static_assert(sizeof(gpu->uuid) == VK_UUID_SIZE);
     561                 :          3 :         memcpy(gpu->uuid, id_props.deviceUUID, sizeof(gpu->uuid));
     562                 :            : 
     563                 :          3 :         gpu->pci.domain = pci_props.pciDomain;
     564                 :          3 :         gpu->pci.bus = pci_props.pciBus;
     565                 :          3 :         gpu->pci.device = pci_props.pciDevice;
     566                 :          3 :         gpu->pci.function = pci_props.pciFunction;
     567                 :            :     }
     568                 :            : 
     569         [ +  - ]:          3 :     if (vk->CmdPushDescriptorSetKHR)
     570                 :          3 :         p->max_push_descriptors = pushd_props.maxPushDescriptors;
     571                 :            : 
     572                 :          3 :     vk_setup_formats(gpu);
     573                 :            : 
     574                 :            :     // Compute the correct minimum texture alignment
     575                 :          3 :     p->min_texel_alignment = 1;
     576         [ +  + ]:        309 :     for (int i = 0; i < gpu->num_formats; i++) {
     577         [ +  + ]:        306 :         if (gpu->formats[i]->emulated || gpu->formats[i]->opaque)
     578                 :         84 :             continue;
     579                 :        222 :         size_t texel_size = gpu->formats[i]->texel_size;
     580                 :        222 :         p->min_texel_alignment = pl_lcm(p->min_texel_alignment, texel_size);
     581                 :            :     }
     582                 :          3 :     PL_DEBUG(gpu, "Minimum texel alignment: %zu", p->min_texel_alignment);
     583                 :            : 
     584                 :            :     // Initialize the samplers
     585         [ +  + ]:          9 :     for (enum pl_tex_sample_mode s = 0; s < PL_TEX_SAMPLE_MODE_COUNT; s++) {
     586         [ +  + ]:         24 :         for (enum pl_tex_address_mode a = 0; a < PL_TEX_ADDRESS_MODE_COUNT; a++) {
     587                 :            :             static const VkSamplerAddressMode modes[PL_TEX_ADDRESS_MODE_COUNT] = {
     588                 :            :                 [PL_TEX_ADDRESS_CLAMP]  = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
     589                 :            :                 [PL_TEX_ADDRESS_REPEAT] = VK_SAMPLER_ADDRESS_MODE_REPEAT,
     590                 :            :                 [PL_TEX_ADDRESS_MIRROR] = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT,
     591                 :            :             };
     592                 :            : 
     593                 :         18 :             VkSamplerCreateInfo sinfo = {
     594                 :            :                 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
     595                 :         18 :                 .magFilter = filters[s],
     596                 :            :                 .minFilter = filters[s],
     597                 :         18 :                 .addressModeU = modes[a],
     598                 :            :                 .addressModeV = modes[a],
     599                 :            :                 .addressModeW = modes[a],
     600                 :            :                 .maxAnisotropy = 1.0,
     601                 :            :             };
     602                 :            : 
     603         [ -  + ]:         18 :             VK(vk->CreateSampler(vk->dev, &sinfo, PL_VK_ALLOC, &p->samplers[s][a]));
     604                 :            :         }
     605                 :            :     }
     606                 :            : 
     607                 :          3 :     return pl_gpu_finalize(gpu);
     608                 :            : 
     609                 :          0 : error:
     610                 :          0 :     vk_gpu_destroy(gpu);
     611                 :          0 :     return NULL;
     612                 :            : }
     613                 :            : 
     614                 :          4 : void pl_vulkan_sem_destroy(pl_gpu gpu, VkSemaphore *semaphore)
     615                 :            : {
     616                 :          4 :     VkSemaphore sem = *semaphore;
     617         [ +  - ]:          4 :     if (!sem)
     618                 :            :         return;
     619                 :            : 
     620                 :          4 :     struct pl_vk *p = PL_PRIV(gpu);
     621                 :          4 :     struct vk_ctx *vk = p->vk;
     622                 :          4 :     vk->DestroySemaphore(vk->dev, sem, PL_VK_ALLOC);
     623                 :          4 :     *semaphore = VK_NULL_HANDLE;
     624                 :            : }
     625                 :            : 
     626                 :          4 : VkSemaphore pl_vulkan_sem_create(pl_gpu gpu, const struct pl_vulkan_sem_params *params)
     627                 :            : {
     628                 :          4 :     struct pl_vk *p = PL_PRIV(gpu);
     629                 :          4 :     struct vk_ctx *vk = p->vk;
     630                 :            : 
     631         [ -  + ]:          4 :     pl_assert(PL_ISPOT(params->export_handle));
     632         [ -  + ]:          4 :     if ((params->export_handle & gpu->export_caps.sync) != params->export_handle) {
     633                 :          0 :         PL_ERR(gpu, "Invalid handle type 0x%"PRIx64" specified for "
     634                 :            :                "`pl_vulkan_sem_create`!", (uint64_t) params->export_handle);
     635                 :          0 :         return VK_NULL_HANDLE;
     636                 :            :     }
     637                 :            : 
     638   [ -  -  -  + ]:          4 :     switch (params->export_handle) {
     639                 :          0 :     case PL_HANDLE_FD:
     640                 :          0 :         params->out_handle->fd = -1;
     641                 :          0 :         break;
     642                 :          0 :     case PL_HANDLE_WIN32:
     643                 :            :     case PL_HANDLE_WIN32_KMT:
     644                 :          0 :         params->out_handle->handle = NULL;
     645                 :          0 :         break;
     646                 :            :     case PL_HANDLE_DMA_BUF:
     647                 :            :     case PL_HANDLE_HOST_PTR:
     648                 :            :     case PL_HANDLE_MTL_TEX:
     649                 :            :     case PL_HANDLE_IOSURFACE:
     650                 :          0 :         pl_unreachable();
     651                 :            :     }
     652                 :            : 
     653                 :          8 :     const VkExportSemaphoreCreateInfoKHR einfo = {
     654                 :            :         .sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHR,
     655                 :          4 :         .handleTypes = vk_sync_handle_type(params->export_handle),
     656                 :            :     };
     657                 :            : 
     658                 :          8 :     const VkSemaphoreTypeCreateInfo stinfo = {
     659                 :            :         .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
     660         [ +  - ]:          4 :         .pNext = params->export_handle ? &einfo : NULL,
     661                 :          4 :         .semaphoreType = params->type,
     662                 :          4 :         .initialValue = params->initial_value,
     663                 :            :     };
     664                 :            : 
     665                 :          4 :     const VkSemaphoreCreateInfo sinfo = {
     666                 :            :         .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
     667                 :            :         .pNext = &stinfo,
     668                 :            :     };
     669                 :            : 
     670                 :          4 :     VkSemaphore sem = VK_NULL_HANDLE;
     671         [ -  + ]:          4 :     VK(vk->CreateSemaphore(vk->dev, &sinfo, PL_VK_ALLOC, &sem));
     672   [ +  -  +  - ]:          8 :     PL_VK_NAME(SEMAPHORE, sem, PL_DEF(params->debug_tag, "pl_vulkan_sem"));
     673                 :            : 
     674                 :            : #ifdef PL_HAVE_UNIX
     675         [ -  + ]:          4 :     if (params->export_handle == PL_HANDLE_FD) {
     676                 :          0 :         VkSemaphoreGetFdInfoKHR finfo = {
     677                 :            :             .sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
     678                 :          0 :             .handleType = einfo.handleTypes,
     679                 :            :             .semaphore = sem,
     680                 :            :         };
     681                 :            : 
     682         [ #  # ]:          0 :         VK(vk->GetSemaphoreFdKHR(vk->dev, &finfo, &params->out_handle->fd));
     683                 :            :     }
     684                 :            : #endif
     685                 :            : 
     686                 :            : #ifdef PL_HAVE_WIN32
     687                 :            :     if (params->export_handle == PL_HANDLE_WIN32 ||
     688                 :            :         params->export_handle == PL_HANDLE_WIN32_KMT)
     689                 :            :     {
     690                 :            :         VkSemaphoreGetWin32HandleInfoKHR handle_info = {
     691                 :            :             .sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR,
     692                 :            :             .handleType = einfo.handleTypes,
     693                 :            :             .semaphore = sem,
     694                 :            :         };
     695                 :            : 
     696                 :            :         VK(vk->GetSemaphoreWin32HandleKHR(vk->dev, &handle_info,
     697                 :            :                                           &params->out_handle->handle));
     698                 :            :     }
     699                 :            : #endif
     700                 :            : 
     701                 :          4 :     return sem;
     702                 :            : 
     703                 :          0 : error:
     704                 :            : #ifdef PL_HAVE_UNIX
     705         [ #  # ]:          0 :     if (params->export_handle == PL_HANDLE_FD) {
     706         [ #  # ]:          0 :         if (params->out_handle->fd > -1)
     707                 :          0 :             close(params->out_handle->fd);
     708                 :            :     }
     709                 :            : #endif
     710                 :            : #ifdef PL_HAVE_WIN32
     711                 :            :     if (params->export_handle == PL_HANDLE_WIN32) {
     712                 :            :         if (params->out_handle->handle != NULL)
     713                 :            :             CloseHandle(params->out_handle->handle);
     714                 :            :     }
     715                 :            :     // PL_HANDLE_WIN32_KMT is just an identifier. It doesn't get closed.
     716                 :            : #endif
     717                 :          0 :     vk->DestroySemaphore(vk->dev, sem, PL_VK_ALLOC);
     718                 :          0 :     return VK_NULL_HANDLE;
     719                 :            : }
     720                 :            : 
     721                 :         91 : static void vk_gpu_flush(pl_gpu gpu)
     722                 :            : {
     723                 :         91 :     struct pl_vk *p = PL_PRIV(gpu);
     724                 :         91 :     struct vk_ctx *vk = p->vk;
     725                 :         91 :     CMD_SUBMIT(NULL);
     726                 :         91 :     vk_rotate_queues(vk);
     727                 :         91 :     vk_malloc_garbage_collect(vk->ma);
     728                 :         91 : }
     729                 :            : 
     730                 :        188 : static void vk_gpu_finish(pl_gpu gpu)
     731                 :            : {
     732                 :        188 :     struct pl_vk *p = PL_PRIV(gpu);
     733                 :        188 :     struct vk_ctx *vk = p->vk;
     734                 :        188 :     CMD_SUBMIT(NULL);
     735                 :        188 :     vk_wait_idle(vk);
     736                 :        188 : }
     737                 :            : 
     738                 :          3 : static bool vk_gpu_is_failed(pl_gpu gpu)
     739                 :            : {
     740                 :          3 :     struct pl_vk *p = PL_PRIV(gpu);
     741                 :          3 :     struct vk_ctx *vk = p->vk;
     742                 :          3 :     return vk->failed;
     743                 :            : }
     744                 :            : 
     745                 :         10 : struct vk_cmd *pl_vk_steal_cmd(pl_gpu gpu)
     746                 :            : {
     747                 :         10 :     struct pl_vk *p = PL_PRIV(gpu);
     748                 :         10 :     struct vk_ctx *vk = p->vk;
     749                 :            : 
     750                 :         10 :     pl_mutex_lock(&p->recording);
     751                 :         10 :     struct vk_cmd *cmd = p->cmd;
     752                 :         10 :     p->cmd = NULL;
     753                 :         10 :     pl_mutex_unlock(&p->recording);
     754                 :            : 
     755                 :         10 :     struct vk_cmdpool *pool = vk->pool_graphics;
     756   [ -  +  -  - ]:         10 :     if (!cmd || cmd->pool != pool) {
     757                 :         10 :         vk_cmd_submit(&cmd);
     758                 :         10 :         cmd = vk_cmd_begin(pool, NULL);
     759                 :            :     }
     760                 :            : 
     761                 :         10 :     return cmd;
     762                 :            : }
     763                 :            : 
     764                 :          1 : void pl_vk_print_heap(pl_gpu gpu, enum pl_log_level lev)
     765                 :            : {
     766                 :          1 :     struct pl_vk *p = PL_PRIV(gpu);
     767                 :          1 :     struct vk_ctx *vk = p->vk;
     768                 :          1 :     vk_malloc_print_stats(vk->ma, lev);
     769                 :          1 : }
     770                 :            : 
     771                 :            : static const struct pl_gpu_fns pl_fns_vk = {
     772                 :            :     .destroy                = vk_gpu_destroy,
     773                 :            :     .tex_create             = vk_tex_create,
     774                 :            :     .tex_destroy            = vk_tex_deref,
     775                 :            :     .tex_invalidate         = vk_tex_invalidate,
     776                 :            :     .tex_clear_ex           = vk_tex_clear_ex,
     777                 :            :     .tex_blit               = vk_tex_blit,
     778                 :            :     .tex_upload             = vk_tex_upload,
     779                 :            :     .tex_download           = vk_tex_download,
     780                 :            :     .tex_poll               = vk_tex_poll,
     781                 :            :     .buf_create             = vk_buf_create,
     782                 :            :     .buf_destroy            = vk_buf_deref,
     783                 :            :     .buf_write              = vk_buf_write,
     784                 :            :     .buf_read               = vk_buf_read,
     785                 :            :     .buf_copy               = vk_buf_copy,
     786                 :            :     .buf_export             = vk_buf_export,
     787                 :            :     .buf_poll               = vk_buf_poll,
     788                 :            :     .desc_namespace         = vk_desc_namespace,
     789                 :            :     .pass_create            = vk_pass_create,
     790                 :            :     .pass_destroy           = vk_pass_destroy,
     791                 :            :     .pass_run               = vk_pass_run,
     792                 :            :     .timer_create           = vk_timer_create,
     793                 :            :     .timer_destroy          = vk_timer_destroy,
     794                 :            :     .timer_query            = vk_timer_query,
     795                 :            :     .gpu_flush              = vk_gpu_flush,
     796                 :            :     .gpu_finish             = vk_gpu_finish,
     797                 :            :     .gpu_is_failed          = vk_gpu_is_failed,
     798                 :            : };

Generated by: LCOV version 1.16