diff --git a/meson_options.txt b/meson_options.txt index e0ce124140c..a3967c50d59 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -221,7 +221,7 @@ option( 'vulkan-layers', type : 'array', value : [], - choices : ['device-select', 'overlay'], + choices : ['device-select', 'intel-nullhw', 'overlay'], description : 'List of vulkan layers to build' ) option( diff --git a/src/intel/meson.build b/src/intel/meson.build index 528146f3bb7..3d5cf817c35 100644 --- a/src/intel/meson.build +++ b/src/intel/meson.build @@ -31,6 +31,9 @@ subdir('perf') if with_intel_tools subdir('tools') endif +if get_option('vulkan-layers').contains('intel-nullhw') + subdir('nullhw-layer') +endif if with_intel_vk subdir('vulkan') endif diff --git a/src/intel/nullhw-layer/README b/src/intel/nullhw-layer/README new file mode 100644 index 00000000000..8ecbd5cdf7d --- /dev/null +++ b/src/intel/nullhw-layer/README @@ -0,0 +1,5 @@ +A Vulkan layer to disable all rendering/compute commands. + +To turn on the layer run : + +VK_INSTANCE_LAYERS=VK_LAYER_INTEL_nullhw /path/to/my_vulkan_app diff --git a/src/intel/nullhw-layer/VkLayer_INTEL_nullhw.json b/src/intel/nullhw-layer/VkLayer_INTEL_nullhw.json new file mode 100644 index 00000000000..49f67a449cd --- /dev/null +++ b/src/intel/nullhw-layer/VkLayer_INTEL_nullhw.json @@ -0,0 +1,11 @@ +{ + "file_format_version" : "1.0.0", + "layer" : { + "name": "VK_LAYER_INTEL_nullhw", + "type": "GLOBAL", + "library_path": "libVkLayer_INTEL_nullhw.so", + "api_version": "1.1.73", + "implementation_version": "1", + "description": "INTEL NULL HW" + } +} diff --git a/src/intel/nullhw-layer/intel_nullhw.c b/src/intel/nullhw-layer/intel_nullhw.c new file mode 100644 index 00000000000..2546f0d492e --- /dev/null +++ b/src/intel/nullhw-layer/intel_nullhw.c @@ -0,0 +1,375 @@ +/* + * 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_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_load_device_commands(*pDevice, fpGetDeviceProcAddr, &device_data->vtable); + + 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_load_instance_commands(instance_data->instance, + fpGetInstanceProcAddr, + &instance_data->vtable); + + 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); +} diff --git a/src/intel/nullhw-layer/meson.build b/src/intel/nullhw-layer/meson.build new file mode 100644 index 00000000000..618b7788776 --- /dev/null +++ b/src/intel/nullhw-layer/meson.build @@ -0,0 +1,38 @@ +# 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 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. + +vklayer_intel_nullhw_files = files( + 'intel_nullhw.c', +) + +vklayer_intel_nullhw = shared_library( + 'VkLayer_INTEL_nullhw', + vklayer_intel_nullhw_files, + c_args : [no_override_init_args, vulkan_wsi_args], + dependencies : [idep_vulkan_util, idep_mesautil, vulkan_wsi_deps, dep_dl], + include_directories : [inc_include, inc_src], + link_args : cc.get_supported_link_arguments(['-Wl,-Bsymbolic-functions', '-Wl,-z,relro']), + install : true +) + +install_data( + files('VkLayer_INTEL_nullhw.json'), + install_dir : join_paths(get_option('datadir'), 'vulkan', 'explicit_layer.d'), +)