diff --git a/src/igl/vulkan/VulkanContext.cpp b/src/igl/vulkan/VulkanContext.cpp index 80a2d9e82e..955b5ab65f 100644 --- a/src/igl/vulkan/VulkanContext.cpp +++ b/src/igl/vulkan/VulkanContext.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -319,16 +320,19 @@ VulkanContext::VulkanContext(const VulkanContextConfig& config, size_t numExtraInstanceExtensions, const char** extraInstanceExtensions, void* display) : - config_(config) { + tableImpl_(std::make_unique()), vf_(*tableImpl_), config_(config) { IGL_PROFILER_THREAD("MainThread"); pimpl_ = std::make_unique(); + // Do not remove for backward compatibility with projects using global functions. if (volkInitialize() != VK_SUCCESS) { IGL_LOG_ERROR("volkInitialize() failed\n"); exit(255); }; + vulkan::functions::initialize(*tableImpl_); + glslang_initialize_process(); createInstance(numExtraInstanceExtensions, extraInstanceExtensions); @@ -434,8 +438,12 @@ void VulkanContext::createInstance(const size_t numExtraExtensions, const char** "ivkCreateInstance() failed. Did you forget to install the Vulkan SDK?"); VK_ASSERT(creationErrorCode); + + // Do not remove for backward compatibility with projects using global functions. volkLoadInstance(vkInstance_); + vulkan::functions::loadInstanceFunctions(*tableImpl_, vkInstance_); + #if defined(VK_EXT_debug_utils) && IGL_PLATFORM_WIN if (extensions_.enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { VK_ASSERT(ivkCreateDebugUtilsMessenger( @@ -628,9 +636,14 @@ igl::Result VulkanContext::initContext(const HWDeviceDesc& desc, config_.enableDescriptorIndexing, &device)); if (!config_.enableConcurrentVkDevicesSupport) { + // Do not remove for backward compatibility with projects using global functions. volkLoadDevice(device); } + // Table functions are always bound to a device. Project using enableConcurrentVkDevicesSupport + // should use own copy of function table bound to a device. + vulkan::functions::loadDeviceFunctions(*tableImpl_, device); + if (config_.enableBufferDeviceAddress && vkGetBufferDeviceAddressKHR == nullptr) { return Result(Result::Code::InvalidOperation, "Cannot initialize VK_KHR_buffer_device_address"); } diff --git a/src/igl/vulkan/VulkanContext.h b/src/igl/vulkan/VulkanContext.h index e5afb15b33..f0fb01f2ad 100644 --- a/src/igl/vulkan/VulkanContext.h +++ b/src/igl/vulkan/VulkanContext.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -206,6 +207,9 @@ class VulkanContext final { friend class igl::vulkan::ComputeCommandEncoder; friend class igl::vulkan::RenderCommandEncoder; + // should be kept on the heap, otherwise global Vulkan functions can cause arbitrary crashes. + std::unique_ptr tableImpl_; + VkInstance vkInstance_ = VK_NULL_HANDLE; VkDebugUtilsMessengerEXT vkDebugUtilsMessenger_ = VK_NULL_HANDLE; VkSurfaceKHR vkSurface_ = VK_NULL_HANDLE; @@ -249,6 +253,7 @@ class VulkanContext final { FOLLY_POP_WARNING public: + const VulkanFunctionTable& vf_; DeviceQueues deviceQueues_; std::unordered_map userQueues_; std::unique_ptr device_; diff --git a/src/igl/vulkan/VulkanFunctions.cpp b/src/igl/vulkan/VulkanFunctions.cpp new file mode 100644 index 0000000000..647fb37c10 --- /dev/null +++ b/src/igl/vulkan/VulkanFunctions.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "VulkanFunctions.h" + +#include + +// clang-format off +#ifdef _WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include +#else + #include +#endif +// clang-format on + +namespace igl::vulkan::functions { + +namespace { +PFN_vkGetInstanceProcAddr getVkGetInstanceProcAddr() { +#if defined(_WIN32) + HMODULE module = LoadLibraryA("vulkan-1.dll"); + if (!module) { + return nullptr; + } + return (PFN_vkGetInstanceProcAddr)GetProcAddress(module, "vkGetInstanceProcAddr"); +#elif defined(__APPLE__) + void* module = dlopen("libvulkan.dylib", RTLD_NOW | RTLD_LOCAL); + if (!module) { + module = dlopen("libvulkan.1.dylib", RTLD_NOW | RTLD_LOCAL); + } + if (!module) { + module = dlopen("libMoltenVK.dylib", RTLD_NOW | RTLD_LOCAL); + } + if (!module) { + return nullptr; + } + return (PFN_vkGetInstanceProcAddr)dlsym(module, "vkGetInstanceProcAddr"); +#else + void* module = dlopen("libvulkan.so.1", RTLD_NOW | RTLD_LOCAL); + if (!module) { + module = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL); + } + if (!module) { + return nullptr; + } + return (PFN_vkGetInstanceProcAddr)dlsym(module, "vkGetInstanceProcAddr"); +#endif + return nullptr; +} +} // namespace + +void initialize(VulkanFunctionTable& table) { + table.vkGetInstanceProcAddr = getVkGetInstanceProcAddr(); + IGL_ASSERT(table.vkGetInstanceProcAddr != nullptr); + + loadVulkanLoaderFunctions(&table, table.vkGetInstanceProcAddr); +} + +void loadInstanceFunctions(VulkanFunctionTable& table, VkInstance instance) { + IGL_ASSERT(table.vkGetInstanceProcAddr != nullptr); + loadVulkanInstanceFunctions(&table, instance, table.vkGetInstanceProcAddr); +} + +void loadDeviceFunctions(VulkanFunctionTable& table, VkDevice device) { + IGL_ASSERT(table.vkGetDeviceProcAddr != nullptr); + loadVulkanDeviceFunctions(&table, device, table.vkGetDeviceProcAddr); +} + +} // namespace igl::vulkan::functions diff --git a/src/igl/vulkan/VulkanFunctions.h b/src/igl/vulkan/VulkanFunctions.h new file mode 100644 index 0000000000..763cd51d85 --- /dev/null +++ b/src/igl/vulkan/VulkanFunctions.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +namespace igl::vulkan::functions { + +void initialize(VulkanFunctionTable& table); + +void loadInstanceFunctions(VulkanFunctionTable& table, VkInstance instance); + +void loadDeviceFunctions(VulkanFunctionTable& table, VkDevice device); + +} // namespace igl::vulkan::functions