intel: Add null hw layer
authorLionel Landwerlin <lionel.g.landwerlin@intel.com>
Mon, 11 Nov 2019 21:45:17 +0000 (23:45 +0200)
committerMarge Bot <eric+marge@anholt.net>
Thu, 25 Mar 2021 19:10:03 +0000 (19:10 +0000)
Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Acked-by: Jason Ekstrand <jason@jlekstrand.net>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/2832>

meson_options.txt
src/intel/meson.build
src/intel/nullhw-layer/README [new file with mode: 0644]
src/intel/nullhw-layer/VkLayer_INTEL_nullhw.json [new file with mode: 0644]
src/intel/nullhw-layer/intel_nullhw.c [new file with mode: 0644]
src/intel/nullhw-layer/meson.build [new file with mode: 0644]

index e0ce124..a3967c5 100644 (file)
@@ -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(
index 528146f..3d5cf81 100644 (file)
@@ -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 (file)
index 0000000..8ecbd5c
--- /dev/null
@@ -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 (file)
index 0000000..49f67a4
--- /dev/null
@@ -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 (file)
index 0000000..2546f0d
--- /dev/null
@@ -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 <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include <vulkan/vulkan.h>
+#include <vulkan/vk_layer.h>
+
+#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 (file)
index 0000000..618b778
--- /dev/null
@@ -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'),
+)