Path: blob/21.2-virgl/src/freedreno/vulkan/tu_drm.c
4565 views
/*1* Copyright © 2018 Google, Inc.2* Copyright © 2015 Intel Corporation3*4* Permission is hereby granted, free of charge, to any person obtaining a5* copy of this software and associated documentation files (the "Software"),6* to deal in the Software without restriction, including without limitation7* the rights to use, copy, modify, merge, publish, distribute, sublicense,8* and/or sell copies of the Software, and to permit persons to whom the9* Software is furnished to do so, subject to the following conditions:10*11* The above copyright notice and this permission notice (including the next12* paragraph) shall be included in all copies or substantial portions of the13* Software.14*15* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR16* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,17* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL18* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER19* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING20* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER21* DEALINGS IN THE SOFTWARE.22*/2324#include <errno.h>25#include <fcntl.h>26#include <stdint.h>27#include <sys/ioctl.h>28#include <sys/mman.h>29#include <xf86drm.h>3031#include "vk_util.h"3233#include "drm-uapi/msm_drm.h"34#include "util/timespec.h"35#include "util/os_time.h"3637#include "tu_private.h"3839struct tu_binary_syncobj {40uint32_t permanent, temporary;41};4243struct tu_timeline_point {44struct list_head link;4546uint64_t value;47uint32_t syncobj;48uint32_t wait_count;49};5051struct tu_timeline {52uint64_t highest_submitted;53uint64_t highest_signaled;5455/* A timeline can have multiple timeline points */56struct list_head points;5758/* A list containing points that has been already submited.59* A point will be moved to 'points' when new point is required60* at submit time.61*/62struct list_head free_points;63};6465typedef enum {66TU_SEMAPHORE_BINARY,67TU_SEMAPHORE_TIMELINE,68} tu_semaphore_type;697071struct tu_syncobj {72struct vk_object_base base;7374tu_semaphore_type type;75union {76struct tu_binary_syncobj binary;77struct tu_timeline timeline;78};79};8081struct tu_queue_submit82{83struct list_head link;8485VkCommandBuffer *cmd_buffers;86uint32_t cmd_buffer_count;8788struct tu_syncobj **wait_semaphores;89uint32_t wait_semaphore_count;90struct tu_syncobj **signal_semaphores;91uint32_t signal_semaphore_count;9293struct tu_syncobj **wait_timelines;94uint64_t *wait_timeline_values;95uint32_t wait_timeline_count;96uint32_t wait_timeline_array_length;9798struct tu_syncobj **signal_timelines;99uint64_t *signal_timeline_values;100uint32_t signal_timeline_count;101uint32_t signal_timeline_array_length;102103struct drm_msm_gem_submit_cmd *cmds;104struct drm_msm_gem_submit_syncobj *in_syncobjs;105uint32_t nr_in_syncobjs;106struct drm_msm_gem_submit_syncobj *out_syncobjs;107uint32_t nr_out_syncobjs;108109bool last_submit;110uint32_t entry_count;111uint32_t counter_pass_index;112};113114static int115tu_drm_get_param(const struct tu_physical_device *dev,116uint32_t param,117uint64_t *value)118{119/* Technically this requires a pipe, but the kernel only supports one pipe120* anyway at the time of writing and most of these are clearly pipe121* independent. */122struct drm_msm_param req = {123.pipe = MSM_PIPE_3D0,124.param = param,125};126127int ret = drmCommandWriteRead(dev->local_fd, DRM_MSM_GET_PARAM, &req,128sizeof(req));129if (ret)130return ret;131132*value = req.value;133134return 0;135}136137static int138tu_drm_get_gpu_id(const struct tu_physical_device *dev, uint32_t *id)139{140uint64_t value;141int ret = tu_drm_get_param(dev, MSM_PARAM_GPU_ID, &value);142if (ret)143return ret;144145*id = value;146return 0;147}148149static int150tu_drm_get_gmem_size(const struct tu_physical_device *dev, uint32_t *size)151{152uint64_t value;153int ret = tu_drm_get_param(dev, MSM_PARAM_GMEM_SIZE, &value);154if (ret)155return ret;156157*size = value;158return 0;159}160161static int162tu_drm_get_gmem_base(const struct tu_physical_device *dev, uint64_t *base)163{164return tu_drm_get_param(dev, MSM_PARAM_GMEM_BASE, base);165}166167int168tu_drm_submitqueue_new(const struct tu_device *dev,169int priority,170uint32_t *queue_id)171{172struct drm_msm_submitqueue req = {173.flags = 0,174.prio = priority,175};176177int ret = drmCommandWriteRead(dev->fd,178DRM_MSM_SUBMITQUEUE_NEW, &req, sizeof(req));179if (ret)180return ret;181182*queue_id = req.id;183return 0;184}185186void187tu_drm_submitqueue_close(const struct tu_device *dev, uint32_t queue_id)188{189drmCommandWrite(dev->fd, DRM_MSM_SUBMITQUEUE_CLOSE,190&queue_id, sizeof(uint32_t));191}192193static void194tu_gem_close(const struct tu_device *dev, uint32_t gem_handle)195{196struct drm_gem_close req = {197.handle = gem_handle,198};199200drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req);201}202203/** Helper for DRM_MSM_GEM_INFO, returns 0 on error. */204static uint64_t205tu_gem_info(const struct tu_device *dev, uint32_t gem_handle, uint32_t info)206{207struct drm_msm_gem_info req = {208.handle = gem_handle,209.info = info,210};211212int ret = drmCommandWriteRead(dev->fd,213DRM_MSM_GEM_INFO, &req, sizeof(req));214if (ret < 0)215return 0;216217return req.value;218}219220static VkResult221tu_bo_init(struct tu_device *dev,222struct tu_bo *bo,223uint32_t gem_handle,224uint64_t size,225bool dump)226{227uint64_t iova = tu_gem_info(dev, gem_handle, MSM_INFO_GET_IOVA);228if (!iova) {229tu_gem_close(dev, gem_handle);230return VK_ERROR_OUT_OF_DEVICE_MEMORY;231}232233*bo = (struct tu_bo) {234.gem_handle = gem_handle,235.size = size,236.iova = iova,237};238239mtx_lock(&dev->bo_mutex);240uint32_t idx = dev->bo_count++;241242/* grow the bo list if needed */243if (idx >= dev->bo_list_size) {244uint32_t new_len = idx + 64;245struct drm_msm_gem_submit_bo *new_ptr =246vk_realloc(&dev->vk.alloc, dev->bo_list, new_len * sizeof(*dev->bo_list),2478, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);248if (!new_ptr) {249tu_gem_close(dev, gem_handle);250return VK_ERROR_OUT_OF_HOST_MEMORY;251}252253dev->bo_list = new_ptr;254dev->bo_list_size = new_len;255}256257/* grow the "bo idx" list (maps gem handles to index in the bo list) */258if (bo->gem_handle >= dev->bo_idx_size) {259uint32_t new_len = bo->gem_handle + 256;260uint32_t *new_ptr =261vk_realloc(&dev->vk.alloc, dev->bo_idx, new_len * sizeof(*dev->bo_idx),2628, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);263if (!new_ptr) {264tu_gem_close(dev, gem_handle);265return VK_ERROR_OUT_OF_HOST_MEMORY;266}267268dev->bo_idx = new_ptr;269dev->bo_idx_size = new_len;270}271272dev->bo_idx[bo->gem_handle] = idx;273dev->bo_list[idx] = (struct drm_msm_gem_submit_bo) {274.flags = MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE |275COND(dump, MSM_SUBMIT_BO_DUMP),276.handle = gem_handle,277.presumed = iova,278};279mtx_unlock(&dev->bo_mutex);280281return VK_SUCCESS;282}283284VkResult285tu_bo_init_new(struct tu_device *dev, struct tu_bo *bo, uint64_t size,286enum tu_bo_alloc_flags flags)287{288/* TODO: Choose better flags. As of 2018-11-12, freedreno/drm/msm_bo.c289* always sets `flags = MSM_BO_WC`, and we copy that behavior here.290*/291struct drm_msm_gem_new req = {292.size = size,293.flags = MSM_BO_WC294};295296if (flags & TU_BO_ALLOC_GPU_READ_ONLY)297req.flags |= MSM_BO_GPU_READONLY;298299int ret = drmCommandWriteRead(dev->fd,300DRM_MSM_GEM_NEW, &req, sizeof(req));301if (ret)302return vk_error(dev->instance, VK_ERROR_OUT_OF_DEVICE_MEMORY);303304return tu_bo_init(dev, bo, req.handle, size, flags & TU_BO_ALLOC_ALLOW_DUMP);305}306307VkResult308tu_bo_init_dmabuf(struct tu_device *dev,309struct tu_bo *bo,310uint64_t size,311int prime_fd)312{313/* lseek() to get the real size */314off_t real_size = lseek(prime_fd, 0, SEEK_END);315lseek(prime_fd, 0, SEEK_SET);316if (real_size < 0 || (uint64_t) real_size < size)317return vk_error(dev->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);318319uint32_t gem_handle;320int ret = drmPrimeFDToHandle(dev->fd, prime_fd,321&gem_handle);322if (ret)323return vk_error(dev->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);324325return tu_bo_init(dev, bo, gem_handle, size, false);326}327328int329tu_bo_export_dmabuf(struct tu_device *dev, struct tu_bo *bo)330{331int prime_fd;332int ret = drmPrimeHandleToFD(dev->fd, bo->gem_handle,333DRM_CLOEXEC, &prime_fd);334335return ret == 0 ? prime_fd : -1;336}337338VkResult339tu_bo_map(struct tu_device *dev, struct tu_bo *bo)340{341if (bo->map)342return VK_SUCCESS;343344uint64_t offset = tu_gem_info(dev, bo->gem_handle, MSM_INFO_GET_OFFSET);345if (!offset)346return vk_error(dev->instance, VK_ERROR_OUT_OF_DEVICE_MEMORY);347348/* TODO: Should we use the wrapper os_mmap() like Freedreno does? */349void *map = mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,350dev->fd, offset);351if (map == MAP_FAILED)352return vk_error(dev->instance, VK_ERROR_MEMORY_MAP_FAILED);353354bo->map = map;355return VK_SUCCESS;356}357358void359tu_bo_finish(struct tu_device *dev, struct tu_bo *bo)360{361assert(bo->gem_handle);362363if (bo->map)364munmap(bo->map, bo->size);365366mtx_lock(&dev->bo_mutex);367uint32_t idx = dev->bo_idx[bo->gem_handle];368dev->bo_count--;369dev->bo_list[idx] = dev->bo_list[dev->bo_count];370dev->bo_idx[dev->bo_list[idx].handle] = idx;371mtx_unlock(&dev->bo_mutex);372373tu_gem_close(dev, bo->gem_handle);374}375376static VkResult377tu_drm_device_init(struct tu_physical_device *device,378struct tu_instance *instance,379drmDevicePtr drm_device)380{381const char *path = drm_device->nodes[DRM_NODE_RENDER];382VkResult result = VK_SUCCESS;383drmVersionPtr version;384int fd;385int master_fd = -1;386387fd = open(path, O_RDWR | O_CLOEXEC);388if (fd < 0) {389return vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,390"failed to open device %s", path);391}392393/* Version 1.6 added SYNCOBJ support. */394const int min_version_major = 1;395const int min_version_minor = 6;396397version = drmGetVersion(fd);398if (!version) {399close(fd);400return vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,401"failed to query kernel driver version for device %s",402path);403}404405if (strcmp(version->name, "msm")) {406drmFreeVersion(version);407close(fd);408return vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,409"device %s does not use the msm kernel driver",410path);411}412413if (version->version_major != min_version_major ||414version->version_minor < min_version_minor) {415result = vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,416"kernel driver for device %s has version %d.%d, "417"but Vulkan requires version >= %d.%d",418path,419version->version_major, version->version_minor,420min_version_major, min_version_minor);421drmFreeVersion(version);422close(fd);423return result;424}425426device->msm_major_version = version->version_major;427device->msm_minor_version = version->version_minor;428429drmFreeVersion(version);430431if (instance->debug_flags & TU_DEBUG_STARTUP)432mesa_logi("Found compatible device '%s'.", path);433434device->instance = instance;435436if (instance->vk.enabled_extensions.KHR_display) {437master_fd =438open(drm_device->nodes[DRM_NODE_PRIMARY], O_RDWR | O_CLOEXEC);439if (master_fd >= 0) {440/* TODO: free master_fd is accel is not working? */441}442}443444device->master_fd = master_fd;445device->local_fd = fd;446447if (tu_drm_get_gpu_id(device, &device->gpu_id)) {448result = vk_startup_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,449"could not get GPU ID");450goto fail;451}452453if (tu_drm_get_gmem_size(device, &device->gmem_size)) {454result = vk_startup_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,455"could not get GMEM size");456goto fail;457}458459if (tu_drm_get_gmem_base(device, &device->gmem_base)) {460result = vk_startup_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,461"could not get GMEM size");462goto fail;463}464465device->heap.size = tu_get_system_heap_size();466device->heap.used = 0u;467device->heap.flags = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT;468469result = tu_physical_device_init(device, instance);470if (result == VK_SUCCESS)471return result;472473fail:474close(fd);475if (master_fd != -1)476close(master_fd);477return result;478}479480VkResult481tu_enumerate_devices(struct tu_instance *instance)482{483/* TODO: Check for more devices ? */484drmDevicePtr devices[8];485VkResult result = VK_ERROR_INCOMPATIBLE_DRIVER;486int max_devices;487488instance->physical_device_count = 0;489490max_devices = drmGetDevices2(0, devices, ARRAY_SIZE(devices));491492if (instance->debug_flags & TU_DEBUG_STARTUP) {493if (max_devices < 0)494mesa_logi("drmGetDevices2 returned error: %s\n", strerror(max_devices));495else496mesa_logi("Found %d drm nodes", max_devices);497}498499if (max_devices < 1)500return vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,501"No DRM devices found");502503for (unsigned i = 0; i < (unsigned) max_devices; i++) {504if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER &&505devices[i]->bustype == DRM_BUS_PLATFORM) {506507result = tu_drm_device_init(508instance->physical_devices + instance->physical_device_count,509instance, devices[i]);510if (result == VK_SUCCESS)511++instance->physical_device_count;512else if (result != VK_ERROR_INCOMPATIBLE_DRIVER)513break;514}515}516drmFreeDevices(devices, max_devices);517518return result;519}520521static void522tu_timeline_finish(struct tu_device *device,523struct tu_timeline *timeline)524{525list_for_each_entry_safe(struct tu_timeline_point, point,526&timeline->free_points, link) {527list_del(&point->link);528drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_DESTROY,529&(struct drm_syncobj_destroy) { .handle = point->syncobj });530531vk_free(&device->vk.alloc, point);532}533list_for_each_entry_safe(struct tu_timeline_point, point,534&timeline->points, link) {535list_del(&point->link);536drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_DESTROY,537&(struct drm_syncobj_destroy) { .handle = point->syncobj });538vk_free(&device->vk.alloc, point);539}540}541542static VkResult543sync_create(VkDevice _device,544bool signaled,545bool fence,546bool binary,547uint64_t timeline_value,548const VkAllocationCallbacks *pAllocator,549void **p_sync)550{551TU_FROM_HANDLE(tu_device, device, _device);552553struct tu_syncobj *sync =554vk_object_alloc(&device->vk, pAllocator, sizeof(*sync),555fence ? VK_OBJECT_TYPE_FENCE : VK_OBJECT_TYPE_SEMAPHORE);556if (!sync)557return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);558559if (binary) {560struct drm_syncobj_create create = {};561if (signaled)562create.flags |= DRM_SYNCOBJ_CREATE_SIGNALED;563564int ret = drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_CREATE, &create);565if (ret) {566vk_free2(&device->vk.alloc, pAllocator, sync);567return VK_ERROR_OUT_OF_HOST_MEMORY;568}569570sync->binary.permanent = create.handle;571sync->binary.temporary = 0;572sync->type = TU_SEMAPHORE_BINARY;573} else {574sync->type = TU_SEMAPHORE_TIMELINE;575sync->timeline.highest_signaled = sync->timeline.highest_submitted =576timeline_value;577list_inithead(&sync->timeline.points);578list_inithead(&sync->timeline.free_points);579}580581*p_sync = sync;582583return VK_SUCCESS;584}585586static void587sync_set_temporary(struct tu_device *device, struct tu_syncobj *sync, uint32_t syncobj)588{589if (sync->binary.temporary) {590drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_DESTROY,591&(struct drm_syncobj_destroy) { .handle = sync->binary.temporary });592}593sync->binary.temporary = syncobj;594}595596static void597sync_destroy(VkDevice _device, struct tu_syncobj *sync, const VkAllocationCallbacks *pAllocator)598{599TU_FROM_HANDLE(tu_device, device, _device);600601if (!sync)602return;603604if (sync->type == TU_SEMAPHORE_BINARY) {605sync_set_temporary(device, sync, 0);606drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_DESTROY,607&(struct drm_syncobj_destroy) { .handle = sync->binary.permanent });608} else {609tu_timeline_finish(device, &sync->timeline);610}611612vk_object_free(&device->vk, pAllocator, sync);613}614615static VkResult616sync_import(VkDevice _device, struct tu_syncobj *sync, bool temporary, bool sync_fd, int fd)617{618TU_FROM_HANDLE(tu_device, device, _device);619int ret;620621if (!sync_fd) {622uint32_t *dst = temporary ? &sync->binary.temporary : &sync->binary.permanent;623624struct drm_syncobj_handle handle = { .fd = fd };625ret = drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &handle);626if (ret)627return VK_ERROR_INVALID_EXTERNAL_HANDLE;628629if (*dst) {630drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_DESTROY,631&(struct drm_syncobj_destroy) { .handle = *dst });632}633*dst = handle.handle;634close(fd);635} else {636assert(temporary);637638struct drm_syncobj_create create = {};639640if (fd == -1)641create.flags |= DRM_SYNCOBJ_CREATE_SIGNALED;642643ret = drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_CREATE, &create);644if (ret)645return VK_ERROR_INVALID_EXTERNAL_HANDLE;646647if (fd != -1) {648ret = drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &(struct drm_syncobj_handle) {649.fd = fd,650.handle = create.handle,651.flags = DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE,652});653if (ret) {654drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_DESTROY,655&(struct drm_syncobj_destroy) { .handle = create.handle });656return VK_ERROR_INVALID_EXTERNAL_HANDLE;657}658close(fd);659}660661sync_set_temporary(device, sync, create.handle);662}663664return VK_SUCCESS;665}666667static VkResult668sync_export(VkDevice _device, struct tu_syncobj *sync, bool sync_fd, int *p_fd)669{670TU_FROM_HANDLE(tu_device, device, _device);671672struct drm_syncobj_handle handle = {673.handle = sync->binary.temporary ?: sync->binary.permanent,674.flags = COND(sync_fd, DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE),675.fd = -1,676};677int ret = drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &handle);678if (ret)679return vk_error(device->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);680681/* restore permanent payload on export */682sync_set_temporary(device, sync, 0);683684*p_fd = handle.fd;685return VK_SUCCESS;686}687688static VkSemaphoreTypeKHR689get_semaphore_type(const void *pNext, uint64_t *initial_value)690{691const VkSemaphoreTypeCreateInfoKHR *type_info =692vk_find_struct_const(pNext, SEMAPHORE_TYPE_CREATE_INFO_KHR);693694if (!type_info)695return VK_SEMAPHORE_TYPE_BINARY_KHR;696697if (initial_value)698*initial_value = type_info->initialValue;699return type_info->semaphoreType;700}701702VKAPI_ATTR VkResult VKAPI_CALL703tu_CreateSemaphore(VkDevice device,704const VkSemaphoreCreateInfo *pCreateInfo,705const VkAllocationCallbacks *pAllocator,706VkSemaphore *pSemaphore)707{708uint64_t timeline_value = 0;709VkSemaphoreTypeKHR sem_type = get_semaphore_type(pCreateInfo->pNext, &timeline_value);710711return sync_create(device, false, false, (sem_type == VK_SEMAPHORE_TYPE_BINARY_KHR),712timeline_value, pAllocator, (void**) pSemaphore);713}714715VKAPI_ATTR void VKAPI_CALL716tu_DestroySemaphore(VkDevice device, VkSemaphore sem, const VkAllocationCallbacks *pAllocator)717{718TU_FROM_HANDLE(tu_syncobj, sync, sem);719sync_destroy(device, sync, pAllocator);720}721722VKAPI_ATTR VkResult VKAPI_CALL723tu_ImportSemaphoreFdKHR(VkDevice device, const VkImportSemaphoreFdInfoKHR *info)724{725TU_FROM_HANDLE(tu_syncobj, sync, info->semaphore);726return sync_import(device, sync, info->flags & VK_SEMAPHORE_IMPORT_TEMPORARY_BIT,727info->handleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, info->fd);728}729730VKAPI_ATTR VkResult VKAPI_CALL731tu_GetSemaphoreFdKHR(VkDevice device, const VkSemaphoreGetFdInfoKHR *info, int *pFd)732{733TU_FROM_HANDLE(tu_syncobj, sync, info->semaphore);734return sync_export(device, sync,735info->handleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, pFd);736}737738VKAPI_ATTR void VKAPI_CALL739tu_GetPhysicalDeviceExternalSemaphoreProperties(740VkPhysicalDevice physicalDevice,741const VkPhysicalDeviceExternalSemaphoreInfo *pExternalSemaphoreInfo,742VkExternalSemaphoreProperties *pExternalSemaphoreProperties)743{744VkSemaphoreTypeKHR type = get_semaphore_type(pExternalSemaphoreInfo->pNext, NULL);745746if (type != VK_SEMAPHORE_TYPE_TIMELINE &&747(pExternalSemaphoreInfo->handleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT ||748pExternalSemaphoreInfo->handleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT )) {749pExternalSemaphoreProperties->exportFromImportedHandleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT | VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;750pExternalSemaphoreProperties->compatibleHandleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT | VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;751pExternalSemaphoreProperties->externalSemaphoreFeatures = VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT |752VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT;753} else {754pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;755pExternalSemaphoreProperties->compatibleHandleTypes = 0;756pExternalSemaphoreProperties->externalSemaphoreFeatures = 0;757}758}759760static VkResult761tu_queue_submit_add_timeline_wait_locked(struct tu_queue_submit* submit,762struct tu_device *device,763struct tu_syncobj *timeline,764uint64_t value)765{766if (submit->wait_timeline_count >= submit->wait_timeline_array_length) {767uint32_t new_len = MAX2(submit->wait_timeline_array_length * 2, 64);768769submit->wait_timelines = vk_realloc(&device->vk.alloc,770submit->wait_timelines,771new_len * sizeof(*submit->wait_timelines),7728, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);773774if (submit->wait_timelines == NULL)775return VK_ERROR_OUT_OF_HOST_MEMORY;776777submit->wait_timeline_values = vk_realloc(&device->vk.alloc,778submit->wait_timeline_values,779new_len * sizeof(*submit->wait_timeline_values),7808, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);781782if (submit->wait_timeline_values == NULL) {783vk_free(&device->vk.alloc, submit->wait_timelines);784return VK_ERROR_OUT_OF_HOST_MEMORY;785}786787submit->wait_timeline_array_length = new_len;788}789790submit->wait_timelines[submit->wait_timeline_count] = timeline;791submit->wait_timeline_values[submit->wait_timeline_count] = value;792793submit->wait_timeline_count++;794795return VK_SUCCESS;796}797798static VkResult799tu_queue_submit_add_timeline_signal_locked(struct tu_queue_submit* submit,800struct tu_device *device,801struct tu_syncobj *timeline,802uint64_t value)803{804if (submit->signal_timeline_count >= submit->signal_timeline_array_length) {805uint32_t new_len = MAX2(submit->signal_timeline_array_length * 2, 32);806807submit->signal_timelines = vk_realloc(&device->vk.alloc,808submit->signal_timelines,809new_len * sizeof(*submit->signal_timelines),8108, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);811812if (submit->signal_timelines == NULL)813return VK_ERROR_OUT_OF_HOST_MEMORY;814815submit->signal_timeline_values = vk_realloc(&device->vk.alloc,816submit->signal_timeline_values,817new_len * sizeof(*submit->signal_timeline_values),8188, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);819820if (submit->signal_timeline_values == NULL) {821vk_free(&device->vk.alloc, submit->signal_timelines);822return VK_ERROR_OUT_OF_HOST_MEMORY;823}824825submit->signal_timeline_array_length = new_len;826}827828submit->signal_timelines[submit->signal_timeline_count] = timeline;829submit->signal_timeline_values[submit->signal_timeline_count] = value;830831submit->signal_timeline_count++;832833return VK_SUCCESS;834}835836static VkResult837tu_queue_submit_create_locked(struct tu_queue *queue,838const VkSubmitInfo *submit_info,839const uint32_t nr_in_syncobjs,840const uint32_t nr_out_syncobjs,841const bool last_submit,842const VkPerformanceQuerySubmitInfoKHR *perf_info,843struct tu_queue_submit **submit)844{845VkResult result;846847const VkTimelineSemaphoreSubmitInfoKHR *timeline_info =848vk_find_struct_const(submit_info->pNext,849TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR);850851const uint32_t wait_values_count =852timeline_info ? timeline_info->waitSemaphoreValueCount : 0;853const uint32_t signal_values_count =854timeline_info ? timeline_info->signalSemaphoreValueCount : 0;855856const uint64_t *wait_values =857wait_values_count ? timeline_info->pWaitSemaphoreValues : NULL;858const uint64_t *signal_values =859signal_values_count ? timeline_info->pSignalSemaphoreValues : NULL;860861struct tu_queue_submit *new_submit = vk_zalloc(&queue->device->vk.alloc,862sizeof(*new_submit), 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);863864new_submit->cmd_buffer_count = submit_info->commandBufferCount;865new_submit->cmd_buffers = vk_zalloc(&queue->device->vk.alloc,866new_submit->cmd_buffer_count * sizeof(*new_submit->cmd_buffers), 8,867VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);868869if (new_submit->cmd_buffers == NULL) {870result = vk_error(queue->device->instance, VK_ERROR_OUT_OF_HOST_MEMORY)871goto fail_cmd_buffers;872}873874memcpy(new_submit->cmd_buffers, submit_info->pCommandBuffers,875new_submit->cmd_buffer_count * sizeof(*new_submit->cmd_buffers));876877new_submit->wait_semaphores = vk_zalloc(&queue->device->vk.alloc,878submit_info->waitSemaphoreCount * sizeof(*new_submit->wait_semaphores),8798, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);880if (new_submit->wait_semaphores == NULL) {881result = vk_error(queue->device->instance, VK_ERROR_OUT_OF_HOST_MEMORY)882goto fail_wait_semaphores;883}884new_submit->wait_semaphore_count = submit_info->waitSemaphoreCount;885886new_submit->signal_semaphores = vk_zalloc(&queue->device->vk.alloc,887submit_info->signalSemaphoreCount *sizeof(*new_submit->signal_semaphores),8888, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);889if (new_submit->signal_semaphores == NULL) {890result = vk_error(queue->device->instance, VK_ERROR_OUT_OF_HOST_MEMORY)891goto fail_signal_semaphores;892}893new_submit->signal_semaphore_count = submit_info->signalSemaphoreCount;894895for (uint32_t i = 0; i < submit_info->waitSemaphoreCount; i++) {896TU_FROM_HANDLE(tu_syncobj, sem, submit_info->pWaitSemaphores[i]);897new_submit->wait_semaphores[i] = sem;898899if (sem->type == TU_SEMAPHORE_TIMELINE) {900result = tu_queue_submit_add_timeline_wait_locked(new_submit,901queue->device, sem, wait_values[i]);902if (result != VK_SUCCESS)903goto fail_wait_timelines;904}905}906907for (uint32_t i = 0; i < submit_info->signalSemaphoreCount; i++) {908TU_FROM_HANDLE(tu_syncobj, sem, submit_info->pSignalSemaphores[i]);909new_submit->signal_semaphores[i] = sem;910911if (sem->type == TU_SEMAPHORE_TIMELINE) {912result = tu_queue_submit_add_timeline_signal_locked(new_submit,913queue->device, sem, signal_values[i]);914if (result != VK_SUCCESS)915goto fail_signal_timelines;916}917}918919uint32_t entry_count = 0;920for (uint32_t j = 0; j < new_submit->cmd_buffer_count; ++j) {921TU_FROM_HANDLE(tu_cmd_buffer, cmdbuf, new_submit->cmd_buffers[j]);922923if (perf_info)924entry_count++;925926entry_count += cmdbuf->cs.entry_count;927}928929new_submit->cmds = vk_zalloc(&queue->device->vk.alloc,930entry_count * sizeof(*new_submit->cmds), 8,931VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);932933if (new_submit->cmds == NULL) {934result = vk_error(queue->device->instance, VK_ERROR_OUT_OF_HOST_MEMORY)935goto fail_cmds;936}937938/* Allocate without wait timeline semaphores */939new_submit->in_syncobjs = vk_zalloc(&queue->device->vk.alloc,940(nr_in_syncobjs - new_submit->wait_timeline_count) *941sizeof(*new_submit->in_syncobjs), 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);942943if (new_submit->in_syncobjs == NULL) {944result = vk_error(queue->device->instance, VK_ERROR_OUT_OF_HOST_MEMORY)945goto fail_in_syncobjs;946}947948/* Allocate with signal timeline semaphores considered */949new_submit->out_syncobjs = vk_zalloc(&queue->device->vk.alloc,950nr_out_syncobjs * sizeof(*new_submit->out_syncobjs), 8,951VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);952953if (new_submit->out_syncobjs == NULL) {954result = vk_error(queue->device->instance, VK_ERROR_OUT_OF_HOST_MEMORY)955goto fail_out_syncobjs;956}957958new_submit->entry_count = entry_count;959new_submit->nr_in_syncobjs = nr_in_syncobjs;960new_submit->nr_out_syncobjs = nr_out_syncobjs;961new_submit->last_submit = last_submit;962new_submit->counter_pass_index = perf_info ? perf_info->counterPassIndex : ~0;963964list_inithead(&new_submit->link);965966*submit = new_submit;967968return VK_SUCCESS;969970fail_out_syncobjs:971vk_free(&queue->device->vk.alloc, new_submit->in_syncobjs);972fail_in_syncobjs:973vk_free(&queue->device->vk.alloc, new_submit->cmds);974fail_cmds:975fail_signal_timelines:976fail_wait_timelines:977vk_free(&queue->device->vk.alloc, new_submit->signal_semaphores);978fail_signal_semaphores:979vk_free(&queue->device->vk.alloc, new_submit->wait_semaphores);980fail_wait_semaphores:981vk_free(&queue->device->vk.alloc, new_submit->cmd_buffers);982fail_cmd_buffers:983return result;984}985986static void987tu_queue_submit_free(struct tu_queue *queue, struct tu_queue_submit *submit)988{989vk_free(&queue->device->vk.alloc, submit->wait_semaphores);990vk_free(&queue->device->vk.alloc, submit->signal_semaphores);991992vk_free(&queue->device->vk.alloc, submit->wait_timelines);993vk_free(&queue->device->vk.alloc, submit->wait_timeline_values);994vk_free(&queue->device->vk.alloc, submit->signal_timelines);995vk_free(&queue->device->vk.alloc, submit->signal_timeline_values);996997vk_free(&queue->device->vk.alloc, submit->cmds);998vk_free(&queue->device->vk.alloc, submit->in_syncobjs);999vk_free(&queue->device->vk.alloc, submit->out_syncobjs);1000vk_free(&queue->device->vk.alloc, submit->cmd_buffers);1001vk_free(&queue->device->vk.alloc, submit);1002}10031004static void1005tu_queue_build_msm_gem_submit_cmds(struct tu_queue *queue,1006struct tu_queue_submit *submit)1007{1008struct drm_msm_gem_submit_cmd *cmds = submit->cmds;10091010uint32_t entry_idx = 0;1011for (uint32_t j = 0; j < submit->cmd_buffer_count; ++j) {1012TU_FROM_HANDLE(tu_cmd_buffer, cmdbuf, submit->cmd_buffers[j]);1013struct tu_cs *cs = &cmdbuf->cs;1014struct tu_device *dev = queue->device;10151016if (submit->counter_pass_index != ~0) {1017struct tu_cs_entry *perf_cs_entry =1018&dev->perfcntrs_pass_cs_entries[submit->counter_pass_index];10191020cmds[entry_idx].type = MSM_SUBMIT_CMD_BUF;1021cmds[entry_idx].submit_idx =1022dev->bo_idx[perf_cs_entry->bo->gem_handle];1023cmds[entry_idx].submit_offset = perf_cs_entry->offset;1024cmds[entry_idx].size = perf_cs_entry->size;1025cmds[entry_idx].pad = 0;1026cmds[entry_idx].nr_relocs = 0;1027cmds[entry_idx++].relocs = 0;1028}10291030for (unsigned i = 0; i < cs->entry_count; ++i, ++entry_idx) {1031cmds[entry_idx].type = MSM_SUBMIT_CMD_BUF;1032cmds[entry_idx].submit_idx =1033dev->bo_idx[cs->entries[i].bo->gem_handle];1034cmds[entry_idx].submit_offset = cs->entries[i].offset;1035cmds[entry_idx].size = cs->entries[i].size;1036cmds[entry_idx].pad = 0;1037cmds[entry_idx].nr_relocs = 0;1038cmds[entry_idx].relocs = 0;1039}1040}1041}10421043static VkResult1044tu_queue_submit_locked(struct tu_queue *queue, struct tu_queue_submit *submit)1045{1046uint32_t flags = MSM_PIPE_3D0;10471048if (submit->nr_in_syncobjs)1049flags |= MSM_SUBMIT_SYNCOBJ_IN;10501051if (submit->nr_out_syncobjs)1052flags |= MSM_SUBMIT_SYNCOBJ_OUT;10531054if (submit->last_submit)1055flags |= MSM_SUBMIT_FENCE_FD_OUT;10561057mtx_lock(&queue->device->bo_mutex);10581059/* drm_msm_gem_submit_cmd requires index of bo which could change at any1060* time when bo_mutex is not locked. So we build submit cmds here the real1061* place to submit.1062*/1063tu_queue_build_msm_gem_submit_cmds(queue, submit);10641065struct drm_msm_gem_submit req = {1066.flags = flags,1067.queueid = queue->msm_queue_id,1068.bos = (uint64_t)(uintptr_t) queue->device->bo_list,1069.nr_bos = queue->device->bo_count,1070.cmds = (uint64_t)(uintptr_t)submit->cmds,1071.nr_cmds = submit->entry_count,1072.in_syncobjs = (uint64_t)(uintptr_t)submit->in_syncobjs,1073.out_syncobjs = (uint64_t)(uintptr_t)submit->out_syncobjs,1074.nr_in_syncobjs = submit->nr_in_syncobjs - submit->wait_timeline_count,1075.nr_out_syncobjs = submit->nr_out_syncobjs,1076.syncobj_stride = sizeof(struct drm_msm_gem_submit_syncobj),1077};10781079int ret = drmCommandWriteRead(queue->device->fd,1080DRM_MSM_GEM_SUBMIT,1081&req, sizeof(req));10821083mtx_unlock(&queue->device->bo_mutex);10841085if (ret)1086return tu_device_set_lost(queue->device, "submit failed: %s\n",1087strerror(errno));10881089/* restore permanent payload on wait */1090for (uint32_t i = 0; i < submit->wait_semaphore_count; i++) {1091TU_FROM_HANDLE(tu_syncobj, sem, submit->wait_semaphores[i]);1092if(sem->type == TU_SEMAPHORE_BINARY)1093sync_set_temporary(queue->device, sem, 0);1094}10951096if (submit->last_submit) {1097if (queue->fence >= 0)1098close(queue->fence);1099queue->fence = req.fence_fd;1100}11011102/* Update highest_submitted values in the timeline. */1103for (uint32_t i = 0; i < submit->signal_timeline_count; i++) {1104struct tu_syncobj *sem = submit->signal_timelines[i];1105uint64_t signal_value = submit->signal_timeline_values[i];11061107assert(signal_value > sem->timeline.highest_submitted);11081109sem->timeline.highest_submitted = signal_value;1110}11111112pthread_cond_broadcast(&queue->device->timeline_cond);11131114return VK_SUCCESS;1115}111611171118static bool1119tu_queue_submit_ready_locked(struct tu_queue_submit *submit)1120{1121for (uint32_t i = 0; i < submit->wait_timeline_count; i++) {1122if (submit->wait_timeline_values[i] >1123submit->wait_timelines[i]->timeline.highest_submitted) {1124return false;1125}1126}11271128return true;1129}11301131static VkResult1132tu_timeline_add_point_locked(struct tu_device *device,1133struct tu_timeline *timeline,1134uint64_t value,1135struct tu_timeline_point **point)1136{11371138if (list_is_empty(&timeline->free_points)) {1139*point = vk_zalloc(&device->vk.alloc, sizeof(**point), 8,1140VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);11411142if (!(*point))1143return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);11441145struct drm_syncobj_create create = {};11461147int ret = drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_CREATE, &create);1148if (ret) {1149vk_free(&device->vk.alloc, *point);1150return vk_error(device->instance, VK_ERROR_DEVICE_LOST);1151}11521153(*point)->syncobj = create.handle;11541155} else {1156*point = list_first_entry(&timeline->free_points,1157struct tu_timeline_point, link);1158list_del(&(*point)->link);1159}11601161(*point)->value = value;1162list_addtail(&(*point)->link, &timeline->points);11631164return VK_SUCCESS;1165}11661167static VkResult1168tu_queue_submit_timeline_locked(struct tu_queue *queue,1169struct tu_queue_submit *submit)1170{1171VkResult result;1172uint32_t timeline_idx =1173submit->nr_out_syncobjs - submit->signal_timeline_count;11741175for (uint32_t i = 0; i < submit->signal_timeline_count; i++) {1176struct tu_timeline *timeline = &submit->signal_timelines[i]->timeline;1177uint64_t signal_value = submit->signal_timeline_values[i];1178struct tu_timeline_point *point;11791180result = tu_timeline_add_point_locked(queue->device, timeline,1181signal_value, &point);1182if (result != VK_SUCCESS)1183return result;11841185submit->out_syncobjs[timeline_idx + i] =1186(struct drm_msm_gem_submit_syncobj) {1187.handle = point->syncobj,1188.flags = 0,1189};1190}11911192return tu_queue_submit_locked(queue, submit);1193}11941195static VkResult1196tu_queue_submit_deferred_locked(struct tu_queue *queue, uint32_t *advance)1197{1198VkResult result = VK_SUCCESS;11991200list_for_each_entry_safe(struct tu_queue_submit, submit,1201&queue->queued_submits, link) {1202if (!tu_queue_submit_ready_locked(submit))1203break;12041205(*advance)++;12061207result = tu_queue_submit_timeline_locked(queue, submit);12081209list_del(&submit->link);1210tu_queue_submit_free(queue, submit);12111212if (result != VK_SUCCESS)1213break;1214}12151216return result;1217}12181219VkResult1220tu_device_submit_deferred_locked(struct tu_device *dev)1221{1222VkResult result = VK_SUCCESS;12231224uint32_t advance = 0;1225do {1226advance = 0;1227for (uint32_t i = 0; i < dev->queue_count[0]; i++) {1228/* Try again if there's signaled submission. */1229result = tu_queue_submit_deferred_locked(&dev->queues[0][i],1230&advance);1231if (result != VK_SUCCESS)1232return result;1233}12341235} while(advance);12361237return result;1238}12391240VKAPI_ATTR VkResult VKAPI_CALL1241tu_QueueSubmit(VkQueue _queue,1242uint32_t submitCount,1243const VkSubmitInfo *pSubmits,1244VkFence _fence)1245{1246TU_FROM_HANDLE(tu_queue, queue, _queue);1247TU_FROM_HANDLE(tu_syncobj, fence, _fence);12481249for (uint32_t i = 0; i < submitCount; ++i) {1250const VkSubmitInfo *submit = pSubmits + i;1251const bool last_submit = (i == submitCount - 1);1252uint32_t out_syncobjs_size = submit->signalSemaphoreCount;12531254const VkPerformanceQuerySubmitInfoKHR *perf_info =1255vk_find_struct_const(pSubmits[i].pNext,1256PERFORMANCE_QUERY_SUBMIT_INFO_KHR);12571258if (last_submit && fence)1259out_syncobjs_size += 1;12601261pthread_mutex_lock(&queue->device->submit_mutex);1262struct tu_queue_submit *submit_req = NULL;12631264VkResult ret = tu_queue_submit_create_locked(queue, submit,1265submit->waitSemaphoreCount, out_syncobjs_size,1266last_submit, perf_info, &submit_req);12671268if (ret != VK_SUCCESS) {1269pthread_mutex_unlock(&queue->device->submit_mutex);1270return ret;1271}12721273/* note: assuming there won't be any very large semaphore counts */1274struct drm_msm_gem_submit_syncobj *in_syncobjs = submit_req->in_syncobjs;1275struct drm_msm_gem_submit_syncobj *out_syncobjs = submit_req->out_syncobjs;1276uint32_t nr_in_syncobjs = 0, nr_out_syncobjs = 0;12771278for (uint32_t i = 0; i < submit->waitSemaphoreCount; i++) {1279TU_FROM_HANDLE(tu_syncobj, sem, submit->pWaitSemaphores[i]);1280if (sem->type == TU_SEMAPHORE_TIMELINE)1281continue;12821283in_syncobjs[nr_in_syncobjs++] = (struct drm_msm_gem_submit_syncobj) {1284.handle = sem->binary.temporary ?: sem->binary.permanent,1285.flags = MSM_SUBMIT_SYNCOBJ_RESET,1286};1287}12881289for (uint32_t i = 0; i < submit->signalSemaphoreCount; i++) {1290TU_FROM_HANDLE(tu_syncobj, sem, submit->pSignalSemaphores[i]);12911292/* In case of timeline semaphores, we can defer the creation of syncobj1293* and adding it at real submit time.1294*/1295if (sem->type == TU_SEMAPHORE_TIMELINE)1296continue;12971298out_syncobjs[nr_out_syncobjs++] = (struct drm_msm_gem_submit_syncobj) {1299.handle = sem->binary.temporary ?: sem->binary.permanent,1300.flags = 0,1301};1302}13031304if (last_submit && fence) {1305out_syncobjs[nr_out_syncobjs++] = (struct drm_msm_gem_submit_syncobj) {1306.handle = fence->binary.temporary ?: fence->binary.permanent,1307.flags = 0,1308};1309}13101311/* Queue the current submit */1312list_addtail(&submit_req->link, &queue->queued_submits);1313ret = tu_device_submit_deferred_locked(queue->device);13141315pthread_mutex_unlock(&queue->device->submit_mutex);1316if (ret != VK_SUCCESS)1317return ret;1318}13191320if (!submitCount && fence) {1321/* signal fence imemediately since we don't have a submit to do it */1322drmIoctl(queue->device->fd, DRM_IOCTL_SYNCOBJ_SIGNAL, &(struct drm_syncobj_array) {1323.handles = (uintptr_t) (uint32_t[]) { fence->binary.temporary ?: fence->binary.permanent },1324.count_handles = 1,1325});1326}13271328return VK_SUCCESS;1329}13301331VKAPI_ATTR VkResult VKAPI_CALL1332tu_CreateFence(VkDevice device,1333const VkFenceCreateInfo *info,1334const VkAllocationCallbacks *pAllocator,1335VkFence *pFence)1336{1337return sync_create(device, info->flags & VK_FENCE_CREATE_SIGNALED_BIT, true, true, 0,1338pAllocator, (void**) pFence);1339}13401341VKAPI_ATTR void VKAPI_CALL1342tu_DestroyFence(VkDevice device, VkFence fence, const VkAllocationCallbacks *pAllocator)1343{1344TU_FROM_HANDLE(tu_syncobj, sync, fence);1345sync_destroy(device, sync, pAllocator);1346}13471348VKAPI_ATTR VkResult VKAPI_CALL1349tu_ImportFenceFdKHR(VkDevice device, const VkImportFenceFdInfoKHR *info)1350{1351TU_FROM_HANDLE(tu_syncobj, sync, info->fence);1352return sync_import(device, sync, info->flags & VK_FENCE_IMPORT_TEMPORARY_BIT,1353info->handleType == VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT, info->fd);1354}13551356VKAPI_ATTR VkResult VKAPI_CALL1357tu_GetFenceFdKHR(VkDevice device, const VkFenceGetFdInfoKHR *info, int *pFd)1358{1359TU_FROM_HANDLE(tu_syncobj, sync, info->fence);1360return sync_export(device, sync,1361info->handleType == VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT, pFd);1362}13631364static VkResult1365drm_syncobj_wait(struct tu_device *device,1366const uint32_t *handles, uint32_t count_handles,1367int64_t timeout_nsec, bool wait_all)1368{1369int ret = drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_WAIT, &(struct drm_syncobj_wait) {1370.handles = (uint64_t) (uintptr_t) handles,1371.count_handles = count_handles,1372.timeout_nsec = timeout_nsec,1373.flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |1374COND(wait_all, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL)1375});1376if (ret) {1377if (errno == ETIME)1378return VK_TIMEOUT;13791380assert(0);1381return VK_ERROR_DEVICE_LOST; /* TODO */1382}1383return VK_SUCCESS;1384}13851386static uint64_t1387gettime_ns(void)1388{1389struct timespec current;1390clock_gettime(CLOCK_MONOTONIC, ¤t);1391return (uint64_t)current.tv_sec * 1000000000 + current.tv_nsec;1392}13931394/* and the kernel converts it right back to relative timeout - very smart UAPI */1395static uint64_t1396absolute_timeout(uint64_t timeout)1397{1398if (timeout == 0)1399return 0;1400uint64_t current_time = gettime_ns();1401uint64_t max_timeout = (uint64_t) INT64_MAX - current_time;14021403timeout = MIN2(max_timeout, timeout);14041405return (current_time + timeout);1406}14071408VKAPI_ATTR VkResult VKAPI_CALL1409tu_WaitForFences(VkDevice _device,1410uint32_t fenceCount,1411const VkFence *pFences,1412VkBool32 waitAll,1413uint64_t timeout)1414{1415TU_FROM_HANDLE(tu_device, device, _device);14161417if (tu_device_is_lost(device))1418return VK_ERROR_DEVICE_LOST;14191420uint32_t handles[fenceCount];1421for (unsigned i = 0; i < fenceCount; ++i) {1422TU_FROM_HANDLE(tu_syncobj, fence, pFences[i]);1423handles[i] = fence->binary.temporary ?: fence->binary.permanent;1424}14251426return drm_syncobj_wait(device, handles, fenceCount, absolute_timeout(timeout), waitAll);1427}14281429VKAPI_ATTR VkResult VKAPI_CALL1430tu_ResetFences(VkDevice _device, uint32_t fenceCount, const VkFence *pFences)1431{1432TU_FROM_HANDLE(tu_device, device, _device);1433int ret;14341435uint32_t handles[fenceCount];1436for (unsigned i = 0; i < fenceCount; ++i) {1437TU_FROM_HANDLE(tu_syncobj, fence, pFences[i]);1438sync_set_temporary(device, fence, 0);1439handles[i] = fence->binary.permanent;1440}14411442ret = drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_RESET, &(struct drm_syncobj_array) {1443.handles = (uint64_t) (uintptr_t) handles,1444.count_handles = fenceCount,1445});1446if (ret) {1447tu_device_set_lost(device, "DRM_IOCTL_SYNCOBJ_RESET failure: %s",1448strerror(errno));1449}14501451return VK_SUCCESS;1452}14531454VKAPI_ATTR VkResult VKAPI_CALL1455tu_GetFenceStatus(VkDevice _device, VkFence _fence)1456{1457TU_FROM_HANDLE(tu_device, device, _device);1458TU_FROM_HANDLE(tu_syncobj, fence, _fence);1459VkResult result;14601461result = drm_syncobj_wait(device, (uint32_t[]){fence->binary.temporary ?: fence->binary.permanent}, 1, 0, false);1462if (result == VK_TIMEOUT)1463result = VK_NOT_READY;1464return result;1465}14661467int1468tu_signal_fences(struct tu_device *device, struct tu_syncobj *fence1, struct tu_syncobj *fence2)1469{1470uint32_t handles[2], count = 0;1471if (fence1)1472handles[count++] = fence1->binary.temporary ?: fence1->binary.permanent;14731474if (fence2)1475handles[count++] = fence2->binary.temporary ?: fence2->binary.permanent;14761477if (!count)1478return 0;14791480return drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_SIGNAL, &(struct drm_syncobj_array) {1481.handles = (uintptr_t) handles,1482.count_handles = count1483});1484}14851486int1487tu_syncobj_to_fd(struct tu_device *device, struct tu_syncobj *sync)1488{1489struct drm_syncobj_handle handle = { .handle = sync->binary.permanent };1490int ret;14911492ret = drmIoctl(device->fd, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &handle);14931494return ret ? -1 : handle.fd;1495}14961497static VkResult1498tu_timeline_gc_locked(struct tu_device *dev, struct tu_timeline *timeline)1499{1500VkResult result = VK_SUCCESS;15011502/* Go through every point in the timeline and check if any signaled point */1503list_for_each_entry_safe(struct tu_timeline_point, point,1504&timeline->points, link) {15051506/* If the value of the point is higher than highest_submitted,1507* the point has not been submited yet.1508*/1509if (point->wait_count || point->value > timeline->highest_submitted)1510return VK_SUCCESS;15111512result = drm_syncobj_wait(dev, (uint32_t[]){point->syncobj}, 1, 0, true);15131514if (result == VK_TIMEOUT) {1515/* This means the syncobj is still busy and it should wait1516* with timeout specified by users via vkWaitSemaphores.1517*/1518result = VK_SUCCESS;1519} else {1520timeline->highest_signaled =1521MAX2(timeline->highest_signaled, point->value);1522list_del(&point->link);1523list_add(&point->link, &timeline->free_points);1524}1525}15261527return result;1528}152915301531static VkResult1532tu_timeline_wait_locked(struct tu_device *device,1533struct tu_timeline *timeline,1534uint64_t value,1535uint64_t abs_timeout)1536{1537VkResult result;15381539while(timeline->highest_submitted < value) {1540struct timespec abstime;1541timespec_from_nsec(&abstime, abs_timeout);15421543pthread_cond_timedwait(&device->timeline_cond, &device->submit_mutex,1544&abstime);15451546if (os_time_get_nano() >= abs_timeout &&1547timeline->highest_submitted < value)1548return VK_TIMEOUT;1549}15501551/* Visit every point in the timeline and wait until1552* the highest_signaled reaches the value.1553*/1554while (1) {1555result = tu_timeline_gc_locked(device, timeline);1556if (result != VK_SUCCESS)1557return result;15581559if (timeline->highest_signaled >= value)1560return VK_SUCCESS;15611562struct tu_timeline_point *point =1563list_first_entry(&timeline->points,1564struct tu_timeline_point, link);15651566point->wait_count++;1567pthread_mutex_unlock(&device->submit_mutex);1568result = drm_syncobj_wait(device, (uint32_t[]){point->syncobj}, 1,1569abs_timeout, true);15701571pthread_mutex_lock(&device->submit_mutex);1572point->wait_count--;15731574if (result != VK_SUCCESS)1575return result;1576}15771578return result;1579}15801581static VkResult1582tu_wait_timelines(struct tu_device *device,1583const VkSemaphoreWaitInfoKHR* pWaitInfo,1584uint64_t abs_timeout)1585{1586if ((pWaitInfo->flags & VK_SEMAPHORE_WAIT_ANY_BIT_KHR) &&1587pWaitInfo->semaphoreCount > 1) {1588pthread_mutex_lock(&device->submit_mutex);15891590/* Visit every timline semaphore in the queue until timeout */1591while (1) {1592for(uint32_t i = 0; i < pWaitInfo->semaphoreCount; ++i) {1593TU_FROM_HANDLE(tu_syncobj, semaphore, pWaitInfo->pSemaphores[i]);1594VkResult result = tu_timeline_wait_locked(device,1595&semaphore->timeline, pWaitInfo->pValues[i], 0);15961597/* Returns result values including VK_SUCCESS except for VK_TIMEOUT */1598if (result != VK_TIMEOUT) {1599pthread_mutex_unlock(&device->submit_mutex);1600return result;1601}1602}16031604if (os_time_get_nano() > abs_timeout) {1605pthread_mutex_unlock(&device->submit_mutex);1606return VK_TIMEOUT;1607}1608}1609} else {1610VkResult result = VK_SUCCESS;16111612pthread_mutex_lock(&device->submit_mutex);1613for(uint32_t i = 0; i < pWaitInfo->semaphoreCount; ++i) {1614TU_FROM_HANDLE(tu_syncobj, semaphore, pWaitInfo->pSemaphores[i]);1615assert(semaphore->type == TU_SEMAPHORE_TIMELINE);16161617result = tu_timeline_wait_locked(device, &semaphore->timeline,1618pWaitInfo->pValues[i], abs_timeout);1619if (result != VK_SUCCESS)1620break;1621}1622pthread_mutex_unlock(&device->submit_mutex);16231624return result;1625}1626}162716281629VKAPI_ATTR VkResult VKAPI_CALL1630tu_GetSemaphoreCounterValue(VkDevice _device,1631VkSemaphore _semaphore,1632uint64_t* pValue)1633{1634TU_FROM_HANDLE(tu_device, device, _device);1635TU_FROM_HANDLE(tu_syncobj, semaphore, _semaphore);16361637assert(semaphore->type == TU_SEMAPHORE_TIMELINE);16381639VkResult result;16401641pthread_mutex_lock(&device->submit_mutex);16421643result = tu_timeline_gc_locked(device, &semaphore->timeline);1644*pValue = semaphore->timeline.highest_signaled;16451646pthread_mutex_unlock(&device->submit_mutex);16471648return result;1649}165016511652VKAPI_ATTR VkResult VKAPI_CALL1653tu_WaitSemaphores(VkDevice _device,1654const VkSemaphoreWaitInfoKHR* pWaitInfo,1655uint64_t timeout)1656{1657TU_FROM_HANDLE(tu_device, device, _device);16581659return tu_wait_timelines(device, pWaitInfo, absolute_timeout(timeout));1660}16611662VKAPI_ATTR VkResult VKAPI_CALL1663tu_SignalSemaphore(VkDevice _device,1664const VkSemaphoreSignalInfoKHR* pSignalInfo)1665{1666TU_FROM_HANDLE(tu_device, device, _device);1667TU_FROM_HANDLE(tu_syncobj, semaphore, pSignalInfo->semaphore);1668VkResult result;16691670assert(semaphore->type == TU_SEMAPHORE_TIMELINE);16711672pthread_mutex_lock(&device->submit_mutex);16731674result = tu_timeline_gc_locked(device, &semaphore->timeline);1675if (result != VK_SUCCESS) {1676pthread_mutex_unlock(&device->submit_mutex);1677return result;1678}16791680semaphore->timeline.highest_submitted = pSignalInfo->value;1681semaphore->timeline.highest_signaled = pSignalInfo->value;16821683result = tu_device_submit_deferred_locked(device);16841685pthread_cond_broadcast(&device->timeline_cond);1686pthread_mutex_unlock(&device->submit_mutex);16871688return result;1689}16901691#ifdef ANDROID1692#include <libsync.h>16931694VKAPI_ATTR VkResult VKAPI_CALL1695tu_QueueSignalReleaseImageANDROID(VkQueue _queue,1696uint32_t waitSemaphoreCount,1697const VkSemaphore *pWaitSemaphores,1698VkImage image,1699int *pNativeFenceFd)1700{1701TU_FROM_HANDLE(tu_queue, queue, _queue);1702VkResult result = VK_SUCCESS;17031704if (waitSemaphoreCount == 0) {1705if (pNativeFenceFd)1706*pNativeFenceFd = -1;1707return VK_SUCCESS;1708}17091710int fd = -1;17111712for (uint32_t i = 0; i < waitSemaphoreCount; ++i) {1713int tmp_fd;1714result = tu_GetSemaphoreFdKHR(1715tu_device_to_handle(queue->device),1716&(VkSemaphoreGetFdInfoKHR) {1717.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,1718.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,1719.semaphore = pWaitSemaphores[i],1720},1721&tmp_fd);1722if (result != VK_SUCCESS) {1723if (fd >= 0)1724close(fd);1725return result;1726}17271728if (fd < 0)1729fd = tmp_fd;1730else if (tmp_fd >= 0) {1731sync_accumulate("tu", &fd, tmp_fd);1732close(tmp_fd);1733}1734}17351736if (pNativeFenceFd) {1737*pNativeFenceFd = fd;1738} else if (fd >= 0) {1739close(fd);1740/* We still need to do the exports, to reset the semaphores, but1741* otherwise we don't wait on them. */1742}1743return VK_SUCCESS;1744}1745#endif174617471748