From 8e3284d8c6b28c4a73752da005e3881e6b270d10 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Wed, 15 May 2024 00:18:49 +1000 Subject: [PATCH] Vulkan: Simplify loader using DynamicLibrary --- src/common/cocoa_tools.h | 3 + src/common/cocoa_tools.mm | 11 +++ src/common/dynamic_library.cpp | 26 +++++++ src/common/error.cpp | 6 ++ src/common/error.h | 1 + src/util/gpu_device.cpp | 48 +++++++----- src/util/vulkan_device.cpp | 9 ++- src/util/vulkan_loader.cpp | 138 ++++++--------------------------- src/util/vulkan_loader.h | 6 +- 9 files changed, 109 insertions(+), 139 deletions(-) diff --git a/src/common/cocoa_tools.h b/src/common/cocoa_tools.h index bf930ac4b..d2b91504a 100644 --- a/src/common/cocoa_tools.h +++ b/src/common/cocoa_tools.h @@ -31,6 +31,9 @@ void RemoveThemeChangeHandler(void* ctx); /// Moves a file from one location to another, using NSFileManager. bool MoveFile(const char* source, const char* destination, Error* error); +/// Returns the bundle path. +std::optional GetBundlePath(); + /// Get the bundle path to the actual application without any translocation fun std::optional GetNonTranslocatedBundlePath(); diff --git a/src/common/cocoa_tools.mm b/src/common/cocoa_tools.mm index bbfe031f4..ab0047e71 100644 --- a/src/common/cocoa_tools.mm +++ b/src/common/cocoa_tools.mm @@ -107,6 +107,17 @@ void CocoaTools::RemoveThemeChangeHandler(void* ctx) [s_themeChangeHandler removeCallback:ctx]; } +std::optional CocoaTools::GetBundlePath() +{ + std::optional ret; + @autoreleasepool { + NSURL* url = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]; + if (url) + ret = std::string([url fileSystemRepresentation]); + } + return ret; +} + std::optional CocoaTools::GetNonTranslocatedBundlePath() { // See https://objective-see.com/blog/blog_0x15.html diff --git a/src/common/dynamic_library.cpp b/src/common/dynamic_library.cpp index 9f66c5104..520bee21f 100644 --- a/src/common/dynamic_library.cpp +++ b/src/common/dynamic_library.cpp @@ -4,7 +4,9 @@ #include "common/dynamic_library.h" #include "common/assert.h" #include "common/error.h" +#include "common/file_system.h" #include "common/log.h" +#include "common/path.h" #include "common/small_string.h" #include "common/string_util.h" @@ -15,6 +17,9 @@ #include "common/windows_headers.h" #else #include +#ifdef __APPLE__ +#include "common/cocoa_tools.h" +#endif #endif Log_SetChannel(DynamicLibrary); @@ -92,6 +97,27 @@ bool DynamicLibrary::Open(const char* filename, Error* error) m_handle = dlopen(filename, RTLD_NOW); if (!m_handle) { +#ifdef __APPLE__ + // On MacOS, try searching in Frameworks. + if (!Path::IsAbsolute(filename)) + { + std::optional bundle_path = CocoaTools::GetBundlePath(); + if (bundle_path.has_value()) + { + std::string frameworks_path = fmt::format("{}/Contents/Frameworks/{}", bundle_path.value(), filename); + if (FileSystem::FileExists(frameworks_path.c_str())) + { + m_handle = dlopen(frameworks_path.c_str(), RTLD_NOW); + if (m_handle) + { + Error::Clear(error); + return true; + } + } + } + } +#endif + const char* err = dlerror(); Error::SetStringFmt(error, "Loading {} failed: {}", filename, err ? err : ""); return false; diff --git a/src/common/error.cpp b/src/common/error.cpp index d2ec4db22..4ab1fe343 100644 --- a/src/common/error.cpp +++ b/src/common/error.cpp @@ -28,6 +28,12 @@ void Error::Clear() m_description = {}; } +void Error::Clear(Error* errptr) +{ + if (errptr) + errptr->Clear(); +} + void Error::SetErrno(int err) { SetErrno(std::string_view(), err); diff --git a/src/common/error.h b/src/common/error.h index 80f49ed40..a101dfd55 100644 --- a/src/common/error.h +++ b/src/common/error.h @@ -68,6 +68,7 @@ public: #endif // helpers for setting + static void Clear(Error* errptr); static void SetErrno(Error* errptr, int err); static void SetErrno(Error* errptr, std::string_view prefix, int err); static void SetSocket(Error* errptr, int err); diff --git a/src/util/gpu_device.cpp b/src/util/gpu_device.cpp index e28569401..11b1902b8 100644 --- a/src/util/gpu_device.cpp +++ b/src/util/gpu_device.cpp @@ -1112,11 +1112,9 @@ std::unique_ptr GPUDevice::CreateDeviceForAPI(RenderAPI api) } #if defined(ENABLE_VULKAN) || defined(__APPLE__) -#define SHADERC_INIT_FUNCTIONS(X) \ - X(shaderc_compiler_initialize) \ - X(shaderc_compiler_release) - #define SHADERC_FUNCTIONS(X) \ + X(shaderc_compiler_initialize) \ + X(shaderc_compiler_release) \ X(shaderc_compile_options_initialize) \ X(shaderc_compile_options_release) \ X(shaderc_compile_options_set_source_language) \ @@ -1134,9 +1132,10 @@ std::unique_ptr GPUDevice::CreateDeviceForAPI(RenderAPI api) // TODO: NOT thread safe, yet. namespace dyn_shaderc { static bool Open(); +static void Close(); static DynamicLibrary s_library; -static std::unique_ptr s_compiler(nullptr, nullptr); +static shaderc_compiler_t s_compiler = nullptr; #define ADD_FUNC(F) static decltype(&::F) F; SHADERC_FUNCTIONS(ADD_FUNC) @@ -1167,27 +1166,40 @@ bool dyn_shaderc::Open() if (!s_library.GetSymbol(#F, &F)) \ { \ Log_ErrorFmt("Failed to find function {}", #F); \ - s_library.Close(); \ - return false; \ - } -#define LOAD_INIT_FUNC(F) \ - decltype(&::F) p##F; \ - if (!s_library.GetSymbol(#F, &p##F)) \ - { \ - Log_ErrorFmt("Failed to find function {}", #F); \ - s_library.Close(); \ + Close(); \ return false; \ } SHADERC_FUNCTIONS(LOAD_FUNC) - SHADERC_INIT_FUNCTIONS(LOAD_INIT_FUNC) #undef LOAD_FUNC -#undef LOAD_INIT_FUNC - s_compiler = decltype(s_compiler)(pshaderc_compiler_initialize(), pshaderc_compiler_release); + s_compiler = shaderc_compiler_initialize(); + if (!s_compiler) + { + Log_ErrorPrint("shaderc_compiler_initialize() failed"); + Close(); + return false; + } + + std::atexit(&dyn_shaderc::Close); return true; } +void dyn_shaderc::Close() +{ + if (s_compiler) + { + shaderc_compiler_release(s_compiler); + s_compiler = nullptr; + } + +#define UNLOAD_FUNC(F) F = nullptr; + SHADERC_FUNCTIONS(UNLOAD_FUNC) +#undef UNLOAD_FUNC + + s_library.Close(); +} + #undef SHADERC_FUNCTIONS #undef SHADERC_INIT_FUNCTIONS @@ -1216,7 +1228,7 @@ bool GPUDevice::CompileGLSLShaderToVulkanSpv(GPUShaderStage stage, std::string_v shaderc_compilation_result_t result; const shaderc_compilation_status status = dyn_shaderc::shaderc_compile_into_spv( - dyn_shaderc::s_compiler.get(), source.data(), source.length(), stage_kinds[static_cast(stage)], "source", + dyn_shaderc::s_compiler, source.data(), source.length(), stage_kinds[static_cast(stage)], "source", entry_point, options, &result); if (status != shaderc_compilation_status_success) { diff --git a/src/util/vulkan_device.cpp b/src/util/vulkan_device.cpp index 826d60ffb..191f0d2c0 100644 --- a/src/util/vulkan_device.cpp +++ b/src/util/vulkan_device.cpp @@ -1845,7 +1845,7 @@ GPUDevice::AdapterAndModeList VulkanDevice::StaticGetAdapterAndModeList() } else { - if (Vulkan::LoadVulkanLibrary()) + if (Vulkan::LoadVulkanLibrary(nullptr)) { ScopedGuard lib_guard([]() { Vulkan::UnloadVulkanLibrary(); }); OptionalExtensions oe = {}; @@ -1857,6 +1857,8 @@ GPUDevice::AdapterAndModeList VulkanDevice::StaticGetAdapterAndModeList() vkDestroyInstance(instance, nullptr); } + + Vulkan::UnloadVulkanLibrary(); } } @@ -1925,9 +1927,10 @@ bool VulkanDevice::CreateDevice(std::string_view adapter, bool threaded_presenta bool enable_debug_utils = m_debug_device; bool enable_validation_layer = m_debug_device; - if (!Vulkan::LoadVulkanLibrary()) + if (!Vulkan::LoadVulkanLibrary(error)) { - Error::SetStringView(error, "Failed to load Vulkan library. Does your GPU and/or driver support Vulkan?"); + Error::AddPrefix(error, + "Failed to load Vulkan library. Does your GPU and/or driver support Vulkan?\nThe error was:"); return false; } diff --git a/src/util/vulkan_loader.cpp b/src/util/vulkan_loader.cpp index 065607dc9..87886fa51 100644 --- a/src/util/vulkan_loader.cpp +++ b/src/util/vulkan_loader.cpp @@ -7,6 +7,7 @@ #include "vulkan_loader.h" #include "common/assert.h" +#include "common/dynamic_library.h" #include "common/log.h" #include @@ -15,14 +16,6 @@ #include #include -#ifndef _WIN32 -#include -#endif - -#ifdef __APPLE__ -#include -#endif - Log_SetChannel(VulkanDevice); extern "C" { @@ -47,134 +40,51 @@ void Vulkan::ResetVulkanLibraryFunctionPointers() #undef VULKAN_MODULE_ENTRY_POINT } -#if defined(_WIN32) - -static HMODULE s_vulkan_module; +static DynamicLibrary s_vulkan_library; bool Vulkan::IsVulkanLibraryLoaded() { - return s_vulkan_module != NULL; + return s_vulkan_library.IsOpen(); } -bool Vulkan::LoadVulkanLibrary() +bool Vulkan::LoadVulkanLibrary(Error* error) { - AssertMsg(!s_vulkan_module, "Vulkan module is not loaded."); + AssertMsg(!s_vulkan_library.IsOpen(), "Vulkan module is not loaded."); - s_vulkan_module = LoadLibraryA("vulkan-1.dll"); - if (!s_vulkan_module) - { - Log_ErrorPrintf("Failed to load vulkan-1.dll"); - return false; - } - - bool required_functions_missing = false; - auto LoadFunction = [&](FARPROC* func_ptr, const char* name, bool is_required) { - *func_ptr = GetProcAddress(s_vulkan_module, name); - if (!(*func_ptr) && is_required) - { - Log_ErrorPrintf("Vulkan: Failed to load required module function %s", name); - required_functions_missing = true; - } - }; - -#define VULKAN_MODULE_ENTRY_POINT(name, required) LoadFunction(reinterpret_cast(&name), #name, required); -#include "vulkan_entry_points.inl" -#undef VULKAN_MODULE_ENTRY_POINT - - if (required_functions_missing) - { - ResetVulkanLibraryFunctionPointers(); - FreeLibrary(s_vulkan_module); - s_vulkan_module = nullptr; - return false; - } - - return true; -} - -void Vulkan::UnloadVulkanLibrary() -{ - ResetVulkanLibraryFunctionPointers(); - if (s_vulkan_module) - FreeLibrary(s_vulkan_module); - s_vulkan_module = nullptr; -} - -#else - -static void* s_vulkan_module; - -bool Vulkan::IsVulkanLibraryLoaded() -{ - return s_vulkan_module != nullptr; -} - -bool Vulkan::LoadVulkanLibrary() -{ - AssertMsg(!s_vulkan_module, "Vulkan module is not loaded."); - -#if defined(__APPLE__) +#ifdef __APPLE__ // Check if a path to a specific Vulkan library has been specified. char* libvulkan_env = getenv("LIBVULKAN_PATH"); if (libvulkan_env) - s_vulkan_module = dlopen(libvulkan_env, RTLD_NOW); - if (!s_vulkan_module) + s_vulkan_library.Open(libvulkan_env, error); + if (!s_vulkan_library.IsOpen() && + !s_vulkan_library.Open(DynamicLibrary::GetVersionedFilename("MoltenVK").c_str(), error)) { - unsigned path_size = 0; - _NSGetExecutablePath(nullptr, &path_size); - std::string path; - path.resize(path_size); - if (_NSGetExecutablePath(path.data(), &path_size) == 0) - { - path[path_size] = 0; - - size_t pos = path.rfind('/'); - if (pos != std::string::npos) - { - path.erase(pos); - path += "/../Frameworks/libMoltenVK.dylib"; - s_vulkan_module = dlopen(path.c_str(), RTLD_NOW); - } - } + return false; } - if (!s_vulkan_module) - s_vulkan_module = dlopen("libvulkan.dylib", RTLD_NOW); #else - // Names of libraries to search. Desktop should use libvulkan.so.1 or libvulkan.so. - static const char* search_lib_names[] = {"libvulkan.so.1", "libvulkan.so"}; - for (size_t i = 0; i < sizeof(search_lib_names) / sizeof(search_lib_names[0]); i++) + // try versioned first, then unversioned. + if (!s_vulkan_library.Open(DynamicLibrary::GetVersionedFilename("vulkan", 1).c_str(), error) && + !s_vulkan_library.Open(DynamicLibrary::GetVersionedFilename("vulkan").c_str(), error)) { - s_vulkan_module = dlopen(search_lib_names[i], RTLD_NOW); - if (s_vulkan_module) - break; + return false; } #endif - if (!s_vulkan_module) - { - Log_ErrorPrintf("Failed to load or locate libvulkan.so"); - return false; - } - bool required_functions_missing = false; - auto LoadFunction = [&](void** func_ptr, const char* name, bool is_required) { - *func_ptr = dlsym(s_vulkan_module, name); - if (!(*func_ptr) && is_required) - { - Log_ErrorPrintf("Vulkan: Failed to load required module function %s", name); - required_functions_missing = true; - } - }; -#define VULKAN_MODULE_ENTRY_POINT(name, required) LoadFunction(reinterpret_cast(&name), #name, required); +#define VULKAN_MODULE_ENTRY_POINT(name, required) \ + if (!s_vulkan_library.GetSymbol(#name, &name)) \ + { \ + Log_ErrorFmt("Vulkan: Failed to load required module function {}", #name); \ + required_functions_missing = true; \ + } #include "vulkan_entry_points.inl" #undef VULKAN_MODULE_ENTRY_POINT if (required_functions_missing) { ResetVulkanLibraryFunctionPointers(); - dlclose(s_vulkan_module); - s_vulkan_module = nullptr; + s_vulkan_library.Close(); return false; } @@ -184,13 +94,9 @@ bool Vulkan::LoadVulkanLibrary() void Vulkan::UnloadVulkanLibrary() { ResetVulkanLibraryFunctionPointers(); - if (s_vulkan_module) - dlclose(s_vulkan_module); - s_vulkan_module = nullptr; + s_vulkan_library.Close(); } -#endif - bool Vulkan::LoadVulkanInstanceFunctions(VkInstance instance) { bool required_functions_missing = false; diff --git a/src/util/vulkan_loader.h b/src/util/vulkan_loader.h index 0457d5609..7fe0ac0a3 100644 --- a/src/util/vulkan_loader.h +++ b/src/util/vulkan_loader.h @@ -1,8 +1,10 @@ -// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #pragma once +class Error; + #define VK_NO_PROTOTYPES #ifdef _WIN32 @@ -93,7 +95,7 @@ namespace Vulkan { bool IsVulkanLibraryLoaded(); -bool LoadVulkanLibrary(); +bool LoadVulkanLibrary(Error* error); bool LoadVulkanInstanceFunctions(VkInstance instance); bool LoadVulkanDeviceFunctions(VkDevice device); void UnloadVulkanLibrary();