summaryrefslogtreecommitdiff
path: root/src/freedreno/vulkan/tu_drm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/freedreno/vulkan/tu_drm.c')
-rw-r--r--src/freedreno/vulkan/tu_drm.c1892
1 files changed, 0 insertions, 1892 deletions
diff --git a/src/freedreno/vulkan/tu_drm.c b/src/freedreno/vulkan/tu_drm.c
deleted file mode 100644
index 5ec0d531fe4..00000000000
--- a/src/freedreno/vulkan/tu_drm.c
+++ /dev/null
@@ -1,1892 +0,0 @@
-/*
- * Copyright © 2018 Google, Inc.
- * Copyright © 2015 Intel Corporation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdint.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <xf86drm.h>
-
-#include "vk_util.h"
-
-#include "drm-uapi/msm_drm.h"
-#include "util/timespec.h"
-#include "util/os_time.h"
-#include "util/perf/u_trace.h"
-
-#include "tu_private.h"
-
-#include "tu_cs.h"
-
-struct tu_binary_syncobj {
- uint32_t permanent, temporary;
-};
-
-struct tu_timeline_point {
- struct list_head link;
-
- uint64_t value;
- uint32_t syncobj;
- uint32_t wait_count;
-};
-
-struct tu_timeline {
- uint64_t highest_submitted;
- uint64_t highest_signaled;
-
- /* A timeline can have multiple timeline points */
- struct list_head points;
-
- /* A list containing points that has been already submited.
- * A point will be moved to 'points' when new point is required
- * at submit time.
- */
- struct list_head free_points;
-};
-
-typedef enum {
- TU_SEMAPHORE_BINARY,
- TU_SEMAPHORE_TIMELINE,
-} tu_semaphore_type;
-
-
-struct tu_syncobj {
- struct vk_object_base base;
-
- tu_semaphore_type type;
- union {
- struct tu_binary_syncobj binary;
- struct tu_timeline timeline;
- };
-};
-
-struct tu_queue_submit
-{
- struct list_head link;
-
- VkCommandBuffer *cmd_buffers;
- struct tu_u_trace_cmd_data *cmd_buffer_trace_data;
- uint32_t cmd_buffer_count;
-
- struct tu_syncobj **wait_semaphores;
- uint32_t wait_semaphore_count;
- struct tu_syncobj **signal_semaphores;
- uint32_t signal_semaphore_count;
-
- struct tu_syncobj **wait_timelines;
- uint64_t *wait_timeline_values;
- uint32_t wait_timeline_count;
- uint32_t wait_timeline_array_length;
-
- struct tu_syncobj **signal_timelines;
- uint64_t *signal_timeline_values;
- uint32_t signal_timeline_count;
- uint32_t signal_timeline_array_length;
-
- struct drm_msm_gem_submit_cmd *cmds;
- struct drm_msm_gem_submit_syncobj *in_syncobjs;
- uint32_t nr_in_syncobjs;
- struct drm_msm_gem_submit_syncobj *out_syncobjs;
- uint32_t nr_out_syncobjs;
-
- bool last_submit;
- uint32_t entry_count;
- uint32_t counter_pass_index;
-};
-
-struct tu_u_trace_syncobj
-{
- uint32_t msm_queue_id;
- uint32_t fence;
-};
-
-static int
-tu_drm_get_param(const struct tu_physical_device *dev,
- uint32_t param,
- uint64_t *value)
-{
- /* Technically this requires a pipe, but the kernel only supports one pipe
- * anyway at the time of writing and most of these are clearly pipe
- * independent. */
- struct drm_msm_param req = {
- .pipe = MSM_PIPE_3D0,
- .param = param,
- };
-
- int ret = drmCommandWriteRead(dev->local_fd, DRM_MSM_GET_PARAM, &req,
- sizeof(req));
- if (ret)
- return ret;
-
- *value = req.value;
-
- return 0;
-}
-
-static int
-tu_drm_get_gpu_id(const struct tu_physical_device *dev, uint32_t *id)
-{
- uint64_t value;
- int ret = tu_drm_get_param(dev, MSM_PARAM_GPU_ID, &value);
- if (ret)
- return ret;
-
- *id = value;
- return 0;
-}
-
-static int
-tu_drm_get_gmem_size(const struct tu_physical_device *dev, uint32_t *size)
-{
- uint64_t value;
- int ret = tu_drm_get_param(dev, MSM_PARAM_GMEM_SIZE, &value);
- if (ret)
- return ret;
-
- *size = value;
- return 0;
-}
-
-static int
-tu_drm_get_gmem_base(const struct tu_physical_device *dev, uint64_t *base)
-{
- return tu_drm_get_param(dev, MSM_PARAM_GMEM_BASE, base);
-}
-
-int
-tu_drm_get_timestamp(struct tu_physical_device *device, uint64_t *ts)
-{
- return tu_drm_get_param(device, MSM_PARAM_TIMESTAMP, ts);
-}
-
-int
-tu_drm_submitqueue_new(const struct tu_device *dev,
- int priority,
- uint32_t *queue_id)
-{
- struct drm_msm_submitqueue req = {
- .flags = 0,
- .prio = priority,
- };
-
- int ret = drmCommandWriteRead(dev->fd,
- DRM_MSM_SUBMITQUEUE_NEW, &req, sizeof(req));
- if (ret)
- return ret;
-
- *queue_id = req.id;
- return 0;
-}
-
-void
-tu_drm_submitqueue_close(const struct tu_device *dev, uint32_t queue_id)
-{
- drmCommandWrite(dev->fd, DRM_MSM_SUBMITQUEUE_CLOSE,
- &queue_id, sizeof(uint32_t));
-}
-
-static void
-tu_gem_close(const struct tu_device *dev, uint32_t gem_handle)
-{
- struct drm_gem_close req = {
- .handle = gem_handle,
- };
-
- drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
-}
-
-/** Helper for DRM_MSM_GEM_INFO, returns 0 on error. */
-static uint64_t
-tu_gem_info(const struct tu_device *dev, uint32_t gem_handle, uint32_t info)
-{
- struct drm_msm_gem_info req = {
- .handle = gem_handle,
- .info = info,
- };
-
- int ret = drmCommandWriteRead(dev->fd,
- DRM_MSM_GEM_INFO, &req, sizeof(req));
- if (ret < 0)
- return 0;
-
- return req.value;
-}
-
-static VkResult
-tu_bo_init(struct tu_device *dev,
- struct tu_bo *bo,
- uint32_t gem_handle,
- uint64_t size,
- bool dump)
-{
- uint64_t iova = tu_gem_info(dev, gem_handle, MSM_INFO_GET_IOVA);
- if (!iova) {
- tu_gem_close(dev, gem_handle);
- return VK_ERROR_OUT_OF_DEVICE_MEMORY;
- }
-
- *bo = (struct tu_bo) {
- .gem_handle = gem_handle,
- .size = size,
- .iova = iova,
- };
-
- mtx_lock(&dev->bo_mutex);
- uint32_t idx = dev->bo_count++;
-
- /* grow the bo list if needed */
- if (idx >= dev->bo_list_size) {
- uint32_t new_len = idx + 64;
- struct drm_msm_gem_submit_bo *new_ptr =
- vk_realloc(&dev->vk.alloc, dev->bo_list, new_len * sizeof(*dev->bo_list),
- 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
- if (!new_ptr)
- goto fail_bo_list;
-
- dev->bo_list = new_ptr;
- dev->bo_list_size = new_len;
- }
-
- /* grow the "bo idx" list (maps gem handles to index in the bo list) */
- if (bo->gem_handle >= dev->bo_idx_size) {
- uint32_t new_len = bo->gem_handle + 256;
- uint32_t *new_ptr =
- vk_realloc(&dev->vk.alloc, dev->bo_idx, new_len * sizeof(*dev->bo_idx),
- 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
- if (!new_ptr)
- goto fail_bo_idx;
-
- dev->bo_idx = new_ptr;
- dev->bo_idx_size = new_len;
- }
-
- dev->bo_idx[bo->gem_handle] = idx;
- dev->bo_list[idx] = (struct drm_msm_gem_submit_bo) {
- .flags = MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE |
- COND(dump, MSM_SUBMIT_BO_DUMP),
- .handle = gem_handle,
- .presumed = iova,
- };
- mtx_unlock(&dev->bo_mutex);
-
- return VK_SUCCESS;
-
-fail_bo_idx:
- vk_free(&dev->vk.alloc, dev->bo_list);
-fail_bo_list:
- tu_gem_close(dev, gem_handle);
- return VK_ERROR_OUT_OF_HOST_MEMORY;
-}
-
-VkResult
-tu_bo_init_new(struct tu_device *dev, struct tu_bo *bo, uint64_t size,
- enum tu_bo_alloc_flags flags)
-{
- /* TODO: Choose better flags. As of 2018-11-12, freedreno/drm/msm_bo.c
- * always sets `flags = MSM_BO_WC`, and we copy that behavior here.
- */
- struct drm_msm_gem_new req = {
- .size = size,
- .flags = MSM_BO_WC
- };
-
- if (flags & TU_BO_ALLOC_GPU_READ_ONLY)
- req.flags |= MSM_BO_GPU_READONLY;
-
- int ret = drmCommandWriteRead(dev->fd,
- DRM_MSM_GEM_NEW, &req, sizeof(req));
- if (ret)
- return vk_error(dev->instance, VK_ERROR_OUT_OF_DEVICE_MEMORY);
-
- return tu_bo_init(dev, bo, req.handle, size, flags & TU_BO_ALLOC_ALLOW_DUMP);
-}
-
-VkResult
-tu_bo_init_dmabuf(struct tu_device *dev,
- struct tu_bo *bo,
- uint64_t size,
- int prime_fd)
-{
- /* lseek() to get the real size */
- off_t real_size = lseek(prime_fd, 0, SEEK_END);
- lseek(prime_fd, 0, SEEK_SET);
- if (real_size < 0 || (uint64_t) real_size < size)
- return vk_error(dev->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
-
- uint32_t gem_handle;
- int ret = drmPrimeFDToHandle(dev->fd, prime_fd,
- &gem_handle);
- if (ret)
- return vk_error(dev->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
-
- return tu_bo_init(dev, bo, gem_handle, size, false);
-}
-
-int
-tu_bo_export_dmabuf(struct tu_device *dev, struct tu_bo *bo)
-{
- int prime_fd;
- int ret = drmPrimeHandleToFD(dev->fd, bo->gem_handle,
- DRM_CLOEXEC, &prime_fd);
-
- return ret == 0 ? prime_fd : -1;
-}
-
-VkResult
-tu_bo_map(struct tu_device *dev, struct tu_bo *bo)
-{
- if (bo->map)
- return VK_SUCCESS;
-
- uint64_t offset = tu_gem_info(dev, bo->gem_handle, MSM_INFO_GET_OFFSET);
- if (!offset)
- return vk_error(dev->instance, VK_ERROR_OUT_OF_DEVICE_MEMORY);
-
- /* TODO: Should we use the wrapper os_mmap() like Freedreno does? */
- void *map = mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
- dev->fd, offset);
- if (map == MAP_FAILED)
- return vk_error(dev->instance, VK_ERROR_MEMORY_MAP_FAILED);
-
- bo->map = map;
- return VK_SUCCESS;
-}
-
-void
-tu_bo_finish(struct tu_device *dev, struct tu_bo *bo)
-{
- assert(bo->gem_handle);
-
- if (bo->map)
- munmap(bo->map, bo->size);
-
- mtx_lock(&dev->bo_mutex);
- uint32_t idx = dev->bo_idx[bo->gem_handle];
- dev->bo_count--;
- dev->bo_list[idx] = dev->bo_list[dev->bo_count];
- dev->bo_idx[dev->bo_list[idx].handle] = idx;
- mtx_unlock(&dev->bo_mutex);
-
- tu_gem_close(dev, bo->gem_handle);
-}
-
-static VkResult
-tu_drm_device_init(struct tu_physical_device *device,
- struct tu_instance *instance,
- drmDevicePtr drm_device)
-{
- const char *path = drm_device->nodes[DRM_NODE_RENDER];
- VkResult result = VK_SUCCESS;
- drmVersionPtr version;
- int fd;
- int master_fd = -1;
-
- fd = open(path, O_RDWR | O_CLOEXEC);
- if (fd < 0) {
- return vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
- "failed to open device %s", path);
- }
-
- /* Version 1.6 added SYNCOBJ support. */
- const int min_version_major = 1;
- const int min_version_minor = 6;
-
- version = drmGetVersion(fd);
- if (!version) {
- close(fd);
- return vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
- "failed to query kernel driver version for device %s",
- path);
- }
-
- if (strcmp(version->name, "msm")) {
- drmFreeVersion(version);
- close(fd);
- return vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
- "device %s does not use the msm kernel driver",
- path);
- }
-
- if (version->version_major != min_version_major ||
- version->version_minor < min_version_minor) {
- result = vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
- "kernel driver for device %s has version %d.%d, "
- "but Vulkan requires version >= %d.%d",
- path,
- version->version_major, version->version_minor,
- min_version_major, min_version_minor);
- drmFreeVersion(version);
- close(fd);
- return result;
- }
-
- device->msm_major_version = version->version_major;
- device->msm_minor_version = version->version_minor;
-
- drmFreeVersion(version);
-
- if (instance->debug_flags & TU_DEBUG_STARTUP)
- mesa_logi("Found compatible device '%s'.", path);
-
- device->instance = instance;
-
- if (instance->vk.enabled_extensions.KHR_display) {
- master_fd =
- open(drm_device->nodes[DRM_NODE_PRIMARY], O_RDWR | O_CLOEXEC);
- if (master_fd >= 0) {
- /* TODO: free master_fd is accel is not working? */
- }
- }
-
- device->master_fd = master_fd;
- device->local_fd = fd;
-
- if (tu_drm_get_gpu_id(device, &device->dev_id.gpu_id)) {
- result = vk_startup_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,
- "could not get GPU ID");
- goto fail;
- }
-
- if (tu_drm_get_param(device, MSM_PARAM_CHIP_ID, &device->dev_id.chip_id)) {
- result = vk_startup_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,
- "could not get CHIP ID");
- goto fail;
- }
-
- if (tu_drm_get_gmem_size(device, &device->gmem_size)) {
- result = vk_startup_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,
- "could not get GMEM size");
- goto fail;
- }
-
- if (tu_drm_get_gmem_base(device, &device->gmem_base)) {
- result = vk_startup_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,
- "could not get GMEM size");
- goto fail;
- }
-
- device->heap.size = tu_get_system_heap_size();
- device->heap.used = 0u;
- device->heap.flags = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT;
-
- result = tu_physical_device_init(device, instance);
- if (result == VK_SUCCESS)
- return result;
-
-fail:
- close(fd);
- if (master_fd != -1)
- close(master_fd);
- return result;
-}
-
-VkResult
-tu_enumerate_devices(struct tu_instance *instance)
-{
- /* TODO: Check for more devices ? */
- drmDevicePtr devices[8];
- VkResult result = VK_ERROR_INCOMPATIBLE_DRIVER;
- int max_devices;
-
- instance->physical_device_count = 0;
-
- max_devices = drmGetDevices2(0, devices, ARRAY_SIZE(devices));
-
- if (instance->debug_flags & TU_DEBUG_STARTUP) {
- if (max_devices < 0)
- mesa_logi("drmGetDevices2 returned error: %s\n", strerror(max_devices));
- else
- mesa_logi("Found %d drm nodes", max_devices);
- }
-
- if (max_devices < 1)
- return vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
- "No DRM devices found");
-
- for (unsigned i = 0; i < (unsigned) max_devices; i++) {
- if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER &&
- devices[i]->bustype == DRM_BUS_PLATFORM) {
-
- result = tu_drm_device_init(
- instance->physical_devices + instance->physical_device_count,
- instance, devices[i]);
- if (result == VK_SUCCESS)
- ++instance->physical_device_count;
- else if (result != VK_ERROR_INCOMPATIBLE_DRIVER)
- break;
- }
- }
- drmFreeDevices(devices, max_devices);
-
- return result;
-}
-
-static void
-tu_timeline_finish(struct tu_device *device,
- struct tu_timeline *timeline)
-{
- list_for_each_entry_safe(struct tu_timeline_point, point,
- &timeline->free_points, link) {
- list_del(&point->link);
- drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_DESTROY,
- &(struct drm_syncobj_destroy) { .handle = point->syncobj });
-
- vk_free(&device->vk.alloc, point);
- }
- list_for_each_entry_safe(struct tu_timeline_point, point,
- &timeline->points, link) {
- list_del(&point->link);
- drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_DESTROY,
- &(struct drm_syncobj_destroy) { .handle = point->syncobj });
- vk_free(&device->vk.alloc, point);
- }
-}
-
-static VkResult
-sync_create(VkDevice _device,
- bool signaled,
- bool fence,
- bool binary,
- uint64_t timeline_value,
- const VkAllocationCallbacks *pAllocator,
- void **p_sync)
-{
- TU_FROM_HANDLE(tu_device, device, _device);
-
- struct tu_syncobj *sync =
- vk_object_alloc(&device->vk, pAllocator, sizeof(*sync),
- fence ? VK_OBJECT_TYPE_FENCE : VK_OBJECT_TYPE_SEMAPHORE);
- if (!sync)
- return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
-
- if (binary) {
- struct drm_syncobj_create create = {};
- if (signaled)
- create.flags |= DRM_SYNCOBJ_CREATE_SIGNALED;
-
- int ret = drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_CREATE, &create);
- if (ret) {
- vk_free2(&device->vk.alloc, pAllocator, sync);
- return VK_ERROR_OUT_OF_HOST_MEMORY;
- }
-
- sync->binary.permanent = create.handle;
- sync->binary.temporary = 0;
- sync->type = TU_SEMAPHORE_BINARY;
- } else {
- sync->type = TU_SEMAPHORE_TIMELINE;
- sync->timeline.highest_signaled = sync->timeline.highest_submitted =
- timeline_value;
- list_inithead(&sync->timeline.points);
- list_inithead(&sync->timeline.free_points);
- }
-
- *p_sync = sync;
-
- return VK_SUCCESS;
-}
-
-static void
-sync_set_temporary(struct tu_device *device, struct tu_syncobj *sync, uint32_t syncobj)
-{
- if (sync->binary.temporary) {
- drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_DESTROY,
- &(struct drm_syncobj_destroy) { .handle = sync->binary.temporary });
- }
- sync->binary.temporary = syncobj;
-}
-
-static void
-sync_destroy(VkDevice _device, struct tu_syncobj *sync, const VkAllocationCallbacks *pAllocator)
-{
- TU_FROM_HANDLE(tu_device, device, _device);
-
- if (!sync)
- return;
-
- if (sync->type == TU_SEMAPHORE_BINARY) {
- sync_set_temporary(device, sync, 0);
- drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_DESTROY,
- &(struct drm_syncobj_destroy) { .handle = sync->binary.permanent });
- } else {
- tu_timeline_finish(device, &sync->timeline);
- }
-
- vk_object_free(&device->vk, pAllocator, sync);
-}
-
-static VkResult
-sync_import(VkDevice _device, struct tu_syncobj *sync, bool temporary, bool sync_fd, int fd)
-{
- TU_FROM_HANDLE(tu_device, device, _device);
- int ret;
-
- if (!sync_fd) {
- uint32_t *dst = temporary ? &sync->binary.temporary : &sync->binary.permanent;
-
- struct drm_syncobj_handle handle = { .fd = fd };
- ret = drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &handle);
- if (ret)
- return VK_ERROR_INVALID_EXTERNAL_HANDLE;
-
- if (*dst) {
- drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_DESTROY,
- &(struct drm_syncobj_destroy) { .handle = *dst });
- }
- *dst = handle.handle;
- close(fd);
- } else {
- assert(temporary);
-
- struct drm_syncobj_create create = {};
-
- if (fd == -1)
- create.flags |= DRM_SYNCOBJ_CREATE_SIGNALED;
-
- ret = drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_CREATE, &create);
- if (ret)
- return VK_ERROR_INVALID_EXTERNAL_HANDLE;
-
- if (fd != -1) {
- ret = drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &(struct drm_syncobj_handle) {
- .fd = fd,
- .handle = create.handle,
- .flags = DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE,
- });
- if (ret) {
- drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_DESTROY,
- &(struct drm_syncobj_destroy) { .handle = create.handle });
- return VK_ERROR_INVALID_EXTERNAL_HANDLE;
- }
- close(fd);
- }
-
- sync_set_temporary(device, sync, create.handle);
- }
-
- return VK_SUCCESS;
-}
-
-static VkResult
-sync_export(VkDevice _device, struct tu_syncobj *sync, bool sync_fd, int *p_fd)
-{
- TU_FROM_HANDLE(tu_device, device, _device);
-
- struct drm_syncobj_handle handle = {
- .handle = sync->binary.temporary ?: sync->binary.permanent,
- .flags = COND(sync_fd, DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE),
- .fd = -1,
- };
- int ret = drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &handle);
- if (ret)
- return vk_error(device->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
-
- /* restore permanent payload on export */
- sync_set_temporary(device, sync, 0);
-
- *p_fd = handle.fd;
- return VK_SUCCESS;
-}
-
-static VkSemaphoreTypeKHR
-get_semaphore_type(const void *pNext, uint64_t *initial_value)
-{
- const VkSemaphoreTypeCreateInfoKHR *type_info =
- vk_find_struct_const(pNext, SEMAPHORE_TYPE_CREATE_INFO_KHR);
-
- if (!type_info)
- return VK_SEMAPHORE_TYPE_BINARY_KHR;
-
- if (initial_value)
- *initial_value = type_info->initialValue;
- return type_info->semaphoreType;
-}
-
-VKAPI_ATTR VkResult VKAPI_CALL
-tu_CreateSemaphore(VkDevice device,
- const VkSemaphoreCreateInfo *pCreateInfo,
- const VkAllocationCallbacks *pAllocator,
- VkSemaphore *pSemaphore)
-{
- uint64_t timeline_value = 0;
- VkSemaphoreTypeKHR sem_type = get_semaphore_type(pCreateInfo->pNext, &timeline_value);
-
- return sync_create(device, false, false, (sem_type == VK_SEMAPHORE_TYPE_BINARY_KHR),
- timeline_value, pAllocator, (void**) pSemaphore);
-}
-
-VKAPI_ATTR void VKAPI_CALL
-tu_DestroySemaphore(VkDevice device, VkSemaphore sem, const VkAllocationCallbacks *pAllocator)
-{
- TU_FROM_HANDLE(tu_syncobj, sync, sem);
- sync_destroy(device, sync, pAllocator);
-}
-
-VKAPI_ATTR VkResult VKAPI_CALL
-tu_ImportSemaphoreFdKHR(VkDevice device, const VkImportSemaphoreFdInfoKHR *info)
-{
- TU_FROM_HANDLE(tu_syncobj, sync, info->semaphore);
- return sync_import(device, sync, info->flags & VK_SEMAPHORE_IMPORT_TEMPORARY_BIT,
- info->handleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, info->fd);
-}
-
-VKAPI_ATTR VkResult VKAPI_CALL
-tu_GetSemaphoreFdKHR(VkDevice device, const VkSemaphoreGetFdInfoKHR *info, int *pFd)
-{
- TU_FROM_HANDLE(tu_syncobj, sync, info->semaphore);
- return sync_export(device, sync,
- info->handleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, pFd);
-}
-
-VKAPI_ATTR void VKAPI_CALL
-tu_GetPhysicalDeviceExternalSemaphoreProperties(
- VkPhysicalDevice physicalDevice,
- const VkPhysicalDeviceExternalSemaphoreInfo *pExternalSemaphoreInfo,
- VkExternalSemaphoreProperties *pExternalSemaphoreProperties)
-{
- VkSemaphoreTypeKHR type = get_semaphore_type(pExternalSemaphoreInfo->pNext, NULL);
-
- if (type != VK_SEMAPHORE_TYPE_TIMELINE &&
- (pExternalSemaphoreInfo->handleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT ||
- pExternalSemaphoreInfo->handleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT )) {
- pExternalSemaphoreProperties->exportFromImportedHandleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT | VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
- pExternalSemaphoreProperties->compatibleHandleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT | VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
- pExternalSemaphoreProperties->externalSemaphoreFeatures = VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT |
- VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT;
- } else {
- pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;
- pExternalSemaphoreProperties->compatibleHandleTypes = 0;
- pExternalSemaphoreProperties->externalSemaphoreFeatures = 0;
- }
-}
-
-static VkResult
-tu_queue_submit_add_timeline_wait_locked(struct tu_queue_submit* submit,
- struct tu_device *device,
- struct tu_syncobj *timeline,
- uint64_t value)
-{
- if (submit->wait_timeline_count >= submit->wait_timeline_array_length) {
- uint32_t new_len = MAX2(submit->wait_timeline_array_length * 2, 64);
-
- submit->wait_timelines = vk_realloc(&device->vk.alloc,
- submit->wait_timelines,
- new_len * sizeof(*submit->wait_timelines),
- 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
-
- if (submit->wait_timelines == NULL)
- return VK_ERROR_OUT_OF_HOST_MEMORY;
-
- submit->wait_timeline_values = vk_realloc(&device->vk.alloc,
- submit->wait_timeline_values,
- new_len * sizeof(*submit->wait_timeline_values),
- 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
-
- if (submit->wait_timeline_values == NULL) {
- vk_free(&device->vk.alloc, submit->wait_timelines);
- return VK_ERROR_OUT_OF_HOST_MEMORY;
- }
-
- submit->wait_timeline_array_length = new_len;
- }
-
- submit->wait_timelines[submit->wait_timeline_count] = timeline;
- submit->wait_timeline_values[submit->wait_timeline_count] = value;
-
- submit->wait_timeline_count++;
-
- return VK_SUCCESS;
-}
-
-static VkResult
-tu_queue_submit_add_timeline_signal_locked(struct tu_queue_submit* submit,
- struct tu_device *device,
- struct tu_syncobj *timeline,
- uint64_t value)
-{
- if (submit->signal_timeline_count >= submit->signal_timeline_array_length) {
- uint32_t new_len = MAX2(submit->signal_timeline_array_length * 2, 32);
-
- submit->signal_timelines = vk_realloc(&device->vk.alloc,
- submit->signal_timelines,
- new_len * sizeof(*submit->signal_timelines),
- 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
-
- if (submit->signal_timelines == NULL)
- return VK_ERROR_OUT_OF_HOST_MEMORY;
-
- submit->signal_timeline_values = vk_realloc(&device->vk.alloc,
- submit->signal_timeline_values,
- new_len * sizeof(*submit->signal_timeline_values),
- 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
-
- if (submit->signal_timeline_values == NULL) {
- vk_free(&device->vk.alloc, submit->signal_timelines);
- return VK_ERROR_OUT_OF_HOST_MEMORY;
- }
-
- submit->signal_timeline_array_length = new_len;
- }
-
- submit->signal_timelines[submit->signal_timeline_count] = timeline;
- submit->signal_timeline_values[submit->signal_timeline_count] = value;
-
- submit->signal_timeline_count++;
-
- return VK_SUCCESS;
-}
-
-static VkResult
-tu_queue_submit_create_locked(struct tu_queue *queue,
- const VkSubmitInfo *submit_info,
- const uint32_t nr_in_syncobjs,
- const uint32_t nr_out_syncobjs,
- const bool last_submit,
- const VkPerformanceQuerySubmitInfoKHR *perf_info,
- struct tu_queue_submit **submit)
-{
- VkResult result;
-
- const VkTimelineSemaphoreSubmitInfoKHR *timeline_info =
- vk_find_struct_const(submit_info->pNext,
- TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR);
-
- const uint32_t wait_values_count =
- timeline_info ? timeline_info->waitSemaphoreValueCount : 0;
- const uint32_t signal_values_count =
- timeline_info ? timeline_info->signalSemaphoreValueCount : 0;
-
- const uint64_t *wait_values =
- wait_values_count ? timeline_info->pWaitSemaphoreValues : NULL;
- const uint64_t *signal_values =
- signal_values_count ? timeline_info->pSignalSemaphoreValues : NULL;
-
- struct tu_queue_submit *new_submit = vk_zalloc(&queue->device->vk.alloc,
- sizeof(*new_submit), 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
-
- new_submit->cmd_buffer_count = submit_info->commandBufferCount;
- new_submit->cmd_buffers = vk_zalloc(&queue->device->vk.alloc,
- new_submit->cmd_buffer_count * sizeof(*new_submit->cmd_buffers), 8,
- VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
-
- if (new_submit->cmd_buffers == NULL) {
- result = vk_error(queue->device->instance, VK_ERROR_OUT_OF_HOST_MEMORY)
- goto fail_cmd_buffers;
- }
-
- memcpy(new_submit->cmd_buffers, submit_info->pCommandBuffers,
- new_submit->cmd_buffer_count * sizeof(*new_submit->cmd_buffers));
-
- new_submit->wait_semaphores = vk_zalloc(&queue->device->vk.alloc,
- submit_info->waitSemaphoreCount * sizeof(*new_submit->wait_semaphores),
- 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
- if (new_submit->wait_semaphores == NULL) {
- result = vk_error(queue->device->instance, VK_ERROR_OUT_OF_HOST_MEMORY)
- goto fail_wait_semaphores;
- }
- new_submit->wait_semaphore_count = submit_info->waitSemaphoreCount;
-
- new_submit->signal_semaphores = vk_zalloc(&queue->device->vk.alloc,
- submit_info->signalSemaphoreCount *sizeof(*new_submit->signal_semaphores),
- 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
- if (new_submit->signal_semaphores == NULL) {
- result = vk_error(queue->device->instance, VK_ERROR_OUT_OF_HOST_MEMORY)
- goto fail_signal_semaphores;
- }
- new_submit->signal_semaphore_count = submit_info->signalSemaphoreCount;
-
- for (uint32_t i = 0; i < submit_info->waitSemaphoreCount; i++) {
- TU_FROM_HANDLE(tu_syncobj, sem, submit_info->pWaitSemaphores[i]);
- new_submit->wait_semaphores[i] = sem;
-
- if (sem->type == TU_SEMAPHORE_TIMELINE) {
- result = tu_queue_submit_add_timeline_wait_locked(new_submit,
- queue->device, sem, wait_values[i]);
- if (result != VK_SUCCESS)
- goto fail_wait_timelines;
- }
- }
-
- for (uint32_t i = 0; i < submit_info->signalSemaphoreCount; i++) {
- TU_FROM_HANDLE(tu_syncobj, sem, submit_info->pSignalSemaphores[i]);
- new_submit->signal_semaphores[i] = sem;
-
- if (sem->type == TU_SEMAPHORE_TIMELINE) {
- result = tu_queue_submit_add_timeline_signal_locked(new_submit,
- queue->device, sem, signal_values[i]);
- if (result != VK_SUCCESS)
- goto fail_signal_timelines;
- }
- }
-
- bool u_trace_enabled = u_trace_context_tracing(&queue->device->trace_context);
- bool has_trace_points = false;
-
- uint32_t entry_count = 0;
- for (uint32_t j = 0; j < new_submit->cmd_buffer_count; ++j) {
- TU_FROM_HANDLE(tu_cmd_buffer, cmdbuf, new_submit->cmd_buffers[j]);
-
- if (perf_info)
- entry_count++;
-
- entry_count += cmdbuf->cs.entry_count;
-
- if (u_trace_enabled && u_trace_has_points(&cmdbuf->trace)) {
- if (!(cmdbuf->usage_flags & VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT))
- entry_count++;
-
- has_trace_points = true;
- }
- }
-
- new_submit->cmds = vk_zalloc(&queue->device->vk.alloc,
- entry_count * sizeof(*new_submit->cmds), 8,
- VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
-
- if (new_submit->cmds == NULL) {
- result = vk_error(queue->device->instance, VK_ERROR_OUT_OF_HOST_MEMORY)
- goto fail_cmds;
- }
-
- if (has_trace_points) {
- new_submit->cmd_buffer_trace_data = vk_zalloc(&queue->device->vk.alloc,
- new_submit->cmd_buffer_count * sizeof(struct tu_u_trace_cmd_data), 8,
- VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
-
- if (new_submit->cmd_buffer_trace_data == NULL) {
- result = vk_error(queue->device->instance, VK_ERROR_OUT_OF_HOST_MEMORY)
- goto fail_cmd_trace_data;
- }
-
- for (uint32_t i = 0; i < new_submit->cmd_buffer_count; ++i) {
- TU_FROM_HANDLE(tu_cmd_buffer, cmdbuf, new_submit->cmd_buffers[i]);
-
- if (!(cmdbuf->usage_flags & VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT) &&
- u_trace_has_points(&cmdbuf->trace)) {
- /* A single command buffer could be submitted several times, but we
- * already backed timestamp iova addresses and trace points are
- * single-use. Therefor we have to copy trace points and create
- * a new timestamp buffer on every submit of reusable command buffer.
- */
- if (tu_create_copy_timestamp_cs(cmdbuf,
- &new_submit->cmd_buffer_trace_data[i].timestamp_copy_cs,
- &new_submit->cmd_buffer_trace_data[i].trace) != VK_SUCCESS) {
- result = vk_error(queue->device->instance, VK_ERROR_OUT_OF_HOST_MEMORY)
- goto fail_copy_timestamp_cs;
- }
- assert(new_submit->cmd_buffer_trace_data[i].timestamp_copy_cs->entry_count == 1);
- } else {
- new_submit->cmd_buffer_trace_data[i].trace = &cmdbuf->trace;
- }
- }
- }
-
- /* Allocate without wait timeline semaphores */
- new_submit->in_syncobjs = vk_zalloc(&queue->device->vk.alloc,
- (nr_in_syncobjs - new_submit->wait_timeline_count) *
- sizeof(*new_submit->in_syncobjs), 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
-
- if (new_submit->in_syncobjs == NULL) {
- result = vk_error(queue->device->instance, VK_ERROR_OUT_OF_HOST_MEMORY)
- goto fail_in_syncobjs;
- }
-
- /* Allocate with signal timeline semaphores considered */
- new_submit->out_syncobjs = vk_zalloc(&queue->device->vk.alloc,
- nr_out_syncobjs * sizeof(*new_submit->out_syncobjs), 8,
- VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
-
- if (new_submit->out_syncobjs == NULL) {
- result = vk_error(queue->device->instance, VK_ERROR_OUT_OF_HOST_MEMORY)
- goto fail_out_syncobjs;
- }
-
- new_submit->entry_count = entry_count;
- new_submit->nr_in_syncobjs = nr_in_syncobjs;
- new_submit->nr_out_syncobjs = nr_out_syncobjs;
- new_submit->last_submit = last_submit;
- new_submit->counter_pass_index = perf_info ? perf_info->counterPassIndex : ~0;
-
- list_inithead(&new_submit->link);
-
- *submit = new_submit;
-
- return VK_SUCCESS;
-
-fail_out_syncobjs:
- vk_free(&queue->device->vk.alloc, new_submit->in_syncobjs);
-fail_in_syncobjs:
- if (new_submit->cmd_buffer_trace_data)
- tu_u_trace_cmd_data_finish(queue->device, new_submit->cmd_buffer_trace_data,
- new_submit->cmd_buffer_count);
-fail_copy_timestamp_cs:
- vk_free(&queue->device->vk.alloc, new_submit->cmd_buffer_trace_data);
-fail_cmd_trace_data:
- vk_free(&queue->device->vk.alloc, new_submit->cmds);
-fail_cmds:
-fail_signal_timelines:
-fail_wait_timelines:
- vk_free(&queue->device->vk.alloc, new_submit->signal_semaphores);
-fail_signal_semaphores:
- vk_free(&queue->device->vk.alloc, new_submit->wait_semaphores);
-fail_wait_semaphores:
- vk_free(&queue->device->vk.alloc, new_submit->cmd_buffers);
-fail_cmd_buffers:
- return result;
-}
-
-static void
-tu_queue_submit_free(struct tu_queue *queue, struct tu_queue_submit *submit)
-{
- vk_free(&queue->device->vk.alloc, submit->wait_semaphores);
- vk_free(&queue->device->vk.alloc, submit->signal_semaphores);
-
- vk_free(&queue->device->vk.alloc, submit->wait_timelines);
- vk_free(&queue->device->vk.alloc, submit->wait_timeline_values);
- vk_free(&queue->device->vk.alloc, submit->signal_timelines);
- vk_free(&queue->device->vk.alloc, submit->signal_timeline_values);
-
- vk_free(&queue->device->vk.alloc, submit->cmds);
- vk_free(&queue->device->vk.alloc, submit->in_syncobjs);
- vk_free(&queue->device->vk.alloc, submit->out_syncobjs);
- vk_free(&queue->device->vk.alloc, submit->cmd_buffers);
- vk_free(&queue->device->vk.alloc, submit);
-}
-
-static void
-tu_queue_build_msm_gem_submit_cmds(struct tu_queue *queue,
- struct tu_queue_submit *submit)
-{
- struct drm_msm_gem_submit_cmd *cmds = submit->cmds;
-
- uint32_t entry_idx = 0;
- for (uint32_t j = 0; j < submit->cmd_buffer_count; ++j) {
- TU_FROM_HANDLE(tu_cmd_buffer, cmdbuf, submit->cmd_buffers[j]);
- struct tu_cs *cs = &cmdbuf->cs;
- struct tu_device *dev = queue->device;
-
- if (submit->counter_pass_index != ~0) {
- struct tu_cs_entry *perf_cs_entry =
- &dev->perfcntrs_pass_cs_entries[submit->counter_pass_index];
-
- cmds[entry_idx].type = MSM_SUBMIT_CMD_BUF;
- cmds[entry_idx].submit_idx =
- dev->bo_idx[perf_cs_entry->bo->gem_handle];
- cmds[entry_idx].submit_offset = perf_cs_entry->offset;
- cmds[entry_idx].size = perf_cs_entry->size;
- cmds[entry_idx].pad = 0;
- cmds[entry_idx].nr_relocs = 0;
- cmds[entry_idx++].relocs = 0;
- }
-
- for (unsigned i = 0; i < cs->entry_count; ++i, ++entry_idx) {
- cmds[entry_idx].type = MSM_SUBMIT_CMD_BUF;
- cmds[entry_idx].submit_idx =
- dev->bo_idx[cs->entries[i].bo->gem_handle];
- cmds[entry_idx].submit_offset = cs->entries[i].offset;
- cmds[entry_idx].size = cs->entries[i].size;
- cmds[entry_idx].pad = 0;
- cmds[entry_idx].nr_relocs = 0;
- cmds[entry_idx].relocs = 0;
- }
-
- if (submit->cmd_buffer_trace_data) {
- struct tu_cs *ts_cs = submit->cmd_buffer_trace_data[j].timestamp_copy_cs;
- if (ts_cs) {
- cmds[entry_idx].type = MSM_SUBMIT_CMD_BUF;
- cmds[entry_idx].submit_idx =
- queue->device->bo_idx[ts_cs->entries[0].bo->gem_handle];
-
- assert(cmds[entry_idx].submit_idx < queue->device->bo_count);
-
- cmds[entry_idx].submit_offset = ts_cs->entries[0].offset;
- cmds[entry_idx].size = ts_cs->entries[0].size;
- cmds[entry_idx].pad = 0;
- cmds[entry_idx].nr_relocs = 0;
- cmds[entry_idx++].relocs = 0;
- }
- }
- }
-}
-
-static VkResult
-tu_queue_submit_locked(struct tu_queue *queue, struct tu_queue_submit *submit)
-{
- queue->device->submit_count++;
-
-#if HAVE_PERFETTO
- tu_perfetto_submit(queue->device, queue->device->submit_count);
-#endif
-
- uint32_t flags = MSM_PIPE_3D0;
-
- if (submit->nr_in_syncobjs)
- flags |= MSM_SUBMIT_SYNCOBJ_IN;
-
- if (submit->nr_out_syncobjs)
- flags |= MSM_SUBMIT_SYNCOBJ_OUT;
-
- if (submit->last_submit)
- flags |= MSM_SUBMIT_FENCE_FD_OUT;
-
- mtx_lock(&queue->device->bo_mutex);
-
- /* drm_msm_gem_submit_cmd requires index of bo which could change at any
- * time when bo_mutex is not locked. So we build submit cmds here the real
- * place to submit.
- */
- tu_queue_build_msm_gem_submit_cmds(queue, submit);
-
- struct drm_msm_gem_submit req = {
- .flags = flags,
- .queueid = queue->msm_queue_id,
- .bos = (uint64_t)(uintptr_t) queue->device->bo_list,
- .nr_bos = queue->device->bo_count,
- .cmds = (uint64_t)(uintptr_t)submit->cmds,
- .nr_cmds = submit->entry_count,
- .in_syncobjs = (uint64_t)(uintptr_t)submit->in_syncobjs,
- .out_syncobjs = (uint64_t)(uintptr_t)submit->out_syncobjs,
- .nr_in_syncobjs = submit->nr_in_syncobjs - submit->wait_timeline_count,
- .nr_out_syncobjs = submit->nr_out_syncobjs,
- .syncobj_stride = sizeof(struct drm_msm_gem_submit_syncobj),
- };
-
- int ret = drmCommandWriteRead(queue->device->fd,
- DRM_MSM_GEM_SUBMIT,
- &req, sizeof(req));
-
- mtx_unlock(&queue->device->bo_mutex);
-
- if (ret)
- return tu_device_set_lost(queue->device, "submit failed: %s\n",
- strerror(errno));
-
- /* restore permanent payload on wait */
- for (uint32_t i = 0; i < submit->wait_semaphore_count; i++) {
- TU_FROM_HANDLE(tu_syncobj, sem, submit->wait_semaphores[i]);
- if(sem->type == TU_SEMAPHORE_BINARY)
- sync_set_temporary(queue->device, sem, 0);
- }
-
- if (submit->last_submit) {
- if (queue->fence >= 0)
- close(queue->fence);
- queue->fence = req.fence_fd;
- }
-
- /* Update highest_submitted values in the timeline. */
- for (uint32_t i = 0; i < submit->signal_timeline_count; i++) {
- struct tu_syncobj *sem = submit->signal_timelines[i];
- uint64_t signal_value = submit->signal_timeline_values[i];
-
- assert(signal_value > sem->timeline.highest_submitted);
-
- sem->timeline.highest_submitted = signal_value;
- }
-
- if (submit->cmd_buffer_trace_data) {
- struct tu_u_trace_flush_data *flush_data =
- vk_alloc(&queue->device->vk.alloc, sizeof(struct tu_u_trace_flush_data),
- 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
- flush_data->submission_id = queue->device->submit_count;
- flush_data->syncobj =
- vk_alloc(&queue->device->vk.alloc, sizeof(struct tu_u_trace_syncobj),
- 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
- flush_data->syncobj->fence = req.fence;
- flush_data->syncobj->msm_queue_id = queue->msm_queue_id;
-
- flush_data->cmd_trace_data = submit->cmd_buffer_trace_data;
- flush_data->trace_count = submit->cmd_buffer_count;
- submit->cmd_buffer_trace_data = NULL;
-
- for (uint32_t i = 0; i < submit->cmd_buffer_count; i++) {
- bool free_data = i == (submit->cmd_buffer_count - 1);
- u_trace_flush(flush_data->cmd_trace_data[i].trace, flush_data, free_data);
- }
- }
-
- pthread_cond_broadcast(&queue->device->timeline_cond);
-
- return VK_SUCCESS;
-}
-
-
-static bool
-tu_queue_submit_ready_locked(struct tu_queue_submit *submit)
-{
- for (uint32_t i = 0; i < submit->wait_timeline_count; i++) {
- if (submit->wait_timeline_values[i] >
- submit->wait_timelines[i]->timeline.highest_submitted) {
- return false;
- }
- }
-
- return true;
-}
-
-static VkResult
-tu_timeline_add_point_locked(struct tu_device *device,
- struct tu_timeline *timeline,
- uint64_t value,
- struct tu_timeline_point **point)
-{
-
- if (list_is_empty(&timeline->free_points)) {
- *point = vk_zalloc(&device->vk.alloc, sizeof(**point), 8,
- VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
-
- if (!(*point))
- return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
-
- struct drm_syncobj_create create = {};
-
- int ret = drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_CREATE, &create);
- if (ret) {
- vk_free(&device->vk.alloc, *point);
- return vk_error(device->instance, VK_ERROR_DEVICE_LOST);
- }
-
- (*point)->syncobj = create.handle;
-
- } else {
- *point = list_first_entry(&timeline->free_points,
- struct tu_timeline_point, link);
- list_del(&(*point)->link);
- }
-
- (*point)->value = value;
- list_addtail(&(*point)->link, &timeline->points);
-
- return VK_SUCCESS;
-}
-
-static VkResult
-tu_queue_submit_timeline_locked(struct tu_queue *queue,
- struct tu_queue_submit *submit)
-{
- VkResult result;
- uint32_t timeline_idx =
- submit->nr_out_syncobjs - submit->signal_timeline_count;
-
- for (uint32_t i = 0; i < submit->signal_timeline_count; i++) {
- struct tu_timeline *timeline = &submit->signal_timelines[i]->timeline;
- uint64_t signal_value = submit->signal_timeline_values[i];
- struct tu_timeline_point *point;
-
- result = tu_timeline_add_point_locked(queue->device, timeline,
- signal_value, &point);
- if (result != VK_SUCCESS)
- return result;
-
- submit->out_syncobjs[timeline_idx + i] =
- (struct drm_msm_gem_submit_syncobj) {
- .handle = point->syncobj,
- .flags = 0,
- };
- }
-
- return tu_queue_submit_locked(queue, submit);
-}
-
-static VkResult
-tu_queue_submit_deferred_locked(struct tu_queue *queue, uint32_t *advance)
-{
- VkResult result = VK_SUCCESS;
-
- list_for_each_entry_safe(struct tu_queue_submit, submit,
- &queue->queued_submits, link) {
- if (!tu_queue_submit_ready_locked(submit))
- break;
-
- (*advance)++;
-
- result = tu_queue_submit_timeline_locked(queue, submit);
-
- list_del(&submit->link);
- tu_queue_submit_free(queue, submit);
-
- if (result != VK_SUCCESS)
- break;
- }
-
- return result;
-}
-
-VkResult
-tu_device_submit_deferred_locked(struct tu_device *dev)
-{
- VkResult result = VK_SUCCESS;
-
- uint32_t advance = 0;
- do {
- advance = 0;
- for (uint32_t i = 0; i < dev->queue_count[0]; i++) {
- /* Try again if there's signaled submission. */
- result = tu_queue_submit_deferred_locked(&dev->queues[0][i],
- &advance);
- if (result != VK_SUCCESS)
- return result;
- }
-
- } while(advance);
-
- return result;
-}
-
-static inline void
-get_abs_timeout(struct drm_msm_timespec *tv, uint64_t ns)
-{
- struct timespec t;
- clock_gettime(CLOCK_MONOTONIC, &t);
- tv->tv_sec = t.tv_sec + ns / 1000000000;
- tv->tv_nsec = t.tv_nsec + ns % 1000000000;
-}
-
-VkResult
-tu_device_wait_u_trace(struct tu_device *dev, struct tu_u_trace_syncobj *syncobj)
-{
- struct drm_msm_wait_fence req = {
- .fence = syncobj->fence,
- .queueid = syncobj->msm_queue_id,
- };
- int ret;
-
- get_abs_timeout(&req.timeout, 1000000000);
-
- ret = drmCommandWrite(dev->fd, DRM_MSM_WAIT_FENCE, &req, sizeof(req));
- if (ret && (ret != -ETIMEDOUT)) {
- fprintf(stderr, "wait-fence failed! %d (%s)", ret, strerror(errno));
- return VK_TIMEOUT;
- }
-
- return VK_SUCCESS;
-}
-
-VKAPI_ATTR VkResult VKAPI_CALL
-tu_QueueSubmit(VkQueue _queue,
- uint32_t submitCount,
- const VkSubmitInfo *pSubmits,
- VkFence _fence)
-{
- TU_FROM_HANDLE(tu_queue, queue, _queue);
- TU_FROM_HANDLE(tu_syncobj, fence, _fence);
-
- for (uint32_t i = 0; i < submitCount; ++i) {
- const VkSubmitInfo *submit = pSubmits + i;
- const bool last_submit = (i == submitCount - 1);
- uint32_t out_syncobjs_size = submit->signalSemaphoreCount;
-
- const VkPerformanceQuerySubmitInfoKHR *perf_info =
- vk_find_struct_const(pSubmits[i].pNext,
- PERFORMANCE_QUERY_SUBMIT_INFO_KHR);
-
- if (last_submit && fence)
- out_syncobjs_size += 1;
-
- pthread_mutex_lock(&queue->device->submit_mutex);
- struct tu_queue_submit *submit_req = NULL;
-
- VkResult ret = tu_queue_submit_create_locked(queue, submit,
- submit->waitSemaphoreCount, out_syncobjs_size,
- last_submit, perf_info, &submit_req);
-
- if (ret != VK_SUCCESS) {
- pthread_mutex_unlock(&queue->device->submit_mutex);
- return ret;
- }
-
- /* note: assuming there won't be any very large semaphore counts */
- struct drm_msm_gem_submit_syncobj *in_syncobjs = submit_req->in_syncobjs;
- struct drm_msm_gem_submit_syncobj *out_syncobjs = submit_req->out_syncobjs;
- uint32_t nr_in_syncobjs = 0, nr_out_syncobjs = 0;
-
- for (uint32_t i = 0; i < submit->waitSemaphoreCount; i++) {
- TU_FROM_HANDLE(tu_syncobj, sem, submit->pWaitSemaphores[i]);
- if (sem->type == TU_SEMAPHORE_TIMELINE)
- continue;
-
- in_syncobjs[nr_in_syncobjs++] = (struct drm_msm_gem_submit_syncobj) {
- .handle = sem->binary.temporary ?: sem->binary.permanent,
- .flags = MSM_SUBMIT_SYNCOBJ_RESET,
- };
- }
-
- for (uint32_t i = 0; i < submit->signalSemaphoreCount; i++) {
- TU_FROM_HANDLE(tu_syncobj, sem, submit->pSignalSemaphores[i]);
-
- /* In case of timeline semaphores, we can defer the creation of syncobj
- * and adding it at real submit time.
- */
- if (sem->type == TU_SEMAPHORE_TIMELINE)
- continue;
-
- out_syncobjs[nr_out_syncobjs++] = (struct drm_msm_gem_submit_syncobj) {
- .handle = sem->binary.temporary ?: sem->binary.permanent,
- .flags = 0,
- };
- }
-
- if (last_submit && fence) {
- out_syncobjs[nr_out_syncobjs++] = (struct drm_msm_gem_submit_syncobj) {
- .handle = fence->binary.temporary ?: fence->binary.permanent,
- .flags = 0,
- };
- }
-
- /* Queue the current submit */
- list_addtail(&submit_req->link, &queue->queued_submits);
- ret = tu_device_submit_deferred_locked(queue->device);
-
- pthread_mutex_unlock(&queue->device->submit_mutex);
- if (ret != VK_SUCCESS)
- return ret;
- }
-
- if (!submitCount && fence) {
- /* signal fence imemediately since we don't have a submit to do it */
- drmIoctl(queue->device->fd, DRM_IOCTL_SYNCOBJ_SIGNAL, &(struct drm_syncobj_array) {
- .handles = (uintptr_t) (uint32_t[]) { fence->binary.temporary ?: fence->binary.permanent },
- .count_handles = 1,
- });
- }
-
- return VK_SUCCESS;
-}
-
-VKAPI_ATTR VkResult VKAPI_CALL
-tu_CreateFence(VkDevice device,
- const VkFenceCreateInfo *info,
- const VkAllocationCallbacks *pAllocator,
- VkFence *pFence)
-{
- return sync_create(device, info->flags & VK_FENCE_CREATE_SIGNALED_BIT, true, true, 0,
- pAllocator, (void**) pFence);
-}
-
-VKAPI_ATTR void VKAPI_CALL
-tu_DestroyFence(VkDevice device, VkFence fence, const VkAllocationCallbacks *pAllocator)
-{
- TU_FROM_HANDLE(tu_syncobj, sync, fence);
- sync_destroy(device, sync, pAllocator);
-}
-
-VKAPI_ATTR VkResult VKAPI_CALL
-tu_ImportFenceFdKHR(VkDevice device, const VkImportFenceFdInfoKHR *info)
-{
- TU_FROM_HANDLE(tu_syncobj, sync, info->fence);
- return sync_import(device, sync, info->flags & VK_FENCE_IMPORT_TEMPORARY_BIT,
- info->handleType == VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT, info->fd);
-}
-
-VKAPI_ATTR VkResult VKAPI_CALL
-tu_GetFenceFdKHR(VkDevice device, const VkFenceGetFdInfoKHR *info, int *pFd)
-{
- TU_FROM_HANDLE(tu_syncobj, sync, info->fence);
- return sync_export(device, sync,
- info->handleType == VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT, pFd);
-}
-
-static VkResult
-drm_syncobj_wait(struct tu_device *device,
- const uint32_t *handles, uint32_t count_handles,
- int64_t timeout_nsec, bool wait_all)
-{
- int ret = drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_WAIT, &(struct drm_syncobj_wait) {
- .handles = (uint64_t) (uintptr_t) handles,
- .count_handles = count_handles,
- .timeout_nsec = timeout_nsec,
- .flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
- COND(wait_all, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL)
- });
- if (ret) {
- if (errno == ETIME)
- return VK_TIMEOUT;
-
- assert(0);
- return VK_ERROR_DEVICE_LOST; /* TODO */
- }
- return VK_SUCCESS;
-}
-
-static uint64_t
-gettime_ns(void)
-{
- struct timespec current;
- clock_gettime(CLOCK_MONOTONIC, &current);
- return (uint64_t)current.tv_sec * 1000000000 + current.tv_nsec;
-}
-
-/* and the kernel converts it right back to relative timeout - very smart UAPI */
-static uint64_t
-absolute_timeout(uint64_t timeout)
-{
- if (timeout == 0)
- return 0;
- uint64_t current_time = gettime_ns();
- uint64_t max_timeout = (uint64_t) INT64_MAX - current_time;
-
- timeout = MIN2(max_timeout, timeout);
-
- return (current_time + timeout);
-}
-
-VKAPI_ATTR VkResult VKAPI_CALL
-tu_WaitForFences(VkDevice _device,
- uint32_t fenceCount,
- const VkFence *pFences,
- VkBool32 waitAll,
- uint64_t timeout)
-{
- TU_FROM_HANDLE(tu_device, device, _device);
-
- if (tu_device_is_lost(device))
- return VK_ERROR_DEVICE_LOST;
-
- uint32_t handles[fenceCount];
- for (unsigned i = 0; i < fenceCount; ++i) {
- TU_FROM_HANDLE(tu_syncobj, fence, pFences[i]);
- handles[i] = fence->binary.temporary ?: fence->binary.permanent;
- }
-
- return drm_syncobj_wait(device, handles, fenceCount, absolute_timeout(timeout), waitAll);
-}
-
-VKAPI_ATTR VkResult VKAPI_CALL
-tu_ResetFences(VkDevice _device, uint32_t fenceCount, const VkFence *pFences)
-{
- TU_FROM_HANDLE(tu_device, device, _device);
- int ret;
-
- uint32_t handles[fenceCount];
- for (unsigned i = 0; i < fenceCount; ++i) {
- TU_FROM_HANDLE(tu_syncobj, fence, pFences[i]);
- sync_set_temporary(device, fence, 0);
- handles[i] = fence->binary.permanent;
- }
-
- ret = drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_RESET, &(struct drm_syncobj_array) {
- .handles = (uint64_t) (uintptr_t) handles,
- .count_handles = fenceCount,
- });
- if (ret) {
- tu_device_set_lost(device, "DRM_IOCTL_SYNCOBJ_RESET failure: %s",
- strerror(errno));
- }
-
- return VK_SUCCESS;
-}
-
-VKAPI_ATTR VkResult VKAPI_CALL
-tu_GetFenceStatus(VkDevice _device, VkFence _fence)
-{
- TU_FROM_HANDLE(tu_device, device, _device);
- TU_FROM_HANDLE(tu_syncobj, fence, _fence);
- VkResult result;
-
- result = drm_syncobj_wait(device, (uint32_t[]){fence->binary.temporary ?: fence->binary.permanent}, 1, 0, false);
- if (result == VK_TIMEOUT)
- result = VK_NOT_READY;
- return result;
-}
-
-int
-tu_signal_fences(struct tu_device *device, struct tu_syncobj *fence1, struct tu_syncobj *fence2)
-{
- uint32_t handles[2], count = 0;
- if (fence1)
- handles[count++] = fence1->binary.temporary ?: fence1->binary.permanent;
-
- if (fence2)
- handles[count++] = fence2->binary.temporary ?: fence2->binary.permanent;
-
- if (!count)
- return 0;
-
- return drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_SIGNAL, &(struct drm_syncobj_array) {
- .handles = (uintptr_t) handles,
- .count_handles = count
- });
-}
-
-int
-tu_syncobj_to_fd(struct tu_device *device, struct tu_syncobj *sync)
-{
- struct drm_syncobj_handle handle = { .handle = sync->binary.permanent };
- int ret;
-
- ret = drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &handle);
-
- return ret ? -1 : handle.fd;
-}
-
-static VkResult
-tu_timeline_gc_locked(struct tu_device *dev, struct tu_timeline *timeline)
-{
- VkResult result = VK_SUCCESS;
-
- /* Go through every point in the timeline and check if any signaled point */
- list_for_each_entry_safe(struct tu_timeline_point, point,
- &timeline->points, link) {
-
- /* If the value of the point is higher than highest_submitted,
- * the point has not been submited yet.
- */
- if (point->wait_count || point->value > timeline->highest_submitted)
- return VK_SUCCESS;
-
- result = drm_syncobj_wait(dev, (uint32_t[]){point->syncobj}, 1, 0, true);
-
- if (result == VK_TIMEOUT) {
- /* This means the syncobj is still busy and it should wait
- * with timeout specified by users via vkWaitSemaphores.
- */
- result = VK_SUCCESS;
- } else {
- timeline->highest_signaled =
- MAX2(timeline->highest_signaled, point->value);
- list_del(&point->link);
- list_add(&point->link, &timeline->free_points);
- }
- }
-
- return result;
-}
-
-
-static VkResult
-tu_timeline_wait_locked(struct tu_device *device,
- struct tu_timeline *timeline,
- uint64_t value,
- uint64_t abs_timeout)
-{
- VkResult result;
-
- while(timeline->highest_submitted < value) {
- struct timespec abstime;
- timespec_from_nsec(&abstime, abs_timeout);
-
- pthread_cond_timedwait(&device->timeline_cond, &device->submit_mutex,
- &abstime);
-
- if (os_time_get_nano() >= abs_timeout &&
- timeline->highest_submitted < value)
- return VK_TIMEOUT;
- }
-
- /* Visit every point in the timeline and wait until
- * the highest_signaled reaches the value.
- */
- while (1) {
- result = tu_timeline_gc_locked(device, timeline);
- if (result != VK_SUCCESS)
- return result;
-
- if (timeline->highest_signaled >= value)
- return VK_SUCCESS;
-
- struct tu_timeline_point *point =
- list_first_entry(&timeline->points,
- struct tu_timeline_point, link);
-
- point->wait_count++;
- pthread_mutex_unlock(&device->submit_mutex);
- result = drm_syncobj_wait(device, (uint32_t[]){point->syncobj}, 1,
- abs_timeout, true);
-
- pthread_mutex_lock(&device->submit_mutex);
- point->wait_count--;
-
- if (result != VK_SUCCESS)
- return result;
- }
-
- return result;
-}
-
-static VkResult
-tu_wait_timelines(struct tu_device *device,
- const VkSemaphoreWaitInfoKHR* pWaitInfo,
- uint64_t abs_timeout)
-{
- if ((pWaitInfo->flags & VK_SEMAPHORE_WAIT_ANY_BIT_KHR) &&
- pWaitInfo->semaphoreCount > 1) {
- pthread_mutex_lock(&device->submit_mutex);
-
- /* Visit every timline semaphore in the queue until timeout */
- while (1) {
- for(uint32_t i = 0; i < pWaitInfo->semaphoreCount; ++i) {
- TU_FROM_HANDLE(tu_syncobj, semaphore, pWaitInfo->pSemaphores[i]);
- VkResult result = tu_timeline_wait_locked(device,
- &semaphore->timeline, pWaitInfo->pValues[i], 0);
-
- /* Returns result values including VK_SUCCESS except for VK_TIMEOUT */
- if (result != VK_TIMEOUT) {
- pthread_mutex_unlock(&device->submit_mutex);
- return result;
- }
- }
-
- if (os_time_get_nano() > abs_timeout) {
- pthread_mutex_unlock(&device->submit_mutex);
- return VK_TIMEOUT;
- }
- }
- } else {
- VkResult result = VK_SUCCESS;
-
- pthread_mutex_lock(&device->submit_mutex);
- for(uint32_t i = 0; i < pWaitInfo->semaphoreCount; ++i) {
- TU_FROM_HANDLE(tu_syncobj, semaphore, pWaitInfo->pSemaphores[i]);
- assert(semaphore->type == TU_SEMAPHORE_TIMELINE);
-
- result = tu_timeline_wait_locked(device, &semaphore->timeline,
- pWaitInfo->pValues[i], abs_timeout);
- if (result != VK_SUCCESS)
- break;
- }
- pthread_mutex_unlock(&device->submit_mutex);
-
- return result;
- }
-}
-
-
-VKAPI_ATTR VkResult VKAPI_CALL
-tu_GetSemaphoreCounterValue(VkDevice _device,
- VkSemaphore _semaphore,
- uint64_t* pValue)
-{
- TU_FROM_HANDLE(tu_device, device, _device);
- TU_FROM_HANDLE(tu_syncobj, semaphore, _semaphore);
-
- assert(semaphore->type == TU_SEMAPHORE_TIMELINE);
-
- VkResult result;
-
- pthread_mutex_lock(&device->submit_mutex);
-
- result = tu_timeline_gc_locked(device, &semaphore->timeline);
- *pValue = semaphore->timeline.highest_signaled;
-
- pthread_mutex_unlock(&device->submit_mutex);
-
- return result;
-}
-
-
-VKAPI_ATTR VkResult VKAPI_CALL
-tu_WaitSemaphores(VkDevice _device,
- const VkSemaphoreWaitInfoKHR* pWaitInfo,
- uint64_t timeout)
-{
- TU_FROM_HANDLE(tu_device, device, _device);
-
- return tu_wait_timelines(device, pWaitInfo, absolute_timeout(timeout));
-}
-
-VKAPI_ATTR VkResult VKAPI_CALL
-tu_SignalSemaphore(VkDevice _device,
- const VkSemaphoreSignalInfoKHR* pSignalInfo)
-{
- TU_FROM_HANDLE(tu_device, device, _device);
- TU_FROM_HANDLE(tu_syncobj, semaphore, pSignalInfo->semaphore);
- VkResult result;
-
- assert(semaphore->type == TU_SEMAPHORE_TIMELINE);
-
- pthread_mutex_lock(&device->submit_mutex);
-
- result = tu_timeline_gc_locked(device, &semaphore->timeline);
- if (result != VK_SUCCESS) {
- pthread_mutex_unlock(&device->submit_mutex);
- return result;
- }
-
- semaphore->timeline.highest_submitted = pSignalInfo->value;
- semaphore->timeline.highest_signaled = pSignalInfo->value;
-
- result = tu_device_submit_deferred_locked(device);
-
- pthread_cond_broadcast(&device->timeline_cond);
- pthread_mutex_unlock(&device->submit_mutex);
-
- return result;
-}
-
-#ifdef ANDROID
-#include <libsync.h>
-
-VKAPI_ATTR VkResult VKAPI_CALL
-tu_QueueSignalReleaseImageANDROID(VkQueue _queue,
- uint32_t waitSemaphoreCount,
- const VkSemaphore *pWaitSemaphores,
- VkImage image,
- int *pNativeFenceFd)
-{
- TU_FROM_HANDLE(tu_queue, queue, _queue);
- VkResult result = VK_SUCCESS;
-
- if (waitSemaphoreCount == 0) {
- if (pNativeFenceFd)
- *pNativeFenceFd = -1;
- return VK_SUCCESS;
- }
-
- int fd = -1;
-
- for (uint32_t i = 0; i < waitSemaphoreCount; ++i) {
- int tmp_fd;
- result = tu_GetSemaphoreFdKHR(
- tu_device_to_handle(queue->device),
- &(VkSemaphoreGetFdInfoKHR) {
- .sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
- .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
- .semaphore = pWaitSemaphores[i],
- },
- &tmp_fd);
- if (result != VK_SUCCESS) {
- if (fd >= 0)
- close(fd);
- return result;
- }
-
- if (fd < 0)
- fd = tmp_fd;
- else if (tmp_fd >= 0) {
- sync_accumulate("tu", &fd, tmp_fd);
- close(tmp_fd);
- }
- }
-
- if (pNativeFenceFd) {
- *pNativeFenceFd = fd;
- } else if (fd >= 0) {
- close(fd);
- /* We still need to do the exports, to reset the semaphores, but
- * otherwise we don't wait on them. */
- }
- return VK_SUCCESS;
-}
-#endif