* DEALINGS IN THE SOFTWARE.
*/
-#include "tu_private.h"
-
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/mman.h>
#include <xf86drm.h>
+#include "vk_util.h"
+
#include "drm-uapi/msm_drm.h"
+#include "tu_private.h"
+
static int
tu_drm_get_param(const struct tu_physical_device *dev,
uint32_t param,
&queue_id, sizeof(uint32_t));
}
-/**
- * Return gem handle on success. Return 0 on failure.
- */
-static uint32_t
-tu_gem_new(const struct tu_device *dev, uint64_t size, uint32_t flags)
-{
- struct drm_msm_gem_new req = {
- .size = size,
- .flags = flags,
- };
-
- int ret = drmCommandWriteRead(dev->physical_device->local_fd,
- DRM_MSM_GEM_NEW, &req, sizeof(req));
- if (ret)
- return 0;
-
- return req.handle;
-}
-
-static uint32_t
-tu_gem_import_dmabuf(const struct tu_device *dev, int prime_fd, uint64_t size)
-{
- /* 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 0;
-
- uint32_t gem_handle;
- int ret = drmPrimeFDToHandle(dev->physical_device->local_fd, prime_fd,
- &gem_handle);
- if (ret)
- return 0;
-
- return gem_handle;
-}
-
-static int
-tu_gem_export_dmabuf(const struct tu_device *dev, uint32_t gem_handle)
-{
- int prime_fd;
- int ret = drmPrimeHandleToFD(dev->physical_device->local_fd, gem_handle,
- DRM_CLOEXEC, &prime_fd);
-
- return ret == 0 ? prime_fd : -1;
-}
-
static void
tu_gem_close(const struct tu_device *dev, uint32_t gem_handle)
{
return req.value;
}
-/** Returns the offset for CPU-side mmap of the gem handle.
- *
- * Returns 0 on error (an invalid mmap offset in the DRM UBI).
- */
-static uint64_t
-tu_gem_info_offset(const struct tu_device *dev, uint32_t gem_handle)
-{
- return tu_gem_info(dev, gem_handle, MSM_INFO_GET_OFFSET);
-}
-
-/** Returns the the iova of the BO in GPU memory.
- *
- * Returns 0 on error (an invalid iova in the MSM DRM UABI).
- */
-static uint64_t
-tu_gem_info_iova(const struct tu_device *dev, uint32_t gem_handle)
-{
- return tu_gem_info(dev, gem_handle, MSM_INFO_GET_IOVA);
-}
-
static VkResult
tu_bo_init(struct tu_device *dev,
struct tu_bo *bo,
uint32_t gem_handle,
uint64_t size)
{
- uint64_t iova = tu_gem_info_iova(dev, gem_handle);
- if (!iova)
+ 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,
/* 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.
*/
- uint32_t gem_handle = tu_gem_new(dev, size, MSM_BO_WC);
- if (!gem_handle)
- return vk_error(dev->instance, VK_ERROR_OUT_OF_DEVICE_MEMORY);
+ struct drm_msm_gem_new req = {
+ .size = size,
+ .flags = MSM_BO_WC
+ };
- VkResult result = tu_bo_init(dev, bo, gem_handle, size);
- if (result != VK_SUCCESS) {
- tu_gem_close(dev, gem_handle);
- return vk_error(dev->instance, result);
- }
+ int ret = drmCommandWriteRead(dev->physical_device->local_fd,
+ DRM_MSM_GEM_NEW, &req, sizeof(req));
+ if (ret)
+ return vk_error(dev->instance, VK_ERROR_OUT_OF_DEVICE_MEMORY);
- return VK_SUCCESS;
+ return tu_bo_init(dev, bo, req.handle, size);
}
VkResult
tu_bo_init_dmabuf(struct tu_device *dev,
struct tu_bo *bo,
uint64_t size,
- int fd)
+ int prime_fd)
{
- uint32_t gem_handle = tu_gem_import_dmabuf(dev, fd, size);
- if (!gem_handle)
+ /* 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);
- VkResult result = tu_bo_init(dev, bo, gem_handle, size);
- if (result != VK_SUCCESS) {
- tu_gem_close(dev, gem_handle);
- return vk_error(dev->instance, result);
- }
+ uint32_t gem_handle;
+ int ret = drmPrimeFDToHandle(dev->physical_device->local_fd, prime_fd,
+ &gem_handle);
+ if (ret)
+ return vk_error(dev->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
- return VK_SUCCESS;
+ return tu_bo_init(dev, bo, gem_handle, size);
}
int
tu_bo_export_dmabuf(struct tu_device *dev, struct tu_bo *bo)
{
- return tu_gem_export_dmabuf(dev, bo->gem_handle);
+ int prime_fd;
+ int ret = drmPrimeHandleToFD(dev->physical_device->local_fd, bo->gem_handle,
+ DRM_CLOEXEC, &prime_fd);
+
+ return ret == 0 ? prime_fd : -1;
}
VkResult
if (bo->map)
return VK_SUCCESS;
- uint64_t offset = tu_gem_info_offset(dev, bo->gem_handle);
+ 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);
return result;
}
+// Queue semaphore functions
+
+static void
+tu_semaphore_part_destroy(struct tu_device *device,
+ struct tu_semaphore_part *part)
+{
+ switch(part->kind) {
+ case TU_SEMAPHORE_NONE:
+ break;
+ case TU_SEMAPHORE_SYNCOBJ:
+ drmSyncobjDestroy(device->physical_device->local_fd, part->syncobj);
+ break;
+ }
+ part->kind = TU_SEMAPHORE_NONE;
+}
+
+static void
+tu_semaphore_remove_temp(struct tu_device *device,
+ struct tu_semaphore *sem)
+{
+ if (sem->temporary.kind != TU_SEMAPHORE_NONE) {
+ tu_semaphore_part_destroy(device, &sem->temporary);
+ }
+}
+
+static VkResult
+tu_get_semaphore_syncobjs(const VkSemaphore *sems,
+ uint32_t sem_count,
+ bool wait,
+ struct drm_msm_gem_submit_syncobj **out,
+ uint32_t *out_count)
+{
+ uint32_t syncobj_count = 0;
+ struct drm_msm_gem_submit_syncobj *syncobjs;
+
+ for (uint32_t i = 0; i < sem_count; ++i) {
+ TU_FROM_HANDLE(tu_semaphore, sem, sems[i]);
+
+ struct tu_semaphore_part *part =
+ sem->temporary.kind != TU_SEMAPHORE_NONE ?
+ &sem->temporary : &sem->permanent;
+
+ if (part->kind == TU_SEMAPHORE_SYNCOBJ)
+ ++syncobj_count;
+ }
+
+ *out = NULL;
+ *out_count = syncobj_count;
+ if (!syncobj_count)
+ return VK_SUCCESS;
+
+ *out = syncobjs = calloc(syncobj_count, sizeof (*syncobjs));
+ if (!syncobjs)
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+ for (uint32_t i = 0, j = 0; i < sem_count; ++i) {
+ TU_FROM_HANDLE(tu_semaphore, sem, sems[i]);
+
+ struct tu_semaphore_part *part =
+ sem->temporary.kind != TU_SEMAPHORE_NONE ?
+ &sem->temporary : &sem->permanent;
+
+ if (part->kind == TU_SEMAPHORE_SYNCOBJ) {
+ syncobjs[j].handle = part->syncobj;
+ syncobjs[j].flags = wait ? MSM_SUBMIT_SYNCOBJ_RESET : 0;
+ ++j;
+ }
+ }
+
+ return VK_SUCCESS;
+}
+
+static void
+tu_semaphores_remove_temp(struct tu_device *device,
+ const VkSemaphore *sems,
+ uint32_t sem_count)
+{
+ for (uint32_t i = 0; i < sem_count; ++i) {
+ TU_FROM_HANDLE(tu_semaphore, sem, sems[i]);
+ tu_semaphore_remove_temp(device, sem);
+ }
+}
+
+VkResult
+tu_CreateSemaphore(VkDevice _device,
+ const VkSemaphoreCreateInfo *pCreateInfo,
+ const VkAllocationCallbacks *pAllocator,
+ VkSemaphore *pSemaphore)
+{
+ TU_FROM_HANDLE(tu_device, device, _device);
+
+ struct tu_semaphore *sem =
+ vk_object_alloc(&device->vk, pAllocator, sizeof(*sem),
+ VK_OBJECT_TYPE_SEMAPHORE);
+ if (!sem)
+ return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
+
+ const VkExportSemaphoreCreateInfo *export =
+ vk_find_struct_const(pCreateInfo->pNext, EXPORT_SEMAPHORE_CREATE_INFO);
+ VkExternalSemaphoreHandleTypeFlags handleTypes =
+ export ? export->handleTypes : 0;
+
+ sem->permanent.kind = TU_SEMAPHORE_NONE;
+ sem->temporary.kind = TU_SEMAPHORE_NONE;
+
+ if (handleTypes) {
+ if (drmSyncobjCreate(device->physical_device->local_fd, 0, &sem->permanent.syncobj) < 0) {
+ vk_free2(&device->vk.alloc, pAllocator, sem);
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
+ }
+ sem->permanent.kind = TU_SEMAPHORE_SYNCOBJ;
+ }
+ *pSemaphore = tu_semaphore_to_handle(sem);
+ return VK_SUCCESS;
+}
+
+void
+tu_DestroySemaphore(VkDevice _device,
+ VkSemaphore _semaphore,
+ const VkAllocationCallbacks *pAllocator)
+{
+ TU_FROM_HANDLE(tu_device, device, _device);
+ TU_FROM_HANDLE(tu_semaphore, sem, _semaphore);
+ if (!_semaphore)
+ return;
+
+ tu_semaphore_part_destroy(device, &sem->permanent);
+ tu_semaphore_part_destroy(device, &sem->temporary);
+
+ vk_object_free(&device->vk, pAllocator, sem);
+}
+
+VkResult
+tu_ImportSemaphoreFdKHR(VkDevice _device,
+ const VkImportSemaphoreFdInfoKHR *pImportSemaphoreFdInfo)
+{
+ TU_FROM_HANDLE(tu_device, device, _device);
+ TU_FROM_HANDLE(tu_semaphore, sem, pImportSemaphoreFdInfo->semaphore);
+ int ret;
+ struct tu_semaphore_part *dst = NULL;
+
+ if (pImportSemaphoreFdInfo->flags & VK_SEMAPHORE_IMPORT_TEMPORARY_BIT) {
+ dst = &sem->temporary;
+ } else {
+ dst = &sem->permanent;
+ }
+
+ uint32_t syncobj = dst->kind == TU_SEMAPHORE_SYNCOBJ ? dst->syncobj : 0;
+
+ switch(pImportSemaphoreFdInfo->handleType) {
+ case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT: {
+ uint32_t old_syncobj = syncobj;
+ ret = drmSyncobjFDToHandle(device->physical_device->local_fd, pImportSemaphoreFdInfo->fd, &syncobj);
+ if (ret == 0) {
+ close(pImportSemaphoreFdInfo->fd);
+ if (old_syncobj)
+ drmSyncobjDestroy(device->physical_device->local_fd, old_syncobj);
+ }
+ break;
+ }
+ case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT: {
+ if (!syncobj) {
+ ret = drmSyncobjCreate(device->physical_device->local_fd, 0, &syncobj);
+ if (ret)
+ break;
+ }
+ if (pImportSemaphoreFdInfo->fd == -1) {
+ ret = drmSyncobjSignal(device->physical_device->local_fd, &syncobj, 1);
+ } else {
+ ret = drmSyncobjImportSyncFile(device->physical_device->local_fd, syncobj, pImportSemaphoreFdInfo->fd);
+ }
+ if (!ret)
+ close(pImportSemaphoreFdInfo->fd);
+ break;
+ }
+ default:
+ unreachable("Unhandled semaphore handle type");
+ }
+
+ if (ret) {
+ return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+ }
+ dst->syncobj = syncobj;
+ dst->kind = TU_SEMAPHORE_SYNCOBJ;
+
+ return VK_SUCCESS;
+}
+
+VkResult
+tu_GetSemaphoreFdKHR(VkDevice _device,
+ const VkSemaphoreGetFdInfoKHR *pGetFdInfo,
+ int *pFd)
+{
+ TU_FROM_HANDLE(tu_device, device, _device);
+ TU_FROM_HANDLE(tu_semaphore, sem, pGetFdInfo->semaphore);
+ int ret;
+ uint32_t syncobj_handle;
+
+ if (sem->temporary.kind != TU_SEMAPHORE_NONE) {
+ assert(sem->temporary.kind == TU_SEMAPHORE_SYNCOBJ);
+ syncobj_handle = sem->temporary.syncobj;
+ } else {
+ assert(sem->permanent.kind == TU_SEMAPHORE_SYNCOBJ);
+ syncobj_handle = sem->permanent.syncobj;
+ }
+
+ switch(pGetFdInfo->handleType) {
+ case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT:
+ ret = drmSyncobjHandleToFD(device->physical_device->local_fd, syncobj_handle, pFd);
+ break;
+ case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT:
+ ret = drmSyncobjExportSyncFile(device->physical_device->local_fd, syncobj_handle, pFd);
+ if (!ret) {
+ if (sem->temporary.kind != TU_SEMAPHORE_NONE) {
+ tu_semaphore_part_destroy(device, &sem->temporary);
+ } else {
+ drmSyncobjReset(device->physical_device->local_fd, &syncobj_handle, 1);
+ }
+ }
+ break;
+ default:
+ unreachable("Unhandled semaphore handle type");
+ }
+
+ if (ret)
+ return vk_error(device->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
+ return VK_SUCCESS;
+}
+
+static bool tu_has_syncobj(struct tu_physical_device *pdev)
+{
+ uint64_t value;
+ if (drmGetCap(pdev->local_fd, DRM_CAP_SYNCOBJ, &value))
+ return false;
+ return value && pdev->msm_major_version == 1 && pdev->msm_minor_version >= 6;
+}
+
+void
+tu_GetPhysicalDeviceExternalSemaphoreProperties(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceExternalSemaphoreInfo *pExternalSemaphoreInfo,
+ VkExternalSemaphoreProperties *pExternalSemaphoreProperties)
+{
+ TU_FROM_HANDLE(tu_physical_device, pdev, physicalDevice);
+
+ if (tu_has_syncobj(pdev) &&
+ (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;
+ }
+}
+
+VkResult
+tu_QueueSubmit(VkQueue _queue,
+ uint32_t submitCount,
+ const VkSubmitInfo *pSubmits,
+ VkFence _fence)
+{
+ TU_FROM_HANDLE(tu_queue, queue, _queue);
+ VkResult result;
+
+ for (uint32_t i = 0; i < submitCount; ++i) {
+ const VkSubmitInfo *submit = pSubmits + i;
+ const bool last_submit = (i == submitCount - 1);
+ struct drm_msm_gem_submit_syncobj *in_syncobjs = NULL, *out_syncobjs = NULL;
+ uint32_t nr_in_syncobjs, nr_out_syncobjs;
+ struct tu_bo_list bo_list;
+ tu_bo_list_init(&bo_list);
+
+ result = tu_get_semaphore_syncobjs(pSubmits[i].pWaitSemaphores,
+ pSubmits[i].waitSemaphoreCount,
+ false, &in_syncobjs, &nr_in_syncobjs);
+ if (result != VK_SUCCESS) {
+ return tu_device_set_lost(queue->device,
+ "failed to allocate space for semaphore submission\n");
+ }
+
+ result = tu_get_semaphore_syncobjs(pSubmits[i].pSignalSemaphores,
+ pSubmits[i].signalSemaphoreCount,
+ false, &out_syncobjs, &nr_out_syncobjs);
+ if (result != VK_SUCCESS) {
+ free(in_syncobjs);
+ return tu_device_set_lost(queue->device,
+ "failed to allocate space for semaphore submission\n");
+ }
+
+ uint32_t entry_count = 0;
+ for (uint32_t j = 0; j < submit->commandBufferCount; ++j) {
+ TU_FROM_HANDLE(tu_cmd_buffer, cmdbuf, submit->pCommandBuffers[j]);
+ entry_count += cmdbuf->cs.entry_count;
+ }
+
+ struct drm_msm_gem_submit_cmd cmds[entry_count];
+ uint32_t entry_idx = 0;
+ for (uint32_t j = 0; j < submit->commandBufferCount; ++j) {
+ TU_FROM_HANDLE(tu_cmd_buffer, cmdbuf, submit->pCommandBuffers[j]);
+ struct tu_cs *cs = &cmdbuf->cs;
+ for (unsigned i = 0; i < cs->entry_count; ++i, ++entry_idx) {
+ cmds[entry_idx].type = MSM_SUBMIT_CMD_BUF;
+ cmds[entry_idx].submit_idx =
+ tu_bo_list_add(&bo_list, cs->entries[i].bo,
+ MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_DUMP);
+ 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;
+ }
+
+ tu_bo_list_merge(&bo_list, &cmdbuf->bo_list);
+ }
+
+ uint32_t flags = MSM_PIPE_3D0;
+ if (nr_in_syncobjs) {
+ flags |= MSM_SUBMIT_SYNCOBJ_IN;
+ }
+ if (nr_out_syncobjs) {
+ flags |= MSM_SUBMIT_SYNCOBJ_OUT;
+ }
+
+ if (last_submit) {
+ flags |= MSM_SUBMIT_FENCE_FD_OUT;
+ }
+
+ struct drm_msm_gem_submit req = {
+ .flags = flags,
+ .queueid = queue->msm_queue_id,
+ .bos = (uint64_t)(uintptr_t) bo_list.bo_infos,
+ .nr_bos = bo_list.count,
+ .cmds = (uint64_t)(uintptr_t)cmds,
+ .nr_cmds = entry_count,
+ .in_syncobjs = (uint64_t)(uintptr_t)in_syncobjs,
+ .out_syncobjs = (uint64_t)(uintptr_t)out_syncobjs,
+ .nr_in_syncobjs = nr_in_syncobjs,
+ .nr_out_syncobjs = nr_out_syncobjs,
+ .syncobj_stride = sizeof(struct drm_msm_gem_submit_syncobj),
+ };
+
+ int ret = drmCommandWriteRead(queue->device->physical_device->local_fd,
+ DRM_MSM_GEM_SUBMIT,
+ &req, sizeof(req));
+ if (ret) {
+ free(in_syncobjs);
+ free(out_syncobjs);
+ return tu_device_set_lost(queue->device, "submit failed: %s\n",
+ strerror(errno));
+ }
+
+ tu_bo_list_destroy(&bo_list);
+ free(in_syncobjs);
+ free(out_syncobjs);
+
+ tu_semaphores_remove_temp(queue->device, pSubmits[i].pWaitSemaphores,
+ pSubmits[i].waitSemaphoreCount);
+ if (last_submit) {
+ /* no need to merge fences as queue execution is serialized */
+ tu_fence_update_fd(&queue->submit_fence, req.fence_fd);
+ } else if (last_submit) {
+ close(req.fence_fd);
+ }
+ }
+
+ if (_fence != VK_NULL_HANDLE) {
+ TU_FROM_HANDLE(tu_fence, fence, _fence);
+ tu_fence_copy(fence, &queue->submit_fence);
+ }
+
+ return VK_SUCCESS;
+}