mirror of
https://github.com/stenzek/duckstation.git
synced 2024-06-11 17:07:54 -04:00
Compare commits
7 commits
4208d41183
...
6621f4298f
Author | SHA1 | Date | |
---|---|---|---|
6621f4298f | |||
c116e5a1d5 | |||
cea061f73f | |||
8e3284d8c6 | |||
88ace6e4ae | |||
d9003b10c3 | |||
0ebc239003 |
40
.github/workflows/rolling-release.yml
vendored
40
.github/workflows/rolling-release.yml
vendored
|
@ -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"
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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..."
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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]);
|
||||
});
|
||||
|
|
|
@ -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 a new issue