GS/Vulkan: Simplify loader using DynamicLibrary

Backport of 8e3284d8c6
This commit is contained in:
Stenzek 2024-05-15 07:55:58 +10:00 committed by Connor McLaughlin
parent 6545c62d26
commit 0628e8cc87
8 changed files with 85 additions and 126 deletions

View File

@ -17,6 +17,8 @@ namespace CocoaTools
void AddThemeChangeHandler(void* ctx, void(handler)(void* ctx)); void AddThemeChangeHandler(void* ctx, void(handler)(void* ctx));
/// Remove a handler previously added using AddThemeChangeHandler with the given context /// Remove a handler previously added using AddThemeChangeHandler with the given context
void RemoveThemeChangeHandler(void* ctx); void RemoveThemeChangeHandler(void* ctx);
/// Returns the bundle path.
std::optional<std::string> GetBundlePath();
/// Get the bundle path to the actual application without any translocation fun /// Get the bundle path to the actual application without any translocation fun
std::optional<std::string> GetNonTranslocatedBundlePath(); std::optional<std::string> GetNonTranslocatedBundlePath();
/// Move the given file to the trash, and return the path to its new location /// Move the given file to the trash, and return the path to its new location

View File

@ -133,6 +133,17 @@ bool Common::PlaySoundAsync(const char* path)
// MARK: - Updater // MARK: - Updater
std::optional<std::string> CocoaTools::GetBundlePath()
{
std::optional<std::string> ret;
@autoreleasepool {
NSURL* url = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
if (url)
ret = std::string([url fileSystemRepresentation]);
}
return ret;
}
std::optional<std::string> CocoaTools::GetNonTranslocatedBundlePath() std::optional<std::string> CocoaTools::GetNonTranslocatedBundlePath()
{ {
// See https://objective-see.com/blog/blog_0x15.html // See https://objective-see.com/blog/blog_0x15.html

View File

@ -5,7 +5,9 @@
#include "common/Assertions.h" #include "common/Assertions.h"
#include "common/Console.h" #include "common/Console.h"
#include "common/Error.h" #include "common/Error.h"
#include "common/FileSystem.h"
#include "common/SmallString.h" #include "common/SmallString.h"
#include "common/Path.h"
#include "common/StringUtil.h" #include "common/StringUtil.h"
#include <cstring> #include <cstring>
@ -15,6 +17,9 @@
#include "common/RedtapeWindows.h" #include "common/RedtapeWindows.h"
#else #else
#include <dlfcn.h> #include <dlfcn.h>
#ifdef __APPLE__
#include "common/CocoaTools.h"
#endif
#endif #endif
DynamicLibrary::DynamicLibrary() = default; DynamicLibrary::DynamicLibrary() = default;
@ -91,6 +96,27 @@ bool DynamicLibrary::Open(const char* filename, Error* error)
m_handle = dlopen(filename, RTLD_NOW); m_handle = dlopen(filename, RTLD_NOW);
if (!m_handle) if (!m_handle)
{ {
#ifdef __APPLE__
// On MacOS, try searching in Frameworks.
if (!Path::IsAbsolute(filename))
{
std::optional<std::string> 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(); const char* err = dlerror();
Error::SetStringFmt(error, "Loading {} failed: {}", filename, err ? err : "<UNKNOWN>"); Error::SetStringFmt(error, "Loading {} failed: {}", filename, err ? err : "<UNKNOWN>");
return false; return false;

View File

@ -30,6 +30,12 @@ void Error::Clear()
m_description = {}; m_description = {};
} }
void Error::Clear(Error* errptr)
{
if (errptr)
errptr->Clear();
}
void Error::SetErrno(int err) void Error::SetErrno(int err)
{ {
SetErrno(std::string_view(), err); SetErrno(std::string_view(), err);

View File

@ -66,6 +66,7 @@ public:
#endif #endif
// helpers for setting // helpers for setting
static void Clear(Error* errptr);
static void SetErrno(Error* errptr, int err); static void SetErrno(Error* errptr, int err);
static void SetErrno(Error* errptr, std::string_view prefix, int err); static void SetErrno(Error* errptr, std::string_view prefix, int err);
static void SetSocket(Error* errptr, int err); static void SetSocket(Error* errptr, int err);

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team // SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+ // SPDX-License-Identifier: LGPL-3.0+
#include "GS/GS.h" #include "GS/GS.h"
@ -14,6 +14,7 @@
#include "common/Console.h" #include "common/Console.h"
#include "common/BitUtils.h" #include "common/BitUtils.h"
#include "common/Error.h"
#include "common/HostSys.h" #include "common/HostSys.h"
#include "common/Path.h" #include "common/Path.h"
#include "common/ScopedGuard.h" #include "common/ScopedGuard.h"
@ -373,8 +374,7 @@ bool GSDeviceVK::SelectDeviceExtensions(ExtensionList* extension_list, bool enab
m_optional_extensions.vk_ext_calibrated_timestamps = m_optional_extensions.vk_ext_calibrated_timestamps =
SupportsExtension(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME, false); SupportsExtension(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME, false);
m_optional_extensions.vk_ext_rasterization_order_attachment_access = m_optional_extensions.vk_ext_rasterization_order_attachment_access =
SupportsExtension(VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME, false) || SupportsExtension(VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME, false);
SupportsExtension(VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME, false);
m_optional_extensions.vk_ext_line_rasterization = SupportsExtension(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, m_optional_extensions.vk_ext_line_rasterization = SupportsExtension(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME,
require_line_rasterization); require_line_rasterization);
m_optional_extensions.vk_khr_driver_properties = SupportsExtension(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, false); m_optional_extensions.vk_khr_driver_properties = SupportsExtension(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, false);
@ -2029,9 +2029,8 @@ void GSDeviceVK::GetAdaptersAndFullscreenModes(
} }
else else
{ {
if (Vulkan::LoadVulkanLibrary()) if (Vulkan::LoadVulkanLibrary(nullptr))
{ {
ScopedGuard lib_guard([]() { Vulkan::UnloadVulkanLibrary(); });
const VkInstance instance = CreateVulkanInstance(WindowInfo(), false, false); const VkInstance instance = CreateVulkanInstance(WindowInfo(), false, false);
if (instance != VK_NULL_HANDLE) if (instance != VK_NULL_HANDLE)
{ {
@ -2040,6 +2039,8 @@ void GSDeviceVK::GetAdaptersAndFullscreenModes(
vkDestroyInstance(instance, nullptr); vkDestroyInstance(instance, nullptr);
} }
Vulkan::UnloadVulkanLibrary();
} }
} }
} }
@ -2497,9 +2498,11 @@ bool GSDeviceVK::CreateDeviceAndSwapChain()
bool enable_debug_utils = GSConfig.UseDebugDevice; bool enable_debug_utils = GSConfig.UseDebugDevice;
bool enable_validation_layer = GSConfig.UseDebugDevice; bool enable_validation_layer = GSConfig.UseDebugDevice;
if (!Vulkan::LoadVulkanLibrary()) Error error;
if (!Vulkan::LoadVulkanLibrary(&error))
{ {
Host::ReportErrorAsync("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:\n");
Host::ReportErrorAsync("Error", error.GetDescription());
return false; return false;
} }

View File

@ -1,9 +1,12 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team // SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+ // SPDX-License-Identifier: LGPL-3.0+
#include "GS/Renderers/Vulkan/VKLoader.h" #include "GS/Renderers/Vulkan/VKLoader.h"
#include "common/Assertions.h" #include "common/Assertions.h"
#include "common/Console.h"
#include "common/DynamicLibrary.h"
#include "common/Error.h"
#include <cstdarg> #include <cstdarg>
#include <cstdio> #include <cstdio>
@ -11,14 +14,6 @@
#include <cstring> #include <cstring>
#include <string> #include <string>
#ifndef _WIN32
#include <dlfcn.h>
#endif
#ifdef __APPLE__
#include <mach-o/dyld.h>
#endif
extern "C" { extern "C" {
#define VULKAN_MODULE_ENTRY_POINT(name, required) PFN_##name name; #define VULKAN_MODULE_ENTRY_POINT(name, required) PFN_##name name;
@ -41,134 +36,51 @@ void Vulkan::ResetVulkanLibraryFunctionPointers()
#undef VULKAN_MODULE_ENTRY_POINT #undef VULKAN_MODULE_ENTRY_POINT
} }
#if defined(_WIN32) static DynamicLibrary s_vulkan_library;
static HMODULE s_vulkan_module;
bool Vulkan::IsVulkanLibraryLoaded() bool Vulkan::IsVulkanLibraryLoaded()
{ {
return s_vulkan_module != NULL; return s_vulkan_library.IsOpen();
} }
bool Vulkan::LoadVulkanLibrary() bool Vulkan::LoadVulkanLibrary(Error* error)
{ {
pxAssertRel(!s_vulkan_module, "Vulkan module is not loaded."); pxAssertRel(!s_vulkan_library.IsOpen(), "Vulkan module is not loaded.");
s_vulkan_module = LoadLibraryA("vulkan-1.dll"); #ifdef __APPLE__
if (!s_vulkan_module)
{
std::fprintf(stderr, "Failed to load vulkan-1.dll\n");
return false;
}
bool required_functions_missing = false;
auto LoadFunction = [&required_functions_missing](FARPROC* func_ptr, const char* name, bool is_required) {
*func_ptr = GetProcAddress(s_vulkan_module, name);
if (!(*func_ptr) && is_required)
{
std::fprintf(stderr, "Vulkan: Failed to load required module function %s\n", name);
required_functions_missing = true;
}
};
#define VULKAN_MODULE_ENTRY_POINT(name, required) LoadFunction(reinterpret_cast<FARPROC*>(&name), #name, required);
#include "VKEntryPoints.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()
{
pxAssertRel(!s_vulkan_module, "Vulkan module is not loaded.");
#if defined(__APPLE__)
// Check if a path to a specific Vulkan library has been specified. // Check if a path to a specific Vulkan library has been specified.
char* libvulkan_env = getenv("LIBVULKAN_PATH"); char* libvulkan_env = getenv("LIBVULKAN_PATH");
if (libvulkan_env) if (libvulkan_env)
s_vulkan_module = dlopen(libvulkan_env, RTLD_NOW); s_vulkan_library.Open(libvulkan_env, error);
if (!s_vulkan_module) if (!s_vulkan_library.IsOpen() &&
!s_vulkan_library.Open(DynamicLibrary::GetVersionedFilename("MoltenVK").c_str(), error))
{ {
unsigned path_size = 0; return false;
_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);
} }
}
}
if (!s_vulkan_module)
s_vulkan_module = dlopen("libvulkan.dylib", RTLD_NOW);
#else #else
// Names of libraries to search. Desktop should use libvulkan.so.1 or libvulkan.so. // try versioned first, then unversioned.
static const char* search_lib_names[] = {"libvulkan.so.1", "libvulkan.so"}; if (!s_vulkan_library.Open(DynamicLibrary::GetVersionedFilename("vulkan", 1).c_str(), error) &&
for (size_t i = 0; i < sizeof(search_lib_names) / sizeof(search_lib_names[0]); i++) !s_vulkan_library.Open(DynamicLibrary::GetVersionedFilename("vulkan").c_str(), error))
{ {
s_vulkan_module = dlopen(search_lib_names[i], RTLD_NOW); return false;
if (s_vulkan_module)
break;
} }
#endif #endif
if (!s_vulkan_module)
{
std::fprintf(stderr, "Failed to load or locate libvulkan.so\n");
return false;
}
bool required_functions_missing = false; bool required_functions_missing = false;
auto LoadFunction = [&required_functions_missing](void** func_ptr, const char* name, bool is_required) { #define VULKAN_MODULE_ENTRY_POINT(name, required) \
*func_ptr = dlsym(s_vulkan_module, name); if (!s_vulkan_library.GetSymbol(#name, &name)) \
if (!(*func_ptr) && is_required) { \
{ ERROR_LOG("Vulkan: Failed to load required module function {}", #name); \
std::fprintf(stderr, "Vulkan: Failed to load required module function %s\n", name); required_functions_missing = true; \
required_functions_missing = true;
} }
};
#define VULKAN_MODULE_ENTRY_POINT(name, required) LoadFunction(reinterpret_cast<void**>(&name), #name, required);
#include "VKEntryPoints.inl" #include "VKEntryPoints.inl"
#undef VULKAN_MODULE_ENTRY_POINT #undef VULKAN_MODULE_ENTRY_POINT
if (required_functions_missing) if (required_functions_missing)
{ {
ResetVulkanLibraryFunctionPointers(); ResetVulkanLibraryFunctionPointers();
dlclose(s_vulkan_module); s_vulkan_library.Close();
s_vulkan_module = nullptr;
return false; return false;
} }
@ -178,13 +90,9 @@ bool Vulkan::LoadVulkanLibrary()
void Vulkan::UnloadVulkanLibrary() void Vulkan::UnloadVulkanLibrary()
{ {
ResetVulkanLibraryFunctionPointers(); ResetVulkanLibraryFunctionPointers();
if (s_vulkan_module) s_vulkan_library.Close();
dlclose(s_vulkan_module);
s_vulkan_module = nullptr;
} }
#endif
bool Vulkan::LoadVulkanInstanceFunctions(VkInstance instance) bool Vulkan::LoadVulkanInstanceFunctions(VkInstance instance)
{ {
bool required_functions_missing = false; bool required_functions_missing = false;

View File

@ -1,8 +1,10 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team // SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+ // SPDX-License-Identifier: LGPL-3.0+
#pragma once #pragma once
class Error;
#define VK_NO_PROTOTYPES #define VK_NO_PROTOTYPES
#ifdef _WIN32 #ifdef _WIN32
@ -86,7 +88,7 @@
namespace Vulkan namespace Vulkan
{ {
bool IsVulkanLibraryLoaded(); bool IsVulkanLibraryLoaded();
bool LoadVulkanLibrary(); bool LoadVulkanLibrary(Error* error);
bool LoadVulkanInstanceFunctions(VkInstance instance); bool LoadVulkanInstanceFunctions(VkInstance instance);
bool LoadVulkanDeviceFunctions(VkDevice device); bool LoadVulkanDeviceFunctions(VkDevice device);
void UnloadVulkanLibrary(); void UnloadVulkanLibrary();