LCOV - code coverage report
Current view: top level - src/vulkan - command.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 163 251 64.9 %
Date: 2025-03-29 09:04:10 Functions: 17 17 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 90 180 50.0 %

           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 "command.h"
      19                 :            : #include "utils.h"
      20                 :            : 
      21                 :            : // returns VK_SUCCESS (completed), VK_TIMEOUT (not yet completed) or an error
      22                 :            : static VkResult vk_cmd_poll(struct vk_cmd *cmd, uint64_t timeout)
      23                 :            : {
      24                 :       2460 :     struct vk_ctx *vk = cmd->pool->vk;
      25                 :       2465 :     return vk->WaitSemaphores(vk->dev, &(VkSemaphoreWaitInfo) {
      26                 :            :         .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO,
      27                 :            :         .semaphoreCount = 1,
      28                 :       2465 :         .pSemaphores = &cmd->sync.sem,
      29                 :       2460 :         .pValues = &cmd->sync.value,
      30                 :            :     }, timeout);
      31                 :            : }
      32                 :            : 
      33                 :            : static void flush_callbacks(struct vk_ctx *vk)
      34                 :            : {
      35   [ -  +  +  + ]:       6546 :     while (vk->num_pending_callbacks) {
      36                 :       4956 :         const struct vk_callback *cb = vk->pending_callbacks++;
      37                 :       4956 :         vk->num_pending_callbacks--;
      38                 :       4956 :         cb->run(cb->priv, cb->arg);
      39                 :            :     }
      40                 :            : }
      41                 :            : 
      42                 :        795 : static void vk_cmd_reset(struct vk_cmd *cmd)
      43                 :            : {
      44                 :        795 :     struct vk_ctx *vk = cmd->pool->vk;
      45                 :            : 
      46                 :            :     // Flush possible callbacks left over from a previous command still in the
      47                 :            :     // process of being reset, whose callback triggered this command being
      48                 :            :     // reset.
      49                 :            :     flush_callbacks(vk);
      50                 :        795 :     vk->pending_callbacks = cmd->callbacks.elem;
      51                 :        795 :     vk->num_pending_callbacks = cmd->callbacks.num;
      52                 :            :     flush_callbacks(vk);
      53                 :            : 
      54                 :        795 :     cmd->callbacks.num = 0;
      55                 :        795 :     cmd->deps.num = 0;
      56                 :        795 :     cmd->sigs.num = 0;
      57                 :        795 : }
      58                 :            : 
      59                 :          5 : static void vk_cmd_destroy(struct vk_cmd *cmd)
      60                 :            : {
      61         [ +  - ]:          5 :     if (!cmd)
      62                 :            :         return;
      63                 :            : 
      64                 :          5 :     struct vk_ctx *vk = cmd->pool->vk;
      65                 :            :     vk_cmd_poll(cmd, UINT64_MAX);
      66                 :          5 :     vk_cmd_reset(cmd);
      67                 :          5 :     vk->DestroySemaphore(vk->dev, cmd->sync.sem, PL_VK_ALLOC);
      68                 :          5 :     vk->FreeCommandBuffers(vk->dev, cmd->pool->pool, 1, &cmd->buf);
      69                 :            : 
      70                 :          5 :     pl_free(cmd);
      71                 :            : }
      72                 :            : 
      73                 :          5 : static struct vk_cmd *vk_cmd_create(struct vk_cmdpool *pool)
      74                 :            : {
      75                 :          5 :     struct vk_ctx *vk = pool->vk;
      76                 :          5 :     struct vk_cmd *cmd = pl_zalloc_ptr(NULL, cmd);
      77                 :          5 :     cmd->pool = pool;
      78                 :            : 
      79                 :          5 :     VkCommandBufferAllocateInfo ainfo = {
      80                 :            :         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
      81                 :          5 :         .commandPool = pool->pool,
      82                 :            :         .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
      83                 :            :         .commandBufferCount = 1,
      84                 :            :     };
      85                 :            : 
      86         [ -  + ]:          5 :     VK(vk->AllocateCommandBuffers(vk->dev, &ainfo, &cmd->buf));
      87                 :            : 
      88                 :            :     static const VkSemaphoreTypeCreateInfo stinfo = {
      89                 :            :         .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
      90                 :            :         .semaphoreType  = VK_SEMAPHORE_TYPE_TIMELINE,
      91                 :            :         .initialValue   = 0,
      92                 :            :     };
      93                 :            : 
      94                 :            :     static const VkSemaphoreCreateInfo sinfo = {
      95                 :            :         .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
      96                 :            :         .pNext = &stinfo,
      97                 :            :     };
      98                 :            : 
      99         [ -  + ]:          5 :     VK(vk->CreateSemaphore(vk->dev, &sinfo, PL_VK_ALLOC, &cmd->sync.sem));
     100         [ +  - ]:          5 :     PL_VK_NAME(SEMAPHORE, cmd->sync.sem, "cmd");
     101                 :            : 
     102                 :            :     return cmd;
     103                 :            : 
     104                 :          0 : error:
     105                 :          0 :     vk_cmd_destroy(cmd);
     106                 :          0 :     vk->failed = true;
     107                 :          0 :     return NULL;
     108                 :            : }
     109                 :            : 
     110                 :        682 : void vk_dev_callback(struct vk_ctx *vk, vk_cb callback,
     111                 :            :                      const void *priv, const void *arg)
     112                 :            : {
     113                 :        682 :     pl_mutex_lock(&vk->lock);
     114         [ +  + ]:        682 :     if (vk->cmds_pending.num > 0) {
     115                 :        216 :         struct vk_cmd *last_cmd = vk->cmds_pending.elem[vk->cmds_pending.num - 1];
     116                 :        216 :         vk_cmd_callback(last_cmd, callback, priv, arg);
     117                 :            :     } else {
     118                 :            :         // The device was already idle, so we can just immediately call it
     119                 :        466 :         callback((void *) priv, (void *) arg);
     120                 :            :     }
     121                 :        682 :     pl_mutex_unlock(&vk->lock);
     122                 :        682 : }
     123                 :            : 
     124                 :       4956 : void vk_cmd_callback(struct vk_cmd *cmd, vk_cb callback,
     125                 :            :                      const void *priv, const void *arg)
     126                 :            : {
     127   [ +  +  +  +  :       4956 :     PL_ARRAY_APPEND(cmd, cmd->callbacks, (struct vk_callback) {
                   -  + ]
     128                 :            :         .run  = callback,
     129                 :            :         .priv = (void *) priv,
     130                 :            :         .arg  = (void *) arg,
     131                 :            :     });
     132                 :       4956 : }
     133                 :            : 
     134                 :         26 : void vk_cmd_dep(struct vk_cmd *cmd, VkPipelineStageFlags2 stage, pl_vulkan_sem dep)
     135                 :            : {
     136   [ +  +  -  +  :         26 :     PL_ARRAY_APPEND(cmd, cmd->deps, (VkSemaphoreSubmitInfo) {
                   -  + ]
     137                 :            :         .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
     138                 :            :         .semaphore  = dep.sem,
     139                 :            :         .value      = dep.value,
     140                 :            :         .stageMask  = stage,
     141                 :            :     });
     142                 :         26 : }
     143                 :            : 
     144                 :        804 : void vk_cmd_sig(struct vk_cmd *cmd, VkPipelineStageFlags2 stage, pl_vulkan_sem sig)
     145                 :            : {
     146                 :            :     VkSemaphoreSubmitInfo sinfo = {
     147                 :            :         .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
     148                 :            :         .semaphore  = sig.sem,
     149                 :            :         .value      = sig.value,
     150                 :            :         .stageMask  = stage,
     151                 :            :     };
     152                 :            : 
     153                 :            :     // Try updating existing semaphore signal operations in-place
     154         [ +  + ]:        818 :     for (int i = 0; i < cmd->sigs.num; i++) {
     155         [ -  + ]:         14 :         if (cmd->sigs.elem[i].semaphore == sig.sem) {
     156         [ #  # ]:          0 :             pl_assert(sig.value > cmd->sigs.elem[i].value);
     157                 :          0 :             cmd->sigs.elem[i] = sinfo;
     158                 :            :             return;
     159                 :            :         }
     160                 :            :     }
     161                 :            : 
     162   [ +  +  -  +  :        804 :     PL_ARRAY_APPEND(cmd, cmd->sigs, sinfo);
                   -  + ]
     163                 :            : }
     164                 :            : 
     165                 :            : #define SET(FLAG, CHECK)  \
     166                 :            :     if (flags2 & (CHECK)) \
     167                 :            :         flags |= FLAG
     168                 :            : 
     169                 :            : static VkAccessFlags lower_access2(VkAccessFlags2 flags2)
     170                 :            : {
     171                 :          0 :     VkAccessFlags flags = flags2 & VK_ACCESS_FLAG_BITS_MAX_ENUM;
     172                 :          0 :     SET(VK_ACCESS_SHADER_READ_BIT,  VK_ACCESS_2_SHADER_SAMPLED_READ_BIT |
     173                 :            :                                     VK_ACCESS_2_SHADER_STORAGE_READ_BIT);
     174   [ #  #  #  #  :          0 :     SET(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT);
             #  #  #  # ]
     175                 :            :     return flags;
     176                 :            : }
     177                 :            : 
     178                 :            : static VkPipelineStageFlags lower_stage2(VkPipelineStageFlags2 flags2)
     179                 :            : {
     180                 :          0 :     VkPipelineStageFlags flags = flags2 & VK_PIPELINE_STAGE_FLAG_BITS_MAX_ENUM;
     181                 :          0 :     SET(VK_PIPELINE_STAGE_TRANSFER_BIT,     VK_PIPELINE_STAGE_2_COPY_BIT |
     182                 :            :                                             VK_PIPELINE_STAGE_2_RESOLVE_BIT |
     183                 :            :                                             VK_PIPELINE_STAGE_2_BLIT_BIT |
     184                 :            :                                             VK_PIPELINE_STAGE_2_CLEAR_BIT);
     185   [ #  #  #  #  :          0 :     SET(VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT |
             #  #  #  # ]
     186                 :            :                                             VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT);
     187                 :            :     return flags;
     188                 :            : }
     189                 :            : 
     190                 :            : #undef SET
     191                 :            : 
     192                 :       2103 : void vk_cmd_barrier(struct vk_cmd *cmd, const VkDependencyInfo *info)
     193                 :            : {
     194                 :       2103 :     struct vk_ctx *vk = cmd->pool->vk;
     195         [ +  - ]:       2103 :     if (vk->CmdPipelineBarrier2KHR) {
     196                 :       2103 :         vk->CmdPipelineBarrier2KHR(cmd->buf, info);
     197                 :       2103 :         return;
     198                 :            :     }
     199                 :            : 
     200         [ #  # ]:          0 :     pl_assert(!info->pNext);
     201         [ #  # ]:          0 :     pl_assert(info->memoryBarrierCount == 0);
     202         [ #  # ]:          0 :     pl_assert(info->bufferMemoryBarrierCount + info->imageMemoryBarrierCount == 1);
     203                 :            : 
     204         [ #  # ]:          0 :     if (info->bufferMemoryBarrierCount) {
     205                 :            : 
     206                 :          0 :         const VkBufferMemoryBarrier2 *barr2 = info->pBufferMemoryBarriers;
     207                 :          0 :         const VkBufferMemoryBarrier barr = {
     208                 :            :             .sType               = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
     209                 :          0 :             .pNext               = barr2->pNext,
     210         [ #  # ]:          0 :             .srcAccessMask       = lower_access2(barr2->srcAccessMask),
     211         [ #  # ]:          0 :             .dstAccessMask       = lower_access2(barr2->dstAccessMask),
     212                 :          0 :             .srcQueueFamilyIndex = barr2->srcQueueFamilyIndex,
     213                 :          0 :             .dstQueueFamilyIndex = barr2->dstQueueFamilyIndex,
     214                 :          0 :             .buffer              = barr2->buffer,
     215                 :          0 :             .offset              = barr2->offset,
     216                 :          0 :             .size                = barr2->size,
     217                 :            :         };
     218                 :            : 
     219         [ #  # ]:          0 :         vk->CmdPipelineBarrier(cmd->buf, lower_stage2(barr2->srcStageMask),
     220                 :          0 :                                lower_stage2(barr2->dstStageMask),
     221         [ #  # ]:          0 :                                info->dependencyFlags,
     222                 :            :                                0, NULL, 1, &barr, 0, NULL);
     223                 :            : 
     224                 :            :     } else {
     225                 :            : 
     226                 :          0 :         const VkImageMemoryBarrier2 *barr2 = info->pImageMemoryBarriers;
     227                 :          0 :         const VkImageMemoryBarrier barr = {
     228                 :            :             .sType               = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
     229                 :          0 :             .pNext               = barr2->pNext,
     230         [ #  # ]:          0 :             .srcAccessMask       = lower_access2(barr2->srcAccessMask),
     231         [ #  # ]:          0 :             .dstAccessMask       = lower_access2(barr2->dstAccessMask),
     232                 :          0 :             .oldLayout           = barr2->oldLayout,
     233                 :          0 :             .newLayout           = barr2->newLayout,
     234                 :          0 :             .srcQueueFamilyIndex = barr2->srcQueueFamilyIndex,
     235                 :          0 :             .dstQueueFamilyIndex = barr2->dstQueueFamilyIndex,
     236                 :          0 :             .image               = barr2->image,
     237                 :            :             .subresourceRange    = barr2->subresourceRange,
     238                 :            :         };
     239                 :            : 
     240         [ #  # ]:          0 :         vk->CmdPipelineBarrier(cmd->buf, lower_stage2(barr2->srcStageMask),
     241                 :          0 :                                lower_stage2(barr2->dstStageMask),
     242         [ #  # ]:          0 :                                info->dependencyFlags,
     243                 :            :                                0, NULL, 0, NULL, 1, &barr);
     244                 :            :     }
     245                 :            : }
     246                 :            : 
     247                 :       3390 : struct vk_sync_scope vk_sem_barrier(struct vk_cmd *cmd, struct vk_sem *sem,
     248                 :            :                                     VkPipelineStageFlags2 stage,
     249                 :            :                                     VkAccessFlags2 access, bool is_trans)
     250                 :            : {
     251   [ +  +  +  + ]:       3390 :     bool is_write = (access & vk_access_write) || is_trans;
     252                 :            : 
     253                 :            :     // Writes need to be synchronized against the last *read* (which is
     254                 :            :     // transitively synchronized against the last write), reads only
     255                 :            :     // need to be synchronized against the last write.
     256                 :       3390 :     struct vk_sync_scope last = sem->write;
     257   [ +  +  +  + ]:       3390 :     if (is_write && sem->read.access)
     258                 :         79 :         last = sem->read;
     259                 :            : 
     260         [ +  + ]:       3390 :     if (last.queue != cmd->queue) {
     261   [ +  +  +  + ]:       1505 :         if (!is_write && sem->read.queue == cmd->queue) {
     262                 :            :             // No semaphore needed in this case because the implicit submission
     263                 :            :             // order execution dependencies already transitively imply a wait
     264                 :            :             // for the previous write
     265         [ +  + ]:       1502 :         } else if (last.sync.sem) {
     266                 :            :             // Image barrier still needs to depend on this stage for implicit
     267                 :            :             // ordering guarantees to apply properly
     268                 :         12 :             vk_cmd_dep(cmd, stage, last.sync);
     269                 :            :             last.stage = stage;
     270                 :            :         }
     271                 :            : 
     272                 :            :         // Last access is on different queue, so no pipeline barrier needed
     273                 :            :         last.access = 0;
     274                 :            :     }
     275                 :            : 
     276   [ +  +  +  + ]:       3390 :     if (!is_write && sem->read.queue == cmd->queue &&
     277         [ +  + ]:        520 :         (sem->read.stage & stage) == stage &&
     278         [ -  + ]:        396 :         (sem->read.access & access) == access)
     279                 :            :     {
     280                 :            :         // A past pipeline barrier already covers this access transitively, so
     281                 :            :         // we don't need to emit another pipeline barrier at all
     282                 :            :         last.access = 0;
     283                 :            :     }
     284                 :            : 
     285                 :            :     if (is_write) {
     286                 :       2100 :         sem->write = (struct vk_sync_scope) {
     287                 :            :             .sync = cmd->sync,
     288                 :       2100 :             .queue = cmd->queue,
     289                 :            :             .stage = stage,
     290                 :            :             .access = access,
     291                 :            :         };
     292                 :            : 
     293                 :       2100 :         sem->read = (struct vk_sync_scope) {
     294                 :            :             .sync = cmd->sync,
     295                 :            :             .queue = cmd->queue,
     296                 :            :             // no stage or access scope, because no reads happened yet
     297                 :            :         };
     298         [ +  + ]:       1290 :     } else if (sem->read.queue == cmd->queue) {
     299                 :            :         // Coalesce multiple same-queue reads into a single access scope
     300                 :        520 :         sem->read.sync = cmd->sync;
     301                 :        520 :         sem->read.stage |= stage;
     302                 :        520 :         sem->read.access |= access;
     303                 :            :     } else {
     304                 :        770 :         sem->read = (struct vk_sync_scope) {
     305                 :            :             .sync = cmd->sync,
     306                 :            :             .queue = cmd->queue,
     307                 :            :             .stage = stage,
     308                 :            :             .access = access,
     309                 :            :         };
     310                 :            :     }
     311                 :            : 
     312                 :            :     // We never need to include pipeline barriers for reads, only writes
     313                 :       3390 :     last.access &= vk_access_write;
     314                 :       3390 :     return last;
     315                 :            : }
     316                 :            : 
     317                 :          3 : struct vk_cmdpool *vk_cmdpool_create(struct vk_ctx *vk, int qf, int qnum,
     318                 :            :                                      VkQueueFamilyProperties props)
     319                 :            : {
     320                 :          3 :     struct vk_cmdpool *pool = pl_alloc_ptr(NULL, pool);
     321                 :          3 :     *pool = (struct vk_cmdpool) {
     322                 :            :         .vk         = vk,
     323                 :            :         .props      = props,
     324                 :            :         .qf         = qf,
     325                 :          3 :         .queues     = pl_calloc(pool, qnum, sizeof(VkQueue)),
     326                 :            :         .num_queues = qnum,
     327                 :            :     };
     328                 :            : 
     329         [ +  + ]:          6 :     for (int n = 0; n < qnum; n++)
     330                 :          3 :         vk->GetDeviceQueue(vk->dev, qf, n, &pool->queues[n]);
     331                 :            : 
     332                 :          3 :     VkCommandPoolCreateInfo cinfo = {
     333                 :            :         .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
     334                 :            :         .flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT |
     335                 :            :                  VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
     336                 :            :         .queueFamilyIndex = qf,
     337                 :            :     };
     338                 :            : 
     339         [ -  + ]:          3 :     VK(vk->CreateCommandPool(vk->dev, &cinfo, PL_VK_ALLOC, &pool->pool));
     340                 :            :     return pool;
     341                 :            : 
     342                 :            : error:
     343                 :          0 :     vk_cmdpool_destroy(pool);
     344                 :          0 :     vk->failed = true;
     345                 :          0 :     return NULL;
     346                 :            : }
     347                 :            : 
     348                 :          3 : void vk_cmdpool_destroy(struct vk_cmdpool *pool)
     349                 :            : {
     350         [ +  - ]:          3 :     if (!pool)
     351                 :            :         return;
     352                 :            : 
     353         [ +  + ]:          8 :     for (int i = 0; i < pool->cmds.num; i++)
     354                 :          5 :         vk_cmd_destroy(pool->cmds.elem[i]);
     355                 :            : 
     356                 :          3 :     struct vk_ctx *vk = pool->vk;
     357                 :          3 :     vk->DestroyCommandPool(vk->dev, pool->pool, PL_VK_ALLOC);
     358                 :          3 :     pl_free(pool);
     359                 :            : }
     360                 :            : 
     361                 :        790 : struct vk_cmd *vk_cmd_begin(struct vk_cmdpool *pool, pl_debug_tag debug_tag)
     362                 :            : {
     363                 :        790 :     struct vk_ctx *vk = pool->vk;
     364                 :            : 
     365                 :            :     // Garbage collect the cmdpool first, to increase the chances of getting
     366                 :            :     // an already-available command buffer.
     367                 :        790 :     vk_poll_commands(vk, 0);
     368                 :            : 
     369                 :            :     struct vk_cmd *cmd = NULL;
     370                 :        790 :     pl_mutex_lock(&vk->lock);
     371         [ +  + ]:        790 :     if (!PL_ARRAY_POP(pool->cmds, &cmd)) {
     372                 :          5 :         cmd = vk_cmd_create(pool);
     373         [ -  + ]:          5 :         if (!cmd) {
     374                 :          0 :             pl_mutex_unlock(&vk->lock);
     375                 :          0 :             goto error;
     376                 :            :         }
     377                 :            :     }
     378                 :            : 
     379                 :        790 :     cmd->qindex = pool->idx_queues;
     380                 :        790 :     cmd->queue = pool->queues[cmd->qindex];
     381                 :        790 :     pl_mutex_unlock(&vk->lock);
     382                 :            : 
     383                 :        790 :     VkCommandBufferBeginInfo binfo = {
     384                 :            :         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
     385                 :            :         .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
     386                 :            :     };
     387                 :            : 
     388         [ -  + ]:        790 :     VK(vk->BeginCommandBuffer(cmd->buf, &binfo));
     389                 :            : 
     390         [ +  + ]:        790 :     debug_tag = PL_DEF(debug_tag, "vk_cmd");
     391         [ +  - ]:        790 :     PL_VK_NAME_HANDLE(COMMAND_BUFFER, cmd->buf, debug_tag);
     392         [ +  - ]:        790 :     PL_VK_NAME(SEMAPHORE, cmd->sync.sem, debug_tag);
     393                 :            : 
     394                 :        790 :     cmd->sync.value++;
     395                 :        790 :     vk_cmd_sig(cmd, VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, cmd->sync);
     396                 :        790 :     return cmd;
     397                 :            : 
     398                 :          0 : error:
     399                 :            :     // Something has to be seriously messed up if we get to this point
     400                 :          0 :     vk_cmd_destroy(cmd);
     401                 :          0 :     vk->failed = true;
     402                 :          0 :     return NULL;
     403                 :            : }
     404                 :            : 
     405                 :        790 : static VkResult vk_queue_submit2(struct vk_ctx *vk, VkQueue queue,
     406                 :            :                                  const VkSubmitInfo2 *info2, VkFence fence)
     407                 :            : {
     408         [ +  - ]:        790 :     if (vk->QueueSubmit2KHR)
     409                 :        790 :         return vk->QueueSubmit2KHR(queue, 1, info2, fence);
     410                 :            : 
     411                 :          0 :     const uint32_t num_deps = info2->waitSemaphoreInfoCount;
     412                 :          0 :     const uint32_t num_sigs = info2->signalSemaphoreInfoCount;
     413                 :          0 :     const uint32_t num_cmds = info2->commandBufferInfoCount;
     414                 :            : 
     415                 :          0 :     void *tmp = pl_tmp(NULL);
     416                 :          0 :     VkSemaphore *deps           = pl_calloc_ptr(tmp, num_deps, deps);
     417                 :            :     VkPipelineStageFlags *masks = pl_calloc_ptr(tmp, num_deps, masks);
     418                 :            :     uint64_t *depvals           = pl_calloc_ptr(tmp, num_deps, depvals);
     419                 :          0 :     VkSemaphore *sigs           = pl_calloc_ptr(tmp, num_sigs, sigs);
     420                 :            :     uint64_t *sigvals           = pl_calloc_ptr(tmp, num_sigs, sigvals);
     421                 :          0 :     VkCommandBuffer *cmds       = pl_calloc_ptr(tmp, num_cmds, cmds);
     422                 :            : 
     423         [ #  # ]:          0 :     for (int i = 0; i < num_deps; i++) {
     424                 :          0 :         deps[i] = info2->pWaitSemaphoreInfos[i].semaphore;
     425                 :          0 :         masks[i] = info2->pWaitSemaphoreInfos[i].stageMask;
     426                 :          0 :         depvals[i] = info2->pWaitSemaphoreInfos[i].value;
     427                 :            :     }
     428         [ #  # ]:          0 :     for (int i = 0; i < num_sigs; i++) {
     429                 :          0 :         sigs[i] = info2->pSignalSemaphoreInfos[i].semaphore;
     430                 :          0 :         sigvals[i] = info2->pSignalSemaphoreInfos[i].value;
     431                 :            :     }
     432         [ #  # ]:          0 :     for (int i = 0; i < num_cmds; i++)
     433                 :          0 :         cmds[i] = info2->pCommandBufferInfos[i].commandBuffer;
     434                 :            : 
     435                 :          0 :     const VkTimelineSemaphoreSubmitInfo tinfo = {
     436                 :            :         .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
     437                 :          0 :         .pNext = info2->pNext,
     438                 :            :         .waitSemaphoreValueCount = num_deps,
     439                 :            :         .pWaitSemaphoreValues = depvals,
     440                 :            :         .signalSemaphoreValueCount = num_sigs,
     441                 :            :         .pSignalSemaphoreValues = sigvals,
     442                 :            :     };
     443                 :            : 
     444                 :          0 :     const VkSubmitInfo info = {
     445                 :            :         .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
     446                 :            :         .pNext = &tinfo,
     447                 :            :         .waitSemaphoreCount = num_deps,
     448                 :            :         .pWaitSemaphores = deps,
     449                 :            :         .pWaitDstStageMask = masks,
     450                 :            :         .commandBufferCount = num_cmds,
     451                 :            :         .pCommandBuffers = cmds,
     452                 :            :         .signalSemaphoreCount = num_sigs,
     453                 :            :         .pSignalSemaphores = sigs,
     454                 :            :     };
     455                 :            : 
     456                 :          0 :     VkResult res = vk->QueueSubmit(queue, 1, &info, fence);
     457                 :          0 :     pl_free(tmp);
     458                 :          0 :     return res;
     459                 :            : }
     460                 :            : 
     461                 :       1687 : bool vk_cmd_submit(struct vk_cmd **pcmd)
     462                 :            : {
     463                 :       1687 :     struct vk_cmd *cmd = *pcmd;
     464         [ +  + ]:       1687 :     if (!cmd)
     465                 :            :         return true;
     466                 :            : 
     467                 :        790 :     *pcmd = NULL;
     468                 :        790 :     struct vk_cmdpool *pool = cmd->pool;
     469                 :        790 :     struct vk_ctx *vk = pool->vk;
     470                 :            : 
     471         [ -  + ]:        790 :     VK(vk->EndCommandBuffer(cmd->buf));
     472                 :            : 
     473                 :        790 :     VkSubmitInfo2 sinfo = {
     474                 :            :         .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2,
     475                 :        790 :         .waitSemaphoreInfoCount = cmd->deps.num,
     476                 :        790 :         .pWaitSemaphoreInfos = cmd->deps.elem,
     477                 :        790 :         .signalSemaphoreInfoCount = cmd->sigs.num,
     478                 :        790 :         .pSignalSemaphoreInfos = cmd->sigs.elem,
     479                 :            :         .commandBufferInfoCount = 1,
     480                 :        790 :         .pCommandBufferInfos = &(VkCommandBufferSubmitInfo) {
     481                 :            :             .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
     482                 :        790 :             .commandBuffer = cmd->buf,
     483                 :            :         },
     484                 :            :     };
     485                 :            : 
     486         [ +  - ]:        790 :     if (pl_msg_test(vk->log, PL_LOG_TRACE)) {
     487                 :          0 :         PL_TRACE(vk, "Submitting command %p on queue %p (QF %d):",
     488                 :            :                  (void *) cmd->buf, (void *) cmd->queue, pool->qf);
     489         [ #  # ]:          0 :         for (int n = 0; n < cmd->deps.num; n++) {
     490                 :          0 :             PL_TRACE(vk, "    waits on semaphore 0x%"PRIx64" = %"PRIu64,
     491                 :            :                      (uint64_t) cmd->deps.elem[n].semaphore, cmd->deps.elem[n].value);
     492                 :            :         }
     493         [ #  # ]:          0 :         for (int n = 0; n < cmd->sigs.num; n++) {
     494                 :          0 :             PL_TRACE(vk, "    signals semaphore 0x%"PRIx64" = %"PRIu64,
     495                 :            :                     (uint64_t) cmd->sigs.elem[n].semaphore, cmd->sigs.elem[n].value);
     496                 :            :         }
     497         [ #  # ]:          0 :         if (cmd->callbacks.num)
     498                 :          0 :             PL_TRACE(vk, "    signals %d callbacks", cmd->callbacks.num);
     499                 :            :     }
     500                 :            : 
     501                 :        790 :     vk->lock_queue(vk->queue_ctx, pool->qf, cmd->qindex);
     502                 :        790 :     VkResult res = vk_queue_submit2(vk, cmd->queue, &sinfo, VK_NULL_HANDLE);
     503                 :        790 :     vk->unlock_queue(vk->queue_ctx, pool->qf, cmd->qindex);
     504         [ -  + ]:        790 :     PL_VK_ASSERT(res, "vkQueueSubmit2");
     505                 :            : 
     506                 :        790 :     pl_mutex_lock(&vk->lock);
     507   [ +  +  -  +  :        790 :     PL_ARRAY_APPEND(vk->alloc, vk->cmds_pending, cmd);
                   -  + ]
     508                 :        790 :     pl_mutex_unlock(&vk->lock);
     509                 :        790 :     return true;
     510                 :            : 
     511                 :          0 : error:
     512                 :          0 :     vk_cmd_reset(cmd);
     513                 :          0 :     pl_mutex_lock(&vk->lock);
     514   [ #  #  #  #  :          0 :     PL_ARRAY_APPEND(pool, pool->cmds, cmd);
                   #  # ]
     515                 :          0 :     pl_mutex_unlock(&vk->lock);
     516                 :          0 :     vk->failed = true;
     517                 :          0 :     return false;
     518                 :            : }
     519                 :            : 
     520                 :       4468 : bool vk_poll_commands(struct vk_ctx *vk, uint64_t timeout)
     521                 :            : {
     522                 :            :     bool ret = false;
     523                 :       4468 :     pl_mutex_lock(&vk->lock);
     524                 :            : 
     525         [ +  + ]:       5258 :     while (vk->cmds_pending.num) {
     526                 :       2460 :         struct vk_cmd *cmd = vk->cmds_pending.elem[0];
     527                 :       2460 :         struct vk_cmdpool *pool = cmd->pool;
     528                 :       2460 :         pl_mutex_unlock(&vk->lock); // don't hold mutex while blocking
     529         [ +  + ]:       2460 :         if (vk_cmd_poll(cmd, timeout) == VK_TIMEOUT)
     530                 :       1670 :             return ret;
     531                 :        790 :         pl_mutex_lock(&vk->lock);
     532   [ +  -  -  + ]:        790 :         if (!vk->cmds_pending.num || vk->cmds_pending.elem[0] != cmd)
     533                 :          0 :             continue; // another thread modified this state while blocking
     534                 :            : 
     535                 :        790 :         PL_TRACE(vk, "VkSemaphore signalled: 0x%"PRIx64" = %"PRIu64,
     536                 :            :                  (uint64_t) cmd->sync.sem, cmd->sync.value);
     537         [ -  + ]:        790 :         PL_ARRAY_REMOVE_AT(vk->cmds_pending, 0); // remove before callbacks
     538                 :        790 :         vk_cmd_reset(cmd);
     539   [ +  +  -  +  :        790 :         PL_ARRAY_APPEND(pool, pool->cmds, cmd);
                   -  + ]
     540                 :            :         ret = true;
     541                 :            : 
     542                 :            :         // If we've successfully spent some time waiting for at least one
     543                 :            :         // command, disable the timeout. This has the dual purpose of both
     544                 :            :         // making sure we don't over-wait due to repeat timeout application,
     545                 :            :         // but also makes sure we don't block on future commands if we've
     546                 :            :         // already spend time waiting for one.
     547                 :            :         timeout = 0;
     548                 :            :     }
     549                 :            : 
     550                 :       2798 :     pl_mutex_unlock(&vk->lock);
     551                 :       2798 :     return ret;
     552                 :            : }
     553                 :            : 
     554                 :        101 : void vk_rotate_queues(struct vk_ctx *vk)
     555                 :            : {
     556                 :        101 :     pl_mutex_lock(&vk->lock);
     557                 :            : 
     558                 :            :     // Rotate the queues to ensure good parallelism across frames
     559         [ +  + ]:        202 :     for (int i = 0; i < vk->pools.num; i++) {
     560                 :        101 :         struct vk_cmdpool *pool = vk->pools.elem[i];
     561                 :        101 :         pool->idx_queues = (pool->idx_queues + 1) % pool->num_queues;
     562                 :        101 :         PL_TRACE(vk, "QF %d: %d/%d", pool->qf, pool->idx_queues, pool->num_queues);
     563                 :            :     }
     564                 :            : 
     565                 :        101 :     pl_mutex_unlock(&vk->lock);
     566                 :        101 : }
     567                 :            : 
     568                 :        192 : void vk_wait_idle(struct vk_ctx *vk)
     569                 :            : {
     570         [ +  + ]:        380 :     while (vk_poll_commands(vk, UINT64_MAX)) ;
     571                 :        192 : }

Generated by: LCOV version 1.16