mirror of
https://github.com/stenzek/duckstation.git
synced 2024-11-27 08:00:36 +00:00
Vulkan: Simplify loader using DynamicLibrary
This commit is contained in:
parent
88ace6e4ae
commit
8e3284d8c6
@ -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<std::string> GetBundlePath();
|
||||
|
||||
/// Get the bundle path to the actual application without any translocation fun
|
||||
std::optional<std::string> GetNonTranslocatedBundlePath();
|
||||
|
||||
|
@ -107,6 +107,17 @@ void CocoaTools::RemoveThemeChangeHandler(void* ctx)
|
||||
[s_themeChangeHandler removeCallback:ctx];
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
// See https://objective-see.com/blog/blog_0x15.html
|
||||
|
@ -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 <dlfcn.h>
|
||||
#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<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();
|
||||
Error::SetStringFmt(error, "Loading {} failed: {}", filename, err ? err : "<UNKNOWN>");
|
||||
return false;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -1112,11 +1112,9 @@ std::unique_ptr<GPUDevice> 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> 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<struct shaderc_compiler, void (*)(shaderc_compiler_t)> 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<size_t>(stage)], "source",
|
||||
dyn_shaderc::s_compiler, source.data(), source.length(), stage_kinds[static_cast<size_t>(stage)], "source",
|
||||
entry_point, options, &result);
|
||||
if (status != shaderc_compilation_status_success)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "vulkan_loader.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/dynamic_library.h"
|
||||
#include "common/log.h"
|
||||
|
||||
#include <cstdarg>
|
||||
@ -15,14 +16,6 @@
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <mach-o/dyld.h>
|
||||
#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<FARPROC*>(&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<void**>(&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;
|
||||
|
@ -1,8 +1,10 @@
|
||||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// 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();
|
||||
|
Loading…
Reference in New Issue
Block a user