blob: 6ecd88e76cf3d9519ab251aaf977b41cecdfa4c8 [file] [edit]
/*
* Copyright 2019 Google LLC
* SPDX-License-Identifier: MIT
*
* based in part on anv and radv which are:
* Copyright © 2015 Intel Corporation
* Copyright © 2016 Red Hat.
* Copyright © 2016 Bas Nieuwenhuizen
*/
#include "vn_queue.h"
#include "venus-protocol/vn_protocol_driver_queue.h"
#include "vn_command_buffer.h"
#include "vn_device.h"
#include "vn_feedback.h"
#include "vn_physical_device.h"
#include "vn_sync.h"
#include "vn_wsi.h"
struct vn_queue_submission_pnext {
/* intercepted structs */
VkDeviceGroupSubmitInfo group;
VkTimelineSemaphoreSubmitInfo timeline;
/* forwarded structs */
VkDeviceGroupBindSparseInfo sparse;
VkProtectedSubmitInfo protected;
};
struct vn_queue_submission {
VkStructureType batch_type;
VkQueue queue_handle;
union {
const void *batch;
const VkSubmitInfo *submit_batch;
const VkSubmitInfo2 *submit2_batch;
const VkBindSparseInfo *sparse_batch;
};
VkFence fence_handle;
bool can_feedback;
uint32_t cmd_count;
uint32_t feedback_types;
bool fix_pnext;
uint32_t dev_mask_count;
uint32_t dev_index_count;
uint32_t sem_val_count;
uint32_t wait_sem_count;
struct vn_sync_payload_external external_payload;
/* Temporary storage allocation for submission
*
* A single alloc for storage is performed and the offsets inside storage
* are set as below:
*
* batch
* - non-empty submission: copy of original batch
* - empty submission: a single batch for fence feedback (ffb)
* cmds
* - copy of original batch cmds
* - a single cmd for query feedback (qfb)
* - one cmd for each signal semaphore that has feedback (sfb)
* - if last batch, a single cmd for ffb
* pnext
* - a single pnext if batch pnext need fix
* dev_masks
* - for device group submit to append new mask for new cmds
* dev_indices
* - fix dev indices in the pNext chain
* sem_vals
* - fix timeline wait values in the pNext chain
* wait_sems
* - fix semaphore handles or infos for dropped wait semaphores
*/
struct {
union {
void *batch;
VkSubmitInfo *submit_batch;
VkSubmitInfo2 *submit2_batch;
VkBindSparseInfo *sparse_batch;
};
union {
void *cmds;
VkCommandBuffer *cmd_handles;
VkCommandBufferSubmitInfo *cmd_infos;
};
union {
void *wait_sems;
VkSemaphore *wait_sem_handles;
VkSemaphoreSubmitInfo *wait_sem_infos;
};
struct vn_queue_submission_pnext *pnext;
uint32_t *dev_masks;
uint32_t *dev_indices;
uint64_t *sem_vals;
} temp;
};
static inline size_t
vn_get_sem_size(struct vn_queue_submission *submit)
{
return submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO_2
? sizeof(VkSemaphoreSubmitInfo)
: sizeof(VkSemaphore);
}
static inline uint32_t
vn_get_wait_semaphore_count(struct vn_queue_submission *submit)
{
if (!submit->batch)
return 0;
switch (submit->batch_type) {
case VK_STRUCTURE_TYPE_SUBMIT_INFO:
return submit->submit_batch->waitSemaphoreCount;
case VK_STRUCTURE_TYPE_SUBMIT_INFO_2:
return submit->submit2_batch->waitSemaphoreInfoCount;
case VK_STRUCTURE_TYPE_BIND_SPARSE_INFO:
return submit->sparse_batch->waitSemaphoreCount;
default:
UNREACHABLE("unexpected batch type");
}
}
static inline uint32_t
vn_get_signal_semaphore_count(struct vn_queue_submission *submit)
{
if (!submit->batch)
return 0;
switch (submit->batch_type) {
case VK_STRUCTURE_TYPE_SUBMIT_INFO:
return submit->submit_batch->signalSemaphoreCount;
case VK_STRUCTURE_TYPE_SUBMIT_INFO_2:
return submit->submit2_batch->signalSemaphoreInfoCount;
case VK_STRUCTURE_TYPE_BIND_SPARSE_INFO:
return submit->sparse_batch->signalSemaphoreCount;
default:
UNREACHABLE("unexpected batch type");
}
}
static inline VkSemaphore
vn_get_wait_semaphore(struct vn_queue_submission *submit, uint32_t index)
{
assert(submit->batch);
switch (submit->batch_type) {
case VK_STRUCTURE_TYPE_SUBMIT_INFO:
return submit->submit_batch->pWaitSemaphores[index];
case VK_STRUCTURE_TYPE_SUBMIT_INFO_2:
return submit->submit2_batch->pWaitSemaphoreInfos[index].semaphore;
case VK_STRUCTURE_TYPE_BIND_SPARSE_INFO:
return submit->sparse_batch->pWaitSemaphores[index];
default:
UNREACHABLE("unexpected batch type");
}
}
static inline VkSemaphore
vn_get_signal_semaphore(struct vn_queue_submission *submit, uint32_t index)
{
assert(submit->batch);
switch (submit->batch_type) {
case VK_STRUCTURE_TYPE_SUBMIT_INFO:
return submit->submit_batch->pSignalSemaphores[index];
case VK_STRUCTURE_TYPE_SUBMIT_INFO_2:
return submit->submit2_batch->pSignalSemaphoreInfos[index].semaphore;
case VK_STRUCTURE_TYPE_BIND_SPARSE_INFO:
return submit->sparse_batch->pSignalSemaphores[index];
default:
UNREACHABLE("unexpected batch type");
}
}
static inline size_t
vn_get_batch_size(struct vn_queue_submission *submit)
{
switch (submit->batch_type) {
case VK_STRUCTURE_TYPE_SUBMIT_INFO:
return sizeof(VkSubmitInfo);
case VK_STRUCTURE_TYPE_SUBMIT_INFO_2:
return sizeof(VkSubmitInfo2);
case VK_STRUCTURE_TYPE_BIND_SPARSE_INFO:
return sizeof(VkBindSparseInfo);
default:
UNREACHABLE("unexpected batch type");
}
}
static inline size_t
vn_get_cmd_size(struct vn_queue_submission *submit)
{
assert((submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO) ||
(submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO_2));
return submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO
? sizeof(VkCommandBuffer)
: sizeof(VkCommandBufferSubmitInfo);
}
static inline uint32_t
vn_get_cmd_count(struct vn_queue_submission *submit)
{
assert((submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO) ||
(submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO_2));
if (!submit->batch)
return 0;
return submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO
? submit->submit_batch->commandBufferCount
: submit->submit2_batch->commandBufferInfoCount;
}
static inline const void *
vn_get_cmds(struct vn_queue_submission *submit)
{
assert(submit->batch);
assert((submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO) ||
(submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO_2));
return submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO
? (const void *)submit->submit_batch->pCommandBuffers
: (const void *)submit->submit2_batch->pCommandBufferInfos;
}
static inline struct vn_command_buffer *
vn_get_cmd(struct vn_queue_submission *submit, uint32_t index)
{
assert(submit->batch);
assert((submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO) ||
(submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO_2));
return vn_command_buffer_from_handle(
submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO
? submit->submit_batch->pCommandBuffers[index]
: submit->submit2_batch->pCommandBufferInfos[index].commandBuffer);
}
static inline void
vn_set_temp_cmd(struct vn_queue_submission *submit,
uint32_t index,
VkCommandBuffer cmd_handle)
{
assert((submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO) ||
(submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO_2));
if (submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO_2) {
submit->temp.cmd_infos[index] = (VkCommandBufferSubmitInfo){
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
.commandBuffer = cmd_handle,
};
} else {
submit->temp.cmd_handles[index] = cmd_handle;
}
}
static uint64_t
vn_get_signal_semaphore_counter(struct vn_queue_submission *submit,
uint32_t index)
{
switch (submit->batch_type) {
case VK_STRUCTURE_TYPE_SUBMIT_INFO: {
const VkTimelineSemaphoreSubmitInfo *timeline_sem_info =
vk_find_struct_const(submit->submit_batch->pNext,
TIMELINE_SEMAPHORE_SUBMIT_INFO);
return timeline_sem_info->pSignalSemaphoreValues[index];
}
case VK_STRUCTURE_TYPE_SUBMIT_INFO_2:
return submit->submit2_batch->pSignalSemaphoreInfos[index].value;
case VK_STRUCTURE_TYPE_BIND_SPARSE_INFO: {
const VkTimelineSemaphoreSubmitInfo *timeline_sem_info =
vk_find_struct_const(submit->sparse_batch->pNext,
TIMELINE_SEMAPHORE_SUBMIT_INFO);
return timeline_sem_info->pSignalSemaphoreValues[index];
}
default:
UNREACHABLE("unexpected batch type");
}
}
static void
vn_queue_submission_init_pnext(struct vn_queue_submission *submit)
{
if (!submit->fix_pnext)
return;
struct vn_queue_submission_pnext *pnext = submit->temp.pnext;
VkBaseOutStructure *cur = (void *)submit->temp.submit_batch;
vk_foreach_struct_const(src, submit->submit_batch->pNext) {
void *next = NULL;
switch (src->sType) {
case VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO: {
memcpy(&pnext->group, src, sizeof(pnext->group));
next = &pnext->group;
VkDeviceGroupSubmitInfo *group = (void *)src;
/* fix the dev masks for the extra feedback cmds */
if (submit->dev_mask_count) {
VK_FROM_HANDLE(vk_queue, queue_vk, submit->queue_handle);
struct vn_device *dev = vn_device_from_vk(queue_vk->base.device);
assert(submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO);
assert(group->commandBufferCount == vn_get_cmd_count(submit));
const uint32_t old_cmd_count = vn_get_cmd_count(submit);
if (old_cmd_count) {
memcpy(submit->temp.dev_masks,
group->pCommandBufferDeviceMasks,
sizeof(uint32_t) * old_cmd_count);
}
/* Set the group device mask. Unlike sync2, zero means skip. */
const uint32_t new_cmd_count =
submit->temp.submit_batch->commandBufferCount;
for (uint32_t i = old_cmd_count; i < new_cmd_count; i++)
submit->temp.dev_masks[i] = dev->device_mask;
pnext->group.commandBufferCount = new_cmd_count;
pnext->group.pCommandBufferDeviceMasks = submit->temp.dev_masks;
}
/* drop the dev indices for the dropped wait semaphores */
if (submit->dev_index_count) {
assert(submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO ||
submit->batch_type == VK_STRUCTURE_TYPE_BIND_SPARSE_INFO);
assert(submit->dev_index_count ==
vn_get_wait_semaphore_count(submit));
uint32_t j = 0;
for (uint32_t i = 0; i < submit->dev_index_count; i++) {
VkSemaphore sem_handle = vn_get_wait_semaphore(submit, i);
if (vn_semaphore_is_imported(sem_handle))
continue;
submit->temp.dev_indices[j++] =
group->pWaitSemaphoreDeviceIndices[i];
}
pnext->group.waitSemaphoreCount = j;
pnext->group.pWaitSemaphoreDeviceIndices =
submit->temp.dev_indices;
}
break;
}
case VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO: {
memcpy(&pnext->timeline, src, sizeof(pnext->timeline));
next = &pnext->timeline;
/* drop the sem vals for the dropped wait semaphores */
if (submit->sem_val_count) {
VkTimelineSemaphoreSubmitInfo *timeline = (void *)src;
assert(submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO ||
submit->batch_type == VK_STRUCTURE_TYPE_BIND_SPARSE_INFO);
assert(submit->sem_val_count ==
vn_get_wait_semaphore_count(submit));
uint32_t j = 0;
for (uint32_t i = 0; i < submit->sem_val_count; i++) {
VkSemaphore sem_handle = vn_get_wait_semaphore(submit, i);
if (vn_semaphore_is_imported(sem_handle))
continue;
submit->temp.sem_vals[j++] = timeline->pWaitSemaphoreValues[i];
}
pnext->timeline.waitSemaphoreValueCount = j;
pnext->timeline.pWaitSemaphoreValues = submit->temp.sem_vals;
}
break;
}
case VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO:
memcpy(&pnext->sparse, src, sizeof(pnext->sparse));
next = &pnext->sparse;
break;
case VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO:
memcpy(&pnext->protected, src, sizeof(pnext->protected));
next = &pnext->protected;
break;
default:
/* The following structs are not supported by venus so are not
* handled here. VkAmigoProfilingSubmitInfoSEC,
* VkD3D12FenceSubmitInfoKHR, VkFrameBoundaryEXT,
* VkLatencySubmissionPresentIdNV, VkPerformanceQuerySubmitInfoKHR,
* VkWin32KeyedMutexAcquireReleaseInfoKHR,
* VkWin32KeyedMutexAcquireReleaseInfoNV
*/
break;
}
if (next) {
cur->pNext = next;
cur = next;
}
}
}
static void
vn_queue_submission_count_wait_semaphores(struct vn_queue_submission *submit)
{
bool has_imported_semaphore = false;
bool has_timeline_semaphore = false;
const uint32_t wait_count = vn_get_wait_semaphore_count(submit);
for (uint32_t i = 0; i < wait_count; i++) {
VkSemaphore sem_handle = vn_get_wait_semaphore(submit, i);
if (vn_semaphore_is_imported(sem_handle))
has_imported_semaphore = true;
has_timeline_semaphore |= vn_semaphore_is_timeline(sem_handle);
}
if (!has_imported_semaphore)
return;
submit->wait_sem_count = wait_count;
if (submit->batch_type != VK_STRUCTURE_TYPE_SUBMIT_INFO_2 &&
has_timeline_semaphore) {
submit->fix_pnext = true;
submit->sem_val_count = wait_count;
}
if (submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO) {
const VkDeviceGroupSubmitInfo *device_group = vk_find_struct_const(
submit->submit_batch->pNext, DEVICE_GROUP_SUBMIT_INFO);
if (device_group) {
submit->fix_pnext = true;
submit->dev_index_count = wait_count;
}
}
}
static void
vn_queue_submission_count_batch_feedback(struct vn_queue_submission *submit)
{
uint32_t extra_cmd_count = 0;
uint32_t feedback_types = 0;
const uint32_t signal_count = vn_get_signal_semaphore_count(submit);
for (uint32_t i = 0; i < signal_count; i++) {
struct vn_semaphore *sem =
vn_semaphore_from_handle(vn_get_signal_semaphore(submit, i));
if (!vn_sync_feedback_enabled(&sem->feedback))
continue;
if (submit->can_feedback) {
feedback_types |= VN_FEEDBACK_TYPE_SEMAPHORE;
extra_cmd_count++;
} else {
const uint64_t counter = vn_get_signal_semaphore_counter(submit, i);
vn_sync_feedback_suspend(&sem->feedback, counter);
}
}
if (submit->batch_type != VK_STRUCTURE_TYPE_BIND_SPARSE_INFO) {
const uint32_t cmd_count = vn_get_cmd_count(submit);
for (uint32_t i = 0; i < cmd_count; i++) {
struct vn_command_buffer *cmd = vn_get_cmd(submit, i);
if (!list_is_empty(&cmd->builder.query_records))
feedback_types |= VN_FEEDBACK_TYPE_QUERY;
/* If a cmd that was submitted previously and already has a feedback
* cmd linked, as long as
* VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT was not set we can
* assume it has completed execution and is no longer in the pending
* state so its safe to recycle the old feedback command.
*/
if (cmd->linked_qfb_cmd) {
assert(!cmd->builder.is_simultaneous);
vn_query_feedback_cmd_free(cmd->linked_qfb_cmd);
cmd->linked_qfb_cmd = NULL;
}
}
if (feedback_types & VN_FEEDBACK_TYPE_QUERY)
extra_cmd_count++;
if (submit->feedback_types & VN_FEEDBACK_TYPE_FENCE) {
feedback_types |= VN_FEEDBACK_TYPE_FENCE;
extra_cmd_count++;
}
if (feedback_types)
extra_cmd_count += cmd_count;
}
if (submit->batch && submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO &&
extra_cmd_count) {
const VkDeviceGroupSubmitInfo *device_group = vk_find_struct_const(
submit->submit_batch->pNext, DEVICE_GROUP_SUBMIT_INFO);
if (device_group) {
submit->fix_pnext = true;
submit->dev_mask_count = extra_cmd_count;
}
}
submit->feedback_types = feedback_types;
submit->cmd_count = extra_cmd_count;
}
static void
vn_queue_submission_prepare(struct vn_queue_submission *submit)
{
struct vn_queue *queue = vn_queue_from_handle(submit->queue_handle);
struct vn_fence *fence = vn_fence_from_handle(submit->fence_handle);
if (fence && vn_sync_feedback_enabled(&fence->feedback)) {
if (submit->can_feedback)
submit->feedback_types |= VN_FEEDBACK_TYPE_FENCE;
else
vn_sync_feedback_suspend(&fence->feedback, fence->signal_counter);
}
submit->external_payload.ring_idx = queue->ring_idx;
vn_queue_submission_count_wait_semaphores(submit);
vn_queue_submission_count_batch_feedback(submit);
}
static VkResult
vn_queue_submission_alloc_storage(struct vn_queue_submission *submit)
{
if (!submit->feedback_types && !submit->wait_sem_count)
return VK_SUCCESS;
size_t total = 0;
struct {
size_t batch;
size_t cmds;
size_t pnext;
size_t dev_masks;
size_t dev_indices;
size_t sem_vals;
size_t wait_sems;
} size = { 0 };
total += size.batch = vn_get_batch_size(submit);
total += size.cmds = submit->cmd_count * vn_get_cmd_size(submit);
total += size.pnext = submit->fix_pnext ? sizeof(*submit->temp.pnext) : 0;
total += size.dev_masks = submit->dev_mask_count * sizeof(uint32_t);
total += size.dev_indices = submit->dev_index_count * sizeof(uint32_t);
total += size.sem_vals = submit->sem_val_count * sizeof(uint64_t);
total += size.wait_sems = submit->wait_sem_count * vn_get_sem_size(submit);
struct vn_queue *queue = vn_queue_from_handle(submit->queue_handle);
uint8_t *storage = vn_cached_storage_get(&queue->storage, total);
if (!storage)
return VK_ERROR_OUT_OF_HOST_MEMORY;
submit->temp.batch = (void *)storage;
submit->temp.cmds = (void *)(storage += size.batch);
submit->temp.pnext = (void *)(storage += size.cmds);
submit->temp.dev_masks = (void *)(storage += size.pnext);
submit->temp.dev_indices = (void *)(storage += size.dev_masks);
submit->temp.sem_vals = (void *)(storage += size.dev_indices);
submit->temp.wait_sems = (void *)(storage += size.sem_vals);
return VK_SUCCESS;
}
static VkResult
vn_queue_submission_get_resolved_query_records(
struct vn_queue_submission *submit,
struct vn_feedback_cmd_pool *fb_cmd_pool,
struct list_head *resolved_records)
{
struct vn_command_pool *cmd_pool =
vn_command_pool_from_handle(fb_cmd_pool->pool_handle);
struct list_head dropped_records;
VkResult result = VK_SUCCESS;
list_inithead(resolved_records);
list_inithead(&dropped_records);
const uint32_t cmd_count = vn_get_cmd_count(submit);
for (uint32_t i = 0; i < cmd_count; i++) {
struct vn_command_buffer *cmd = vn_get_cmd(submit, i);
list_for_each_entry(struct vn_cmd_query_record, record,
&cmd->builder.query_records, head) {
if (!record->copy) {
list_for_each_entry_safe(struct vn_cmd_query_record, prev,
resolved_records, head) {
/* If we previously added a query feedback that is now getting
* reset, remove it since it is now a no-op and the deferred
* feedback copy will cause a hang waiting for the reset query
* to become available.
*/
if (prev->copy && prev->query_pool == record->query_pool &&
prev->query >= record->query &&
prev->query < record->query + record->query_count)
list_move_to(&prev->head, &dropped_records);
}
}
simple_mtx_lock(&fb_cmd_pool->mutex);
struct vn_cmd_query_record *curr = vn_cmd_pool_alloc_query_record(
cmd_pool, record->query_pool, record->query, record->query_count,
record->copy);
simple_mtx_unlock(&fb_cmd_pool->mutex);
if (!curr) {
list_splicetail(resolved_records, &dropped_records);
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto out_free_dropped_records;
}
list_addtail(&curr->head, resolved_records);
}
}
/* further resolve to batch sequential queries */
struct vn_cmd_query_record *curr =
list_first_entry(resolved_records, struct vn_cmd_query_record, head);
list_for_each_entry_safe(struct vn_cmd_query_record, next,
resolved_records, head) {
if (curr->query_pool == next->query_pool && curr->copy == next->copy) {
if (curr->query + curr->query_count == next->query) {
curr->query_count += next->query_count;
list_move_to(&next->head, &dropped_records);
} else if (curr->query == next->query + next->query_count) {
curr->query = next->query;
curr->query_count += next->query_count;
list_move_to(&next->head, &dropped_records);
} else {
curr = next;
}
} else {
curr = next;
}
}
out_free_dropped_records:
simple_mtx_lock(&fb_cmd_pool->mutex);
vn_cmd_pool_free_query_records(cmd_pool, &dropped_records);
simple_mtx_unlock(&fb_cmd_pool->mutex);
return result;
}
static VkResult
vn_queue_submission_add_query_feedback(struct vn_queue_submission *submit,
uint32_t *new_cmd_count)
{
struct vk_queue *queue_vk = vk_queue_from_handle(submit->queue_handle);
struct vn_device *dev = vn_device_from_vk(queue_vk->base.device);
VkResult result;
struct vn_feedback_cmd_pool *fb_cmd_pool = NULL;
for (uint32_t i = 0; i < dev->queue_family_count; i++) {
if (dev->queue_families[i] == queue_vk->queue_family_index) {
fb_cmd_pool = &dev->fb_cmd_pools[i];
break;
}
}
assert(fb_cmd_pool);
struct list_head resolved_records;
result = vn_queue_submission_get_resolved_query_records(
submit, fb_cmd_pool, &resolved_records);
if (result != VK_SUCCESS)
return result;
/* currently the reset query is always recorded */
assert(!list_is_empty(&resolved_records));
struct vn_query_feedback_cmd *qfb_cmd;
result = vn_query_feedback_cmd_alloc(vn_device_to_handle(dev), fb_cmd_pool,
&resolved_records, &qfb_cmd);
if (result == VK_SUCCESS) {
/* link query feedback cmd lifecycle with a cmd in the original batch so
* that the feedback cmd can be reset and recycled when that cmd gets
* reset/freed.
*
* Avoid cmd buffers with VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT
* since we don't know if all its instances have completed execution.
* Should be rare enough to just log and leak the feedback cmd.
*/
bool found_companion_cmd = false;
const uint32_t cmd_count = vn_get_cmd_count(submit);
for (uint32_t i = 0; i < cmd_count; i++) {
struct vn_command_buffer *cmd = vn_get_cmd(submit, i);
if (!cmd->builder.is_simultaneous) {
cmd->linked_qfb_cmd = qfb_cmd;
found_companion_cmd = true;
break;
}
}
if (!found_companion_cmd)
vn_log(dev->instance, "WARN: qfb cmd has leaked!");
vn_set_temp_cmd(submit, (*new_cmd_count)++, qfb_cmd->cmd_handle);
}
simple_mtx_lock(&fb_cmd_pool->mutex);
vn_cmd_pool_free_query_records(
vn_command_pool_from_handle(fb_cmd_pool->pool_handle),
&resolved_records);
simple_mtx_unlock(&fb_cmd_pool->mutex);
return result;
}
static VkResult
vn_queue_submission_add_semaphore_feedback(struct vn_queue_submission *submit,
uint32_t index,
uint32_t *new_cmd_count)
{
struct vn_semaphore *sem =
vn_semaphore_from_handle(vn_get_signal_semaphore(submit, index));
if (!vn_sync_feedback_enabled(&sem->feedback))
return VK_SUCCESS;
VK_FROM_HANDLE(vk_queue, queue_vk, submit->queue_handle);
struct vn_device *dev = vn_device_from_vk(queue_vk->base.device);
const uint64_t counter = vn_get_signal_semaphore_counter(submit, index);
VkCommandBuffer sfb_cmd_handle = vn_sync_feedback_command(
dev, &sem->feedback, queue_vk->queue_family_index, counter);
if (sfb_cmd_handle == VK_NULL_HANDLE)
return VK_ERROR_OUT_OF_HOST_MEMORY;
vn_set_temp_cmd(submit, (*new_cmd_count)++, sfb_cmd_handle);
return VK_SUCCESS;
}
static VkResult
vn_queue_submission_add_fence_feedback(struct vn_queue_submission *submit,
uint32_t *new_cmd_count)
{
VK_FROM_HANDLE(vk_queue, queue_vk, submit->queue_handle);
struct vn_device *dev = vn_device_from_vk(queue_vk->base.device);
struct vn_fence *fence = vn_fence_from_handle(submit->fence_handle);
VkCommandBuffer ffb_cmd_handle = vn_sync_feedback_command(
dev, &fence->feedback, queue_vk->queue_family_index,
fence->signal_counter);
if (ffb_cmd_handle == VK_NULL_HANDLE)
return VK_ERROR_OUT_OF_HOST_MEMORY;
vn_set_temp_cmd(submit, (*new_cmd_count)++, ffb_cmd_handle);
return VK_SUCCESS;
}
static VkResult
vn_queue_submission_add_feedback_cmds(struct vn_queue_submission *submit)
{
VkResult result;
uint32_t new_cmd_count = vn_get_cmd_count(submit);
if (submit->feedback_types & VN_FEEDBACK_TYPE_QUERY) {
result = vn_queue_submission_add_query_feedback(submit, &new_cmd_count);
if (result != VK_SUCCESS)
return result;
}
if (submit->feedback_types & VN_FEEDBACK_TYPE_SEMAPHORE) {
const uint32_t signal_count = vn_get_signal_semaphore_count(submit);
for (uint32_t i = 0; i < signal_count; i++) {
result = vn_queue_submission_add_semaphore_feedback(submit, i,
&new_cmd_count);
if (result != VK_SUCCESS)
return result;
}
}
if (submit->feedback_types & VN_FEEDBACK_TYPE_FENCE) {
result = vn_queue_submission_add_fence_feedback(submit, &new_cmd_count);
if (result != VK_SUCCESS)
return result;
}
if (submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO_2) {
submit->temp.submit2_batch->pCommandBufferInfos =
submit->temp.cmd_infos;
submit->temp.submit2_batch->commandBufferInfoCount = new_cmd_count;
} else {
submit->temp.submit_batch->pCommandBuffers = submit->temp.cmd_handles;
submit->temp.submit_batch->commandBufferCount = new_cmd_count;
}
return VK_SUCCESS;
}
static VkResult
vn_queue_submission_init_cmds(struct vn_queue_submission *submit)
{
if (!submit->feedback_types)
return VK_SUCCESS;
assert(submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO_2 ||
submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO);
/* For a submission that is:
* - empty: initialize a batch for fence feedback
*/
if (!submit->batch) {
assert(submit->feedback_types & VN_FEEDBACK_TYPE_FENCE);
if (submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO_2) {
*submit->temp.submit2_batch = (VkSubmitInfo2){
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2,
};
} else {
*submit->temp.submit_batch = (VkSubmitInfo){
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
};
}
submit->batch = submit->temp.batch;
}
/* If the batch has qfb, sfb or ffb, copy the original commands and append
* feedback cmds. Copy is only needed for non-empty batch.
*/
const size_t cmd_size = vn_get_cmd_count(submit) * vn_get_cmd_size(submit);
if (cmd_size)
memcpy(submit->temp.cmds, vn_get_cmds(submit), cmd_size);
return vn_queue_submission_add_feedback_cmds(submit);
}
static VkResult
vn_queue_submission_init_wait_semaphores(struct vn_queue_submission *submit)
{
if (!submit->wait_sem_count)
return VK_SUCCESS;
struct vk_queue *queue_vk = vk_queue_from_handle(submit->queue_handle);
VkDevice dev_handle = vk_device_to_handle(queue_vk->base.device);
uint32_t j = 0;
const uint32_t wait_count = vn_get_wait_semaphore_count(submit);
for (uint32_t i = 0; i < wait_count; i++) {
VkSemaphore sem_handle = vn_get_wait_semaphore(submit, i);
if (vn_semaphore_is_imported(sem_handle)) {
if (!vn_semaphore_wait_imported(dev_handle, sem_handle))
return VK_ERROR_DEVICE_LOST;
/* drop the imported wait semaphore */
continue;
}
if (submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO_2) {
submit->temp.wait_sem_infos[j++] =
submit->submit2_batch->pWaitSemaphoreInfos[i];
} else if (submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO) {
submit->temp.wait_sem_handles[j++] =
submit->submit_batch->pWaitSemaphores[i];
} else {
assert(submit->batch_type == VK_STRUCTURE_TYPE_BIND_SPARSE_INFO);
submit->temp.wait_sem_handles[j++] =
submit->sparse_batch->pWaitSemaphores[i];
}
}
if (submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO_2) {
submit->temp.submit2_batch->pWaitSemaphoreInfos =
submit->temp.wait_sem_infos;
submit->temp.submit2_batch->waitSemaphoreInfoCount = j;
} else if (submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO) {
submit->temp.submit_batch->pWaitSemaphores =
submit->temp.wait_sem_handles;
submit->temp.submit_batch->waitSemaphoreCount = j;
} else {
assert(submit->batch_type == VK_STRUCTURE_TYPE_BIND_SPARSE_INFO);
submit->temp.sparse_batch->pWaitSemaphores =
submit->temp.wait_sem_handles;
submit->temp.sparse_batch->waitSemaphoreCount = j;
}
return VK_SUCCESS;
}
static VkResult
vn_queue_submission_init(struct vn_queue_submission *submit)
{
if (!submit->temp.batch)
return VK_SUCCESS;
/* For a submission that is:
* - non-empty: copy batch for adding feedbacks or dropping semaphores
*/
if (submit->batch)
memcpy(submit->temp.batch, submit->batch, vn_get_batch_size(submit));
VkResult result = vn_queue_submission_init_cmds(submit);
if (result != VK_SUCCESS)
return result;
vn_queue_submission_init_pnext(submit);
/* wait semaphores are initialized the last to ensure validity of
* vn_semaphore_is_imported used in init pnext
*/
result = vn_queue_submission_init_wait_semaphores(submit);
if (result != VK_SUCCESS)
return result;
submit->batch = submit->temp.batch;
return VK_SUCCESS;
}
static void
vn_queue_submission_cleanup_fence_feedback(struct vn_queue_submission *submit)
{
struct vk_queue *queue_vk = vk_queue_from_handle(submit->queue_handle);
VkDevice dev_handle = vk_device_to_handle(queue_vk->base.device);
if (submit->fence_handle == VK_NULL_HANDLE)
return;
struct vn_fence *fence = vn_fence_from_handle(submit->fence_handle);
if (!vn_sync_feedback_enabled(&fence->feedback))
return;
/* sfb pending cmds are recycled when signaled counter is updated */
vn_GetFenceStatus(dev_handle, submit->fence_handle);
}
static void
vn_queue_submission_cleanup_semaphore_feedback(
struct vn_queue_submission *submit)
{
struct vk_queue *queue_vk = vk_queue_from_handle(submit->queue_handle);
VkDevice dev_handle = vk_device_to_handle(queue_vk->base.device);
const uint32_t wait_count = vn_get_wait_semaphore_count(submit);
for (uint32_t i = 0; i < wait_count; i++) {
VkSemaphore sem_handle = vn_get_wait_semaphore(submit, i);
struct vn_semaphore *sem = vn_semaphore_from_handle(sem_handle);
if (!vn_sync_feedback_enabled(&sem->feedback))
continue;
/* sfb pending cmds are recycled when signaled counter is updated */
uint64_t counter = 0;
vn_GetSemaphoreCounterValue(dev_handle, sem_handle, &counter);
}
const uint32_t signal_count = vn_get_signal_semaphore_count(submit);
for (uint32_t i = 0; i < signal_count; i++) {
VkSemaphore sem_handle = vn_get_signal_semaphore(submit, i);
struct vn_semaphore *sem = vn_semaphore_from_handle(sem_handle);
if (!vn_sync_feedback_enabled(&sem->feedback))
continue;
/* sfb pending cmds are recycled when signaled counter is updated */
uint64_t counter = 0;
vn_GetSemaphoreCounterValue(dev_handle, sem_handle, &counter);
}
}
static void
vn_queue_submission_cleanup(struct vn_queue_submission *submit)
{
/* TODO clean up pending src feedbacks on failure? */
if (submit->feedback_types & VN_FEEDBACK_TYPE_SEMAPHORE)
vn_queue_submission_cleanup_semaphore_feedback(submit);
if (submit->feedback_types & VN_FEEDBACK_TYPE_FENCE)
vn_queue_submission_cleanup_fence_feedback(submit);
}
static VkResult
vn_queue_submission_prepare_submit(struct vn_queue_submission *submit)
{
vn_queue_submission_prepare(submit);
VkResult result = vn_queue_submission_alloc_storage(submit);
if (result != VK_SUCCESS)
return result;
result = vn_queue_submission_init(submit);
if (result != VK_SUCCESS) {
vn_queue_submission_cleanup(submit);
return result;
}
return VK_SUCCESS;
}
static VkResult
vn_queue_submit(struct vn_queue_submission *submit)
{
struct vn_queue *queue = vn_queue_from_handle(submit->queue_handle);
struct vn_device *dev = vn_device_from_vk(queue->base.vk.base.device);
struct vn_instance *instance = dev->instance;
VkResult result;
/* To ensure external components waiting on the correct fence payload,
* below sync primitives must be installed after the submission:
* - explicit fencing: sync file export
*
* We enforce above via an asynchronous vkQueueSubmit(2) via ring followed
* by an asynchronous renderer submission to wait for the ring submission:
* - fence is an external fence
* - has an external signal semaphore
*/
result = vn_queue_submission_prepare_submit(submit);
if (result != VK_SUCCESS)
return vn_error(instance, result);
/* skip no-op submit */
if (!submit->batch && submit->fence_handle == VK_NULL_HANDLE)
return VK_SUCCESS;
const uint32_t batch_count = submit->batch ? 1 : 0;
if (VN_PERF(NO_ASYNC_QUEUE_SUBMIT)) {
if (submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO_2) {
result = vn_call_vkQueueSubmit2(
dev->primary_ring, submit->queue_handle, batch_count,
submit->submit2_batch, submit->fence_handle);
} else if (submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO) {
result = vn_call_vkQueueSubmit(
dev->primary_ring, submit->queue_handle, batch_count,
submit->submit_batch, submit->fence_handle);
} else {
result = vn_call_vkQueueBindSparse(
dev->primary_ring, submit->queue_handle, batch_count,
submit->sparse_batch, submit->fence_handle);
}
if (result != VK_SUCCESS) {
vn_queue_submission_cleanup(submit);
return vn_error(instance, result);
}
} else {
struct vn_ring_submit_command ring_submit;
if (submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO_2) {
vn_submit_vkQueueSubmit2(dev->primary_ring, 0, submit->queue_handle,
batch_count, submit->submit2_batch,
submit->fence_handle, &ring_submit);
} else if (submit->batch_type == VK_STRUCTURE_TYPE_SUBMIT_INFO) {
vn_submit_vkQueueSubmit(dev->primary_ring, 0, submit->queue_handle,
batch_count, submit->submit_batch,
submit->fence_handle, &ring_submit);
} else {
vn_submit_vkQueueBindSparse(
dev->primary_ring, 0, submit->queue_handle, batch_count,
submit->sparse_batch, submit->fence_handle, &ring_submit);
}
if (!ring_submit.ring_seqno_valid) {
vn_queue_submission_cleanup(submit);
return vn_error(instance, VK_ERROR_DEVICE_LOST);
}
submit->external_payload.ring_seqno_valid = true;
submit->external_payload.ring_seqno = ring_submit.ring_seqno;
}
/* If external fence, track the submission's ring_idx to facilitate
* sync_file export.
*
* Imported syncs don't need a proxy renderer sync on subsequent export,
* because an fd is already available.
*/
struct vn_fence *fence = vn_fence_from_handle(submit->fence_handle);
if (fence && fence->is_external) {
assert(fence->payload->type == VN_SYNC_TYPE_DEVICE_ONLY);
fence->external_payload = submit->external_payload;
}
if (submit->batch) {
const uint32_t signal_count = vn_get_signal_semaphore_count(submit);
for (uint32_t i = 0; i < signal_count; i++) {
struct vn_semaphore *sem =
vn_semaphore_from_handle(vn_get_signal_semaphore(submit, i));
if (sem->is_external) {
assert(sem->payload->type == VN_SYNC_TYPE_DEVICE_ONLY);
sem->external_payload = submit->external_payload;
}
}
}
vn_queue_submission_cleanup(submit);
return VK_SUCCESS;
}
static inline bool
vn_queue_can_feedback(VkQueue queue_handle)
{
struct vn_queue *queue = vn_queue_from_handle(queue_handle);
return queue->can_feedback;
}
VKAPI_ATTR VkResult VKAPI_CALL
vn_QueueSubmit(VkQueue queue,
uint32_t submitCount,
const VkSubmitInfo *pSubmits,
VkFence fence)
{
VN_TRACE_FUNC();
vn_tls_set_async_pipeline_create();
vn_wsi_flush(queue);
for (uint32_t i = 0; i < submitCount; i++) {
struct vn_queue_submission submit = {
.batch_type = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.queue_handle = queue,
.submit_batch = &pSubmits[i],
.fence_handle = i == submitCount - 1 ? fence : VK_NULL_HANDLE,
.can_feedback = vn_queue_can_feedback(queue),
};
VkResult result = vn_queue_submit(&submit);
if (result != VK_SUCCESS)
return result;
}
if (!submitCount) {
return vn_queue_submit(&(struct vn_queue_submission){
.batch_type = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.queue_handle = queue,
.fence_handle = fence,
.can_feedback = vn_queue_can_feedback(queue),
});
}
return VK_SUCCESS;
}
static VkResult
vn_queue_submit_2_to_1(struct vn_device *dev,
VkQueue queue_handle,
const VkSubmitInfo2 *submit,
VkFence fence_handle)
{
VkResult result;
const void *pnext = NULL;
assert(submit);
VkProtectedSubmitInfo _protected;
VkDeviceGroupSubmitInfo _group;
VkTimelineSemaphoreSubmitInfo _timeline;
STACK_ARRAY(VkSemaphore, _wait_sem_handles,
submit->waitSemaphoreInfoCount);
STACK_ARRAY(VkPipelineStageFlags, _wait_stages,
submit->waitSemaphoreInfoCount);
STACK_ARRAY(uint32_t, _wait_dev_indices, submit->waitSemaphoreInfoCount);
STACK_ARRAY(uint64_t, _wait_values, submit->waitSemaphoreInfoCount);
STACK_ARRAY(VkCommandBuffer, _cmd_handles, submit->commandBufferInfoCount);
STACK_ARRAY(uint32_t, _cmd_dev_indices, submit->commandBufferInfoCount);
STACK_ARRAY(VkSemaphore, _signal_sem_handles,
submit->signalSemaphoreInfoCount);
STACK_ARRAY(uint32_t, _signal_dev_indices,
submit->signalSemaphoreInfoCount);
STACK_ARRAY(uint64_t, _signal_values, submit->signalSemaphoreInfoCount);
if (submit->flags & VK_SUBMIT_PROTECTED_BIT) {
_protected = (VkProtectedSubmitInfo){
.sType = VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO,
.pNext = pnext,
.protectedSubmit = VK_TRUE,
};
pnext = &_protected;
}
if (dev->device_mask > 1) {
for (uint32_t i = 0; i < submit->waitSemaphoreInfoCount; i++) {
_wait_dev_indices[i] = submit->pWaitSemaphoreInfos[i].deviceIndex;
}
for (uint32_t i = 0; i < submit->commandBufferInfoCount; i++) {
_cmd_dev_indices[i] = submit->pCommandBufferInfos[i].deviceMask;
}
for (uint32_t i = 0; i < submit->signalSemaphoreInfoCount; i++) {
_signal_dev_indices[i] = submit->pSignalSemaphoreInfos[i].deviceIndex;
}
_group = (VkDeviceGroupSubmitInfo){
.sType = VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO,
.pNext = pnext,
.waitSemaphoreCount = submit->waitSemaphoreInfoCount,
.pWaitSemaphoreDeviceIndices = _wait_dev_indices,
.commandBufferCount = submit->commandBufferInfoCount,
.pCommandBufferDeviceMasks = _cmd_dev_indices,
.signalSemaphoreCount = submit->signalSemaphoreInfoCount,
.pSignalSemaphoreDeviceIndices = _signal_dev_indices,
};
pnext = &_group;
}
bool has_wait_timeline_sem = false;
for (uint32_t i = 0; i < submit->waitSemaphoreInfoCount; i++) {
_wait_sem_handles[i] = submit->pWaitSemaphoreInfos[i].semaphore;
_wait_stages[i] = submit->pWaitSemaphoreInfos[i].stageMask
? submit->pWaitSemaphoreInfos[i].stageMask
: VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
VK_FROM_HANDLE(vn_semaphore, sem, _wait_sem_handles[i]);
has_wait_timeline_sem |= sem->type == VK_SEMAPHORE_TYPE_TIMELINE;
}
if (has_wait_timeline_sem) {
for (uint32_t i = 0; i < submit->waitSemaphoreInfoCount; i++)
_wait_values[i] = submit->pWaitSemaphoreInfos[i].value;
}
bool has_signal_timeline_sem = false;
for (uint32_t i = 0; i < submit->signalSemaphoreInfoCount; i++) {
_signal_sem_handles[i] = submit->pSignalSemaphoreInfos[i].semaphore;
VK_FROM_HANDLE(vn_semaphore, sem, _signal_sem_handles[i]);
has_signal_timeline_sem |= sem->type == VK_SEMAPHORE_TYPE_TIMELINE;
}
if (has_signal_timeline_sem) {
for (uint32_t i = 0; i < submit->signalSemaphoreInfoCount; i++)
_signal_values[i] = submit->pSignalSemaphoreInfos[i].value;
}
if (has_wait_timeline_sem || has_signal_timeline_sem) {
_timeline = (VkTimelineSemaphoreSubmitInfo){
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
.pNext = pnext,
.waitSemaphoreValueCount =
has_wait_timeline_sem ? submit->waitSemaphoreInfoCount : 0,
.pWaitSemaphoreValues = _wait_values,
.signalSemaphoreValueCount =
has_signal_timeline_sem ? submit->signalSemaphoreInfoCount : 0,
.pSignalSemaphoreValues = _signal_values,
};
pnext = &_timeline;
}
for (uint32_t i = 0; i < submit->commandBufferInfoCount; i++)
_cmd_handles[i] = submit->pCommandBufferInfos[i].commandBuffer;
const VkSubmitInfo _submit = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = pnext,
.waitSemaphoreCount = submit->waitSemaphoreInfoCount,
.pWaitSemaphores = _wait_sem_handles,
.pWaitDstStageMask = _wait_stages,
.commandBufferCount = submit->commandBufferInfoCount,
.pCommandBuffers = _cmd_handles,
.signalSemaphoreCount = submit->signalSemaphoreInfoCount,
.pSignalSemaphores = _signal_sem_handles,
};
result = vn_queue_submit(&(struct vn_queue_submission){
.batch_type = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.queue_handle = queue_handle,
.submit_batch = &_submit,
.fence_handle = fence_handle,
.can_feedback = vn_queue_can_feedback(queue_handle),
});
STACK_ARRAY_FINISH(_wait_sem_handles);
STACK_ARRAY_FINISH(_wait_stages);
STACK_ARRAY_FINISH(_wait_dev_indices);
STACK_ARRAY_FINISH(_wait_values);
STACK_ARRAY_FINISH(_cmd_handles);
STACK_ARRAY_FINISH(_cmd_dev_indices);
STACK_ARRAY_FINISH(_signal_sem_handles);
STACK_ARRAY_FINISH(_signal_dev_indices);
STACK_ARRAY_FINISH(_signal_values);
return result;
}
static VkResult
vn_queue_submit_2(VkQueue queue_handle,
const VkSubmitInfo2 *batch,
VkFence fence_handle)
{
VK_FROM_HANDLE(vk_queue, queue_vk, queue_handle);
struct vn_device *dev = vn_device_from_vk(queue_vk->base.device);
assert(batch);
if (!dev->has_sync2) {
VN_TRACE_SCOPE("2->1");
return vn_queue_submit_2_to_1(dev, queue_handle, batch, fence_handle);
}
struct vn_queue_submission submit = {
.batch_type = VK_STRUCTURE_TYPE_SUBMIT_INFO_2,
.queue_handle = queue_handle,
.submit2_batch = batch,
.fence_handle = fence_handle,
.can_feedback = vn_queue_can_feedback(queue_handle),
};
return vn_queue_submit(&submit);
}
VKAPI_ATTR VkResult VKAPI_CALL
vn_QueueSubmit2(VkQueue queue,
uint32_t submitCount,
const VkSubmitInfo2 *pSubmits,
VkFence fence)
{
VN_TRACE_FUNC();
vn_tls_set_async_pipeline_create();
vn_wsi_flush(queue);
for (uint32_t i = 0; i < submitCount; i++) {
VkResult result = vn_queue_submit_2(
queue, &pSubmits[i], i == submitCount - 1 ? fence : VK_NULL_HANDLE);
if (result != VK_SUCCESS)
return result;
}
if (!submitCount) {
return vn_queue_submit(&(struct vn_queue_submission){
.batch_type = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.queue_handle = queue,
.fence_handle = fence,
.can_feedback = vn_queue_can_feedback(queue),
});
}
return vn_wsi_fence_wait(queue);
}
VKAPI_ATTR VkResult VKAPI_CALL
vn_QueueBindSparse(VkQueue queue,
uint32_t bindInfoCount,
const VkBindSparseInfo *pBindInfo,
VkFence fence)
{
VN_TRACE_FUNC();
vn_wsi_flush(queue);
for (uint32_t i = 0; i < bindInfoCount; i++) {
struct vn_queue_submission submit = {
.batch_type = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO,
.queue_handle = queue,
.sparse_batch = &pBindInfo[i],
.fence_handle = i == bindInfoCount - 1 ? fence : VK_NULL_HANDLE,
};
VkResult result = vn_queue_submit(&submit);
if (result != VK_SUCCESS)
return result;
}
if (!bindInfoCount) {
return vn_queue_submit(&(struct vn_queue_submission){
.batch_type = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO,
.queue_handle = queue,
.fence_handle = fence,
});
}
return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL
vn_QueueWaitIdle(VkQueue _queue)
{
VN_TRACE_FUNC();
struct vn_queue *queue = vn_queue_from_handle(_queue);
VkDevice dev_handle = vk_device_to_handle(queue->base.vk.base.device);
struct vn_device *dev = vn_device_from_handle(dev_handle);
VkResult result;
vn_wsi_flush(_queue);
/* lazily create queue wait fence for queue idle waiting */
if (queue->wait_fence == VK_NULL_HANDLE) {
const VkFenceCreateInfo create_info = {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.flags = 0,
};
result =
vn_CreateFence(dev_handle, &create_info, NULL, &queue->wait_fence);
if (result != VK_SUCCESS)
return result;
}
/* ensure the idle wait occurs after renderer fence submit */
vn_ring_roundtrip(dev->primary_ring);
result = vn_queue_submit(&(struct vn_queue_submission){
.batch_type = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.queue_handle = _queue,
.fence_handle = queue->wait_fence,
.can_feedback = vn_queue_can_feedback(_queue),
});
if (result != VK_SUCCESS)
return result;
result =
vn_WaitForFences(dev_handle, 1, &queue->wait_fence, true, UINT64_MAX);
vn_ResetFences(dev_handle, 1, &queue->wait_fence);
return vn_result(dev->instance, result);
}