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 {
private:
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__)
library = dlopen("libvulkan.so.1", RTLD_NOW | RTLD_LOCAL);
if (!library) library = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL);
library = dlopen("libvulkan.so.1", RTLD_NOW | RTLD_LOCAL);
if (!library) library = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL);
#elif defined(__APPLE__)
library = dlopen("libvulkan.dylib", RTLD_NOW | RTLD_LOCAL);
if (!library) library = dlopen("libvulkan.1.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("libMoltenVK.dylib", RTLD_NOW | RTLD_LOCAL);
#elif defined(_WIN32)
library = LoadLibrary(TEXT("vulkan-1.dll"));
library = LoadLibrary(TEXT("vulkan-1.dll"));
#else
assert(false && "Unsupported platform");
assert(false && "Unsupported platform");
#endif
if (!library) return;
load_func(ptr_vkGetInstanceProcAddr, "vkGetInstanceProcAddr");
}
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;
if (!library) return false;
load_func(ptr_vkGetInstanceProcAddr, "vkGetInstanceProcAddr");
return ptr_vkGetInstanceProcAddr != nullptr;
}
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) {
ptr_vkGetInstanceProcAddr = fp_vkGetInstanceProcAddr;
return true;
} else {
auto& lib = get_vulkan_library();
ptr_vkGetInstanceProcAddr = lib.ptr_vkGetInstanceProcAddr;
return lib.library != nullptr && lib.ptr_vkGetInstanceProcAddr != VK_NULL_HANDLE;
bool ret = load_vulkan_library();
if (!ret) return false;
}
}
void init_pre_instance_funcs() {
fp_vkEnumerateInstanceExtensionProperties = reinterpret_cast<PFN_vkEnumerateInstanceExtensionProperties>(
ptr_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties"));
fp_vkEnumerateInstanceLayerProperties = reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>(
@ -115,6 +113,7 @@ class VulkanFunctions {
ptr_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceVersion"));
fp_vkCreateInstance =
reinterpret_cast<PFN_vkCreateInstance>(ptr_vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance"));
return true;
}
public:
@ -155,13 +154,6 @@ class VulkanFunctions {
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fp_vkGetPhysicalDeviceSurfacePresentModesKHR = 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) {
instance = inst;
get_inst_proc_addr(fp_vkDestroyInstance, "vkDestroyInstance");
@ -188,7 +180,7 @@ class VulkanFunctions {
}
};
VulkanFunctions& vulkan_functions() {
static VulkanFunctions& vulkan_functions() {
static VulkanFunctions v;
return v;
}