/* * Copyright © 2016 Red Hat. * Copyright © 2016 Bas Nieuwenhuizen * * based in part on anv driver which is: * 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 "tu_private.h" #include #include #include #include #include #include #include #include #include "compiler/glsl_types.h" #include "util/debug.h" #include "util/disk_cache.h" #include "util/u_atomic.h" #include "vk_format.h" #include "vk_util.h" #include "drm-uapi/msm_drm.h" /* for fd_get_driver/device_uuid() */ #include "freedreno/common/freedreno_uuid.h" static void tu_semaphore_remove_temp(struct tu_device *device, struct tu_semaphore *sem); static int tu_device_get_cache_uuid(uint16_t family, void *uuid) { uint32_t mesa_timestamp; uint16_t f = family; memset(uuid, 0, VK_UUID_SIZE); if (!disk_cache_get_function_timestamp(tu_device_get_cache_uuid, &mesa_timestamp)) return -1; memcpy(uuid, &mesa_timestamp, 4); memcpy((char *) uuid + 4, &f, 2); snprintf((char *) uuid + 6, VK_UUID_SIZE - 10, "tu"); return 0; } 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) return VK_ERROR_OUT_OF_DEVICE_MEMORY; *bo = (struct tu_bo) { .gem_handle = gem_handle, .size = size, .iova = iova, }; return VK_SUCCESS; } VkResult tu_bo_init_new(struct tu_device *dev, struct tu_bo *bo, uint64_t size) { /* 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); 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); } return VK_SUCCESS; } VkResult tu_bo_init_dmabuf(struct tu_device *dev, struct tu_bo *bo, uint64_t size, int fd) { uint32_t gem_handle = tu_gem_import_dmabuf(dev, fd, size); if (!gem_handle) 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); } return VK_SUCCESS; } int tu_bo_export_dmabuf(struct tu_device *dev, struct tu_bo *bo) { return tu_gem_export_dmabuf(dev, bo->gem_handle); } VkResult tu_bo_map(struct tu_device *dev, struct tu_bo *bo) { if (bo->map) return VK_SUCCESS; uint64_t offset = tu_gem_info_offset(dev, bo->gem_handle); 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->physical_device->local_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); tu_gem_close(dev, bo->gem_handle); } static VkResult tu_physical_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_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER, "failed to open device %s", path); } /* Version 1.3 added MSM_INFO_IOVA. */ const int min_version_major = 1; const int min_version_minor = 3; version = drmGetVersion(fd); if (!version) { close(fd); return vk_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_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_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) tu_logi("Found compatible device '%s'.", path); vk_object_base_init(NULL, &device->base, VK_OBJECT_TYPE_PHYSICAL_DEVICE); device->instance = instance; assert(strlen(path) < ARRAY_SIZE(device->path)); strncpy(device->path, path, ARRAY_SIZE(device->path)); if (instance->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->gpu_id)) { if (instance->debug_flags & TU_DEBUG_STARTUP) tu_logi("Could not query the GPU ID"); result = vk_errorf(instance, VK_ERROR_INITIALIZATION_FAILED, "could not get GPU ID"); goto fail; } if (tu_drm_get_gmem_size(device, &device->gmem_size)) { if (instance->debug_flags & TU_DEBUG_STARTUP) tu_logi("Could not query the GMEM size"); result = vk_errorf(instance, VK_ERROR_INITIALIZATION_FAILED, "could not get GMEM size"); goto fail; } if (tu_drm_get_gmem_base(device, &device->gmem_base)) { if (instance->debug_flags & TU_DEBUG_STARTUP) tu_logi("Could not query the GMEM size"); result = vk_errorf(instance, VK_ERROR_INITIALIZATION_FAILED, "could not get GMEM size"); goto fail; } memset(device->name, 0, sizeof(device->name)); sprintf(device->name, "FD%d", device->gpu_id); switch (device->gpu_id) { case 618: device->ccu_offset_gmem = 0x7c000; /* 0x7e000 in some cases? */ device->ccu_offset_bypass = 0x10000; device->tile_align_w = 32; device->magic.PC_UNKNOWN_9805 = 0x0; device->magic.SP_UNKNOWN_A0F8 = 0x0; break; case 630: case 640: device->ccu_offset_gmem = 0xf8000; device->ccu_offset_bypass = 0x20000; device->tile_align_w = 32; device->magic.PC_UNKNOWN_9805 = 0x1; device->magic.SP_UNKNOWN_A0F8 = 0x1; break; case 650: device->ccu_offset_gmem = 0x114000; device->ccu_offset_bypass = 0x30000; device->tile_align_w = 96; device->magic.PC_UNKNOWN_9805 = 0x2; device->magic.SP_UNKNOWN_A0F8 = 0x2; break; default: result = vk_errorf(instance, VK_ERROR_INITIALIZATION_FAILED, "device %s is unsupported", device->name); goto fail; } if (tu_device_get_cache_uuid(device->gpu_id, device->cache_uuid)) { result = vk_errorf(instance, VK_ERROR_INITIALIZATION_FAILED, "cannot generate UUID"); goto fail; } /* The gpu id is already embedded in the uuid so we just pass "tu" * when creating the cache. */ char buf[VK_UUID_SIZE * 2 + 1]; disk_cache_format_hex_id(buf, device->cache_uuid, VK_UUID_SIZE * 2); device->disk_cache = disk_cache_create(device->name, buf, 0); fprintf(stderr, "WARNING: tu is not a conformant vulkan implementation, " "testing use only.\n"); fd_get_driver_uuid(device->driver_uuid); fd_get_device_uuid(device->device_uuid, device->gpu_id); tu_physical_device_get_supported_extensions(device, &device->supported_extensions); if (result != VK_SUCCESS) { vk_error(instance, result); goto fail; } result = tu_wsi_init(device); if (result != VK_SUCCESS) { vk_error(instance, result); goto fail; } return VK_SUCCESS; fail: close(fd); if (master_fd != -1) close(master_fd); return result; } static void tu_physical_device_finish(struct tu_physical_device *device) { tu_wsi_finish(device); disk_cache_destroy(device->disk_cache); close(device->local_fd); if (device->master_fd != -1) close(device->master_fd); vk_object_base_finish(&device->base); } static VKAPI_ATTR void * default_alloc_func(void *pUserData, size_t size, size_t align, VkSystemAllocationScope allocationScope) { return malloc(size); } static VKAPI_ATTR void * default_realloc_func(void *pUserData, void *pOriginal, size_t size, size_t align, VkSystemAllocationScope allocationScope) { return realloc(pOriginal, size); } static VKAPI_ATTR void default_free_func(void *pUserData, void *pMemory) { free(pMemory); } static const VkAllocationCallbacks default_alloc = { .pUserData = NULL, .pfnAllocation = default_alloc_func, .pfnReallocation = default_realloc_func, .pfnFree = default_free_func, }; static const struct debug_control tu_debug_options[] = { { "startup", TU_DEBUG_STARTUP }, { "nir", TU_DEBUG_NIR }, { "ir3", TU_DEBUG_IR3 }, { "nobin", TU_DEBUG_NOBIN }, { "sysmem", TU_DEBUG_SYSMEM }, { "forcebin", TU_DEBUG_FORCEBIN }, { "noubwc", TU_DEBUG_NOUBWC }, { NULL, 0 } }; const char * tu_get_debug_option_name(int id) { assert(id < ARRAY_SIZE(tu_debug_options) - 1); return tu_debug_options[id].string; } static int tu_get_instance_extension_index(const char *name) { for (unsigned i = 0; i < TU_INSTANCE_EXTENSION_COUNT; ++i) { if (strcmp(name, tu_instance_extensions[i].extensionName) == 0) return i; } return -1; } VkResult tu_CreateInstance(const VkInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkInstance *pInstance) { struct tu_instance *instance; VkResult result; assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO); uint32_t client_version; if (pCreateInfo->pApplicationInfo && pCreateInfo->pApplicationInfo->apiVersion != 0) { client_version = pCreateInfo->pApplicationInfo->apiVersion; } else { tu_EnumerateInstanceVersion(&client_version); } instance = vk_zalloc2(&default_alloc, pAllocator, sizeof(*instance), 8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); if (!instance) return vk_error(NULL, VK_ERROR_OUT_OF_HOST_MEMORY); vk_object_base_init(NULL, &instance->base, VK_OBJECT_TYPE_INSTANCE); if (pAllocator) instance->alloc = *pAllocator; else instance->alloc = default_alloc; instance->api_version = client_version; instance->physical_device_count = -1; instance->debug_flags = parse_debug_string(getenv("TU_DEBUG"), tu_debug_options); if (instance->debug_flags & TU_DEBUG_STARTUP) tu_logi("Created an instance"); for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) { const char *ext_name = pCreateInfo->ppEnabledExtensionNames[i]; int index = tu_get_instance_extension_index(ext_name); if (index < 0 || !tu_instance_extensions_supported.extensions[index]) { vk_object_base_finish(&instance->base); vk_free2(&default_alloc, pAllocator, instance); return vk_error(instance, VK_ERROR_EXTENSION_NOT_PRESENT); } instance->enabled_extensions.extensions[index] = true; } result = vk_debug_report_instance_init(&instance->debug_report_callbacks); if (result != VK_SUCCESS) { vk_object_base_finish(&instance->base); vk_free2(&default_alloc, pAllocator, instance); return vk_error(instance, result); } glsl_type_singleton_init_or_ref(); VG(VALGRIND_CREATE_MEMPOOL(instance, 0, false)); *pInstance = tu_instance_to_handle(instance); return VK_SUCCESS; } void tu_DestroyInstance(VkInstance _instance, const VkAllocationCallbacks *pAllocator) { TU_FROM_HANDLE(tu_instance, instance, _instance); if (!instance) return; for (int i = 0; i < instance->physical_device_count; ++i) { tu_physical_device_finish(instance->physical_devices + i); } VG(VALGRIND_DESTROY_MEMPOOL(instance)); glsl_type_singleton_decref(); vk_debug_report_instance_destroy(&instance->debug_report_callbacks); vk_object_base_finish(&instance->base); vk_free(&instance->alloc, instance); } static 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) tu_logi("drmGetDevices2 returned error: %s\n", strerror(max_devices)); else tu_logi("Found %d drm nodes", max_devices); } if (max_devices < 1) return vk_error(instance, VK_ERROR_INCOMPATIBLE_DRIVER); 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_physical_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; } VkResult tu_EnumeratePhysicalDevices(VkInstance _instance, uint32_t *pPhysicalDeviceCount, VkPhysicalDevice *pPhysicalDevices) { TU_FROM_HANDLE(tu_instance, instance, _instance); VK_OUTARRAY_MAKE(out, pPhysicalDevices, pPhysicalDeviceCount); VkResult result; if (instance->physical_device_count < 0) { result = tu_enumerate_devices(instance); if (result != VK_SUCCESS && result != VK_ERROR_INCOMPATIBLE_DRIVER) return result; } for (uint32_t i = 0; i < instance->physical_device_count; ++i) { vk_outarray_append(&out, p) { *p = tu_physical_device_to_handle(instance->physical_devices + i); } } return vk_outarray_status(&out); } VkResult tu_EnumeratePhysicalDeviceGroups( VkInstance _instance, uint32_t *pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties *pPhysicalDeviceGroupProperties) { TU_FROM_HANDLE(tu_instance, instance, _instance); VK_OUTARRAY_MAKE(out, pPhysicalDeviceGroupProperties, pPhysicalDeviceGroupCount); VkResult result; if (instance->physical_device_count < 0) { result = tu_enumerate_devices(instance); if (result != VK_SUCCESS && result != VK_ERROR_INCOMPATIBLE_DRIVER) return result; } for (uint32_t i = 0; i < instance->physical_device_count; ++i) { vk_outarray_append(&out, p) { p->physicalDeviceCount = 1; p->physicalDevices[0] = tu_physical_device_to_handle(instance->physical_devices + i); p->subsetAllocation = false; } } return vk_outarray_status(&out); } void tu_GetPhysicalDeviceFeatures(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures *pFeatures) { memset(pFeatures, 0, sizeof(*pFeatures)); *pFeatures = (VkPhysicalDeviceFeatures) { .robustBufferAccess = true, .fullDrawIndexUint32 = true, .imageCubeArray = true, .independentBlend = true, .geometryShader = true, .tessellationShader = true, .sampleRateShading = true, .dualSrcBlend = true, .logicOp = true, .multiDrawIndirect = true, .drawIndirectFirstInstance = true, .depthClamp = true, .depthBiasClamp = true, .fillModeNonSolid = true, .depthBounds = true, .wideLines = false, .largePoints = true, .alphaToOne = true, .multiViewport = false, .samplerAnisotropy = true, .textureCompressionETC2 = true, .textureCompressionASTC_LDR = true, .textureCompressionBC = true, .occlusionQueryPrecise = true, .pipelineStatisticsQuery = false, .vertexPipelineStoresAndAtomics = false, .fragmentStoresAndAtomics = false, .shaderTessellationAndGeometryPointSize = false, .shaderImageGatherExtended = false, .shaderStorageImageExtendedFormats = false, .shaderStorageImageMultisample = false, .shaderUniformBufferArrayDynamicIndexing = false, .shaderSampledImageArrayDynamicIndexing = false, .shaderStorageBufferArrayDynamicIndexing = false, .shaderStorageImageArrayDynamicIndexing = false, .shaderStorageImageReadWithoutFormat = false, .shaderStorageImageWriteWithoutFormat = false, .shaderClipDistance = false, .shaderCullDistance = false, .shaderFloat64 = false, .shaderInt64 = false, .shaderInt16 = false, .sparseBinding = false, .variableMultisampleRate = false, .inheritedQueries = false, }; } void tu_GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2 *pFeatures) { vk_foreach_struct(ext, pFeatures->pNext) { switch (ext->sType) { case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES: { VkPhysicalDeviceVulkan11Features *features = (void *) ext; features->storageBuffer16BitAccess = false; features->uniformAndStorageBuffer16BitAccess = false; features->storagePushConstant16 = false; features->storageInputOutput16 = false; features->multiview = false; features->multiviewGeometryShader = false; features->multiviewTessellationShader = false; features->variablePointersStorageBuffer = true; features->variablePointers = true; features->protectedMemory = false; features->samplerYcbcrConversion = true; features->shaderDrawParameters = true; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES: { VkPhysicalDeviceVulkan12Features *features = (void *) ext; features->samplerMirrorClampToEdge = true; features->drawIndirectCount = true; features->storageBuffer8BitAccess = false; features->uniformAndStorageBuffer8BitAccess = false; features->storagePushConstant8 = false; features->shaderBufferInt64Atomics = false; features->shaderSharedInt64Atomics = false; features->shaderFloat16 = false; features->shaderInt8 = false; features->descriptorIndexing = false; features->shaderInputAttachmentArrayDynamicIndexing = false; features->shaderUniformTexelBufferArrayDynamicIndexing = false; features->shaderStorageTexelBufferArrayDynamicIndexing = false; features->shaderUniformBufferArrayNonUniformIndexing = false; features->shaderSampledImageArrayNonUniformIndexing = false; features->shaderStorageBufferArrayNonUniformIndexing = false; features->shaderStorageImageArrayNonUniformIndexing = false; features->shaderInputAttachmentArrayNonUniformIndexing = false; features->shaderUniformTexelBufferArrayNonUniformIndexing = false; features->shaderStorageTexelBufferArrayNonUniformIndexing = false; features->descriptorBindingUniformBufferUpdateAfterBind = false; features->descriptorBindingSampledImageUpdateAfterBind = false; features->descriptorBindingStorageImageUpdateAfterBind = false; features->descriptorBindingStorageBufferUpdateAfterBind = false; features->descriptorBindingUniformTexelBufferUpdateAfterBind = false; features->descriptorBindingStorageTexelBufferUpdateAfterBind = false; features->descriptorBindingUpdateUnusedWhilePending = false; features->descriptorBindingPartiallyBound = false; features->descriptorBindingVariableDescriptorCount = false; features->runtimeDescriptorArray = false; features->samplerFilterMinmax = true; features->scalarBlockLayout = false; features->imagelessFramebuffer = false; features->uniformBufferStandardLayout = false; features->shaderSubgroupExtendedTypes = false; features->separateDepthStencilLayouts = false; features->hostQueryReset = false; features->timelineSemaphore = false; features->bufferDeviceAddress = false; features->bufferDeviceAddressCaptureReplay = false; features->bufferDeviceAddressMultiDevice = false; features->vulkanMemoryModel = false; features->vulkanMemoryModelDeviceScope = false; features->vulkanMemoryModelAvailabilityVisibilityChains = false; features->shaderOutputViewportIndex = false; features->shaderOutputLayer = false; features->subgroupBroadcastDynamicId = false; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES: { VkPhysicalDeviceVariablePointersFeatures *features = (void *) ext; features->variablePointersStorageBuffer = true; features->variablePointers = true; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES: { VkPhysicalDeviceMultiviewFeatures *features = (VkPhysicalDeviceMultiviewFeatures *) ext; features->multiview = false; features->multiviewGeometryShader = false; features->multiviewTessellationShader = false; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES: { VkPhysicalDeviceShaderDrawParametersFeatures *features = (VkPhysicalDeviceShaderDrawParametersFeatures *) ext; features->shaderDrawParameters = true; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES: { VkPhysicalDeviceProtectedMemoryFeatures *features = (VkPhysicalDeviceProtectedMemoryFeatures *) ext; features->protectedMemory = false; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES: { VkPhysicalDevice16BitStorageFeatures *features = (VkPhysicalDevice16BitStorageFeatures *) ext; features->storageBuffer16BitAccess = false; features->uniformAndStorageBuffer16BitAccess = false; features->storagePushConstant16 = false; features->storageInputOutput16 = false; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES: { VkPhysicalDeviceSamplerYcbcrConversionFeatures *features = (VkPhysicalDeviceSamplerYcbcrConversionFeatures *) ext; features->samplerYcbcrConversion = true; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT: { VkPhysicalDeviceDescriptorIndexingFeaturesEXT *features = (VkPhysicalDeviceDescriptorIndexingFeaturesEXT *) ext; features->shaderInputAttachmentArrayDynamicIndexing = false; features->shaderUniformTexelBufferArrayDynamicIndexing = false; features->shaderStorageTexelBufferArrayDynamicIndexing = false; features->shaderUniformBufferArrayNonUniformIndexing = false; features->shaderSampledImageArrayNonUniformIndexing = false; features->shaderStorageBufferArrayNonUniformIndexing = false; features->shaderStorageImageArrayNonUniformIndexing = false; features->shaderInputAttachmentArrayNonUniformIndexing = false; features->shaderUniformTexelBufferArrayNonUniformIndexing = false; features->shaderStorageTexelBufferArrayNonUniformIndexing = false; features->descriptorBindingUniformBufferUpdateAfterBind = false; features->descriptorBindingSampledImageUpdateAfterBind = false; features->descriptorBindingStorageImageUpdateAfterBind = false; features->descriptorBindingStorageBufferUpdateAfterBind = false; features->descriptorBindingUniformTexelBufferUpdateAfterBind = false; features->descriptorBindingStorageTexelBufferUpdateAfterBind = false; features->descriptorBindingUpdateUnusedWhilePending = false; features->descriptorBindingPartiallyBound = false; features->descriptorBindingVariableDescriptorCount = false; features->runtimeDescriptorArray = false; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONDITIONAL_RENDERING_FEATURES_EXT: { VkPhysicalDeviceConditionalRenderingFeaturesEXT *features = (VkPhysicalDeviceConditionalRenderingFeaturesEXT *) ext; features->conditionalRendering = false; features->inheritedConditionalRendering = false; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT: { VkPhysicalDeviceTransformFeedbackFeaturesEXT *features = (VkPhysicalDeviceTransformFeedbackFeaturesEXT *) ext; features->transformFeedback = true; features->geometryStreams = false; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT: { VkPhysicalDeviceIndexTypeUint8FeaturesEXT *features = (VkPhysicalDeviceIndexTypeUint8FeaturesEXT *)ext; features->indexTypeUint8 = true; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT: { VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT *features = (VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT *)ext; features->vertexAttributeInstanceRateDivisor = true; features->vertexAttributeInstanceRateZeroDivisor = true; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES_EXT: { VkPhysicalDevicePrivateDataFeaturesEXT *features = (VkPhysicalDevicePrivateDataFeaturesEXT *)ext; features->privateData = true; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT: { VkPhysicalDeviceDepthClipEnableFeaturesEXT *features = (VkPhysicalDeviceDepthClipEnableFeaturesEXT *)ext; features->depthClipEnable = true; break; } default: break; } } return tu_GetPhysicalDeviceFeatures(physicalDevice, &pFeatures->features); } void tu_GetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties *pProperties) { TU_FROM_HANDLE(tu_physical_device, pdevice, physicalDevice); VkSampleCountFlags sample_counts = VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_2_BIT | VK_SAMPLE_COUNT_4_BIT; /* I have no idea what the maximum size is, but the hardware supports very * large numbers of descriptors (at least 2^16). This limit is based on * CP_LOAD_STATE6, which has a 28-bit field for the DWORD offset, so that * we don't have to think about what to do if that overflows, but really * nothing is likely to get close to this. */ const size_t max_descriptor_set_size = (1 << 28) / A6XX_TEX_CONST_DWORDS; VkPhysicalDeviceLimits limits = { .maxImageDimension1D = (1 << 14), .maxImageDimension2D = (1 << 14), .maxImageDimension3D = (1 << 11), .maxImageDimensionCube = (1 << 14), .maxImageArrayLayers = (1 << 11), .maxTexelBufferElements = 128 * 1024 * 1024, .maxUniformBufferRange = MAX_UNIFORM_BUFFER_RANGE, .maxStorageBufferRange = MAX_STORAGE_BUFFER_RANGE, .maxPushConstantsSize = MAX_PUSH_CONSTANTS_SIZE, .maxMemoryAllocationCount = UINT32_MAX, .maxSamplerAllocationCount = 64 * 1024, .bufferImageGranularity = 64, /* A cache line */ .sparseAddressSpaceSize = 0xffffffffu, /* buffer max size */ .maxBoundDescriptorSets = MAX_SETS, .maxPerStageDescriptorSamplers = max_descriptor_set_size, .maxPerStageDescriptorUniformBuffers = max_descriptor_set_size, .maxPerStageDescriptorStorageBuffers = max_descriptor_set_size, .maxPerStageDescriptorSampledImages = max_descriptor_set_size, .maxPerStageDescriptorStorageImages = max_descriptor_set_size, .maxPerStageDescriptorInputAttachments = MAX_RTS, .maxPerStageResources = max_descriptor_set_size, .maxDescriptorSetSamplers = max_descriptor_set_size, .maxDescriptorSetUniformBuffers = max_descriptor_set_size, .maxDescriptorSetUniformBuffersDynamic = MAX_DYNAMIC_UNIFORM_BUFFERS, .maxDescriptorSetStorageBuffers = max_descriptor_set_size, .maxDescriptorSetStorageBuffersDynamic = MAX_DYNAMIC_STORAGE_BUFFERS, .maxDescriptorSetSampledImages = max_descriptor_set_size, .maxDescriptorSetStorageImages = max_descriptor_set_size, .maxDescriptorSetInputAttachments = MAX_RTS, .maxVertexInputAttributes = 32, .maxVertexInputBindings = 32, .maxVertexInputAttributeOffset = 4095, .maxVertexInputBindingStride = 2048, .maxVertexOutputComponents = 128, .maxTessellationGenerationLevel = 64, .maxTessellationPatchSize = 32, .maxTessellationControlPerVertexInputComponents = 128, .maxTessellationControlPerVertexOutputComponents = 128, .maxTessellationControlPerPatchOutputComponents = 120, .maxTessellationControlTotalOutputComponents = 4096, .maxTessellationEvaluationInputComponents = 128, .maxTessellationEvaluationOutputComponents = 128, .maxGeometryShaderInvocations = 32, .maxGeometryInputComponents = 64, .maxGeometryOutputComponents = 128, .maxGeometryOutputVertices = 256, .maxGeometryTotalOutputComponents = 1024, .maxFragmentInputComponents = 124, .maxFragmentOutputAttachments = 8, .maxFragmentDualSrcAttachments = 1, .maxFragmentCombinedOutputResources = 8, .maxComputeSharedMemorySize = 32768, .maxComputeWorkGroupCount = { 65535, 65535, 65535 }, .maxComputeWorkGroupInvocations = 2048, .maxComputeWorkGroupSize = { 2048, 2048, 2048 }, .subPixelPrecisionBits = 8, .subTexelPrecisionBits = 8, .mipmapPrecisionBits = 8, .maxDrawIndexedIndexValue = UINT32_MAX, .maxDrawIndirectCount = UINT32_MAX, .maxSamplerLodBias = 4095.0 / 256.0, /* [-16, 15.99609375] */ .maxSamplerAnisotropy = 16, .maxViewports = MAX_VIEWPORTS, .maxViewportDimensions = { (1 << 14), (1 << 14) }, .viewportBoundsRange = { INT16_MIN, INT16_MAX }, .viewportSubPixelBits = 8, .minMemoryMapAlignment = 4096, /* A page */ .minTexelBufferOffsetAlignment = 64, .minUniformBufferOffsetAlignment = 64, .minStorageBufferOffsetAlignment = 64, .minTexelOffset = -16, .maxTexelOffset = 15, .minTexelGatherOffset = -32, .maxTexelGatherOffset = 31, .minInterpolationOffset = -0.5, .maxInterpolationOffset = 0.4375, .subPixelInterpolationOffsetBits = 4, .maxFramebufferWidth = (1 << 14), .maxFramebufferHeight = (1 << 14), .maxFramebufferLayers = (1 << 10), .framebufferColorSampleCounts = sample_counts, .framebufferDepthSampleCounts = sample_counts, .framebufferStencilSampleCounts = sample_counts, .framebufferNoAttachmentsSampleCounts = sample_counts, .maxColorAttachments = MAX_RTS, .sampledImageColorSampleCounts = sample_counts, .sampledImageIntegerSampleCounts = VK_SAMPLE_COUNT_1_BIT, .sampledImageDepthSampleCounts = sample_counts, .sampledImageStencilSampleCounts = sample_counts, .storageImageSampleCounts = VK_SAMPLE_COUNT_1_BIT, .maxSampleMaskWords = 1, .timestampComputeAndGraphics = true, .timestampPeriod = 1000000000.0 / 19200000.0, /* CP_ALWAYS_ON_COUNTER is fixed 19.2MHz */ .maxClipDistances = 8, .maxCullDistances = 8, .maxCombinedClipAndCullDistances = 8, .discreteQueuePriorities = 1, .pointSizeRange = { 1, 4092 }, .lineWidthRange = { 0.0, 7.9921875 }, .pointSizeGranularity = 0.0625, .lineWidthGranularity = (1.0 / 128.0), .strictLines = false, /* FINISHME */ .standardSampleLocations = true, .optimalBufferCopyOffsetAlignment = 128, .optimalBufferCopyRowPitchAlignment = 128, .nonCoherentAtomSize = 64, }; *pProperties = (VkPhysicalDeviceProperties) { .apiVersion = tu_physical_device_api_version(pdevice), .driverVersion = vk_get_driver_version(), .vendorID = 0, /* TODO */ .deviceID = 0, .deviceType = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU, .limits = limits, .sparseProperties = { 0 }, }; strcpy(pProperties->deviceName, pdevice->name); memcpy(pProperties->pipelineCacheUUID, pdevice->cache_uuid, VK_UUID_SIZE); } void tu_GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2 *pProperties) { TU_FROM_HANDLE(tu_physical_device, pdevice, physicalDevice); tu_GetPhysicalDeviceProperties(physicalDevice, &pProperties->properties); vk_foreach_struct(ext, pProperties->pNext) { switch (ext->sType) { case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR: { VkPhysicalDevicePushDescriptorPropertiesKHR *properties = (VkPhysicalDevicePushDescriptorPropertiesKHR *) ext; properties->maxPushDescriptors = MAX_PUSH_DESCRIPTORS; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES: { VkPhysicalDeviceIDProperties *properties = (VkPhysicalDeviceIDProperties *) ext; memcpy(properties->driverUUID, pdevice->driver_uuid, VK_UUID_SIZE); memcpy(properties->deviceUUID, pdevice->device_uuid, VK_UUID_SIZE); properties->deviceLUIDValid = false; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES: { VkPhysicalDeviceMultiviewProperties *properties = (VkPhysicalDeviceMultiviewProperties *) ext; properties->maxMultiviewViewCount = MAX_VIEWS; properties->maxMultiviewInstanceIndex = INT_MAX; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES: { VkPhysicalDevicePointClippingProperties *properties = (VkPhysicalDevicePointClippingProperties *) ext; properties->pointClippingBehavior = VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES: { VkPhysicalDeviceMaintenance3Properties *properties = (VkPhysicalDeviceMaintenance3Properties *) ext; /* Make sure everything is addressable by a signed 32-bit int, and * our largest descriptors are 96 bytes. */ properties->maxPerSetDescriptors = (1ull << 31) / 96; /* Our buffer size fields allow only this much */ properties->maxMemoryAllocationSize = 0xFFFFFFFFull; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT: { VkPhysicalDeviceTransformFeedbackPropertiesEXT *properties = (VkPhysicalDeviceTransformFeedbackPropertiesEXT *)ext; properties->maxTransformFeedbackStreams = IR3_MAX_SO_STREAMS; properties->maxTransformFeedbackBuffers = IR3_MAX_SO_BUFFERS; properties->maxTransformFeedbackBufferSize = UINT32_MAX; properties->maxTransformFeedbackStreamDataSize = 512; properties->maxTransformFeedbackBufferDataSize = 512; properties->maxTransformFeedbackBufferDataStride = 512; properties->transformFeedbackQueries = true; properties->transformFeedbackStreamsLinesTriangles = false; properties->transformFeedbackRasterizationStreamSelect = false; properties->transformFeedbackDraw = true; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT: { VkPhysicalDeviceSampleLocationsPropertiesEXT *properties = (VkPhysicalDeviceSampleLocationsPropertiesEXT *)ext; properties->sampleLocationSampleCounts = 0; if (pdevice->supported_extensions.EXT_sample_locations) { properties->sampleLocationSampleCounts = VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_2_BIT | VK_SAMPLE_COUNT_4_BIT; } properties->maxSampleLocationGridSize = (VkExtent2D) { 1 , 1 }; properties->sampleLocationCoordinateRange[0] = 0.0f; properties->sampleLocationCoordinateRange[1] = 0.9375f; properties->sampleLocationSubPixelBits = 4; properties->variableSampleLocations = true; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES: { VkPhysicalDeviceSamplerFilterMinmaxProperties *properties = (VkPhysicalDeviceSamplerFilterMinmaxProperties *)ext; properties->filterMinmaxImageComponentMapping = true; properties->filterMinmaxSingleComponentFormats = true; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES: { VkPhysicalDeviceSubgroupProperties *properties = (VkPhysicalDeviceSubgroupProperties *)ext; properties->subgroupSize = 64; properties->supportedStages = VK_SHADER_STAGE_COMPUTE_BIT; properties->supportedOperations = VK_SUBGROUP_FEATURE_BASIC_BIT | VK_SUBGROUP_FEATURE_VOTE_BIT; properties->quadOperationsInAllStages = false; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT: { VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT *props = (VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT *)ext; props->maxVertexAttribDivisor = UINT32_MAX; break; } default: break; } } } static const VkQueueFamilyProperties tu_queue_family_properties = { .queueFlags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, .queueCount = 1, .timestampValidBits = 48, .minImageTransferGranularity = { 1, 1, 1 }, }; void tu_GetPhysicalDeviceQueueFamilyProperties( VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties *pQueueFamilyProperties) { VK_OUTARRAY_MAKE(out, pQueueFamilyProperties, pQueueFamilyPropertyCount); vk_outarray_append(&out, p) { *p = tu_queue_family_properties; } } void tu_GetPhysicalDeviceQueueFamilyProperties2( VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties2 *pQueueFamilyProperties) { VK_OUTARRAY_MAKE(out, pQueueFamilyProperties, pQueueFamilyPropertyCount); vk_outarray_append(&out, p) { p->queueFamilyProperties = tu_queue_family_properties; } } static uint64_t tu_get_system_heap_size() { struct sysinfo info; sysinfo(&info); uint64_t total_ram = (uint64_t) info.totalram * (uint64_t) info.mem_unit; /* We don't want to burn too much ram with the GPU. If the user has 4GiB * or less, we use at most half. If they have more than 4GiB, we use 3/4. */ uint64_t available_ram; if (total_ram <= 4ull * 1024ull * 1024ull * 1024ull) available_ram = total_ram / 2; else available_ram = total_ram * 3 / 4; return available_ram; } void tu_GetPhysicalDeviceMemoryProperties( VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties *pMemoryProperties) { pMemoryProperties->memoryHeapCount = 1; pMemoryProperties->memoryHeaps[0].size = tu_get_system_heap_size(); pMemoryProperties->memoryHeaps[0].flags = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT; pMemoryProperties->memoryTypeCount = 1; pMemoryProperties->memoryTypes[0].propertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; pMemoryProperties->memoryTypes[0].heapIndex = 0; } void tu_GetPhysicalDeviceMemoryProperties2( VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2 *pMemoryProperties) { return tu_GetPhysicalDeviceMemoryProperties( physicalDevice, &pMemoryProperties->memoryProperties); } static VkResult tu_queue_init(struct tu_device *device, struct tu_queue *queue, uint32_t queue_family_index, int idx, VkDeviceQueueCreateFlags flags) { vk_object_base_init(&device->vk, &queue->base, VK_OBJECT_TYPE_QUEUE); queue->device = device; queue->queue_family_index = queue_family_index; queue->queue_idx = idx; queue->flags = flags; int ret = tu_drm_submitqueue_new(device, 0, &queue->msm_queue_id); if (ret) return VK_ERROR_INITIALIZATION_FAILED; tu_fence_init(&queue->submit_fence, false); return VK_SUCCESS; } static void tu_queue_finish(struct tu_queue *queue) { tu_fence_finish(&queue->submit_fence); tu_drm_submitqueue_close(queue->device, queue->msm_queue_id); } static int tu_get_device_extension_index(const char *name) { for (unsigned i = 0; i < TU_DEVICE_EXTENSION_COUNT; ++i) { if (strcmp(name, tu_device_extensions[i].extensionName) == 0) return i; } return -1; } struct PACKED bcolor_entry { uint32_t fp32[4]; uint16_t ui16[4]; int16_t si16[4]; uint16_t fp16[4]; uint16_t rgb565; uint16_t rgb5a1; uint16_t rgba4; uint8_t __pad0[2]; uint8_t ui8[4]; int8_t si8[4]; uint32_t rgb10a2; uint32_t z24; /* also s8? */ uint16_t srgb[4]; /* appears to duplicate fp16[], but clamped, used for srgb */ uint8_t __pad1[56]; } border_color[] = { [VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK] = {}, [VK_BORDER_COLOR_INT_TRANSPARENT_BLACK] = {}, [VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK] = { .fp32[3] = 0x3f800000, .ui16[3] = 0xffff, .si16[3] = 0x7fff, .fp16[3] = 0x3c00, .rgb5a1 = 0x8000, .rgba4 = 0xf000, .ui8[3] = 0xff, .si8[3] = 0x7f, .rgb10a2 = 0xc0000000, .srgb[3] = 0x3c00, }, [VK_BORDER_COLOR_INT_OPAQUE_BLACK] = { .fp32[3] = 1, .fp16[3] = 1, }, [VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE] = { .fp32[0 ... 3] = 0x3f800000, .ui16[0 ... 3] = 0xffff, .si16[0 ... 3] = 0x7fff, .fp16[0 ... 3] = 0x3c00, .rgb565 = 0xffff, .rgb5a1 = 0xffff, .rgba4 = 0xffff, .ui8[0 ... 3] = 0xff, .si8[0 ... 3] = 0x7f, .rgb10a2 = 0xffffffff, .z24 = 0xffffff, .srgb[0 ... 3] = 0x3c00, }, [VK_BORDER_COLOR_INT_OPAQUE_WHITE] = { .fp32[0 ... 3] = 1, .fp16[0 ... 3] = 1, }, }; VkResult tu_CreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) { TU_FROM_HANDLE(tu_physical_device, physical_device, physicalDevice); VkResult result; struct tu_device *device; /* Check enabled features */ if (pCreateInfo->pEnabledFeatures) { VkPhysicalDeviceFeatures supported_features; tu_GetPhysicalDeviceFeatures(physicalDevice, &supported_features); VkBool32 *supported_feature = (VkBool32 *) &supported_features; VkBool32 *enabled_feature = (VkBool32 *) pCreateInfo->pEnabledFeatures; unsigned num_features = sizeof(VkPhysicalDeviceFeatures) / sizeof(VkBool32); for (uint32_t i = 0; i < num_features; i++) { if (enabled_feature[i] && !supported_feature[i]) return vk_error(physical_device->instance, VK_ERROR_FEATURE_NOT_PRESENT); } } device = vk_zalloc2(&physical_device->instance->alloc, pAllocator, sizeof(*device), 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE); if (!device) return vk_error(physical_device->instance, VK_ERROR_OUT_OF_HOST_MEMORY); vk_device_init(&device->vk, pCreateInfo, &physical_device->instance->alloc, pAllocator); device->instance = physical_device->instance; device->physical_device = physical_device; device->_lost = false; for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) { const char *ext_name = pCreateInfo->ppEnabledExtensionNames[i]; int index = tu_get_device_extension_index(ext_name); if (index < 0 || !physical_device->supported_extensions.extensions[index]) { vk_free(&device->vk.alloc, device); return vk_error(physical_device->instance, VK_ERROR_EXTENSION_NOT_PRESENT); } device->enabled_extensions.extensions[index] = true; } for (unsigned i = 0; i < pCreateInfo->queueCreateInfoCount; i++) { const VkDeviceQueueCreateInfo *queue_create = &pCreateInfo->pQueueCreateInfos[i]; uint32_t qfi = queue_create->queueFamilyIndex; device->queues[qfi] = vk_alloc( &device->vk.alloc, queue_create->queueCount * sizeof(struct tu_queue), 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE); if (!device->queues[qfi]) { result = VK_ERROR_OUT_OF_HOST_MEMORY; goto fail_queues; } memset(device->queues[qfi], 0, queue_create->queueCount * sizeof(struct tu_queue)); device->queue_count[qfi] = queue_create->queueCount; for (unsigned q = 0; q < queue_create->queueCount; q++) { result = tu_queue_init(device, &device->queues[qfi][q], qfi, q, queue_create->flags); if (result != VK_SUCCESS) goto fail_queues; } } device->compiler = ir3_compiler_create(NULL, physical_device->gpu_id); if (!device->compiler) goto fail_queues; /* initial sizes, these will increase if there is overflow */ device->vsc_draw_strm_pitch = 0x1000 + VSC_PAD; device->vsc_prim_strm_pitch = 0x4000 + VSC_PAD; STATIC_ASSERT(sizeof(border_color) == sizeof(((struct tu6_global*) 0)->border_color)); result = tu_bo_init_new(device, &device->global_bo, sizeof(struct tu6_global)); if (result != VK_SUCCESS) goto fail_global_bo; result = tu_bo_map(device, &device->global_bo); if (result != VK_SUCCESS) goto fail_global_bo_map; memcpy(device->global_bo.map + gb_offset(border_color), border_color, sizeof(border_color)); tu_init_clear_blit_shaders(device->global_bo.map); VkPipelineCacheCreateInfo ci; ci.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; ci.pNext = NULL; ci.flags = 0; ci.pInitialData = NULL; ci.initialDataSize = 0; VkPipelineCache pc; result = tu_CreatePipelineCache(tu_device_to_handle(device), &ci, NULL, &pc); if (result != VK_SUCCESS) goto fail_pipeline_cache; device->mem_cache = tu_pipeline_cache_from_handle(pc); for (unsigned i = 0; i < ARRAY_SIZE(device->scratch_bos); i++) mtx_init(&device->scratch_bos[i].construct_mtx, mtx_plain); mtx_init(&device->vsc_pitch_mtx, mtx_plain); *pDevice = tu_device_to_handle(device); return VK_SUCCESS; fail_pipeline_cache: fail_global_bo_map: tu_bo_finish(device, &device->global_bo); fail_global_bo: ralloc_free(device->compiler); fail_queues: for (unsigned i = 0; i < TU_MAX_QUEUE_FAMILIES; i++) { for (unsigned q = 0; q < device->queue_count[i]; q++) tu_queue_finish(&device->queues[i][q]); if (device->queue_count[i]) vk_object_free(&device->vk, NULL, device->queues[i]); } vk_free(&device->vk.alloc, device); return result; } void tu_DestroyDevice(VkDevice _device, const VkAllocationCallbacks *pAllocator) { TU_FROM_HANDLE(tu_device, device, _device); if (!device) return; for (unsigned i = 0; i < TU_MAX_QUEUE_FAMILIES; i++) { for (unsigned q = 0; q < device->queue_count[i]; q++) tu_queue_finish(&device->queues[i][q]); if (device->queue_count[i]) vk_object_free(&device->vk, NULL, device->queues[i]); } for (unsigned i = 0; i < ARRAY_SIZE(device->scratch_bos); i++) { if (device->scratch_bos[i].initialized) tu_bo_finish(device, &device->scratch_bos[i].bo); } ir3_compiler_destroy(device->compiler); VkPipelineCache pc = tu_pipeline_cache_to_handle(device->mem_cache); tu_DestroyPipelineCache(tu_device_to_handle(device), pc, NULL); vk_free(&device->vk.alloc, device); } VkResult _tu_device_set_lost(struct tu_device *device, const char *file, int line, const char *msg, ...) { /* Set the flag indicating that waits should return in finite time even * after device loss. */ p_atomic_inc(&device->_lost); /* TODO: Report the log message through VkDebugReportCallbackEXT instead */ fprintf(stderr, "%s:%d: ", file, line); va_list ap; va_start(ap, msg); vfprintf(stderr, msg, ap); va_end(ap); if (env_var_as_boolean("TU_ABORT_ON_DEVICE_LOSS", false)) abort(); return VK_ERROR_DEVICE_LOST; } VkResult tu_get_scratch_bo(struct tu_device *dev, uint64_t size, struct tu_bo **bo) { unsigned size_log2 = MAX2(util_logbase2_ceil64(size), MIN_SCRATCH_BO_SIZE_LOG2); unsigned index = size_log2 - MIN_SCRATCH_BO_SIZE_LOG2; assert(index < ARRAY_SIZE(dev->scratch_bos)); for (unsigned i = index; i < ARRAY_SIZE(dev->scratch_bos); i++) { if (p_atomic_read(&dev->scratch_bos[i].initialized)) { /* Fast path: just return the already-allocated BO. */ *bo = &dev->scratch_bos[i].bo; return VK_SUCCESS; } } /* Slow path: actually allocate the BO. We take a lock because the process * of allocating it is slow, and we don't want to block the CPU while it * finishes. */ mtx_lock(&dev->scratch_bos[index].construct_mtx); /* Another thread may have allocated it already while we were waiting on * the lock. We need to check this in order to avoid double-allocating. */ if (dev->scratch_bos[index].initialized) { mtx_unlock(&dev->scratch_bos[index].construct_mtx); *bo = &dev->scratch_bos[index].bo; return VK_SUCCESS; } unsigned bo_size = 1ull << size_log2; VkResult result = tu_bo_init_new(dev, &dev->scratch_bos[index].bo, bo_size); if (result != VK_SUCCESS) { mtx_unlock(&dev->scratch_bos[index].construct_mtx); return result; } p_atomic_set(&dev->scratch_bos[index].initialized, true); mtx_unlock(&dev->scratch_bos[index].construct_mtx); *bo = &dev->scratch_bos[index].bo; return VK_SUCCESS; } VkResult tu_EnumerateInstanceLayerProperties(uint32_t *pPropertyCount, VkLayerProperties *pProperties) { *pPropertyCount = 0; return VK_SUCCESS; } VkResult tu_EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice, uint32_t *pPropertyCount, VkLayerProperties *pProperties) { *pPropertyCount = 0; return VK_SUCCESS; } void tu_GetDeviceQueue2(VkDevice _device, const VkDeviceQueueInfo2 *pQueueInfo, VkQueue *pQueue) { TU_FROM_HANDLE(tu_device, device, _device); struct tu_queue *queue; queue = &device->queues[pQueueInfo->queueFamilyIndex][pQueueInfo->queueIndex]; if (pQueueInfo->flags != queue->flags) { /* From the Vulkan 1.1.70 spec: * * "The queue returned by vkGetDeviceQueue2 must have the same * flags value from this structure as that used at device * creation time in a VkDeviceQueueCreateInfo instance. If no * matching flags were specified at device creation time then * pQueue will return VK_NULL_HANDLE." */ *pQueue = VK_NULL_HANDLE; return; } *pQueue = tu_queue_to_handle(queue); } void tu_GetDeviceQueue(VkDevice _device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue *pQueue) { const VkDeviceQueueInfo2 info = (VkDeviceQueueInfo2) { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2, .queueFamilyIndex = queueFamilyIndex, .queueIndex = queueIndex }; tu_GetDeviceQueue2(_device, &info, pQueue); } 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_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; } VkResult tu_QueueWaitIdle(VkQueue _queue) { TU_FROM_HANDLE(tu_queue, queue, _queue); if (tu_device_is_lost(queue->device)) return VK_ERROR_DEVICE_LOST; tu_fence_wait_idle(&queue->submit_fence); return VK_SUCCESS; } VkResult tu_DeviceWaitIdle(VkDevice _device) { TU_FROM_HANDLE(tu_device, device, _device); if (tu_device_is_lost(device)) return VK_ERROR_DEVICE_LOST; for (unsigned i = 0; i < TU_MAX_QUEUE_FAMILIES; i++) { for (unsigned q = 0; q < device->queue_count[i]; q++) { tu_QueueWaitIdle(tu_queue_to_handle(&device->queues[i][q])); } } return VK_SUCCESS; } VkResult tu_EnumerateInstanceExtensionProperties(const char *pLayerName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties) { VK_OUTARRAY_MAKE(out, pProperties, pPropertyCount); /* We spport no lyaers */ if (pLayerName) return vk_error(NULL, VK_ERROR_LAYER_NOT_PRESENT); for (int i = 0; i < TU_INSTANCE_EXTENSION_COUNT; i++) { if (tu_instance_extensions_supported.extensions[i]) { vk_outarray_append(&out, prop) { *prop = tu_instance_extensions[i]; } } } return vk_outarray_status(&out); } VkResult tu_EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char *pLayerName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties) { /* We spport no lyaers */ TU_FROM_HANDLE(tu_physical_device, device, physicalDevice); VK_OUTARRAY_MAKE(out, pProperties, pPropertyCount); /* We spport no lyaers */ if (pLayerName) return vk_error(NULL, VK_ERROR_LAYER_NOT_PRESENT); for (int i = 0; i < TU_DEVICE_EXTENSION_COUNT; i++) { if (device->supported_extensions.extensions[i]) { vk_outarray_append(&out, prop) { *prop = tu_device_extensions[i]; } } } return vk_outarray_status(&out); } PFN_vkVoidFunction tu_GetInstanceProcAddr(VkInstance _instance, const char *pName) { TU_FROM_HANDLE(tu_instance, instance, _instance); return tu_lookup_entrypoint_checked( pName, instance ? instance->api_version : 0, instance ? &instance->enabled_extensions : NULL, NULL); } /* The loader wants us to expose a second GetInstanceProcAddr function * to work around certain LD_PRELOAD issues seen in apps. */ PUBLIC VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vk_icdGetInstanceProcAddr(VkInstance instance, const char *pName); PUBLIC VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vk_icdGetInstanceProcAddr(VkInstance instance, const char *pName) { return tu_GetInstanceProcAddr(instance, pName); } PFN_vkVoidFunction tu_GetDeviceProcAddr(VkDevice _device, const char *pName) { TU_FROM_HANDLE(tu_device, device, _device); return tu_lookup_entrypoint_checked(pName, device->instance->api_version, &device->instance->enabled_extensions, &device->enabled_extensions); } static VkResult tu_alloc_memory(struct tu_device *device, const VkMemoryAllocateInfo *pAllocateInfo, const VkAllocationCallbacks *pAllocator, VkDeviceMemory *pMem) { struct tu_device_memory *mem; VkResult result; assert(pAllocateInfo->sType == VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO); if (pAllocateInfo->allocationSize == 0) { /* Apparently, this is allowed */ *pMem = VK_NULL_HANDLE; return VK_SUCCESS; } mem = vk_object_alloc(&device->vk, pAllocator, sizeof(*mem), VK_OBJECT_TYPE_DEVICE_MEMORY); if (mem == NULL) return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY); const VkImportMemoryFdInfoKHR *fd_info = vk_find_struct_const(pAllocateInfo->pNext, IMPORT_MEMORY_FD_INFO_KHR); if (fd_info && !fd_info->handleType) fd_info = NULL; if (fd_info) { assert(fd_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT || fd_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT); /* * TODO Importing the same fd twice gives us the same handle without * reference counting. We need to maintain a per-instance handle-to-bo * table and add reference count to tu_bo. */ result = tu_bo_init_dmabuf(device, &mem->bo, pAllocateInfo->allocationSize, fd_info->fd); if (result == VK_SUCCESS) { /* take ownership and close the fd */ close(fd_info->fd); } } else { result = tu_bo_init_new(device, &mem->bo, pAllocateInfo->allocationSize); } if (result != VK_SUCCESS) { vk_object_free(&device->vk, pAllocator, mem); return result; } mem->size = pAllocateInfo->allocationSize; mem->type_index = pAllocateInfo->memoryTypeIndex; mem->map = NULL; mem->user_ptr = NULL; *pMem = tu_device_memory_to_handle(mem); return VK_SUCCESS; } VkResult tu_AllocateMemory(VkDevice _device, const VkMemoryAllocateInfo *pAllocateInfo, const VkAllocationCallbacks *pAllocator, VkDeviceMemory *pMem) { TU_FROM_HANDLE(tu_device, device, _device); return tu_alloc_memory(device, pAllocateInfo, pAllocator, pMem); } void tu_FreeMemory(VkDevice _device, VkDeviceMemory _mem, const VkAllocationCallbacks *pAllocator) { TU_FROM_HANDLE(tu_device, device, _device); TU_FROM_HANDLE(tu_device_memory, mem, _mem); if (mem == NULL) return; tu_bo_finish(device, &mem->bo); vk_object_free(&device->vk, pAllocator, mem); } VkResult tu_MapMemory(VkDevice _device, VkDeviceMemory _memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void **ppData) { TU_FROM_HANDLE(tu_device, device, _device); TU_FROM_HANDLE(tu_device_memory, mem, _memory); VkResult result; if (mem == NULL) { *ppData = NULL; return VK_SUCCESS; } if (mem->user_ptr) { *ppData = mem->user_ptr; } else if (!mem->map) { result = tu_bo_map(device, &mem->bo); if (result != VK_SUCCESS) return result; *ppData = mem->map = mem->bo.map; } else *ppData = mem->map; if (*ppData) { *ppData += offset; return VK_SUCCESS; } return vk_error(device->instance, VK_ERROR_MEMORY_MAP_FAILED); } void tu_UnmapMemory(VkDevice _device, VkDeviceMemory _memory) { /* I do not see any unmapping done by the freedreno Gallium driver. */ } VkResult tu_FlushMappedMemoryRanges(VkDevice _device, uint32_t memoryRangeCount, const VkMappedMemoryRange *pMemoryRanges) { return VK_SUCCESS; } VkResult tu_InvalidateMappedMemoryRanges(VkDevice _device, uint32_t memoryRangeCount, const VkMappedMemoryRange *pMemoryRanges) { return VK_SUCCESS; } void tu_GetBufferMemoryRequirements(VkDevice _device, VkBuffer _buffer, VkMemoryRequirements *pMemoryRequirements) { TU_FROM_HANDLE(tu_buffer, buffer, _buffer); pMemoryRequirements->memoryTypeBits = 1; pMemoryRequirements->alignment = 64; pMemoryRequirements->size = align64(buffer->size, pMemoryRequirements->alignment); } void tu_GetBufferMemoryRequirements2( VkDevice device, const VkBufferMemoryRequirementsInfo2 *pInfo, VkMemoryRequirements2 *pMemoryRequirements) { tu_GetBufferMemoryRequirements(device, pInfo->buffer, &pMemoryRequirements->memoryRequirements); } void tu_GetImageMemoryRequirements(VkDevice _device, VkImage _image, VkMemoryRequirements *pMemoryRequirements) { TU_FROM_HANDLE(tu_image, image, _image); pMemoryRequirements->memoryTypeBits = 1; pMemoryRequirements->size = image->total_size; pMemoryRequirements->alignment = image->layout[0].base_align; } void tu_GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2 *pInfo, VkMemoryRequirements2 *pMemoryRequirements) { tu_GetImageMemoryRequirements(device, pInfo->image, &pMemoryRequirements->memoryRequirements); } void tu_GetImageSparseMemoryRequirements( VkDevice device, VkImage image, uint32_t *pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements *pSparseMemoryRequirements) { tu_stub(); } void tu_GetImageSparseMemoryRequirements2( VkDevice device, const VkImageSparseMemoryRequirementsInfo2 *pInfo, uint32_t *pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2 *pSparseMemoryRequirements) { tu_stub(); } void tu_GetDeviceMemoryCommitment(VkDevice device, VkDeviceMemory memory, VkDeviceSize *pCommittedMemoryInBytes) { *pCommittedMemoryInBytes = 0; } VkResult tu_BindBufferMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo *pBindInfos) { for (uint32_t i = 0; i < bindInfoCount; ++i) { TU_FROM_HANDLE(tu_device_memory, mem, pBindInfos[i].memory); TU_FROM_HANDLE(tu_buffer, buffer, pBindInfos[i].buffer); if (mem) { buffer->bo = &mem->bo; buffer->bo_offset = pBindInfos[i].memoryOffset; } else { buffer->bo = NULL; } } return VK_SUCCESS; } VkResult tu_BindBufferMemory(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset) { const VkBindBufferMemoryInfo info = { .sType = VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO, .buffer = buffer, .memory = memory, .memoryOffset = memoryOffset }; return tu_BindBufferMemory2(device, 1, &info); } VkResult tu_BindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo *pBindInfos) { for (uint32_t i = 0; i < bindInfoCount; ++i) { TU_FROM_HANDLE(tu_image, image, pBindInfos[i].image); TU_FROM_HANDLE(tu_device_memory, mem, pBindInfos[i].memory); if (mem) { image->bo = &mem->bo; image->bo_offset = pBindInfos[i].memoryOffset; } else { image->bo = NULL; image->bo_offset = 0; } } return VK_SUCCESS; } VkResult tu_BindImageMemory(VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset) { const VkBindImageMemoryInfo info = { .sType = VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO, .image = image, .memory = memory, .memoryOffset = memoryOffset }; return tu_BindImageMemory2(device, 1, &info); } VkResult tu_QueueBindSparse(VkQueue _queue, uint32_t bindInfoCount, const VkBindSparseInfo *pBindInfo, VkFence _fence) { return VK_SUCCESS; } // 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); } } 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_CreateEvent(VkDevice _device, const VkEventCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkEvent *pEvent) { TU_FROM_HANDLE(tu_device, device, _device); struct tu_event *event = vk_object_alloc(&device->vk, pAllocator, sizeof(*event), VK_OBJECT_TYPE_EVENT); if (!event) return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY); VkResult result = tu_bo_init_new(device, &event->bo, 0x1000); if (result != VK_SUCCESS) goto fail_alloc; result = tu_bo_map(device, &event->bo); if (result != VK_SUCCESS) goto fail_map; *pEvent = tu_event_to_handle(event); return VK_SUCCESS; fail_map: tu_bo_finish(device, &event->bo); fail_alloc: vk_object_free(&device->vk, pAllocator, event); return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY); } void tu_DestroyEvent(VkDevice _device, VkEvent _event, const VkAllocationCallbacks *pAllocator) { TU_FROM_HANDLE(tu_device, device, _device); TU_FROM_HANDLE(tu_event, event, _event); if (!event) return; tu_bo_finish(device, &event->bo); vk_object_free(&device->vk, pAllocator, event); } VkResult tu_GetEventStatus(VkDevice _device, VkEvent _event) { TU_FROM_HANDLE(tu_event, event, _event); if (*(uint64_t*) event->bo.map == 1) return VK_EVENT_SET; return VK_EVENT_RESET; } VkResult tu_SetEvent(VkDevice _device, VkEvent _event) { TU_FROM_HANDLE(tu_event, event, _event); *(uint64_t*) event->bo.map = 1; return VK_SUCCESS; } VkResult tu_ResetEvent(VkDevice _device, VkEvent _event) { TU_FROM_HANDLE(tu_event, event, _event); *(uint64_t*) event->bo.map = 0; return VK_SUCCESS; } VkResult tu_CreateBuffer(VkDevice _device, const VkBufferCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkBuffer *pBuffer) { TU_FROM_HANDLE(tu_device, device, _device); struct tu_buffer *buffer; assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO); buffer = vk_object_alloc(&device->vk, pAllocator, sizeof(*buffer), VK_OBJECT_TYPE_BUFFER); if (buffer == NULL) return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY); buffer->size = pCreateInfo->size; buffer->usage = pCreateInfo->usage; buffer->flags = pCreateInfo->flags; *pBuffer = tu_buffer_to_handle(buffer); return VK_SUCCESS; } void tu_DestroyBuffer(VkDevice _device, VkBuffer _buffer, const VkAllocationCallbacks *pAllocator) { TU_FROM_HANDLE(tu_device, device, _device); TU_FROM_HANDLE(tu_buffer, buffer, _buffer); if (!buffer) return; vk_object_free(&device->vk, pAllocator, buffer); } VkResult tu_CreateFramebuffer(VkDevice _device, const VkFramebufferCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFramebuffer *pFramebuffer) { TU_FROM_HANDLE(tu_device, device, _device); TU_FROM_HANDLE(tu_render_pass, pass, pCreateInfo->renderPass); struct tu_framebuffer *framebuffer; assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO); size_t size = sizeof(*framebuffer) + sizeof(struct tu_attachment_info) * pCreateInfo->attachmentCount; framebuffer = vk_object_alloc(&device->vk, pAllocator, size, VK_OBJECT_TYPE_FRAMEBUFFER); if (framebuffer == NULL) return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY); framebuffer->attachment_count = pCreateInfo->attachmentCount; framebuffer->width = pCreateInfo->width; framebuffer->height = pCreateInfo->height; framebuffer->layers = pCreateInfo->layers; for (uint32_t i = 0; i < pCreateInfo->attachmentCount; i++) { VkImageView _iview = pCreateInfo->pAttachments[i]; struct tu_image_view *iview = tu_image_view_from_handle(_iview); framebuffer->attachments[i].attachment = iview; } tu_framebuffer_tiling_config(framebuffer, device, pass); *pFramebuffer = tu_framebuffer_to_handle(framebuffer); return VK_SUCCESS; } void tu_DestroyFramebuffer(VkDevice _device, VkFramebuffer _fb, const VkAllocationCallbacks *pAllocator) { TU_FROM_HANDLE(tu_device, device, _device); TU_FROM_HANDLE(tu_framebuffer, fb, _fb); if (!fb) return; vk_object_free(&device->vk, pAllocator, fb); } static void tu_init_sampler(struct tu_device *device, struct tu_sampler *sampler, const VkSamplerCreateInfo *pCreateInfo) { const struct VkSamplerReductionModeCreateInfo *reduction = vk_find_struct_const(pCreateInfo->pNext, SAMPLER_REDUCTION_MODE_CREATE_INFO); const struct VkSamplerYcbcrConversionInfo *ycbcr_conversion = vk_find_struct_const(pCreateInfo->pNext, SAMPLER_YCBCR_CONVERSION_INFO); unsigned aniso = pCreateInfo->anisotropyEnable ? util_last_bit(MIN2((uint32_t)pCreateInfo->maxAnisotropy >> 1, 8)) : 0; bool miplinear = (pCreateInfo->mipmapMode == VK_SAMPLER_MIPMAP_MODE_LINEAR); float min_lod = CLAMP(pCreateInfo->minLod, 0.0f, 4095.0f / 256.0f); float max_lod = CLAMP(pCreateInfo->maxLod, 0.0f, 4095.0f / 256.0f); sampler->descriptor[0] = COND(miplinear, A6XX_TEX_SAMP_0_MIPFILTER_LINEAR_NEAR) | A6XX_TEX_SAMP_0_XY_MAG(tu6_tex_filter(pCreateInfo->magFilter, aniso)) | A6XX_TEX_SAMP_0_XY_MIN(tu6_tex_filter(pCreateInfo->minFilter, aniso)) | A6XX_TEX_SAMP_0_ANISO(aniso) | A6XX_TEX_SAMP_0_WRAP_S(tu6_tex_wrap(pCreateInfo->addressModeU)) | A6XX_TEX_SAMP_0_WRAP_T(tu6_tex_wrap(pCreateInfo->addressModeV)) | A6XX_TEX_SAMP_0_WRAP_R(tu6_tex_wrap(pCreateInfo->addressModeW)) | A6XX_TEX_SAMP_0_LOD_BIAS(pCreateInfo->mipLodBias); sampler->descriptor[1] = /* COND(!cso->seamless_cube_map, A6XX_TEX_SAMP_1_CUBEMAPSEAMLESSFILTOFF) | */ COND(pCreateInfo->unnormalizedCoordinates, A6XX_TEX_SAMP_1_UNNORM_COORDS) | A6XX_TEX_SAMP_1_MIN_LOD(min_lod) | A6XX_TEX_SAMP_1_MAX_LOD(max_lod) | COND(pCreateInfo->compareEnable, A6XX_TEX_SAMP_1_COMPARE_FUNC(tu6_compare_func(pCreateInfo->compareOp))); /* This is an offset into the border_color BO, which we fill with all the * possible Vulkan border colors in the correct order, so we can just use * the Vulkan enum with no translation necessary. */ sampler->descriptor[2] = A6XX_TEX_SAMP_2_BCOLOR_OFFSET((unsigned) pCreateInfo->borderColor * sizeof(struct bcolor_entry)); sampler->descriptor[3] = 0; if (reduction) { sampler->descriptor[2] |= A6XX_TEX_SAMP_2_REDUCTION_MODE( tu6_reduction_mode(reduction->reductionMode)); } sampler->ycbcr_sampler = ycbcr_conversion ? tu_sampler_ycbcr_conversion_from_handle(ycbcr_conversion->conversion) : NULL; if (sampler->ycbcr_sampler && sampler->ycbcr_sampler->chroma_filter == VK_FILTER_LINEAR) { sampler->descriptor[2] |= A6XX_TEX_SAMP_2_CHROMA_LINEAR; } /* TODO: * A6XX_TEX_SAMP_1_MIPFILTER_LINEAR_FAR disables mipmapping, but vk has no NONE mipfilter? */ } VkResult tu_CreateSampler(VkDevice _device, const VkSamplerCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSampler *pSampler) { TU_FROM_HANDLE(tu_device, device, _device); struct tu_sampler *sampler; assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO); sampler = vk_object_alloc(&device->vk, pAllocator, sizeof(*sampler), VK_OBJECT_TYPE_SAMPLER); if (!sampler) return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY); tu_init_sampler(device, sampler, pCreateInfo); *pSampler = tu_sampler_to_handle(sampler); return VK_SUCCESS; } void tu_DestroySampler(VkDevice _device, VkSampler _sampler, const VkAllocationCallbacks *pAllocator) { TU_FROM_HANDLE(tu_device, device, _device); TU_FROM_HANDLE(tu_sampler, sampler, _sampler); if (!sampler) return; vk_object_free(&device->vk, pAllocator, sampler); } /* vk_icd.h does not declare this function, so we declare it here to * suppress Wmissing-prototypes. */ PUBLIC VKAPI_ATTR VkResult VKAPI_CALL vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t *pSupportedVersion); PUBLIC VKAPI_ATTR VkResult VKAPI_CALL vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t *pSupportedVersion) { /* For the full details on loader interface versioning, see * . * What follows is a condensed summary, to help you navigate the large and * confusing official doc. * * - Loader interface v0 is incompatible with later versions. We don't * support it. * * - In loader interface v1: * - The first ICD entrypoint called by the loader is * vk_icdGetInstanceProcAddr(). The ICD must statically expose this * entrypoint. * - The ICD must statically expose no other Vulkan symbol unless it * is linked with -Bsymbolic. * - Each dispatchable Vulkan handle created by the ICD must be * a pointer to a struct whose first member is VK_LOADER_DATA. The * ICD must initialize VK_LOADER_DATA.loadMagic to * ICD_LOADER_MAGIC. * - The loader implements vkCreate{PLATFORM}SurfaceKHR() and * vkDestroySurfaceKHR(). The ICD must be capable of working with * such loader-managed surfaces. * * - Loader interface v2 differs from v1 in: * - The first ICD entrypoint called by the loader is * vk_icdNegotiateLoaderICDInterfaceVersion(). The ICD must * statically expose this entrypoint. * * - Loader interface v3 differs from v2 in: * - The ICD must implement vkCreate{PLATFORM}SurfaceKHR(), * vkDestroySurfaceKHR(), and other API which uses VKSurfaceKHR, * because the loader no longer does so. */ *pSupportedVersion = MIN2(*pSupportedVersion, 3u); return VK_SUCCESS; } VkResult tu_GetMemoryFdKHR(VkDevice _device, const VkMemoryGetFdInfoKHR *pGetFdInfo, int *pFd) { TU_FROM_HANDLE(tu_device, device, _device); TU_FROM_HANDLE(tu_device_memory, memory, pGetFdInfo->memory); assert(pGetFdInfo->sType == VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR); /* At the moment, we support only the below handle types. */ assert(pGetFdInfo->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT || pGetFdInfo->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT); int prime_fd = tu_bo_export_dmabuf(device, &memory->bo); if (prime_fd < 0) return vk_error(device->instance, VK_ERROR_OUT_OF_DEVICE_MEMORY); *pFd = prime_fd; return VK_SUCCESS; } VkResult tu_GetMemoryFdPropertiesKHR(VkDevice _device, VkExternalMemoryHandleTypeFlagBits handleType, int fd, VkMemoryFdPropertiesKHR *pMemoryFdProperties) { assert(handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT); pMemoryFdProperties->memoryTypeBits = 1; return VK_SUCCESS; } VkResult tu_ImportFenceFdKHR(VkDevice _device, const VkImportFenceFdInfoKHR *pImportFenceFdInfo) { tu_stub(); return VK_SUCCESS; } VkResult tu_GetFenceFdKHR(VkDevice _device, const VkFenceGetFdInfoKHR *pGetFdInfo, int *pFd) { tu_stub(); return VK_SUCCESS; } 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; } } void tu_GetPhysicalDeviceExternalFenceProperties( VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo *pExternalFenceInfo, VkExternalFenceProperties *pExternalFenceProperties) { pExternalFenceProperties->exportFromImportedHandleTypes = 0; pExternalFenceProperties->compatibleHandleTypes = 0; pExternalFenceProperties->externalFenceFeatures = 0; } VkResult tu_CreateDebugReportCallbackEXT( VkInstance _instance, const VkDebugReportCallbackCreateInfoEXT *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDebugReportCallbackEXT *pCallback) { TU_FROM_HANDLE(tu_instance, instance, _instance); return vk_create_debug_report_callback(&instance->debug_report_callbacks, pCreateInfo, pAllocator, &instance->alloc, pCallback); } void tu_DestroyDebugReportCallbackEXT(VkInstance _instance, VkDebugReportCallbackEXT _callback, const VkAllocationCallbacks *pAllocator) { TU_FROM_HANDLE(tu_instance, instance, _instance); vk_destroy_debug_report_callback(&instance->debug_report_callbacks, _callback, pAllocator, &instance->alloc); } void tu_DebugReportMessageEXT(VkInstance _instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char *pLayerPrefix, const char *pMessage) { TU_FROM_HANDLE(tu_instance, instance, _instance); vk_debug_report(&instance->debug_report_callbacks, flags, objectType, object, location, messageCode, pLayerPrefix, pMessage); } void tu_GetDeviceGroupPeerMemoryFeatures( VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags *pPeerMemoryFeatures) { assert(localDeviceIndex == remoteDeviceIndex); *pPeerMemoryFeatures = VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT | VK_PEER_MEMORY_FEATURE_COPY_DST_BIT | VK_PEER_MEMORY_FEATURE_GENERIC_SRC_BIT | VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT; } void tu_GetPhysicalDeviceMultisamplePropertiesEXT( VkPhysicalDevice physicalDevice, VkSampleCountFlagBits samples, VkMultisamplePropertiesEXT* pMultisampleProperties) { TU_FROM_HANDLE(tu_physical_device, pdevice, physicalDevice); if (samples <= VK_SAMPLE_COUNT_4_BIT && pdevice->supported_extensions.EXT_sample_locations) pMultisampleProperties->maxSampleLocationGridSize = (VkExtent2D){ 1, 1 }; else pMultisampleProperties->maxSampleLocationGridSize = (VkExtent2D){ 0, 0 }; } VkResult tu_CreatePrivateDataSlotEXT(VkDevice _device, const VkPrivateDataSlotCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPrivateDataSlotEXT* pPrivateDataSlot) { TU_FROM_HANDLE(tu_device, device, _device); return vk_private_data_slot_create(&device->vk, pCreateInfo, pAllocator, pPrivateDataSlot); } void tu_DestroyPrivateDataSlotEXT(VkDevice _device, VkPrivateDataSlotEXT privateDataSlot, const VkAllocationCallbacks* pAllocator) { TU_FROM_HANDLE(tu_device, device, _device); vk_private_data_slot_destroy(&device->vk, privateDataSlot, pAllocator); } VkResult tu_SetPrivateDataEXT(VkDevice _device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlotEXT privateDataSlot, uint64_t data) { TU_FROM_HANDLE(tu_device, device, _device); return vk_object_base_set_private_data(&device->vk, objectType, objectHandle, privateDataSlot, data); } void tu_GetPrivateDataEXT(VkDevice _device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlotEXT privateDataSlot, uint64_t* pData) { TU_FROM_HANDLE(tu_device, device, _device); vk_object_base_get_private_data(&device->vk, objectType, objectHandle, privateDataSlot, pData); }