Cleanup internal class VulkanFunctions

Removes the subclassing of VulkanLibrary, bringing its logic into the VulkanFunctions
class. This resolves a weird linking issue which was causing hot-reloading of shared
libraries that statically linked vk-bootstrap to fail to unload correctly, preventing
reloading.
The commit also consolidates the logic in load_vulkan_funcs() into a single function
rather than having it be split in three, which only made the code harder to reason
about.

Also made vk-bootstrap look for `libMoltenVK.dylib` on apple platforms, in case the
application only has the MoltenVK shared library but not the loader (and the user
didn't manually load GetInstanceProcAddr from MoltenVK then give it to vk-bootstrap).
This commit is contained in:
Charles Giessen 2023-11-12 19:24:16 -07:00 committed by Charles Giessen
parent 49491c28c7
commit 9864d2b838

View File

@ -50,63 +50,61 @@ bool GenericFeaturesPNextNode::match(GenericFeaturesPNextNode const& requested,
class VulkanFunctions { class VulkanFunctions {
private: private:
std::mutex init_mutex; std::mutex init_mutex;
struct VulkanLibrary {
#if defined(__linux__) || defined(__APPLE__)
void* library;
#elif defined(_WIN32)
HMODULE library;
#endif
PFN_vkGetInstanceProcAddr ptr_vkGetInstanceProcAddr = VK_NULL_HANDLE;
VulkanLibrary() { #if defined(__linux__) || defined(__APPLE__)
void* library = nullptr;
#elif defined(_WIN32)
HMODULE library = nullptr;
#endif
bool load_vulkan_library() {
// Can immediately return if it has already been loaded
if (library) {
return true;
}
#if defined(__linux__) #if defined(__linux__)
library = dlopen("libvulkan.so.1", RTLD_NOW | RTLD_LOCAL); library = dlopen("libvulkan.so.1", RTLD_NOW | RTLD_LOCAL);
if (!library) library = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL); if (!library) library = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL);
#elif defined(__APPLE__) #elif defined(__APPLE__)
library = dlopen("libvulkan.dylib", RTLD_NOW | RTLD_LOCAL); library = dlopen("libvulkan.dylib", RTLD_NOW | RTLD_LOCAL);
if (!library) library = dlopen("libvulkan.1.dylib", RTLD_NOW | RTLD_LOCAL); if (!library) library = dlopen("libvulkan.1.dylib", RTLD_NOW | RTLD_LOCAL);
if (!library) library = dlopen("libMoltenVK.dylib", RTLD_NOW | RTLD_LOCAL);
#elif defined(_WIN32) #elif defined(_WIN32)
library = LoadLibrary(TEXT("vulkan-1.dll")); library = LoadLibrary(TEXT("vulkan-1.dll"));
#else #else
assert(false && "Unsupported platform"); assert(false && "Unsupported platform");
#endif #endif
if (!library) return; if (!library) return false;
load_func(ptr_vkGetInstanceProcAddr, "vkGetInstanceProcAddr"); load_func(ptr_vkGetInstanceProcAddr, "vkGetInstanceProcAddr");
} return ptr_vkGetInstanceProcAddr != nullptr;
template <typename T> void load_func(T& func_dest, const char* func_name) {
#if defined(__linux__) || defined(__APPLE__)
func_dest = reinterpret_cast<T>(dlsym(library, func_name));
#elif defined(_WIN32)
func_dest = reinterpret_cast<T>(GetProcAddress(library, func_name));
#endif
}
void close() {
#if defined(__linux__) || defined(__APPLE__)
dlclose(library);
#elif defined(_WIN32)
FreeLibrary(library);
#endif
library = 0;
}
};
VulkanLibrary& get_vulkan_library() {
static VulkanLibrary lib;
return lib;
} }
bool load_vulkan(PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr = nullptr) { template <typename T> void load_func(T& func_dest, const char* func_name) {
#if defined(__linux__) || defined(__APPLE__)
func_dest = reinterpret_cast<T>(dlsym(library, func_name));
#elif defined(_WIN32)
func_dest = reinterpret_cast<T>(GetProcAddress(library, func_name));
#endif
}
void close() {
#if defined(__linux__) || defined(__APPLE__)
dlclose(library);
#elif defined(_WIN32)
FreeLibrary(library);
#endif
library = 0;
}
public:
bool init_vulkan_funcs(PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr = nullptr) {
std::lock_guard<std::mutex> lg(init_mutex);
if (fp_vkGetInstanceProcAddr != nullptr) { if (fp_vkGetInstanceProcAddr != nullptr) {
ptr_vkGetInstanceProcAddr = fp_vkGetInstanceProcAddr; ptr_vkGetInstanceProcAddr = fp_vkGetInstanceProcAddr;
return true;
} else { } else {
auto& lib = get_vulkan_library(); bool ret = load_vulkan_library();
ptr_vkGetInstanceProcAddr = lib.ptr_vkGetInstanceProcAddr; if (!ret) return false;
return lib.library != nullptr && lib.ptr_vkGetInstanceProcAddr != VK_NULL_HANDLE;
} }
}
void init_pre_instance_funcs() {
fp_vkEnumerateInstanceExtensionProperties = reinterpret_cast<PFN_vkEnumerateInstanceExtensionProperties>( fp_vkEnumerateInstanceExtensionProperties = reinterpret_cast<PFN_vkEnumerateInstanceExtensionProperties>(
ptr_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties")); ptr_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties"));
fp_vkEnumerateInstanceLayerProperties = reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>( fp_vkEnumerateInstanceLayerProperties = reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>(
@ -115,6 +113,7 @@ class VulkanFunctions {
ptr_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceVersion")); ptr_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceVersion"));
fp_vkCreateInstance = fp_vkCreateInstance =
reinterpret_cast<PFN_vkCreateInstance>(ptr_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance")); reinterpret_cast<PFN_vkCreateInstance>(ptr_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance"));
return true;
} }
public: public:
@ -155,13 +154,6 @@ class VulkanFunctions {
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fp_vkGetPhysicalDeviceSurfacePresentModesKHR = nullptr; PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fp_vkGetPhysicalDeviceSurfacePresentModesKHR = nullptr;
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fp_vkGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr; PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fp_vkGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr;
bool init_vulkan_funcs(PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr) {
std::lock_guard<std::mutex> lg(init_mutex);
if (!load_vulkan(fp_vkGetInstanceProcAddr)) return false;
init_pre_instance_funcs();
return true;
}
void init_instance_funcs(VkInstance inst) { void init_instance_funcs(VkInstance inst) {
instance = inst; instance = inst;
get_inst_proc_addr(fp_vkDestroyInstance, "vkDestroyInstance"); get_inst_proc_addr(fp_vkDestroyInstance, "vkDestroyInstance");
@ -188,7 +180,7 @@ class VulkanFunctions {
} }
}; };
VulkanFunctions& vulkan_functions() { static VulkanFunctions& vulkan_functions() {
static VulkanFunctions v; static VulkanFunctions v;
return v; return v;
} }