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, ®ion);
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 : : }
|