17ec681f3Smrg/*
27ec681f3Smrg * Copyright © 2019 Intel Corporation
37ec681f3Smrg *
47ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
57ec681f3Smrg * copy of this software and associated documentation files (the "Software"),
67ec681f3Smrg * to deal in the Software without restriction, including without limitation
77ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
87ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the
97ec681f3Smrg * Software is furnished to do so, subject to the following conditions:
107ec681f3Smrg *
117ec681f3Smrg * The above copyright notice and this permission notice (including the next
127ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the
137ec681f3Smrg * Software.
147ec681f3Smrg *
157ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
167ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
177ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
187ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
197ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
207ec681f3Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
217ec681f3Smrg * IN THE SOFTWARE.
227ec681f3Smrg */
237ec681f3Smrg
247ec681f3Smrg#include <string.h>
257ec681f3Smrg#include <stdlib.h>
267ec681f3Smrg#include <assert.h>
277ec681f3Smrg#include <stdio.h>
287ec681f3Smrg
297ec681f3Smrg#include <vulkan/vulkan.h>
307ec681f3Smrg#include <vulkan/vk_layer.h>
317ec681f3Smrg
327ec681f3Smrg#include "util/debug.h"
337ec681f3Smrg#include "util/hash_table.h"
347ec681f3Smrg#include "util/macros.h"
357ec681f3Smrg#include "util/simple_mtx.h"
367ec681f3Smrg
377ec681f3Smrg#include "vk_dispatch_table.h"
387ec681f3Smrg#include "vk_enum_to_str.h"
397ec681f3Smrg#include "vk_util.h"
407ec681f3Smrg
417ec681f3Smrgstruct instance_data {
427ec681f3Smrg   struct vk_instance_dispatch_table vtable;
437ec681f3Smrg   VkInstance instance;
447ec681f3Smrg};
457ec681f3Smrg
467ec681f3Smrgstruct device_data {
477ec681f3Smrg   struct instance_data *instance;
487ec681f3Smrg
497ec681f3Smrg   PFN_vkSetDeviceLoaderData set_device_loader_data;
507ec681f3Smrg
517ec681f3Smrg   struct vk_device_dispatch_table vtable;
527ec681f3Smrg   VkPhysicalDevice physical_device;
537ec681f3Smrg   VkDevice device;
547ec681f3Smrg};
557ec681f3Smrg
567ec681f3Smrgstatic struct hash_table_u64 *vk_object_to_data = NULL;
577ec681f3Smrgstatic simple_mtx_t vk_object_to_data_mutex = _SIMPLE_MTX_INITIALIZER_NP;
587ec681f3Smrg
597ec681f3Smrgstatic inline void ensure_vk_object_map(void)
607ec681f3Smrg{
617ec681f3Smrg   if (!vk_object_to_data)
627ec681f3Smrg      vk_object_to_data = _mesa_hash_table_u64_create(NULL);
637ec681f3Smrg}
647ec681f3Smrg
657ec681f3Smrg#define HKEY(obj) ((uint64_t)(obj))
667ec681f3Smrg#define FIND(type, obj) ((type *)find_object_data(HKEY(obj)))
677ec681f3Smrg
687ec681f3Smrgstatic void *find_object_data(uint64_t obj)
697ec681f3Smrg{
707ec681f3Smrg   simple_mtx_lock(&vk_object_to_data_mutex);
717ec681f3Smrg   ensure_vk_object_map();
727ec681f3Smrg   void *data = _mesa_hash_table_u64_search(vk_object_to_data, obj);
737ec681f3Smrg   simple_mtx_unlock(&vk_object_to_data_mutex);
747ec681f3Smrg   return data;
757ec681f3Smrg}
767ec681f3Smrg
777ec681f3Smrgstatic void map_object(uint64_t obj, void *data)
787ec681f3Smrg{
797ec681f3Smrg   simple_mtx_lock(&vk_object_to_data_mutex);
807ec681f3Smrg   ensure_vk_object_map();
817ec681f3Smrg   _mesa_hash_table_u64_insert(vk_object_to_data, obj, data);
827ec681f3Smrg   simple_mtx_unlock(&vk_object_to_data_mutex);
837ec681f3Smrg}
847ec681f3Smrg
857ec681f3Smrgstatic void unmap_object(uint64_t obj)
867ec681f3Smrg{
877ec681f3Smrg   simple_mtx_lock(&vk_object_to_data_mutex);
887ec681f3Smrg   _mesa_hash_table_u64_remove(vk_object_to_data, obj);
897ec681f3Smrg   simple_mtx_unlock(&vk_object_to_data_mutex);
907ec681f3Smrg}
917ec681f3Smrg
927ec681f3Smrg/**/
937ec681f3Smrg
947ec681f3Smrg#define VK_CHECK(expr) \
957ec681f3Smrg   do { \
967ec681f3Smrg      VkResult __result = (expr); \
977ec681f3Smrg      if (__result != VK_SUCCESS) { \
987ec681f3Smrg         fprintf(stderr, "'%s' line %i failed with %s\n", \
997ec681f3Smrg                 #expr, __LINE__, vk_Result_to_str(__result)); \
1007ec681f3Smrg      } \
1017ec681f3Smrg   } while (0)
1027ec681f3Smrg
1037ec681f3Smrg/**/
1047ec681f3Smrg
1057ec681f3Smrgstatic void override_queue(struct device_data *device_data,
1067ec681f3Smrg                           VkDevice device,
1077ec681f3Smrg                           uint32_t queue_family_index,
1087ec681f3Smrg                           VkQueue queue)
1097ec681f3Smrg{
1107ec681f3Smrg   VkCommandPoolCreateInfo cmd_buffer_pool_info = {
1117ec681f3Smrg      .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
1127ec681f3Smrg      .queueFamilyIndex = queue_family_index,
1137ec681f3Smrg   };
1147ec681f3Smrg   VkCommandPool cmd_pool;
1157ec681f3Smrg   VK_CHECK(device_data->vtable.CreateCommandPool(device,
1167ec681f3Smrg                                                  &cmd_buffer_pool_info,
1177ec681f3Smrg                                                  NULL, &cmd_pool));
1187ec681f3Smrg
1197ec681f3Smrg
1207ec681f3Smrg   VkCommandBufferAllocateInfo cmd_buffer_info = {
1217ec681f3Smrg      .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
1227ec681f3Smrg      .commandPool = cmd_pool,
1237ec681f3Smrg      .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
1247ec681f3Smrg      .commandBufferCount = 1,
1257ec681f3Smrg   };
1267ec681f3Smrg   VkCommandBuffer cmd_buffer;
1277ec681f3Smrg   VK_CHECK(device_data->vtable.AllocateCommandBuffers(device,
1287ec681f3Smrg                                                       &cmd_buffer_info,
1297ec681f3Smrg                                                       &cmd_buffer));
1307ec681f3Smrg   VK_CHECK(device_data->set_device_loader_data(device, cmd_buffer));
1317ec681f3Smrg
1327ec681f3Smrg   VkCommandBufferBeginInfo buffer_begin_info = {
1337ec681f3Smrg      .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
1347ec681f3Smrg   };
1357ec681f3Smrg   device_data->vtable.BeginCommandBuffer(cmd_buffer, &buffer_begin_info);
1367ec681f3Smrg
1377ec681f3Smrg   VkPerformanceOverrideInfoINTEL override_info = {
1387ec681f3Smrg      .sType = VK_STRUCTURE_TYPE_PERFORMANCE_OVERRIDE_INFO_INTEL,
1397ec681f3Smrg      .type = VK_PERFORMANCE_OVERRIDE_TYPE_NULL_HARDWARE_INTEL,
1407ec681f3Smrg      .enable = VK_TRUE,
1417ec681f3Smrg   };
1427ec681f3Smrg   device_data->vtable.CmdSetPerformanceOverrideINTEL(cmd_buffer, &override_info);
1437ec681f3Smrg
1447ec681f3Smrg   device_data->vtable.EndCommandBuffer(cmd_buffer);
1457ec681f3Smrg
1467ec681f3Smrg   VkSubmitInfo submit_info = {
1477ec681f3Smrg      .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
1487ec681f3Smrg      .commandBufferCount = 1,
1497ec681f3Smrg      .pCommandBuffers = &cmd_buffer,
1507ec681f3Smrg   };
1517ec681f3Smrg   VK_CHECK(device_data->vtable.QueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE));
1527ec681f3Smrg
1537ec681f3Smrg   VK_CHECK(device_data->vtable.QueueWaitIdle(queue));
1547ec681f3Smrg
1557ec681f3Smrg   device_data->vtable.DestroyCommandPool(device, cmd_pool, NULL);
1567ec681f3Smrg}
1577ec681f3Smrg
1587ec681f3Smrgstatic void device_override_queues(struct device_data *device_data,
1597ec681f3Smrg                                   const VkDeviceCreateInfo *pCreateInfo)
1607ec681f3Smrg{
1617ec681f3Smrg   for (uint32_t i = 0; i < pCreateInfo->queueCreateInfoCount; i++) {
1627ec681f3Smrg      for (uint32_t j = 0; j < pCreateInfo->pQueueCreateInfos[i].queueCount; j++) {
1637ec681f3Smrg         VkQueue queue;
1647ec681f3Smrg         device_data->vtable.GetDeviceQueue(device_data->device,
1657ec681f3Smrg                                            pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex,
1667ec681f3Smrg                                            j, &queue);
1677ec681f3Smrg
1687ec681f3Smrg         VK_CHECK(device_data->set_device_loader_data(device_data->device, queue));
1697ec681f3Smrg
1707ec681f3Smrg         override_queue(device_data, device_data->device,
1717ec681f3Smrg                        pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex, queue);
1727ec681f3Smrg      }
1737ec681f3Smrg   }
1747ec681f3Smrg}
1757ec681f3Smrg
1767ec681f3Smrgstatic VkLayerDeviceCreateInfo *get_device_chain_info(const VkDeviceCreateInfo *pCreateInfo,
1777ec681f3Smrg                                                      VkLayerFunction func)
1787ec681f3Smrg{
1797ec681f3Smrg   vk_foreach_struct(item, pCreateInfo->pNext) {
1807ec681f3Smrg      if (item->sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO &&
1817ec681f3Smrg          ((VkLayerDeviceCreateInfo *) item)->function == func)
1827ec681f3Smrg         return (VkLayerDeviceCreateInfo *)item;
1837ec681f3Smrg   }
1847ec681f3Smrg   unreachable("device chain info not found");
1857ec681f3Smrg   return NULL;
1867ec681f3Smrg}
1877ec681f3Smrg
1887ec681f3Smrgstatic struct device_data *new_device_data(VkDevice device, struct instance_data *instance)
1897ec681f3Smrg{
1907ec681f3Smrg   struct device_data *data = calloc(1, sizeof(*data));
1917ec681f3Smrg   data->instance = instance;
1927ec681f3Smrg   data->device = device;
1937ec681f3Smrg   map_object(HKEY(data->device), data);
1947ec681f3Smrg   return data;
1957ec681f3Smrg}
1967ec681f3Smrg
1977ec681f3Smrgstatic void destroy_device_data(struct device_data *data)
1987ec681f3Smrg{
1997ec681f3Smrg   unmap_object(HKEY(data->device));
2007ec681f3Smrg   free(data);
2017ec681f3Smrg}
2027ec681f3Smrg
2037ec681f3Smrgstatic VkResult nullhw_CreateDevice(
2047ec681f3Smrg    VkPhysicalDevice                            physicalDevice,
2057ec681f3Smrg    const VkDeviceCreateInfo*                   pCreateInfo,
2067ec681f3Smrg    const VkAllocationCallbacks*                pAllocator,
2077ec681f3Smrg    VkDevice*                                   pDevice)
2087ec681f3Smrg{
2097ec681f3Smrg   VkLayerDeviceCreateInfo *chain_info =
2107ec681f3Smrg      get_device_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
2117ec681f3Smrg
2127ec681f3Smrg   assert(chain_info->u.pLayerInfo);
2137ec681f3Smrg   PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
2147ec681f3Smrg   PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr = chain_info->u.pLayerInfo->pfnNextGetDeviceProcAddr;
2157ec681f3Smrg   PFN_vkCreateDevice fpCreateDevice = (PFN_vkCreateDevice)fpGetInstanceProcAddr(NULL, "vkCreateDevice");
2167ec681f3Smrg   if (fpCreateDevice == NULL) {
2177ec681f3Smrg      return VK_ERROR_INITIALIZATION_FAILED;
2187ec681f3Smrg   }
2197ec681f3Smrg
2207ec681f3Smrg   // Advance the link info for the next element on the chain
2217ec681f3Smrg   chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
2227ec681f3Smrg
2237ec681f3Smrg   VkDeviceCreateInfo device_info = *pCreateInfo;
2247ec681f3Smrg   const char **extensions = calloc(device_info.enabledExtensionCount + 1, sizeof(*extensions));
2257ec681f3Smrg   bool found = false;
2267ec681f3Smrg   for (uint32_t i = 0; i < device_info.enabledExtensionCount; i++) {
2277ec681f3Smrg      if (!strcmp(device_info.ppEnabledExtensionNames[i], "VK_INTEL_performance_query")) {
2287ec681f3Smrg         found = true;
2297ec681f3Smrg         break;
2307ec681f3Smrg      }
2317ec681f3Smrg   }
2327ec681f3Smrg   if (!found) {
2337ec681f3Smrg      memcpy(extensions, device_info.ppEnabledExtensionNames,
2347ec681f3Smrg             sizeof(*extensions) * device_info.enabledExtensionCount);
2357ec681f3Smrg      extensions[device_info.enabledExtensionCount++] = "VK_INTEL_performance_query";
2367ec681f3Smrg      device_info.ppEnabledExtensionNames = extensions;
2377ec681f3Smrg   }
2387ec681f3Smrg
2397ec681f3Smrg   VkResult result = fpCreateDevice(physicalDevice, &device_info, pAllocator, pDevice);
2407ec681f3Smrg   free(extensions);
2417ec681f3Smrg   if (result != VK_SUCCESS) return result;
2427ec681f3Smrg
2437ec681f3Smrg   struct instance_data *instance_data = FIND(struct instance_data, physicalDevice);
2447ec681f3Smrg   struct device_data *device_data = new_device_data(*pDevice, instance_data);
2457ec681f3Smrg   device_data->physical_device = physicalDevice;
2467ec681f3Smrg   vk_device_dispatch_table_load(&device_data->vtable, fpGetDeviceProcAddr, *pDevice);
2477ec681f3Smrg
2487ec681f3Smrg   VkLayerDeviceCreateInfo *load_data_info =
2497ec681f3Smrg      get_device_chain_info(pCreateInfo, VK_LOADER_DATA_CALLBACK);
2507ec681f3Smrg   device_data->set_device_loader_data = load_data_info->u.pfnSetDeviceLoaderData;
2517ec681f3Smrg
2527ec681f3Smrg   device_override_queues(device_data, pCreateInfo);
2537ec681f3Smrg
2547ec681f3Smrg   return result;
2557ec681f3Smrg}
2567ec681f3Smrg
2577ec681f3Smrgstatic void nullhw_DestroyDevice(
2587ec681f3Smrg    VkDevice                                    device,
2597ec681f3Smrg    const VkAllocationCallbacks*                pAllocator)
2607ec681f3Smrg{
2617ec681f3Smrg   struct device_data *device_data = FIND(struct device_data, device);
2627ec681f3Smrg   device_data->vtable.DestroyDevice(device, pAllocator);
2637ec681f3Smrg   destroy_device_data(device_data);
2647ec681f3Smrg}
2657ec681f3Smrg
2667ec681f3Smrgstatic struct instance_data *new_instance_data(VkInstance instance)
2677ec681f3Smrg{
2687ec681f3Smrg   struct instance_data *data = calloc(1, sizeof(*data));
2697ec681f3Smrg   data->instance = instance;
2707ec681f3Smrg   map_object(HKEY(data->instance), data);
2717ec681f3Smrg   return data;
2727ec681f3Smrg}
2737ec681f3Smrg
2747ec681f3Smrgstatic void destroy_instance_data(struct instance_data *data)
2757ec681f3Smrg{
2767ec681f3Smrg   unmap_object(HKEY(data->instance));
2777ec681f3Smrg   free(data);
2787ec681f3Smrg}
2797ec681f3Smrg
2807ec681f3Smrgstatic VkLayerInstanceCreateInfo *get_instance_chain_info(const VkInstanceCreateInfo *pCreateInfo,
2817ec681f3Smrg                                                          VkLayerFunction func)
2827ec681f3Smrg{
2837ec681f3Smrg   vk_foreach_struct(item, pCreateInfo->pNext) {
2847ec681f3Smrg      if (item->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO &&
2857ec681f3Smrg          ((VkLayerInstanceCreateInfo *) item)->function == func)
2867ec681f3Smrg         return (VkLayerInstanceCreateInfo *) item;
2877ec681f3Smrg   }
2887ec681f3Smrg   unreachable("instance chain info not found");
2897ec681f3Smrg   return NULL;
2907ec681f3Smrg}
2917ec681f3Smrg
2927ec681f3Smrgstatic VkResult nullhw_CreateInstance(
2937ec681f3Smrg    const VkInstanceCreateInfo*                 pCreateInfo,
2947ec681f3Smrg    const VkAllocationCallbacks*                pAllocator,
2957ec681f3Smrg    VkInstance*                                 pInstance)
2967ec681f3Smrg{
2977ec681f3Smrg   VkLayerInstanceCreateInfo *chain_info =
2987ec681f3Smrg      get_instance_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
2997ec681f3Smrg
3007ec681f3Smrg   assert(chain_info->u.pLayerInfo);
3017ec681f3Smrg   PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr =
3027ec681f3Smrg      chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
3037ec681f3Smrg   PFN_vkCreateInstance fpCreateInstance =
3047ec681f3Smrg      (PFN_vkCreateInstance)fpGetInstanceProcAddr(NULL, "vkCreateInstance");
3057ec681f3Smrg   if (fpCreateInstance == NULL) {
3067ec681f3Smrg      return VK_ERROR_INITIALIZATION_FAILED;
3077ec681f3Smrg   }
3087ec681f3Smrg
3097ec681f3Smrg   // Advance the link info for the next element on the chain
3107ec681f3Smrg   chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
3117ec681f3Smrg
3127ec681f3Smrg   VkResult result = fpCreateInstance(pCreateInfo, pAllocator, pInstance);
3137ec681f3Smrg   if (result != VK_SUCCESS) return result;
3147ec681f3Smrg
3157ec681f3Smrg   struct instance_data *instance_data = new_instance_data(*pInstance);
3167ec681f3Smrg   vk_instance_dispatch_table_load(&instance_data->vtable,
3177ec681f3Smrg                                   fpGetInstanceProcAddr,
3187ec681f3Smrg                                   instance_data->instance);
3197ec681f3Smrg
3207ec681f3Smrg   return result;
3217ec681f3Smrg}
3227ec681f3Smrg
3237ec681f3Smrgstatic void nullhw_DestroyInstance(
3247ec681f3Smrg    VkInstance                                  instance,
3257ec681f3Smrg    const VkAllocationCallbacks*                pAllocator)
3267ec681f3Smrg{
3277ec681f3Smrg   struct instance_data *instance_data = FIND(struct instance_data, instance);
3287ec681f3Smrg   instance_data->vtable.DestroyInstance(instance, pAllocator);
3297ec681f3Smrg   destroy_instance_data(instance_data);
3307ec681f3Smrg}
3317ec681f3Smrg
3327ec681f3Smrgstatic const struct {
3337ec681f3Smrg   const char *name;
3347ec681f3Smrg   void *ptr;
3357ec681f3Smrg} name_to_funcptr_map[] = {
3367ec681f3Smrg   { "vkGetDeviceProcAddr", (void *) vkGetDeviceProcAddr },
3377ec681f3Smrg#define ADD_HOOK(fn) { "vk" # fn, (void *) nullhw_ ## fn }
3387ec681f3Smrg   ADD_HOOK(CreateInstance),
3397ec681f3Smrg   ADD_HOOK(DestroyInstance),
3407ec681f3Smrg   ADD_HOOK(CreateDevice),
3417ec681f3Smrg   ADD_HOOK(DestroyDevice),
3427ec681f3Smrg};
3437ec681f3Smrg
3447ec681f3Smrgstatic void *find_ptr(const char *name)
3457ec681f3Smrg{
3467ec681f3Smrg   for (uint32_t i = 0; i < ARRAY_SIZE(name_to_funcptr_map); i++) {
3477ec681f3Smrg      if (strcmp(name, name_to_funcptr_map[i].name) == 0)
3487ec681f3Smrg         return name_to_funcptr_map[i].ptr;
3497ec681f3Smrg   }
3507ec681f3Smrg
3517ec681f3Smrg   return NULL;
3527ec681f3Smrg}
3537ec681f3Smrg
3547ec681f3SmrgVK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice dev,
3557ec681f3Smrg                                                                             const char *funcName)
3567ec681f3Smrg{
3577ec681f3Smrg   void *ptr = find_ptr(funcName);
3587ec681f3Smrg   if (ptr) return (PFN_vkVoidFunction)(ptr);
3597ec681f3Smrg
3607ec681f3Smrg   if (dev == NULL) return NULL;
3617ec681f3Smrg
3627ec681f3Smrg   struct device_data *device_data = FIND(struct device_data, dev);
3637ec681f3Smrg   if (device_data->vtable.GetDeviceProcAddr == NULL) return NULL;
3647ec681f3Smrg   return device_data->vtable.GetDeviceProcAddr(dev, funcName);
3657ec681f3Smrg}
3667ec681f3Smrg
3677ec681f3SmrgVK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance,
3687ec681f3Smrg                                                                               const char *funcName)
3697ec681f3Smrg{
3707ec681f3Smrg   void *ptr = find_ptr(funcName);
3717ec681f3Smrg   if (ptr) return (PFN_vkVoidFunction) ptr;
3727ec681f3Smrg
3737ec681f3Smrg   struct instance_data *instance_data = FIND(struct instance_data, instance);
3747ec681f3Smrg   if (instance_data->vtable.GetInstanceProcAddr == NULL) return NULL;
3757ec681f3Smrg   return instance_data->vtable.GetInstanceProcAddr(instance, funcName);
3767ec681f3Smrg}
377