/* * Copyright © 2019 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 #include #include #include #include #include #include "util/debug.h" #include "util/hash_table.h" #include "util/macros.h" #include "util/simple_mtx.h" #include "vk_dispatch_table.h" #include "vk_enum_to_str.h" #include "vk_util.h" struct instance_data { struct vk_instance_dispatch_table vtable; VkInstance instance; }; struct device_data { struct instance_data *instance; PFN_vkSetDeviceLoaderData set_device_loader_data; struct vk_device_dispatch_table vtable; VkPhysicalDevice physical_device; VkDevice device; }; static struct hash_table_u64 *vk_object_to_data = NULL; static simple_mtx_t vk_object_to_data_mutex = _SIMPLE_MTX_INITIALIZER_NP; static inline void ensure_vk_object_map(void) { if (!vk_object_to_data) vk_object_to_data = _mesa_hash_table_u64_create(NULL); } #define HKEY(obj) ((uint64_t)(obj)) #define FIND(type, obj) ((type *)find_object_data(HKEY(obj))) static void *find_object_data(uint64_t obj) { simple_mtx_lock(&vk_object_to_data_mutex); ensure_vk_object_map(); void *data = _mesa_hash_table_u64_search(vk_object_to_data, obj); simple_mtx_unlock(&vk_object_to_data_mutex); return data; } static void map_object(uint64_t obj, void *data) { simple_mtx_lock(&vk_object_to_data_mutex); ensure_vk_object_map(); _mesa_hash_table_u64_insert(vk_object_to_data, obj, data); simple_mtx_unlock(&vk_object_to_data_mutex); } static void unmap_object(uint64_t obj) { simple_mtx_lock(&vk_object_to_data_mutex); _mesa_hash_table_u64_remove(vk_object_to_data, obj); simple_mtx_unlock(&vk_object_to_data_mutex); } /**/ #define VK_CHECK(expr) \ do { \ VkResult __result = (expr); \ if (__result != VK_SUCCESS) { \ fprintf(stderr, "'%s' line %i failed with %s\n", \ #expr, __LINE__, vk_Result_to_str(__result)); \ } \ } while (0) /**/ static void override_queue(struct device_data *device_data, VkDevice device, uint32_t queue_family_index, VkQueue queue) { VkCommandPoolCreateInfo cmd_buffer_pool_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .queueFamilyIndex = queue_family_index, }; VkCommandPool cmd_pool; VK_CHECK(device_data->vtable.CreateCommandPool(device, &cmd_buffer_pool_info, NULL, &cmd_pool)); VkCommandBufferAllocateInfo cmd_buffer_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .commandPool = cmd_pool, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = 1, }; VkCommandBuffer cmd_buffer; VK_CHECK(device_data->vtable.AllocateCommandBuffers(device, &cmd_buffer_info, &cmd_buffer)); VK_CHECK(device_data->set_device_loader_data(device, cmd_buffer)); VkCommandBufferBeginInfo buffer_begin_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, }; device_data->vtable.BeginCommandBuffer(cmd_buffer, &buffer_begin_info); VkPerformanceOverrideInfoINTEL override_info = { .sType = VK_STRUCTURE_TYPE_PERFORMANCE_OVERRIDE_INFO_INTEL, .type = VK_PERFORMANCE_OVERRIDE_TYPE_NULL_HARDWARE_INTEL, .enable = VK_TRUE, }; device_data->vtable.CmdSetPerformanceOverrideINTEL(cmd_buffer, &override_info); device_data->vtable.EndCommandBuffer(cmd_buffer); VkSubmitInfo submit_info = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .commandBufferCount = 1, .pCommandBuffers = &cmd_buffer, }; VK_CHECK(device_data->vtable.QueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE)); VK_CHECK(device_data->vtable.QueueWaitIdle(queue)); device_data->vtable.DestroyCommandPool(device, cmd_pool, NULL); } static void device_override_queues(struct device_data *device_data, const VkDeviceCreateInfo *pCreateInfo) { for (uint32_t i = 0; i < pCreateInfo->queueCreateInfoCount; i++) { for (uint32_t j = 0; j < pCreateInfo->pQueueCreateInfos[i].queueCount; j++) { VkQueue queue; device_data->vtable.GetDeviceQueue(device_data->device, pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex, j, &queue); VK_CHECK(device_data->set_device_loader_data(device_data->device, queue)); override_queue(device_data, device_data->device, pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex, queue); } } } static VkLayerDeviceCreateInfo *get_device_chain_info(const VkDeviceCreateInfo *pCreateInfo, VkLayerFunction func) { vk_foreach_struct(item, pCreateInfo->pNext) { if (item->sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO && ((VkLayerDeviceCreateInfo *) item)->function == func) return (VkLayerDeviceCreateInfo *)item; } unreachable("device chain info not found"); return NULL; } static struct device_data *new_device_data(VkDevice device, struct instance_data *instance) { struct device_data *data = calloc(1, sizeof(*data)); data->instance = instance; data->device = device; map_object(HKEY(data->device), data); return data; } static void destroy_device_data(struct device_data *data) { unmap_object(HKEY(data->device)); free(data); } static VkResult nullhw_CreateDevice( VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice) { VkLayerDeviceCreateInfo *chain_info = get_device_chain_info(pCreateInfo, VK_LAYER_LINK_INFO); assert(chain_info->u.pLayerInfo); PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr; PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr = chain_info->u.pLayerInfo->pfnNextGetDeviceProcAddr; PFN_vkCreateDevice fpCreateDevice = (PFN_vkCreateDevice)fpGetInstanceProcAddr(NULL, "vkCreateDevice"); if (fpCreateDevice == NULL) { return VK_ERROR_INITIALIZATION_FAILED; } // Advance the link info for the next element on the chain chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext; VkDeviceCreateInfo device_info = *pCreateInfo; const char **extensions = calloc(device_info.enabledExtensionCount + 1, sizeof(*extensions)); bool found = false; for (uint32_t i = 0; i < device_info.enabledExtensionCount; i++) { if (!strcmp(device_info.ppEnabledExtensionNames[i], "VK_INTEL_performance_query")) { found = true; break; } } if (!found) { memcpy(extensions, device_info.ppEnabledExtensionNames, sizeof(*extensions) * device_info.enabledExtensionCount); extensions[device_info.enabledExtensionCount++] = "VK_INTEL_performance_query"; device_info.ppEnabledExtensionNames = extensions; } VkResult result = fpCreateDevice(physicalDevice, &device_info, pAllocator, pDevice); free(extensions); if (result != VK_SUCCESS) return result; struct instance_data *instance_data = FIND(struct instance_data, physicalDevice); struct device_data *device_data = new_device_data(*pDevice, instance_data); device_data->physical_device = physicalDevice; vk_device_dispatch_table_load(&device_data->vtable, fpGetDeviceProcAddr, *pDevice); VkLayerDeviceCreateInfo *load_data_info = get_device_chain_info(pCreateInfo, VK_LOADER_DATA_CALLBACK); device_data->set_device_loader_data = load_data_info->u.pfnSetDeviceLoaderData; device_override_queues(device_data, pCreateInfo); return result; } static void nullhw_DestroyDevice( VkDevice device, const VkAllocationCallbacks* pAllocator) { struct device_data *device_data = FIND(struct device_data, device); device_data->vtable.DestroyDevice(device, pAllocator); destroy_device_data(device_data); } static struct instance_data *new_instance_data(VkInstance instance) { struct instance_data *data = calloc(1, sizeof(*data)); data->instance = instance; map_object(HKEY(data->instance), data); return data; } static void destroy_instance_data(struct instance_data *data) { unmap_object(HKEY(data->instance)); free(data); } static VkLayerInstanceCreateInfo *get_instance_chain_info(const VkInstanceCreateInfo *pCreateInfo, VkLayerFunction func) { vk_foreach_struct(item, pCreateInfo->pNext) { if (item->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO && ((VkLayerInstanceCreateInfo *) item)->function == func) return (VkLayerInstanceCreateInfo *) item; } unreachable("instance chain info not found"); return NULL; } static VkResult nullhw_CreateInstance( const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance) { VkLayerInstanceCreateInfo *chain_info = get_instance_chain_info(pCreateInfo, VK_LAYER_LINK_INFO); assert(chain_info->u.pLayerInfo); PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr; PFN_vkCreateInstance fpCreateInstance = (PFN_vkCreateInstance)fpGetInstanceProcAddr(NULL, "vkCreateInstance"); if (fpCreateInstance == NULL) { return VK_ERROR_INITIALIZATION_FAILED; } // Advance the link info for the next element on the chain chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext; VkResult result = fpCreateInstance(pCreateInfo, pAllocator, pInstance); if (result != VK_SUCCESS) return result; struct instance_data *instance_data = new_instance_data(*pInstance); vk_instance_dispatch_table_load(&instance_data->vtable, fpGetInstanceProcAddr, instance_data->instance); return result; } static void nullhw_DestroyInstance( VkInstance instance, const VkAllocationCallbacks* pAllocator) { struct instance_data *instance_data = FIND(struct instance_data, instance); instance_data->vtable.DestroyInstance(instance, pAllocator); destroy_instance_data(instance_data); } static const struct { const char *name; void *ptr; } name_to_funcptr_map[] = { { "vkGetDeviceProcAddr", (void *) vkGetDeviceProcAddr }, #define ADD_HOOK(fn) { "vk" # fn, (void *) nullhw_ ## fn } ADD_HOOK(CreateInstance), ADD_HOOK(DestroyInstance), ADD_HOOK(CreateDevice), ADD_HOOK(DestroyDevice), }; static void *find_ptr(const char *name) { for (uint32_t i = 0; i < ARRAY_SIZE(name_to_funcptr_map); i++) { if (strcmp(name, name_to_funcptr_map[i].name) == 0) return name_to_funcptr_map[i].ptr; } return NULL; } VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice dev, const char *funcName) { void *ptr = find_ptr(funcName); if (ptr) return (PFN_vkVoidFunction)(ptr); if (dev == NULL) return NULL; struct device_data *device_data = FIND(struct device_data, dev); if (device_data->vtable.GetDeviceProcAddr == NULL) return NULL; return device_data->vtable.GetDeviceProcAddr(dev, funcName); } VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, const char *funcName) { void *ptr = find_ptr(funcName); if (ptr) return (PFN_vkVoidFunction) ptr; struct instance_data *instance_data = FIND(struct instance_data, instance); if (instance_data->vtable.GetInstanceProcAddr == NULL) return NULL; return instance_data->vtable.GetInstanceProcAddr(instance, funcName); }