Compare commits

...

7 commits

Author SHA1 Message Date
Stenzek 6621f4298f
CI: Fix Windows build upload 2024-05-15 02:10:35 +10:00
Stenzek c116e5a1d5
Qt: Consider per-game overrides for Edit Memory Cards menu 2024-05-15 01:50:43 +10:00
shinra-electric cea061f73f
Update upload-artifact & download-artifact actions to v4 (#3202)
- upload-artifact@v1 --> v4
- download-artifact@v1 --> v4
2024-05-15 01:50:24 +10:00
Stenzek 8e3284d8c6
Vulkan: Simplify loader using DynamicLibrary 2024-05-15 00:50:25 +10:00
Stenzek 88ace6e4ae
CMake: Detect cache line size dynamically on AArch64 Linux 2024-05-15 00:50:24 +10:00
Stenzek d9003b10c3
SettingsInterface: Fix TinyString helper 2024-05-15 00:50:24 +10:00
Stenzek 0ebc239003
CI: Remove ninja dependency for Mac deps 2024-05-15 00:50:24 +10:00
19 changed files with 292 additions and 214 deletions

View file

@ -86,12 +86,6 @@ jobs:
run: |
"C:\Program Files\7-Zip\7z.exe" a -r duckstation-windows-x64-release-symbols.zip ./bin/x64/*.pdb
- name: Upload x64 release symbols artifact
uses: actions/upload-artifact@v1
with:
name: "windows"
path: "duckstation-windows-x64-release-symbols.zip"
- name: Remove extra bloat before archiving
shell: cmd
run: |
@ -104,10 +98,12 @@ jobs:
"C:\Program Files\7-Zip\7z.exe" a -r duckstation-windows-x64-release.zip ./bin/x64/*
- name: Upload x64 release artifact
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v4
with:
name: "windows"
path: "duckstation-windows-x64-release.zip"
path: |
"duckstation-windows-x64-release.zip"
"duckstation-windows-x64-release-symbols.zip"
windows-arm64-build:
@ -177,12 +173,6 @@ jobs:
run: |
"C:\Program Files\7-Zip\7z.exe" a -r duckstation-windows-arm64-release-symbols.zip ./bin/ARM64/*.pdb
- name: Upload arm64 release symbols artifact
uses: actions/upload-artifact@v1
with:
name: "windows-arm64"
path: "duckstation-windows-arm64-release-symbols.zip"
- name: Remove extra bloat before archiving
shell: cmd
run: |
@ -195,10 +185,12 @@ jobs:
"C:\Program Files\7-Zip\7z.exe" a -r duckstation-windows-arm64-release.zip ./bin/ARM64/*
- name: Upload arm64 release artifact
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v4
with:
name: "windows-arm64"
path: "duckstation-windows-arm64-release.zip"
path: |
"duckstation-windows-arm64-release.zip"
"duckstation-windows-arm64-release-symbols.zip"
linux-build:
@ -272,7 +264,7 @@ jobs:
scripts/appimage/make-appimage.sh $(realpath .) $(realpath ./build) $HOME/deps DuckStation-x64
- name: Upload Qt AppImage
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v4
with:
name: "linux-x64-appimage-qt"
path: "DuckStation-x64.AppImage"
@ -335,7 +327,7 @@ jobs:
flatpak-builder-lint repo repo
- name: Upload Flatpak
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v4
with:
name: "linux-flatpak"
path: "duckstation-x64.flatpak"
@ -394,7 +386,7 @@ jobs:
zip -r duckstation-mac-release.zip DuckStation.app/
- name: Upload macOS .app
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v4
with:
name: "macos"
path: "build/duckstation-mac-release.zip"
@ -406,27 +398,27 @@ jobs:
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev'
steps:
- name: Download Windows Artifacts
uses: actions/download-artifact@v1
uses: actions/download-artifact@v4
with:
name: "windows"
- name: Download Windows ARM64 Artifact
uses: actions/download-artifact@v1
uses: actions/download-artifact@v4
with:
name: "windows-arm64"
- name: Download Qt AppImage Artifact
uses: actions/download-artifact@v1
uses: actions/download-artifact@v4
with:
name: "linux-x64-appimage-qt"
- name: Download Flatpak Artifact
uses: actions/download-artifact@v1
uses: actions/download-artifact@v4
with:
name: "linux-flatpak"
- name: Download MacOS Artifact
uses: actions/download-artifact@v1
uses: actions/download-artifact@v4
with:
name: "macos"

View file

@ -29,6 +29,7 @@ detect_operating_system()
detect_compiler()
detect_architecture()
detect_page_size()
detect_cache_line_size()
# Build options. Depends on system attributes.
include(DuckStationBuildOptions)

View file

@ -139,3 +139,58 @@ int main() {
set(HOST_PAGE_SIZE ${detect_page_size_output} CACHE STRING "Reported host page size")
endif()
endfunction()
function(detect_cache_line_size)
# This is only needed for ARM64, or if the user hasn't overridden it explicitly.
if(NOT CPU_ARCH_ARM64 OR HOST_CACHE_LINE_SIZE)
return()
endif()
if(NOT LINUX)
# For universal Apple builds, we use preprocessor macros to determine page size.
# Similar for Windows, except it's always 64 bytes.
return()
endif()
if(CMAKE_CROSSCOMPILING)
message(WARNING "Cross-compiling and can't determine page size, assuming default.")
return()
endif()
message(STATUS "Determining host cache line size")
set(detect_cache_line_size_file ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.c)
file(WRITE ${detect_cache_line_size_file} "
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int l1i = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
int l1d = sysconf(_SC_LEVEL1_ICACHE_LINESIZE);
int res = (l1i > l1d) ? l1i : l1d;
for (int index = 0; index < 16; index++) {
char buf[128];
snprintf(buf, sizeof(buf), \"/sys/devices/system/cpu/cpu0/cache/index%d/coherency_line_size\", index);
FILE* fp = fopen(buf, \"rb\");
if (!fp)
break;
fread(buf, sizeof(buf), 1, fp);
fclose(fp);
int val = atoi(buf);
res = (val > res) ? val : res;
}
printf(\"%d\", res);
return (res > 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}")
try_run(
detect_cache_line_size_run_result
detect_cache_line_size_compile_result
${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}
${detect_cache_line_size_file}
RUN_OUTPUT_VARIABLE detect_cache_line_size_output)
if(NOT detect_cache_line_size_compile_result OR NOT detect_cache_line_size_run_result EQUAL 0)
message(FATAL_ERROR "Could not determine host cache line size.")
else()
message(STATUS "Host cache line size: ${detect_cache_line_size_output}")
set(HOST_CACHE_LINE_SIZE ${detect_cache_line_size_output} CACHE STRING "Reported host cache line size")
endif()
endfunction()

View file

@ -342,9 +342,9 @@ rm -fr "SPIRV-Cross-$SPIRV_CROSS"
tar xf "SPIRV-Cross-$SPIRV_CROSS.tar.gz"
cd "SPIRV-Cross-$SPIRV_CROSS"
patch -p1 < "$SCRIPTDIR/spirv-cross-changes.patch"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DSPIRV_CROSS_SHARED=ON -DSPIRV_CROSS_STATIC=OFF -DSPIRV_CROSS_CLI=OFF -DSPIRV_CROSS_ENABLE_TESTS=OFF -DSPIRV_CROSS_ENABLE_GLSL=ON -DSPIRV_CROSS_ENABLE_HLSL=OFF -DSPIRV_CROSS_ENABLE_MSL=ON -DSPIRV_CROSS_ENABLE_CPP=OFF -DSPIRV_CROSS_ENABLE_REFLECT=OFF -DSPIRV_CROSS_ENABLE_C_API=ON -DSPIRV_CROSS_ENABLE_UTIL=ON -B build -G Ninja
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DSPIRV_CROSS_SHARED=ON -DSPIRV_CROSS_STATIC=OFF -DSPIRV_CROSS_CLI=OFF -DSPIRV_CROSS_ENABLE_TESTS=OFF -DSPIRV_CROSS_ENABLE_GLSL=ON -DSPIRV_CROSS_ENABLE_HLSL=OFF -DSPIRV_CROSS_ENABLE_MSL=ON -DSPIRV_CROSS_ENABLE_CPP=OFF -DSPIRV_CROSS_ENABLE_REFLECT=OFF -DSPIRV_CROSS_ENABLE_C_API=ON -DSPIRV_CROSS_ENABLE_UTIL=ON -B build
cmake --build build --parallel
ninja -C build install
cmake --install build
cd ..
echo "Cleaning up..."

View file

@ -111,3 +111,6 @@ endif()
if(HOST_PAGE_SIZE)
target_compile_definitions(common PUBLIC "-DOVERRIDE_HOST_PAGE_SIZE=${HOST_PAGE_SIZE}")
endif()
if(HOST_CACHE_LINE_SIZE)
target_compile_definitions(common PUBLIC "-DOVERRIDE_HOST_CACHE_LINE_SIZE=${HOST_CACHE_LINE_SIZE}")
endif()

View file

@ -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();

View file

@ -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

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -97,8 +97,8 @@ public:
return value;
}
ALWAYS_INLINE SmallString GetTinyStringValue(const char* section, const char* key,
const char* default_value = "") const
ALWAYS_INLINE TinyString GetTinyStringValue(const char* section, const char* key,
const char* default_value = "") const
{
TinyString value;
if (!GetStringValue(section, key, &value))

View file

@ -192,7 +192,9 @@ static constexpr u32 HOST_PAGE_SHIFT = 12;
#endif
// Host cache line sizes.
#if defined(__APPLE__) && defined(__aarch64__)
#if defined(OVERRIDE_HOST_CACHE_LINE_SIZE)
static constexpr u32 HOST_CACHE_LINE_SIZE = OVERRIDE_HOST_CACHE_LINE_SIZE;
#elif defined(__APPLE__) && defined(__aarch64__)
static constexpr u32 HOST_CACHE_LINE_SIZE = 128; // Apple Silicon uses 128b cache lines.
#else
static constexpr u32 HOST_CACHE_LINE_SIZE = 64; // Everything else is 64b.

View file

@ -4634,6 +4634,103 @@ void System::DeleteSaveStates(const char* serial, bool resume)
}
}
std::string System::GetGameMemoryCardPath(std::string_view serial, std::string_view path, u32 slot)
{
const char* section = "MemoryCards";
const TinyString type_key = TinyString::from_format("Card{}Type", slot + 1);
const MemoryCardType default_type =
(slot == 0) ? Settings::DEFAULT_MEMORY_CARD_1_TYPE : Settings::DEFAULT_MEMORY_CARD_2_TYPE;
const MemoryCardType global_type =
Settings::ParseMemoryCardTypeName(
Host::GetBaseTinyStringSettingValue(section, type_key, Settings::GetMemoryCardTypeName(default_type)))
.value_or(default_type);
MemoryCardType type = global_type;
std::unique_ptr<INISettingsInterface> ini;
if (!serial.empty())
{
std::string game_settings_path = GetGameSettingsPath(serial);
if (FileSystem::FileExists(game_settings_path.c_str()))
{
ini = std::make_unique<INISettingsInterface>(std::move(game_settings_path));
if (!ini->Load())
{
ini.reset();
}
else if (ini->ContainsValue(section, type_key))
{
type = Settings::ParseMemoryCardTypeName(
ini->GetTinyStringValue(section, type_key, Settings::GetMemoryCardTypeName(global_type)))
.value_or(global_type);
}
}
}
else if (type == MemoryCardType::PerGame)
{
// always shared without serial
type = MemoryCardType::Shared;
}
std::string ret;
switch (type)
{
case MemoryCardType::None:
break;
case MemoryCardType::Shared:
{
const TinyString path_key = TinyString::from_format("Card{}Path", slot + 1);
std::string global_path =
Host::GetBaseStringSettingValue(section, path_key, Settings::GetDefaultSharedMemoryCardName(slot + 1).c_str());
if (ini && ini->ContainsValue(section, path_key))
ret = ini->GetStringValue(section, path_key, global_path.c_str());
else
ret = std::move(global_path);
if (!Path::IsAbsolute(ret))
ret = Path::Combine(EmuFolders::MemoryCards, ret);
}
break;
case MemoryCardType::PerGame:
ret = g_settings.GetGameMemoryCardPath(serial, slot);
break;
case MemoryCardType::PerGameTitle:
{
const GameDatabase::Entry* entry = GameDatabase::GetEntryForSerial(serial);
if (entry)
{
ret = g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(entry->title), slot);
// Use disc set name if there isn't a per-disc card present.
const bool global_use_playlist_title = Host::GetBaseBoolSettingValue(section, "UsePlaylistTitle", true);
const bool use_playlist_title =
ini ? ini->GetBoolValue(section, "UsePlaylistTitle", global_use_playlist_title) : global_use_playlist_title;
if (entry->disc_set_name.empty() && use_playlist_title && !FileSystem::FileExists(ret.c_str()))
ret = g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(entry->disc_set_name), slot);
}
else
{
ret = g_settings.GetGameMemoryCardPath(
Path::SanitizeFileName(Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path))), slot);
}
}
break;
case MemoryCardType::PerGameFileTitle:
{
ret = g_settings.GetGameMemoryCardPath(
Path::SanitizeFileName(Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path))), slot);
}
break;
default:
break;
}
return ret;
}
std::string System::GetMostRecentResumeSaveStatePath()
{
std::vector<FILESYSTEM_FIND_DATA> files;

View file

@ -407,6 +407,9 @@ std::optional<ExtendedSaveStateInfo> GetExtendedSaveStateInfo(const char* path);
/// Deletes save states for the specified game code. If resume is set, the resume state is deleted too.
void DeleteSaveStates(const char* serial, bool resume);
/// Returns the path to the memory card for the specified game, considering game settings.
std::string GetGameMemoryCardPath(std::string_view serial, std::string_view path, u32 slot);
/// Returns intended output volume considering fast forwarding.
s32 GetAudioOutputVolume();
void UpdateVolume();

View file

@ -809,52 +809,7 @@ void MainWindow::populateGameListContextMenu(const GameList::Entry* entry, QWidg
connect(open_memory_cards_action, &QAction::triggered, [entry]() {
QString paths[2];
for (u32 i = 0; i < 2; i++)
{
MemoryCardType type = g_settings.memory_card_types[i];
if (entry->serial.empty() && type == MemoryCardType::PerGame)
type = MemoryCardType::Shared;
switch (type)
{
case MemoryCardType::None:
continue;
case MemoryCardType::Shared:
if (g_settings.memory_card_paths[i].empty())
{
paths[i] = QString::fromStdString(g_settings.GetSharedMemoryCardPath(i));
}
else
{
QFileInfo path(QString::fromStdString(g_settings.memory_card_paths[i]));
path.makeAbsolute();
paths[i] = QDir::toNativeSeparators(path.canonicalFilePath());
}
break;
case MemoryCardType::PerGame:
paths[i] = QString::fromStdString(g_settings.GetGameMemoryCardPath(entry->serial, i));
break;
case MemoryCardType::PerGameTitle:
{
paths[i] = QString::fromStdString(g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(entry->title), i));
if (!entry->disc_set_name.empty() && g_settings.memory_card_use_playlist_title && !QFile::exists(paths[i]))
{
paths[i] =
QString::fromStdString(g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(entry->disc_set_name), i));
}
}
break;
case MemoryCardType::PerGameFileTitle:
{
const std::string display_name(FileSystem::GetDisplayNameFromPath(entry->path));
paths[i] = QString::fromStdString(
g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(Path::GetFileTitle(display_name)), i));
}
break;
default:
break;
}
}
paths[i] = QString::fromStdString(System::GetGameMemoryCardPath(entry->serial, entry->path, i));
g_main_window->openMemoryCardEditor(paths[0], paths[1]);
});

View file

@ -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)
{

View file

@ -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;
}

View file

@ -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;

View file

@ -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();