mesa/src/virtio/vulkan/vn_queue.c
Yiwei Zhang c7b405b39d venus: refactor vn_AcquireImageANDROID with globalFencing
This patch refactors to use vn_Import*FdKHR for Android WSI native sync
fence import when globalFencing is supported. Currently there's no perf
win from this, but will move the composer release fence waiting to the
GPU device side automatically when the entire Venus fencing support is
improved.

Signed-off-by: Yiwei Zhang <zzyiwei@chromium.org>
Reviewed-by: Chia-I Wu <olvaffe@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/11342>
2021-06-15 19:50:10 +00:00

1105 lines
33 KiB
C

/*
* 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 "util/libsync.h"
#include "venus-protocol/vn_protocol_driver_event.h"
#include "venus-protocol/vn_protocol_driver_fence.h"
#include "venus-protocol/vn_protocol_driver_queue.h"
#include "venus-protocol/vn_protocol_driver_semaphore.h"
#include "vn_device.h"
#include "vn_device_memory.h"
#include "vn_renderer.h"
/* queue commands */
void
vn_GetDeviceQueue(VkDevice device,
uint32_t queueFamilyIndex,
uint32_t queueIndex,
VkQueue *pQueue)
{
struct vn_device *dev = vn_device_from_handle(device);
for (uint32_t i = 0; i < dev->queue_count; i++) {
struct vn_queue *queue = &dev->queues[i];
if (queue->family == queueFamilyIndex && queue->index == queueIndex) {
assert(!queue->flags);
*pQueue = vn_queue_to_handle(queue);
return;
}
}
unreachable("bad queue family/index");
}
void
vn_GetDeviceQueue2(VkDevice device,
const VkDeviceQueueInfo2 *pQueueInfo,
VkQueue *pQueue)
{
struct vn_device *dev = vn_device_from_handle(device);
for (uint32_t i = 0; i < dev->queue_count; i++) {
struct vn_queue *queue = &dev->queues[i];
if (queue->family == pQueueInfo->queueFamilyIndex &&
queue->index == pQueueInfo->queueIndex &&
queue->flags == pQueueInfo->flags) {
*pQueue = vn_queue_to_handle(queue);
return;
}
}
unreachable("bad queue family/index");
}
static void
vn_semaphore_reset_wsi(struct vn_device *dev, struct vn_semaphore *sem);
struct vn_queue_submission {
VkStructureType batch_type;
VkQueue queue;
uint32_t batch_count;
union {
const void *batches;
const VkSubmitInfo *submit_batches;
const VkBindSparseInfo *bind_sparse_batches;
};
VkFence fence;
uint32_t wait_semaphore_count;
uint32_t wait_wsi_count;
struct {
void *storage;
union {
void *batches;
VkSubmitInfo *submit_batches;
VkBindSparseInfo *bind_sparse_batches;
};
VkSemaphore *semaphores;
} temp;
};
static void
vn_queue_submission_count_batch_semaphores(struct vn_queue_submission *submit,
uint32_t batch_index)
{
union {
const VkSubmitInfo *submit_batch;
const VkBindSparseInfo *bind_sparse_batch;
} u;
const VkSemaphore *wait_sems;
uint32_t wait_count;
switch (submit->batch_type) {
case VK_STRUCTURE_TYPE_SUBMIT_INFO:
u.submit_batch = &submit->submit_batches[batch_index];
wait_sems = u.submit_batch->pWaitSemaphores;
wait_count = u.submit_batch->waitSemaphoreCount;
break;
case VK_STRUCTURE_TYPE_BIND_SPARSE_INFO:
u.bind_sparse_batch = &submit->bind_sparse_batches[batch_index];
wait_sems = u.bind_sparse_batch->pWaitSemaphores;
wait_count = u.bind_sparse_batch->waitSemaphoreCount;
break;
default:
unreachable("unexpected batch type");
break;
}
submit->wait_semaphore_count += wait_count;
for (uint32_t i = 0; i < wait_count; i++) {
struct vn_semaphore *sem = vn_semaphore_from_handle(wait_sems[i]);
const struct vn_sync_payload *payload = sem->payload;
if (payload->type == VN_SYNC_TYPE_WSI_SIGNALED)
submit->wait_wsi_count++;
}
}
static void
vn_queue_submission_count_semaphores(struct vn_queue_submission *submit)
{
submit->wait_semaphore_count = 0;
submit->wait_wsi_count = 0;
for (uint32_t i = 0; i < submit->batch_count; i++)
vn_queue_submission_count_batch_semaphores(submit, i);
}
static VkResult
vn_queue_submission_alloc_storage(struct vn_queue_submission *submit)
{
struct vn_queue *queue = vn_queue_from_handle(submit->queue);
const VkAllocationCallbacks *alloc = &queue->device->base.base.alloc;
size_t alloc_size = 0;
size_t semaphores_offset = 0;
/* we want to filter out VN_SYNC_TYPE_WSI_SIGNALED wait semaphores */
if (submit->wait_wsi_count) {
switch (submit->batch_type) {
case VK_STRUCTURE_TYPE_SUBMIT_INFO:
alloc_size += sizeof(VkSubmitInfo) * submit->batch_count;
break;
case VK_STRUCTURE_TYPE_BIND_SPARSE_INFO:
alloc_size += sizeof(VkBindSparseInfo) * submit->batch_count;
break;
default:
unreachable("unexpected batch type");
break;
}
semaphores_offset = alloc_size;
alloc_size += sizeof(*submit->temp.semaphores) *
(submit->wait_semaphore_count - submit->wait_wsi_count);
}
if (!alloc_size) {
submit->temp.storage = NULL;
return VK_SUCCESS;
}
submit->temp.storage = vk_alloc(alloc, alloc_size, VN_DEFAULT_ALIGN,
VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
if (!submit->temp.storage)
return VK_ERROR_OUT_OF_HOST_MEMORY;
submit->temp.batches = submit->temp.storage;
submit->temp.semaphores = submit->temp.storage + semaphores_offset;
return VK_SUCCESS;
}
static uint32_t
vn_queue_submission_filter_batch_wsi_semaphores(
struct vn_queue_submission *submit,
uint32_t batch_index,
uint32_t sem_base)
{
struct vn_queue *queue = vn_queue_from_handle(submit->queue);
union {
VkSubmitInfo *submit_batch;
VkBindSparseInfo *bind_sparse_batch;
} u;
const VkSemaphore *src_sems;
uint32_t src_count;
switch (submit->batch_type) {
case VK_STRUCTURE_TYPE_SUBMIT_INFO:
u.submit_batch = &submit->temp.submit_batches[batch_index];
src_sems = u.submit_batch->pWaitSemaphores;
src_count = u.submit_batch->waitSemaphoreCount;
break;
case VK_STRUCTURE_TYPE_BIND_SPARSE_INFO:
u.bind_sparse_batch = &submit->temp.bind_sparse_batches[batch_index];
src_sems = u.bind_sparse_batch->pWaitSemaphores;
src_count = u.bind_sparse_batch->waitSemaphoreCount;
break;
default:
unreachable("unexpected batch type");
break;
}
VkSemaphore *dst_sems = &submit->temp.semaphores[sem_base];
uint32_t dst_count = 0;
/* filter out VN_SYNC_TYPE_WSI_SIGNALED wait semaphores */
for (uint32_t i = 0; i < src_count; i++) {
struct vn_semaphore *sem = vn_semaphore_from_handle(src_sems[i]);
const struct vn_sync_payload *payload = sem->payload;
if (payload->type == VN_SYNC_TYPE_WSI_SIGNALED)
vn_semaphore_reset_wsi(queue->device, sem);
else
dst_sems[dst_count++] = src_sems[i];
}
switch (submit->batch_type) {
case VK_STRUCTURE_TYPE_SUBMIT_INFO:
u.submit_batch->pWaitSemaphores = dst_sems;
u.submit_batch->waitSemaphoreCount = dst_count;
break;
case VK_STRUCTURE_TYPE_BIND_SPARSE_INFO:
u.bind_sparse_batch->pWaitSemaphores = dst_sems;
u.bind_sparse_batch->waitSemaphoreCount = dst_count;
break;
default:
break;
}
return dst_count;
}
static void
vn_queue_submission_setup_batches(struct vn_queue_submission *submit)
{
if (!submit->temp.storage)
return;
/* make a copy because we need to filter out WSI semaphores */
if (submit->wait_wsi_count) {
switch (submit->batch_type) {
case VK_STRUCTURE_TYPE_SUBMIT_INFO:
memcpy(submit->temp.submit_batches, submit->submit_batches,
sizeof(submit->submit_batches[0]) * submit->batch_count);
submit->submit_batches = submit->temp.submit_batches;
break;
case VK_STRUCTURE_TYPE_BIND_SPARSE_INFO:
memcpy(submit->temp.bind_sparse_batches, submit->bind_sparse_batches,
sizeof(submit->bind_sparse_batches[0]) * submit->batch_count);
submit->bind_sparse_batches = submit->temp.bind_sparse_batches;
break;
default:
unreachable("unexpected batch type");
break;
}
}
uint32_t wait_sem_base = 0;
for (uint32_t i = 0; i < submit->batch_count; i++) {
if (submit->wait_wsi_count) {
wait_sem_base += vn_queue_submission_filter_batch_wsi_semaphores(
submit, i, wait_sem_base);
}
}
}
static VkResult
vn_queue_submission_prepare_submit(struct vn_queue_submission *submit,
VkQueue queue,
uint32_t batch_count,
const VkSubmitInfo *submit_batches,
VkFence fence)
{
submit->batch_type = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit->queue = queue;
submit->batch_count = batch_count;
submit->submit_batches = submit_batches;
submit->fence = fence;
vn_queue_submission_count_semaphores(submit);
VkResult result = vn_queue_submission_alloc_storage(submit);
if (result != VK_SUCCESS)
return result;
vn_queue_submission_setup_batches(submit);
return VK_SUCCESS;
}
static VkResult
vn_queue_submission_prepare_bind_sparse(
struct vn_queue_submission *submit,
VkQueue queue,
uint32_t batch_count,
const VkBindSparseInfo *bind_sparse_batches,
VkFence fence)
{
submit->batch_type = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO;
submit->queue = queue;
submit->batch_count = batch_count;
submit->bind_sparse_batches = bind_sparse_batches;
submit->fence = fence;
vn_queue_submission_count_semaphores(submit);
VkResult result = vn_queue_submission_alloc_storage(submit);
if (result != VK_SUCCESS)
return result;
vn_queue_submission_setup_batches(submit);
return VK_SUCCESS;
}
static void
vn_queue_submission_cleanup(struct vn_queue_submission *submit)
{
struct vn_queue *queue = vn_queue_from_handle(submit->queue);
const VkAllocationCallbacks *alloc = &queue->device->base.base.alloc;
vk_free(alloc, submit->temp.storage);
}
VkResult
vn_QueueSubmit(VkQueue _queue,
uint32_t submitCount,
const VkSubmitInfo *pSubmits,
VkFence fence)
{
struct vn_queue *queue = vn_queue_from_handle(_queue);
struct vn_device *dev = queue->device;
struct vn_queue_submission submit;
VkResult result = vn_queue_submission_prepare_submit(
&submit, _queue, submitCount, pSubmits, fence);
if (result != VK_SUCCESS)
return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
const struct vn_device_memory *wsi_mem = NULL;
if (submit.batch_count == 1) {
const struct wsi_memory_signal_submit_info *info = vk_find_struct_const(
submit.submit_batches[0].pNext, WSI_MEMORY_SIGNAL_SUBMIT_INFO_MESA);
if (info) {
wsi_mem = vn_device_memory_from_handle(info->memory);
assert(!wsi_mem->base_memory && wsi_mem->base_bo);
}
}
result =
vn_call_vkQueueSubmit(dev->instance, submit.queue, submit.batch_count,
submit.submit_batches, submit.fence);
if (result != VK_SUCCESS) {
vn_queue_submission_cleanup(&submit);
return vn_error(dev->instance, result);
}
if (wsi_mem) {
/* XXX this is always false and kills the performance */
if (dev->instance->renderer_info.has_implicit_fencing) {
vn_renderer_submit(dev->renderer, &(const struct vn_renderer_submit){
.bos = &wsi_mem->base_bo,
.bo_count = 1,
});
} else {
if (VN_DEBUG(WSI)) {
static uint32_t ratelimit;
if (ratelimit < 10) {
vn_log(dev->instance,
"forcing vkQueueWaitIdle before presenting");
ratelimit++;
}
}
vn_QueueWaitIdle(submit.queue);
}
}
vn_queue_submission_cleanup(&submit);
return VK_SUCCESS;
}
VkResult
vn_QueueBindSparse(VkQueue _queue,
uint32_t bindInfoCount,
const VkBindSparseInfo *pBindInfo,
VkFence fence)
{
struct vn_queue *queue = vn_queue_from_handle(_queue);
struct vn_device *dev = queue->device;
struct vn_queue_submission submit;
VkResult result = vn_queue_submission_prepare_bind_sparse(
&submit, _queue, bindInfoCount, pBindInfo, fence);
if (result != VK_SUCCESS)
return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
result = vn_call_vkQueueBindSparse(
dev->instance, submit.queue, submit.batch_count,
submit.bind_sparse_batches, submit.fence);
if (result != VK_SUCCESS) {
vn_queue_submission_cleanup(&submit);
return vn_error(dev->instance, result);
}
vn_queue_submission_cleanup(&submit);
return VK_SUCCESS;
}
VkResult
vn_QueueWaitIdle(VkQueue _queue)
{
struct vn_queue *queue = vn_queue_from_handle(_queue);
VkDevice device = vn_device_to_handle(queue->device);
VkResult result = vn_QueueSubmit(_queue, 0, NULL, queue->wait_fence);
if (result != VK_SUCCESS)
return result;
result = vn_WaitForFences(device, 1, &queue->wait_fence, true, UINT64_MAX);
vn_ResetFences(device, 1, &queue->wait_fence);
return vn_result(queue->device->instance, result);
}
/* fence commands */
static void
vn_sync_payload_release(struct vn_device *dev,
struct vn_sync_payload *payload)
{
payload->type = VN_SYNC_TYPE_INVALID;
}
static VkResult
vn_fence_init_payloads(struct vn_device *dev,
struct vn_fence *fence,
bool signaled,
const VkAllocationCallbacks *alloc)
{
fence->permanent.type = VN_SYNC_TYPE_DEVICE_ONLY;
fence->temporary.type = VN_SYNC_TYPE_INVALID;
fence->payload = &fence->permanent;
return VK_SUCCESS;
}
void
vn_fence_signal_wsi(struct vn_device *dev, struct vn_fence *fence)
{
struct vn_sync_payload *temp = &fence->temporary;
vn_sync_payload_release(dev, temp);
temp->type = VN_SYNC_TYPE_WSI_SIGNALED;
fence->payload = temp;
}
VkResult
vn_CreateFence(VkDevice device,
const VkFenceCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkFence *pFence)
{
struct vn_device *dev = vn_device_from_handle(device);
const VkAllocationCallbacks *alloc =
pAllocator ? pAllocator : &dev->base.base.alloc;
VkFenceCreateInfo local_create_info;
if (vk_find_struct_const(pCreateInfo->pNext, EXPORT_FENCE_CREATE_INFO)) {
local_create_info = *pCreateInfo;
local_create_info.pNext = NULL;
pCreateInfo = &local_create_info;
}
struct vn_fence *fence = vk_zalloc(alloc, sizeof(*fence), VN_DEFAULT_ALIGN,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (!fence)
return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
vn_object_base_init(&fence->base, VK_OBJECT_TYPE_FENCE, &dev->base);
VkResult result = vn_fence_init_payloads(
dev, fence, pCreateInfo->flags & VK_FENCE_CREATE_SIGNALED_BIT, alloc);
if (result != VK_SUCCESS) {
vk_free(alloc, fence);
return vn_error(dev->instance, result);
}
VkFence fence_handle = vn_fence_to_handle(fence);
vn_async_vkCreateFence(dev->instance, device, pCreateInfo, NULL,
&fence_handle);
*pFence = fence_handle;
return VK_SUCCESS;
}
void
vn_DestroyFence(VkDevice device,
VkFence _fence,
const VkAllocationCallbacks *pAllocator)
{
struct vn_device *dev = vn_device_from_handle(device);
struct vn_fence *fence = vn_fence_from_handle(_fence);
const VkAllocationCallbacks *alloc =
pAllocator ? pAllocator : &dev->base.base.alloc;
if (!fence)
return;
vn_async_vkDestroyFence(dev->instance, device, _fence, NULL);
vn_sync_payload_release(dev, &fence->permanent);
vn_sync_payload_release(dev, &fence->temporary);
vn_object_base_fini(&fence->base);
vk_free(alloc, fence);
}
VkResult
vn_ResetFences(VkDevice device, uint32_t fenceCount, const VkFence *pFences)
{
struct vn_device *dev = vn_device_from_handle(device);
/* TODO if the fence is shared-by-ref, this needs to be synchronous */
if (false)
vn_call_vkResetFences(dev->instance, device, fenceCount, pFences);
else
vn_async_vkResetFences(dev->instance, device, fenceCount, pFences);
for (uint32_t i = 0; i < fenceCount; i++) {
struct vn_fence *fence = vn_fence_from_handle(pFences[i]);
struct vn_sync_payload *perm = &fence->permanent;
vn_sync_payload_release(dev, &fence->temporary);
assert(perm->type == VN_SYNC_TYPE_DEVICE_ONLY);
fence->payload = perm;
}
return VK_SUCCESS;
}
VkResult
vn_GetFenceStatus(VkDevice device, VkFence _fence)
{
struct vn_device *dev = vn_device_from_handle(device);
struct vn_fence *fence = vn_fence_from_handle(_fence);
struct vn_sync_payload *payload = fence->payload;
VkResult result;
switch (payload->type) {
case VN_SYNC_TYPE_DEVICE_ONLY:
result = vn_call_vkGetFenceStatus(dev->instance, device, _fence);
break;
case VN_SYNC_TYPE_WSI_SIGNALED:
result = VK_SUCCESS;
break;
default:
unreachable("unexpected fence payload type");
break;
}
return vn_result(dev->instance, result);
}
static VkResult
vn_find_first_signaled_fence(VkDevice device,
const VkFence *fences,
uint32_t count)
{
for (uint32_t i = 0; i < count; i++) {
VkResult result = vn_GetFenceStatus(device, fences[i]);
if (result == VK_SUCCESS || result < 0)
return result;
}
return VK_NOT_READY;
}
static VkResult
vn_remove_signaled_fences(VkDevice device, VkFence *fences, uint32_t *count)
{
uint32_t cur = 0;
for (uint32_t i = 0; i < *count; i++) {
VkResult result = vn_GetFenceStatus(device, fences[i]);
if (result != VK_SUCCESS) {
if (result < 0)
return result;
fences[cur++] = fences[i];
}
}
*count = cur;
return cur ? VK_NOT_READY : VK_SUCCESS;
}
static VkResult
vn_update_sync_result(VkResult result, int64_t abs_timeout, uint32_t *iter)
{
switch (result) {
case VK_NOT_READY:
if (abs_timeout != OS_TIMEOUT_INFINITE &&
os_time_get_nano() >= abs_timeout)
result = VK_TIMEOUT;
else
vn_relax(iter);
break;
default:
assert(result == VK_SUCCESS || result < 0);
break;
}
return result;
}
VkResult
vn_WaitForFences(VkDevice device,
uint32_t fenceCount,
const VkFence *pFences,
VkBool32 waitAll,
uint64_t timeout)
{
struct vn_device *dev = vn_device_from_handle(device);
const VkAllocationCallbacks *alloc = &dev->base.base.alloc;
const int64_t abs_timeout = os_time_get_absolute_timeout(timeout);
VkResult result = VK_NOT_READY;
uint32_t iter = 0;
if (fenceCount > 1 && waitAll) {
VkFence local_fences[8];
VkFence *fences = local_fences;
if (fenceCount > ARRAY_SIZE(local_fences)) {
fences =
vk_alloc(alloc, sizeof(*fences) * fenceCount, VN_DEFAULT_ALIGN,
VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
if (!fences)
return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
}
memcpy(fences, pFences, sizeof(*fences) * fenceCount);
while (result == VK_NOT_READY) {
result = vn_remove_signaled_fences(device, fences, &fenceCount);
result = vn_update_sync_result(result, abs_timeout, &iter);
}
if (fences != local_fences)
vk_free(alloc, fences);
} else {
while (result == VK_NOT_READY) {
result = vn_find_first_signaled_fence(device, pFences, fenceCount);
result = vn_update_sync_result(result, abs_timeout, &iter);
}
}
return vn_result(dev->instance, result);
}
static VkResult
vn_create_sync_file(struct vn_device *dev, int *out_fd)
{
struct vn_renderer_sync *sync;
VkResult result = vn_renderer_sync_create(dev->renderer, 0,
VN_RENDERER_SYNC_BINARY, &sync);
if (result != VK_SUCCESS)
return vn_error(dev->instance, result);
const struct vn_renderer_submit submit = {
.batches =
&(const struct vn_renderer_submit_batch){
.syncs = &sync,
.sync_values = &(const uint64_t){ 1 },
.sync_count = 1,
},
.batch_count = 1,
};
result = vn_renderer_submit(dev->renderer, &submit);
if (result != VK_SUCCESS) {
vn_renderer_sync_destroy(dev->renderer, sync);
return vn_error(dev->instance, result);
}
*out_fd = vn_renderer_sync_export_syncobj(dev->renderer, sync, true);
vn_renderer_sync_destroy(dev->renderer, sync);
return *out_fd >= 0 ? VK_SUCCESS : VK_ERROR_TOO_MANY_OBJECTS;
}
VkResult
vn_ImportFenceFdKHR(VkDevice device,
const VkImportFenceFdInfoKHR *pImportFenceFdInfo)
{
struct vn_device *dev = vn_device_from_handle(device);
struct vn_fence *fence = vn_fence_from_handle(pImportFenceFdInfo->fence);
ASSERTED const bool sync_file = pImportFenceFdInfo->handleType ==
VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
const int fd = pImportFenceFdInfo->fd;
assert(dev->instance->experimental.globalFencing);
assert(sync_file);
if (fd >= 0) {
if (sync_wait(fd, -1))
return vn_error(dev->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
close(fd);
}
/* abuse VN_SYNC_TYPE_WSI_SIGNALED */
vn_fence_signal_wsi(dev, fence);
return VK_SUCCESS;
}
VkResult
vn_GetFenceFdKHR(VkDevice device,
const VkFenceGetFdInfoKHR *pGetFdInfo,
int *pFd)
{
struct vn_device *dev = vn_device_from_handle(device);
struct vn_fence *fence = vn_fence_from_handle(pGetFdInfo->fence);
const bool sync_file =
pGetFdInfo->handleType == VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
struct vn_sync_payload *payload = fence->payload;
assert(dev->instance->experimental.globalFencing);
assert(sync_file);
int fd = -1;
if (payload->type == VN_SYNC_TYPE_DEVICE_ONLY) {
VkResult result = vn_create_sync_file(dev, &fd);
if (result != VK_SUCCESS)
return vn_error(dev->instance, result);
}
if (sync_file) {
vn_sync_payload_release(dev, &fence->temporary);
fence->payload = &fence->permanent;
/* XXX implies reset operation on the host fence */
}
*pFd = fd;
return VK_SUCCESS;
}
/* semaphore commands */
static VkResult
vn_semaphore_init_payloads(struct vn_device *dev,
struct vn_semaphore *sem,
uint64_t initial_val,
const VkAllocationCallbacks *alloc)
{
sem->permanent.type = VN_SYNC_TYPE_DEVICE_ONLY;
sem->temporary.type = VN_SYNC_TYPE_INVALID;
sem->payload = &sem->permanent;
return VK_SUCCESS;
}
static void
vn_semaphore_reset_wsi(struct vn_device *dev, struct vn_semaphore *sem)
{
struct vn_sync_payload *perm = &sem->permanent;
vn_sync_payload_release(dev, &sem->temporary);
sem->payload = perm;
}
void
vn_semaphore_signal_wsi(struct vn_device *dev, struct vn_semaphore *sem)
{
struct vn_sync_payload *temp = &sem->temporary;
vn_sync_payload_release(dev, temp);
temp->type = VN_SYNC_TYPE_WSI_SIGNALED;
sem->payload = temp;
}
VkResult
vn_CreateSemaphore(VkDevice device,
const VkSemaphoreCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkSemaphore *pSemaphore)
{
struct vn_device *dev = vn_device_from_handle(device);
const VkAllocationCallbacks *alloc =
pAllocator ? pAllocator : &dev->base.base.alloc;
struct vn_semaphore *sem = vk_zalloc(alloc, sizeof(*sem), VN_DEFAULT_ALIGN,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (!sem)
return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
vn_object_base_init(&sem->base, VK_OBJECT_TYPE_SEMAPHORE, &dev->base);
const VkSemaphoreTypeCreateInfo *type_info =
vk_find_struct_const(pCreateInfo->pNext, SEMAPHORE_TYPE_CREATE_INFO);
uint64_t initial_val = 0;
if (type_info && type_info->semaphoreType == VK_SEMAPHORE_TYPE_TIMELINE) {
sem->type = VK_SEMAPHORE_TYPE_TIMELINE;
initial_val = type_info->initialValue;
} else {
sem->type = VK_SEMAPHORE_TYPE_BINARY;
}
VkResult result = vn_semaphore_init_payloads(dev, sem, initial_val, alloc);
if (result != VK_SUCCESS) {
vk_free(alloc, sem);
return vn_error(dev->instance, result);
}
VkSemaphore sem_handle = vn_semaphore_to_handle(sem);
vn_async_vkCreateSemaphore(dev->instance, device, pCreateInfo, NULL,
&sem_handle);
*pSemaphore = sem_handle;
return VK_SUCCESS;
}
void
vn_DestroySemaphore(VkDevice device,
VkSemaphore semaphore,
const VkAllocationCallbacks *pAllocator)
{
struct vn_device *dev = vn_device_from_handle(device);
struct vn_semaphore *sem = vn_semaphore_from_handle(semaphore);
const VkAllocationCallbacks *alloc =
pAllocator ? pAllocator : &dev->base.base.alloc;
if (!sem)
return;
vn_async_vkDestroySemaphore(dev->instance, device, semaphore, NULL);
vn_sync_payload_release(dev, &sem->permanent);
vn_sync_payload_release(dev, &sem->temporary);
vn_object_base_fini(&sem->base);
vk_free(alloc, sem);
}
VkResult
vn_GetSemaphoreCounterValue(VkDevice device,
VkSemaphore semaphore,
uint64_t *pValue)
{
struct vn_device *dev = vn_device_from_handle(device);
struct vn_semaphore *sem = vn_semaphore_from_handle(semaphore);
ASSERTED struct vn_sync_payload *payload = sem->payload;
assert(payload->type == VN_SYNC_TYPE_DEVICE_ONLY);
return vn_call_vkGetSemaphoreCounterValue(dev->instance, device, semaphore,
pValue);
}
VkResult
vn_SignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo *pSignalInfo)
{
struct vn_device *dev = vn_device_from_handle(device);
/* TODO if the semaphore is shared-by-ref, this needs to be synchronous */
if (false)
vn_call_vkSignalSemaphore(dev->instance, device, pSignalInfo);
else
vn_async_vkSignalSemaphore(dev->instance, device, pSignalInfo);
return VK_SUCCESS;
}
static VkResult
vn_find_first_signaled_semaphore(VkDevice device,
const VkSemaphore *semaphores,
const uint64_t *values,
uint32_t count)
{
for (uint32_t i = 0; i < count; i++) {
uint64_t val = 0;
VkResult result =
vn_GetSemaphoreCounterValue(device, semaphores[i], &val);
if (result != VK_SUCCESS || val >= values[i])
return result;
}
return VK_NOT_READY;
}
static VkResult
vn_remove_signaled_semaphores(VkDevice device,
VkSemaphore *semaphores,
uint64_t *values,
uint32_t *count)
{
uint32_t cur = 0;
for (uint32_t i = 0; i < *count; i++) {
uint64_t val = 0;
VkResult result =
vn_GetSemaphoreCounterValue(device, semaphores[i], &val);
if (result != VK_SUCCESS)
return result;
if (val < values[i])
semaphores[cur++] = semaphores[i];
}
*count = cur;
return cur ? VK_NOT_READY : VK_SUCCESS;
}
VkResult
vn_WaitSemaphores(VkDevice device,
const VkSemaphoreWaitInfo *pWaitInfo,
uint64_t timeout)
{
struct vn_device *dev = vn_device_from_handle(device);
const VkAllocationCallbacks *alloc = &dev->base.base.alloc;
const int64_t abs_timeout = os_time_get_absolute_timeout(timeout);
VkResult result = VK_NOT_READY;
uint32_t iter = 0;
if (pWaitInfo->semaphoreCount > 1 &&
!(pWaitInfo->flags & VK_SEMAPHORE_WAIT_ANY_BIT)) {
uint32_t semaphore_count = pWaitInfo->semaphoreCount;
VkSemaphore local_semaphores[8];
uint64_t local_values[8];
VkSemaphore *semaphores = local_semaphores;
uint64_t *values = local_values;
if (semaphore_count > ARRAY_SIZE(local_semaphores)) {
semaphores = vk_alloc(
alloc, (sizeof(*semaphores) + sizeof(*values)) * semaphore_count,
VN_DEFAULT_ALIGN, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
if (!semaphores)
return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
values = (uint64_t *)&semaphores[semaphore_count];
}
memcpy(semaphores, pWaitInfo->pSemaphores,
sizeof(*semaphores) * semaphore_count);
memcpy(values, pWaitInfo->pValues, sizeof(*values) * semaphore_count);
while (result == VK_NOT_READY) {
result = vn_remove_signaled_semaphores(device, semaphores, values,
&semaphore_count);
result = vn_update_sync_result(result, abs_timeout, &iter);
}
if (semaphores != local_semaphores)
vk_free(alloc, semaphores);
} else {
while (result == VK_NOT_READY) {
result = vn_find_first_signaled_semaphore(
device, pWaitInfo->pSemaphores, pWaitInfo->pValues,
pWaitInfo->semaphoreCount);
result = vn_update_sync_result(result, abs_timeout, &iter);
}
}
return vn_result(dev->instance, result);
}
VkResult
vn_ImportSemaphoreFdKHR(
VkDevice device, const VkImportSemaphoreFdInfoKHR *pImportSemaphoreFdInfo)
{
struct vn_device *dev = vn_device_from_handle(device);
struct vn_semaphore *sem =
vn_semaphore_from_handle(pImportSemaphoreFdInfo->semaphore);
ASSERTED const bool sync_file =
pImportSemaphoreFdInfo->handleType ==
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
const int fd = pImportSemaphoreFdInfo->fd;
assert(dev->instance->experimental.globalFencing);
assert(sync_file);
if (fd >= 0) {
if (sync_wait(fd, -1))
return vn_error(dev->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
close(fd);
}
/* abuse VN_SYNC_TYPE_WSI_SIGNALED */
vn_semaphore_signal_wsi(dev, sem);
return VK_SUCCESS;
}
VkResult
vn_GetSemaphoreFdKHR(VkDevice device,
const VkSemaphoreGetFdInfoKHR *pGetFdInfo,
int *pFd)
{
struct vn_device *dev = vn_device_from_handle(device);
struct vn_semaphore *sem = vn_semaphore_from_handle(pGetFdInfo->semaphore);
const bool sync_file =
pGetFdInfo->handleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
struct vn_sync_payload *payload = sem->payload;
assert(dev->instance->experimental.globalFencing);
assert(sync_file);
int fd = -1;
if (payload->type == VN_SYNC_TYPE_DEVICE_ONLY) {
VkResult result = vn_create_sync_file(dev, &fd);
if (result != VK_SUCCESS)
return vn_error(dev->instance, result);
}
if (sync_file) {
vn_sync_payload_release(dev, &sem->temporary);
sem->payload = &sem->permanent;
/* XXX implies wait operation on the host semaphore */
}
*pFd = fd;
return VK_SUCCESS;
}
/* event commands */
VkResult
vn_CreateEvent(VkDevice device,
const VkEventCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkEvent *pEvent)
{
struct vn_device *dev = vn_device_from_handle(device);
const VkAllocationCallbacks *alloc =
pAllocator ? pAllocator : &dev->base.base.alloc;
struct vn_event *ev = vk_zalloc(alloc, sizeof(*ev), VN_DEFAULT_ALIGN,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (!ev)
return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
vn_object_base_init(&ev->base, VK_OBJECT_TYPE_EVENT, &dev->base);
VkEvent ev_handle = vn_event_to_handle(ev);
vn_async_vkCreateEvent(dev->instance, device, pCreateInfo, NULL,
&ev_handle);
*pEvent = ev_handle;
return VK_SUCCESS;
}
void
vn_DestroyEvent(VkDevice device,
VkEvent event,
const VkAllocationCallbacks *pAllocator)
{
struct vn_device *dev = vn_device_from_handle(device);
struct vn_event *ev = vn_event_from_handle(event);
const VkAllocationCallbacks *alloc =
pAllocator ? pAllocator : &dev->base.base.alloc;
if (!ev)
return;
vn_async_vkDestroyEvent(dev->instance, device, event, NULL);
vn_object_base_fini(&ev->base);
vk_free(alloc, ev);
}
VkResult
vn_GetEventStatus(VkDevice device, VkEvent event)
{
struct vn_device *dev = vn_device_from_handle(device);
/* TODO When the renderer supports it (requires a new vk extension), there
* should be a coherent memory backing the event.
*/
VkResult result = vn_call_vkGetEventStatus(dev->instance, device, event);
return vn_result(dev->instance, result);
}
VkResult
vn_SetEvent(VkDevice device, VkEvent event)
{
struct vn_device *dev = vn_device_from_handle(device);
VkResult result = vn_call_vkSetEvent(dev->instance, device, event);
return vn_result(dev->instance, result);
}
VkResult
vn_ResetEvent(VkDevice device, VkEvent event)
{
struct vn_device *dev = vn_device_from_handle(device);
VkResult result = vn_call_vkResetEvent(dev->instance, device, event);
return vn_result(dev->instance, result);
}