LCOV - code coverage report
Current view: top level - src/vulkan - gpu_buf.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 183 219 83.6 %
Date: 2025-03-29 09:04:10 Functions: 12 12 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 91 137 66.4 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * This file is part of libplacebo.
       3                 :            :  *
       4                 :            :  * libplacebo is free software; you can redistribute it and/or
       5                 :            :  * modify it under the terms of the GNU Lesser General Public
       6                 :            :  * License as published by the Free Software Foundation; either
       7                 :            :  * version 2.1 of the License, or (at your option) any later version.
       8                 :            :  *
       9                 :            :  * libplacebo is distributed in the hope that it will be useful,
      10                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12                 :            :  * GNU Lesser General Public License for more details.
      13                 :            :  *
      14                 :            :  * You should have received a copy of the GNU Lesser General Public
      15                 :            :  * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
      16                 :            :  */
      17                 :            : 
      18                 :            : #include "gpu.h"
      19                 :            : 
      20                 :       1057 : VK_CB_FUNC_DEF(vk_buf_deref);
      21                 :            : 
      22                 :       1057 : void vk_buf_barrier(pl_gpu gpu, struct vk_cmd *cmd, pl_buf buf,
      23                 :            :                     VkPipelineStageFlags2 stage, VkAccessFlags2 access,
      24                 :            :                     size_t offset, size_t size, bool export)
      25                 :            : {
      26                 :       1057 :     struct pl_vk *p = PL_PRIV(gpu);
      27                 :       1057 :     struct vk_ctx *vk = p->vk;
      28                 :       1057 :     struct pl_buf_vk *buf_vk = PL_PRIV(buf);
      29   [ -  +  -  - ]:       1057 :     pl_assert(!export || !buf_vk->exported); // can't re-export exported buffers
      30                 :       1057 :     pl_rc_ref(&buf_vk->rc);
      31                 :            : 
      32   [ +  +  -  + ]:       1057 :     bool needs_flush = buf_vk->needs_flush || buf->params.host_mapped ||
      33         [ +  + ]:        287 :                        buf->params.import_handle == PL_HANDLE_HOST_PTR;
      34   [ +  -  +  + ]:       1057 :     bool noncoherent = buf_vk->mem.data && !buf_vk->mem.coherent;
      35         [ +  + ]:       1057 :     if (needs_flush && noncoherent) {
      36         [ -  + ]:          7 :         VK(vk->FlushMappedMemoryRanges(vk->dev, 1, &(struct VkMappedMemoryRange) {
      37                 :            :             .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
      38                 :            :             .memory = buf_vk->mem.vkmem,
      39                 :            :             .offset = buf_vk->mem.map_offset,
      40                 :            :             .size = buf_vk->mem.map_size,
      41                 :            :         }));
      42                 :            : 
      43                 :            :         // Just ignore errors, not much we can do about them other than
      44                 :            :         // logging them and moving on...
      45                 :       1057 :     error: ;
      46                 :            :     }
      47                 :            : 
      48                 :            :     struct vk_sync_scope last;
      49                 :       1057 :     last = vk_sem_barrier(cmd, &buf_vk->sem, stage, access, export);
      50                 :            : 
      51                 :            :     // CONCURRENT buffers require transitioning to/from IGNORED, EXCLUSIVE
      52                 :            :     // buffers require transitioning to/from the concrete QF index
      53         [ +  - ]:       1057 :     uint32_t qf = vk->pools.num > 1 ? VK_QUEUE_FAMILY_IGNORED : cmd->pool->qf;
      54         [ +  - ]:       1057 :     uint32_t src_qf = buf_vk->exported ? VK_QUEUE_FAMILY_EXTERNAL_KHR : qf;
      55         [ +  - ]:       1057 :     uint32_t dst_qf = export ? VK_QUEUE_FAMILY_EXTERNAL_KHR : qf;
      56                 :            : 
      57   [ +  +  -  + ]:       1057 :     if (last.access || src_qf != dst_qf) {
      58                 :         25 :         vk_cmd_barrier(cmd, &(VkDependencyInfo) {
      59                 :            :             .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
      60                 :            :             .bufferMemoryBarrierCount = 1,
      61                 :         25 :             .pBufferMemoryBarriers = &(VkBufferMemoryBarrier2) {
      62                 :            :                 .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2,
      63                 :         25 :                 .srcStageMask = last.stage,
      64                 :            :                 .srcAccessMask = last.access,
      65                 :            :                 .dstStageMask = stage,
      66                 :            :                 .dstAccessMask = access,
      67                 :            :                 .srcQueueFamilyIndex = src_qf,
      68                 :            :                 .dstQueueFamilyIndex = dst_qf,
      69                 :         25 :                 .buffer = buf_vk->mem.buf,
      70                 :         25 :                 .offset = buf_vk->mem.offset + offset,
      71                 :            :                 .size = size,
      72                 :            :             },
      73                 :            :         });
      74                 :            :     }
      75                 :            : 
      76                 :       1057 :     buf_vk->needs_flush = false;
      77                 :       1057 :     buf_vk->exported = export;
      78                 :       1057 :     vk_cmd_callback(cmd, VK_CB_FUNC(vk_buf_deref), gpu, buf);
      79                 :       1057 : }
      80                 :            : 
      81                 :       2350 : void vk_buf_deref(pl_gpu gpu, pl_buf buf)
      82                 :            : {
      83         [ +  - ]:       2350 :     if (!buf)
      84                 :            :         return;
      85                 :            : 
      86                 :       2350 :     struct pl_vk *p = PL_PRIV(gpu);
      87                 :       2350 :     struct vk_ctx *vk = p->vk;
      88                 :       2350 :     struct pl_buf_vk *buf_vk = PL_PRIV(buf);
      89                 :            : 
      90         [ +  + ]:       2350 :     if (pl_rc_deref(&buf_vk->rc)) {
      91                 :       1046 :         vk->DestroyBufferView(vk->dev, buf_vk->view, PL_VK_ALLOC);
      92                 :       1046 :         vk_malloc_free(vk->ma, &buf_vk->mem);
      93                 :       1046 :         pl_free((void *) buf);
      94                 :            :     }
      95                 :            : }
      96                 :            : 
      97                 :       1046 : pl_buf vk_buf_create(pl_gpu gpu, const struct pl_buf_params *params)
      98                 :            : {
      99                 :       1046 :     struct pl_vk *p = PL_PRIV(gpu);
     100                 :       1046 :     struct vk_ctx *vk = p->vk;
     101                 :            : 
     102                 :       1046 :     struct pl_buf_t *buf = pl_zalloc_obj(NULL, buf, struct pl_buf_vk);
     103                 :       1046 :     buf->params = *params;
     104                 :       1046 :     buf->params.initial_data = NULL;
     105                 :            : 
     106                 :       1046 :     struct pl_buf_vk *buf_vk = PL_PRIV(buf);
     107                 :       1046 :     pl_rc_init(&buf_vk->rc);
     108                 :            : 
     109                 :       1046 :     struct vk_malloc_params mparams = {
     110                 :            :         .reqs = {
     111                 :       1046 :             .size = PL_ALIGN2(params->size, 4), // for vk_buf_write
     112                 :            :             .memoryTypeBits = UINT32_MAX,
     113                 :            :             .alignment = 1,
     114                 :            :         },
     115                 :            :         // these are always set, because `vk_buf_copy` can always be used
     116                 :            :         .buf_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
     117                 :            :                      VK_BUFFER_USAGE_TRANSFER_DST_BIT,
     118                 :       1046 :         .export_handle = params->export_handle,
     119                 :       1046 :         .import_handle = params->import_handle,
     120                 :            :         .shared_mem = params->shared_mem,
     121                 :       1046 :         .debug_tag = params->debug_tag,
     122                 :            :     };
     123                 :            : 
     124                 :            :     // Mandatory/optimal buffer offset alignment
     125                 :            :     VkDeviceSize *align = &mparams.reqs.alignment;
     126                 :       1046 :     VkDeviceSize extra_align = vk->props.limits.optimalBufferCopyOffsetAlignment;
     127                 :            : 
     128                 :            :     // Try and align all buffers to the minimum texel alignment, to make sure
     129                 :            :     // tex_upload/tex_download always gets aligned buffer copies if possible
     130                 :       1046 :     extra_align = pl_lcm(extra_align, p->min_texel_alignment);
     131                 :            : 
     132                 :       1046 :     enum pl_buf_mem_type mem_type = params->memory_type;
     133                 :            :     bool is_texel = false;
     134                 :            : 
     135         [ +  + ]:       1046 :     if (params->uniform) {
     136                 :         10 :         mparams.buf_usage |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
     137                 :         10 :         *align = pl_lcm(*align, vk->props.limits.minUniformBufferOffsetAlignment);
     138                 :            :         mem_type = PL_BUF_MEM_DEVICE;
     139         [ -  + ]:         10 :         if (params->format) {
     140                 :          0 :             mparams.buf_usage |= VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
     141                 :            :             is_texel = true;
     142                 :            :         }
     143                 :            :     }
     144                 :            : 
     145         [ +  + ]:       1046 :     if (params->storable) {
     146                 :         28 :         mparams.buf_usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
     147                 :         28 :         *align = pl_lcm(*align, vk->props.limits.minStorageBufferOffsetAlignment);
     148                 :         28 :         buf_vk->update_queue = COMPUTE;
     149                 :            :         mem_type = PL_BUF_MEM_DEVICE;
     150         [ +  + ]:         28 :         if (params->format) {
     151                 :         18 :             mparams.buf_usage |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
     152                 :            :             is_texel = true;
     153                 :            :         }
     154                 :            :     }
     155                 :            : 
     156         [ -  + ]:       1028 :     if (is_texel) {
     157                 :         18 :         *align = pl_lcm(*align, vk->props.limits.minTexelBufferOffsetAlignment);
     158                 :         18 :         *align = pl_lcm(*align, params->format->texel_size);
     159                 :            :     }
     160                 :            : 
     161         [ +  + ]:       1046 :     if (params->drawable) {
     162                 :        475 :         mparams.buf_usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
     163                 :            :                              VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
     164                 :            :         mem_type = PL_BUF_MEM_DEVICE;
     165                 :            :     }
     166                 :            : 
     167   [ +  +  +  + ]:       1046 :     if (params->host_writable || params->initial_data) {
     168                 :            :         // Buffers should be written using mapped memory if possible
     169                 :        773 :         mparams.optimal = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
     170                 :            :         // Use the transfer queue for updates on very large buffers (1 MB)
     171         [ +  + ]:        773 :         if (params->size > 1024*1024)
     172                 :          8 :             buf_vk->update_queue = TRANSFER;
     173                 :            :     }
     174                 :            : 
     175         [ +  + ]:       1046 :     if (params->host_mapped || params->host_readable) {
     176                 :        232 :         mparams.required |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
     177                 :            : 
     178         [ +  + ]:        232 :         if (params->size > 1024) {
     179                 :            :             // Prefer cached memory for large buffers (1 kB) which may be read
     180                 :            :             // from, because uncached reads are extremely slow
     181                 :        128 :             mparams.optimal |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
     182                 :            :         }
     183                 :            :     }
     184                 :            : 
     185   [ +  +  -  -  :       1046 :     switch (mem_type) {
                      - ]
     186                 :        527 :     case PL_BUF_MEM_AUTO:
     187                 :            :         // We generally prefer VRAM since it's faster than RAM, but any number
     188                 :            :         // of other requirements could potentially exclude it, so just mark it
     189                 :            :         // as optimal by default. Additionally, don't do this if the available
     190                 :            :         // VRAM size is very small.
     191         [ +  + ]:        527 :         if (!(mparams.optimal & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) &&
     192         [ +  - ]:        406 :             params->size * MAPPED_VRAM_THRESHOLD <= gpu->limits.max_mapped_vram)
     193                 :            :         {
     194                 :        406 :             mparams.optimal |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
     195                 :            :         }
     196                 :            :         break;
     197                 :        519 :     case PL_BUF_MEM_DEVICE:
     198                 :            :         // Force device local memory.
     199                 :        519 :         mparams.required |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
     200                 :        519 :         break;
     201                 :          0 :     case PL_BUF_MEM_HOST:
     202                 :            :         // This isn't a true guarantee, but actually trying to restrict the
     203                 :            :         // device-local bit locks out all memory heaps on iGPUs. Requiring
     204                 :            :         // the memory be host-mapped is the easiest compromise.
     205                 :          0 :         mparams.required |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
     206                 :          0 :         mparams.optimal  |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
     207                 :          0 :         break;
     208                 :            :     case PL_BUF_MEM_TYPE_COUNT:
     209                 :          0 :         pl_unreachable();
     210                 :            :     }
     211                 :            : 
     212         [ +  + ]:       1046 :     if (params->import_handle) {
     213                 :         22 :         size_t offset = params->shared_mem.offset;
     214   [ +  -  -  + ]:         22 :         if (PL_ALIGN(offset, *align) != offset) {
     215                 :          0 :             PL_ERR(gpu, "Imported memory offset %zu violates minimum alignment "
     216                 :            :                    "requirement of enabled usage flags (%zu)!",
     217                 :            :                    offset, (size_t) *align);
     218                 :          0 :             goto error;
     219                 :            :         }
     220                 :            :     } else {
     221                 :       1024 :         *align = pl_lcm(*align, extra_align);
     222                 :            :     }
     223                 :            : 
     224         [ +  + ]:       1046 :     if (!vk_malloc_slice(vk->ma, &buf_vk->mem, &mparams))
     225                 :          4 :         goto error;
     226                 :            : 
     227         [ +  + ]:       1042 :     if (params->host_mapped)
     228                 :          3 :         buf->data = buf_vk->mem.data;
     229                 :            : 
     230         [ +  + ]:       1042 :     if (params->export_handle) {
     231                 :          6 :         buf->shared_mem = buf_vk->mem.shared_mem;
     232                 :          6 :         buf->shared_mem.drm_format_mod = DRM_FORMAT_MOD_LINEAR;
     233                 :          6 :         buf_vk->exported = true;
     234                 :            :     }
     235                 :            : 
     236         [ +  + ]:       1042 :     if (is_texel) {
     237                 :         18 :         struct pl_fmt_vk *fmtp = PL_PRIV(params->format);
     238                 :         36 :         VkBufferViewCreateInfo vinfo = {
     239                 :            :             .sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO,
     240                 :         18 :             .buffer = buf_vk->mem.buf,
     241         [ +  - ]:         18 :             .format = PL_DEF(fmtp->vk_fmt->bfmt, fmtp->vk_fmt->tfmt),
     242                 :         18 :             .offset = buf_vk->mem.offset,
     243                 :         18 :             .range = buf_vk->mem.size,
     244                 :            :         };
     245                 :            : 
     246         [ -  + ]:         18 :         VK(vk->CreateBufferView(vk->dev, &vinfo, PL_VK_ALLOC, &buf_vk->view));
     247   [ +  -  +  - ]:         36 :         PL_VK_NAME(BUFFER_VIEW, buf_vk->view, PL_DEF(params->debug_tag, "texel"));
     248                 :            :     }
     249                 :            : 
     250         [ +  + ]:       1042 :     if (params->initial_data)
     251                 :        487 :         vk_buf_write(gpu, buf, 0, params->initial_data, params->size);
     252                 :            : 
     253                 :            :     return buf;
     254                 :            : 
     255                 :          4 : error:
     256                 :          4 :     vk_buf_deref(gpu, buf);
     257                 :          4 :     return NULL;
     258                 :            : }
     259                 :            : 
     260                 :        247 : static void invalidate_buf(pl_gpu gpu, pl_buf buf)
     261                 :            : {
     262                 :        247 :     struct pl_vk *p = PL_PRIV(gpu);
     263                 :        247 :     struct vk_ctx *vk = p->vk;
     264                 :        247 :     struct pl_buf_vk *buf_vk = PL_PRIV(buf);
     265                 :            : 
     266   [ -  +  +  + ]:        247 :     if (buf_vk->mem.data && !buf_vk->mem.coherent) {
     267         [ -  + ]:        126 :         VK(vk->InvalidateMappedMemoryRanges(vk->dev, 1, &(VkMappedMemoryRange) {
     268                 :            :             .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
     269                 :            :             .memory = buf_vk->mem.vkmem,
     270                 :            :             .offset = buf_vk->mem.map_offset,
     271                 :            :             .size = buf_vk->mem.map_size,
     272                 :            :         }));
     273                 :            :     }
     274                 :            : 
     275                 :            :     // Ignore errors (after logging), nothing useful we can do anyway
     276                 :        121 : error: ;
     277                 :        247 :     vk_buf_deref(gpu, buf);
     278                 :        247 : }
     279                 :            : 
     280                 :        247 : VK_CB_FUNC_DEF(invalidate_buf);
     281                 :            : 
     282                 :        270 : void vk_buf_flush(pl_gpu gpu, struct vk_cmd *cmd, pl_buf buf,
     283                 :            :                   size_t offset, size_t size)
     284                 :            : {
     285                 :        270 :     struct pl_buf_vk *buf_vk = PL_PRIV(buf);
     286                 :            : 
     287                 :            :     // We need to perform a flush if the host is capable of reading back from
     288                 :            :     // the buffer, or if we intend to overwrite it using mapped memory
     289                 :        270 :     bool can_read = buf->params.host_readable;
     290   [ +  -  +  + ]:        270 :     bool can_write = buf_vk->mem.data && buf->params.host_writable;
     291   [ +  -  +  + ]:        270 :     if (buf->params.host_mapped || buf->params.import_handle == PL_HANDLE_HOST_PTR)
     292                 :            :         can_read = can_write = true;
     293                 :            : 
     294         [ +  + ]:        270 :     if (!can_read && !can_write)
     295                 :         23 :         return;
     296                 :            : 
     297                 :        494 :     vk_cmd_barrier(cmd, &(VkDependencyInfo) {
     298                 :            :         .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
     299                 :            :         .bufferMemoryBarrierCount = 1,
     300                 :        494 :         .pBufferMemoryBarriers = &(VkBufferMemoryBarrier2) {
     301                 :            :             .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2,
     302                 :        247 :             .srcStageMask = buf_vk->sem.write.stage,
     303                 :        247 :             .srcAccessMask = buf_vk->sem.write.access,
     304                 :            :             .dstStageMask = VK_PIPELINE_STAGE_2_HOST_BIT,
     305         [ +  + ]:        247 :             .dstAccessMask = (can_read ? VK_ACCESS_2_HOST_READ_BIT : 0)
     306         [ +  + ]:        247 :                            | (can_write ? VK_ACCESS_2_HOST_WRITE_BIT : 0),
     307                 :            :             .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
     308                 :            :             .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
     309                 :        247 :             .buffer = buf_vk->mem.buf,
     310                 :        247 :             .offset = buf_vk->mem.offset + offset,
     311                 :            :             .size = size,
     312                 :            :         },
     313                 :            :     });
     314                 :            : 
     315                 :            :     // We need to hold on to the buffer until this barrier completes
     316                 :        247 :     vk_cmd_callback(cmd, VK_CB_FUNC(invalidate_buf), gpu, buf);
     317                 :        247 :     pl_rc_ref(&buf_vk->rc);
     318                 :            : }
     319                 :            : 
     320                 :       1067 : bool vk_buf_poll(pl_gpu gpu, pl_buf buf, uint64_t timeout)
     321                 :            : {
     322                 :       1067 :     struct pl_vk *p = PL_PRIV(gpu);
     323                 :       1067 :     struct vk_ctx *vk = p->vk;
     324                 :       1067 :     struct pl_buf_vk *buf_vk = PL_PRIV(buf);
     325                 :            : 
     326                 :            :     // Opportunistically check if we can re-use this buffer without flush
     327                 :       1067 :     vk_poll_commands(vk, 0);
     328         [ +  + ]:       1067 :     if (pl_rc_count(&buf_vk->rc) == 1)
     329                 :            :         return false;
     330                 :            : 
     331                 :            :     // Otherwise, we're force to submit any queued command so that the
     332                 :            :     // user is guaranteed to see progress eventually, even if they call
     333                 :            :     // this in a tight loop
     334                 :         55 :     CMD_SUBMIT(NULL);
     335                 :         55 :     vk_poll_commands(vk, timeout);
     336                 :            : 
     337                 :         55 :     return pl_rc_count(&buf_vk->rc) > 1;
     338                 :            : }
     339                 :            : 
     340                 :        789 : void vk_buf_write(pl_gpu gpu, pl_buf buf, size_t offset,
     341                 :            :                   const void *data, size_t size)
     342                 :            : {
     343                 :        789 :     struct pl_vk *p = PL_PRIV(gpu);
     344                 :        789 :     struct vk_ctx *vk = p->vk;
     345                 :        789 :     struct pl_buf_vk *buf_vk = PL_PRIV(buf);
     346                 :            : 
     347                 :            :     // For host-mapped buffers, we can just directly memcpy the buffer contents.
     348                 :            :     // Otherwise, we can update the buffer from the GPU using a command buffer.
     349         [ +  - ]:        789 :     if (buf_vk->mem.data) {
     350                 :            :         // ensure no queued operations
     351         [ -  + ]:        789 :         while (vk_buf_poll(gpu, buf, UINT64_MAX))
     352                 :            :             ; // do nothing
     353                 :            : 
     354                 :        789 :         uintptr_t addr = (uintptr_t) buf_vk->mem.data + offset;
     355                 :        789 :         memcpy((void *) addr, data, size);
     356                 :        789 :         buf_vk->needs_flush = true;
     357                 :            :     } else {
     358                 :          0 :         struct vk_cmd *cmd = CMD_BEGIN(buf_vk->update_queue);
     359         [ #  # ]:          0 :         if (!cmd) {
     360                 :          0 :             PL_ERR(gpu, "Failed updating buffer!");
     361                 :          0 :             return;
     362                 :            :         }
     363                 :            : 
     364                 :          0 :         vk_buf_barrier(gpu, cmd, buf, VK_PIPELINE_STAGE_2_COPY_BIT,
     365                 :            :                        VK_ACCESS_2_TRANSFER_WRITE_BIT, offset, size, false);
     366                 :            : 
     367                 :            :         // Vulkan requires `size` to be a multiple of 4, so we need to make
     368                 :            :         // sure to handle the end separately if the original data is not
     369                 :            :         const size_t max_transfer = 64 * 1024;
     370                 :          0 :         size_t size_rem = size % 4;
     371                 :          0 :         size_t size_base = size - size_rem;
     372                 :          0 :         VkDeviceSize buf_offset = buf_vk->mem.offset + offset;
     373                 :            : 
     374         [ #  # ]:          0 :         if (size_base > max_transfer) {
     375                 :          0 :             PL_TRACE(gpu, "Using multiple vkCmdUpdateBuffer calls to upload "
     376                 :            :                      "large buffer. Consider using buffer-buffer transfers "
     377                 :            :                      "instead!");
     378                 :            :         }
     379                 :            : 
     380         [ #  # ]:          0 :         for (size_t xfer = 0; xfer < size_base; xfer += max_transfer) {
     381                 :          0 :             vk->CmdUpdateBuffer(cmd->buf, buf_vk->mem.buf,
     382                 :            :                                 buf_offset + xfer,
     383                 :          0 :                                 PL_MIN(size_base - xfer, max_transfer),
     384                 :            :                                 (void *) ((uint8_t *) data + xfer));
     385                 :            :         }
     386                 :            : 
     387         [ #  # ]:          0 :         if (size_rem) {
     388                 :          0 :             uint8_t tail[4] = {0};
     389                 :            :             memcpy(tail, data, size_rem);
     390                 :          0 :             vk->CmdUpdateBuffer(cmd->buf, buf_vk->mem.buf, buf_offset + size_base,
     391                 :            :                                 sizeof(tail), tail);
     392                 :            :         }
     393                 :            : 
     394         [ #  # ]:          0 :         pl_assert(!buf->params.host_readable); // no flush needed due to this
     395                 :          0 :         CMD_FINISH(&cmd);
     396                 :            :     }
     397                 :            : }
     398                 :            : 
     399                 :        231 : bool vk_buf_read(pl_gpu gpu, pl_buf buf, size_t offset, void *dest, size_t size)
     400                 :            : {
     401                 :        231 :     struct pl_vk *p = PL_PRIV(gpu);
     402                 :        231 :     struct vk_ctx *vk = p->vk;
     403                 :        231 :     struct pl_buf_vk *buf_vk = PL_PRIV(buf);
     404         [ -  + ]:        231 :     pl_assert(buf_vk->mem.data);
     405                 :            : 
     406   [ +  +  +  - ]:        231 :     if (vk_buf_poll(gpu, buf, 0) && buf_vk->sem.write.sync.sem) {
     407                 :            :         // ensure no more queued writes
     408         [ -  + ]:         10 :         VK(vk->WaitSemaphores(vk->dev, &(VkSemaphoreWaitInfo) {
     409                 :            :             .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO,
     410                 :            :             .semaphoreCount = 1,
     411                 :            :             .pSemaphores = &buf_vk->sem.write.sync.sem,
     412                 :            :             .pValues = &buf_vk->sem.write.sync.value,
     413                 :            :         }, UINT64_MAX));
     414                 :            : 
     415                 :            :         // process callbacks
     416                 :         10 :         vk_poll_commands(vk, 0);
     417                 :            :     }
     418                 :            : 
     419                 :        231 :     uintptr_t addr = (uintptr_t) buf_vk->mem.data + (size_t) offset;
     420                 :        231 :     memcpy(dest, (void *) addr, size);
     421                 :        231 :     return true;
     422                 :            : 
     423                 :            : error:
     424                 :          0 :     return false;
     425                 :            : }
     426                 :            : 
     427                 :          1 : void vk_buf_copy(pl_gpu gpu, pl_buf dst, size_t dst_offset,
     428                 :            :                  pl_buf src, size_t src_offset, size_t size)
     429                 :            : {
     430                 :          1 :     struct pl_vk *p = PL_PRIV(gpu);
     431                 :          1 :     struct vk_ctx *vk = p->vk;
     432                 :          1 :     struct pl_buf_vk *dst_vk = PL_PRIV(dst);
     433                 :          1 :     struct pl_buf_vk *src_vk = PL_PRIV(src);
     434                 :            : 
     435                 :          1 :     struct vk_cmd *cmd = CMD_BEGIN(dst_vk->update_queue);
     436         [ -  + ]:          1 :     if (!cmd) {
     437                 :          0 :         PL_ERR(gpu, "Failed copying buffer!");
     438                 :          0 :         return;
     439                 :            :     }
     440                 :            : 
     441                 :          1 :     vk_buf_barrier(gpu, cmd, dst, VK_PIPELINE_STAGE_2_COPY_BIT,
     442                 :            :                    VK_ACCESS_2_TRANSFER_WRITE_BIT, dst_offset, size, false);
     443                 :          1 :     vk_buf_barrier(gpu, cmd, src, VK_PIPELINE_STAGE_2_COPY_BIT,
     444                 :            :                    VK_ACCESS_2_TRANSFER_READ_BIT, src_offset, size, false);
     445                 :            : 
     446                 :          1 :     VkBufferCopy region = {
     447                 :          1 :         .srcOffset = src_vk->mem.offset + src_offset,
     448                 :          1 :         .dstOffset = dst_vk->mem.offset + dst_offset,
     449                 :            :         .size = size,
     450                 :            :     };
     451                 :            : 
     452                 :          1 :     vk->CmdCopyBuffer(cmd->buf, src_vk->mem.buf, dst_vk->mem.buf,
     453                 :            :                       1, &region);
     454                 :            : 
     455                 :          1 :     vk_buf_flush(gpu, cmd, dst, dst_offset, size);
     456                 :          1 :     CMD_FINISH(&cmd);
     457                 :            : }
     458                 :            : 
     459                 :          4 : bool vk_buf_export(pl_gpu gpu, pl_buf buf)
     460                 :            : {
     461                 :          4 :     struct pl_buf_vk *buf_vk = PL_PRIV(buf);
     462         [ -  + ]:          4 :     if (buf_vk->exported)
     463                 :            :         return true;
     464                 :            : 
     465                 :          0 :     struct vk_cmd *cmd = CMD_BEGIN(ANY);
     466         [ #  # ]:          0 :     if (!cmd) {
     467                 :          0 :         PL_ERR(gpu, "Failed exporting buffer!");
     468                 :          0 :         return false;
     469                 :            :     }
     470                 :            : 
     471                 :            :     // For the queue family ownership transfer, we can ignore all pipeline
     472                 :            :     // stages since the synchronization via fences/semaphores is required
     473                 :          0 :     vk_buf_barrier(gpu, cmd, buf, VK_PIPELINE_STAGE_2_NONE, 0, 0,
     474                 :          0 :                    buf->params.size, true);
     475                 :            : 
     476                 :            : 
     477                 :          0 :     return CMD_SUBMIT(&cmd);
     478                 :            : }

Generated by: LCOV version 1.16