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 : }
|