mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-15 21:19:47 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fbf96ad587 | |||
| 866881d0e3 | |||
| a56b8d3de8 |
+2
-2
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
@@ -110,7 +110,7 @@ class FreedrenoSettingsFragment : Fragment() {
|
||||
val commonVars = listOf(
|
||||
"TU_DEBUG", "FD_MESA_DEBUG", "IR3_SHADER_DEBUG",
|
||||
"FD_RD_DUMP", "FD_RD_DUMP_FRAMES", "FD_RD_DUMP_TESTNAME",
|
||||
"TU_BREADCRUMBS"
|
||||
"TU_BREADCRUMBS", "FD_DEV_FEATURES"
|
||||
)
|
||||
|
||||
for (varName in commonVars) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.utils
|
||||
@@ -144,6 +144,15 @@ object FreedrenoPresets {
|
||||
)
|
||||
)
|
||||
|
||||
val DEV_FEATURES_UBWC_HINT = FreedrenoPreset(
|
||||
name = "Dev - UBWC Flag Hint",
|
||||
description = "Enable TP UBWC flag hint for development",
|
||||
icon = "ic_dev_features",
|
||||
variables = mapOf(
|
||||
"FD_DEV_FEATURES" to "enable_tp_ubwc_flag_hint=1"
|
||||
)
|
||||
)
|
||||
|
||||
val PERFORMANCE_DEFAULT = FreedrenoPreset(
|
||||
name = "Performance - Default",
|
||||
description = "Clear all debug options for performance",
|
||||
@@ -159,6 +168,7 @@ object FreedrenoPresets {
|
||||
CAPTURE_FRAMES,
|
||||
SHADER_DEBUG,
|
||||
GPU_HANG_TRACE,
|
||||
DEV_FEATURES_UBWC_HINT,
|
||||
PERFORMANCE_DEFAULT
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/**
|
||||
@@ -258,6 +258,11 @@ Java_org_yuzu_yuzu_1emu_utils_NativeFreedrenoConfig_setFreedrenoEnv(
|
||||
}
|
||||
|
||||
LOG_INFO(Frontend, "[Freedreno] Set {}={}", var_name, value);
|
||||
|
||||
if (var_name == "FD_DEV_FEATURES") {
|
||||
LOG_INFO(Frontend, "[Freedreno] FD_DEV_FEATURES enabled: {}", value);
|
||||
}
|
||||
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project SPDX-License-Identifier:
|
||||
@@ -186,8 +186,6 @@ if(ARCHITECTURE_x86_64)
|
||||
common
|
||||
PRIVATE x64/cpu_detect.cpp
|
||||
x64/cpu_detect.h
|
||||
x64/cpu_wait.cpp
|
||||
x64/cpu_wait.h
|
||||
x64/native_clock.cpp
|
||||
x64/native_clock.h
|
||||
x64/rdtsc.cpp
|
||||
|
||||
@@ -795,7 +795,6 @@ struct Values {
|
||||
|
||||
// Per-game overrides
|
||||
bool use_squashed_iterated_blend;
|
||||
bool enable_global_overrides{true};
|
||||
|
||||
Setting<bool> enable_overlay{linkage, false, "enable_overlay", Category::Core};
|
||||
};
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <thread>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
#include "common/x64/cpu_detect.h"
|
||||
#include "common/x64/cpu_wait.h"
|
||||
#include "common/x64/rdtsc.h"
|
||||
|
||||
namespace Common::X64 {
|
||||
|
||||
namespace {
|
||||
|
||||
// 100,000 cycles is a reasonable amount of time to wait to save on CPU resources.
|
||||
// For reference:
|
||||
// At 1 GHz, 100K cycles is 100us
|
||||
// At 2 GHz, 100K cycles is 50us
|
||||
// At 4 GHz, 100K cycles is 25us
|
||||
constexpr auto PauseCycles = 100'000U;
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
__forceinline static void TPAUSE() {
|
||||
static constexpr auto RequestC02State = 0U;
|
||||
_tpause(RequestC02State, FencedRDTSC() + PauseCycles);
|
||||
}
|
||||
|
||||
__forceinline static void MWAITX() {
|
||||
static constexpr auto EnableWaitTimeFlag = 1U << 1;
|
||||
static constexpr auto RequestC1State = 0U;
|
||||
|
||||
// monitor_var should be aligned to a cache line.
|
||||
alignas(64) u64 monitor_var{};
|
||||
_mm_monitorx(&monitor_var, 0, 0);
|
||||
_mm_mwaitx(EnableWaitTimeFlag, RequestC1State, PauseCycles);
|
||||
}
|
||||
#else
|
||||
static void TPAUSE() {
|
||||
static constexpr auto RequestC02State = 0U;
|
||||
const auto tsc = FencedRDTSC() + PauseCycles;
|
||||
const auto eax = static_cast<u32>(tsc & 0xFFFFFFFF);
|
||||
const auto edx = static_cast<u32>(tsc >> 32);
|
||||
asm volatile("tpause %0" : : "r"(RequestC02State), "d"(edx), "a"(eax));
|
||||
}
|
||||
|
||||
static void MWAITX() {
|
||||
static constexpr auto EnableWaitTimeFlag = 1U << 1;
|
||||
static constexpr auto RequestC1State = 0U;
|
||||
|
||||
// monitor_var should be aligned to a cache line.
|
||||
alignas(64) u64 monitor_var{};
|
||||
asm volatile("monitorx" : : "a"(&monitor_var), "c"(0), "d"(0));
|
||||
asm volatile("mwaitx" : : "a"(RequestC1State), "b"(PauseCycles), "c"(EnableWaitTimeFlag));
|
||||
}
|
||||
#endif
|
||||
|
||||
void MicroSleep() {
|
||||
static const bool has_waitpkg = GetCPUCaps().waitpkg;
|
||||
static const bool has_monitorx = GetCPUCaps().monitorx;
|
||||
|
||||
if (has_waitpkg) {
|
||||
TPAUSE();
|
||||
} else if (has_monitorx) {
|
||||
MWAITX();
|
||||
} else {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Common::X64
|
||||
@@ -1,10 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Common::X64 {
|
||||
|
||||
void MicroSleep();
|
||||
|
||||
} // namespace Common::X64
|
||||
@@ -17,8 +17,6 @@ add_library(core STATIC
|
||||
constants.h
|
||||
core.cpp
|
||||
core.h
|
||||
game_overrides.cpp
|
||||
game_overrides.h
|
||||
game_settings.cpp
|
||||
game_settings.h
|
||||
core_timing.cpp
|
||||
|
||||
+4
-2
@@ -371,8 +371,7 @@ struct System::Impl {
|
||||
LOG_ERROR(Core, "Failed to find program id for ROM");
|
||||
}
|
||||
|
||||
GameSettings::LoadOverrides(program_id, gpu_core->Renderer(),
|
||||
Settings::values.enable_global_overrides);
|
||||
GameSettings::LoadOverrides(program_id, gpu_core->Renderer());
|
||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||
Network::GameInfo game_info;
|
||||
game_info.name = name;
|
||||
@@ -386,6 +385,9 @@ struct System::Impl {
|
||||
void ShutdownMainProcess() {
|
||||
SetShuttingDown(true);
|
||||
|
||||
// Reset per-game flags
|
||||
Settings::values.use_squashed_iterated_blend = false;
|
||||
|
||||
is_powered_on = false;
|
||||
exit_locked = false;
|
||||
exit_requested = false;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
@@ -13,10 +13,6 @@
|
||||
#include "common/windows/timer_resolution.h"
|
||||
#endif
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "common/x64/cpu_wait.h"
|
||||
#endif
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hardware_properties.h"
|
||||
@@ -287,28 +283,7 @@ void CoreTiming::ThreadLoop() {
|
||||
if (next_time) {
|
||||
// There are more events left in the queue, wait until the next event.
|
||||
auto wait_time = *next_time - GetGlobalTimeNs().count();
|
||||
if (wait_time > 0) {
|
||||
#ifdef _WIN32
|
||||
while (!paused && !event.IsSet() && wait_time > 0) {
|
||||
wait_time = *next_time - GetGlobalTimeNs().count();
|
||||
if (wait_time >= timer_resolution_ns) {
|
||||
Common::Windows::SleepForOneTick();
|
||||
} else {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
Common::X64::MicroSleep();
|
||||
#else
|
||||
std::this_thread::yield();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (event.IsSet()) {
|
||||
event.Reset();
|
||||
}
|
||||
#else
|
||||
event.WaitFor(std::chrono::nanoseconds(wait_time));
|
||||
#endif
|
||||
}
|
||||
event.WaitFor(std::chrono::nanoseconds(wait_time));
|
||||
} else {
|
||||
// Queue is empty, wait until another event is scheduled and signals us to
|
||||
// continue.
|
||||
|
||||
@@ -1,615 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/game_overrides.h"
|
||||
#include "core/game_settings.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <charconv>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
|
||||
namespace Core::GameOverrides {
|
||||
namespace {
|
||||
|
||||
std::string Trim(std::string_view str) {
|
||||
const auto start = str.find_first_not_of(" \t\r\n");
|
||||
if (start == std::string_view::npos) return "";
|
||||
const auto end = str.find_last_not_of(" \t\r\n");
|
||||
return std::string(str.substr(start, end - start + 1));
|
||||
}
|
||||
|
||||
std::string ToLower(std::string str) {
|
||||
std::transform(str.begin(), str.end(), str.begin(),
|
||||
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
return str;
|
||||
}
|
||||
|
||||
bool ToBool(const std::string& value) {
|
||||
return value == "true" || value == "1" || value == "yes" || value == "on";
|
||||
}
|
||||
|
||||
GameSettings::OS DetectOS() {
|
||||
#if defined(_WIN32)
|
||||
return GameSettings::OS::Windows;
|
||||
#elif defined(__FIREOS__)
|
||||
return GameSettings::OS::FireOS;
|
||||
#elif defined(__ANDROID__)
|
||||
return GameSettings::OS::Android;
|
||||
#elif defined(__OHOS__)
|
||||
return GameSettings::OS::HarmonyOS;
|
||||
#elif defined(__HAIKU__)
|
||||
return GameSettings::OS::HaikuOS;
|
||||
#elif defined(__DragonFly__)
|
||||
return GameSettings::OS::DragonFlyBSD;
|
||||
#elif defined(__NetBSD__)
|
||||
return GameSettings::OS::NetBSD;
|
||||
#elif defined(__OpenBSD__)
|
||||
return GameSettings::OS::OpenBSD;
|
||||
#elif defined(_AIX)
|
||||
return GameSettings::OS::AIX;
|
||||
#elif defined(__managarm__)
|
||||
return GameSettings::OS::Managarm;
|
||||
#elif defined(__redox__)
|
||||
return GameSettings::OS::RedoxOS;
|
||||
#elif defined(__APPLE__)
|
||||
return GameSettings::OS::MacOS;
|
||||
#elif defined(__FreeBSD__)
|
||||
return GameSettings::OS::FreeBSD;
|
||||
#elif defined(__sun) && defined(__SVR4)
|
||||
return GameSettings::OS::Solaris;
|
||||
#elif defined(__linux__)
|
||||
return GameSettings::OS::Linux;
|
||||
#else
|
||||
return GameSettings::OS::Unknown;
|
||||
#endif
|
||||
}
|
||||
|
||||
GameSettings::GPUVendor DetectGPUVendor(const std::string& vendor_string) {
|
||||
std::string gpu = vendor_string;
|
||||
std::transform(gpu.begin(), gpu.end(), gpu.begin(),
|
||||
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
|
||||
if (gpu.find("nvidia") != std::string::npos || gpu.find("geforce") != std::string::npos) {
|
||||
return GameSettings::GPUVendor::Nvidia;
|
||||
}
|
||||
if (gpu.find("amd") != std::string::npos || gpu.find("radeon") != std::string::npos ||
|
||||
gpu.find("radv") != std::string::npos) {
|
||||
return GameSettings::GPUVendor::AMD;
|
||||
}
|
||||
if (gpu.find("intel") != std::string::npos) {
|
||||
return GameSettings::GPUVendor::Intel;
|
||||
}
|
||||
if (gpu.find("apple") != std::string::npos || gpu.find("molten") != std::string::npos) {
|
||||
return GameSettings::GPUVendor::Apple;
|
||||
}
|
||||
if (gpu.find("qualcomm") != std::string::npos || gpu.find("adreno") != std::string::npos ||
|
||||
gpu.find("turnip") != std::string::npos) {
|
||||
return GameSettings::GPUVendor::Qualcomm;
|
||||
}
|
||||
if (gpu.find("mali") != std::string::npos) {
|
||||
return GameSettings::GPUVendor::ARM;
|
||||
}
|
||||
if (gpu.find("powervr") != std::string::npos || gpu.find("pvr") != std::string::npos) {
|
||||
return GameSettings::GPUVendor::Imagination;
|
||||
}
|
||||
if (gpu.find("microsoft") != std::string::npos) {
|
||||
return GameSettings::GPUVendor::Microsoft;
|
||||
}
|
||||
return GameSettings::GPUVendor::Unknown;
|
||||
}
|
||||
|
||||
std::string OSToString(GameSettings::OS os) {
|
||||
switch (os) {
|
||||
case GameSettings::OS::Windows: return "windows";
|
||||
case GameSettings::OS::Linux: return "linux";
|
||||
case GameSettings::OS::MacOS: return "macos";
|
||||
case GameSettings::OS::Android: return "android";
|
||||
case GameSettings::OS::FireOS: return "fireos";
|
||||
case GameSettings::OS::HarmonyOS: return "harmonyos";
|
||||
case GameSettings::OS::FreeBSD: return "freebsd";
|
||||
case GameSettings::OS::DragonFlyBSD: return "dragonflybsd";
|
||||
case GameSettings::OS::NetBSD: return "netbsd";
|
||||
case GameSettings::OS::OpenBSD: return "openbsd";
|
||||
case GameSettings::OS::HaikuOS: return "haikuos";
|
||||
case GameSettings::OS::AIX: return "aix";
|
||||
case GameSettings::OS::Managarm: return "managarm";
|
||||
case GameSettings::OS::RedoxOS: return "redoxos";
|
||||
case GameSettings::OS::Solaris: return "solaris";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
std::string VendorToString(GameSettings::GPUVendor vendor) {
|
||||
switch (vendor) {
|
||||
case GameSettings::GPUVendor::Nvidia: return "nvidia";
|
||||
case GameSettings::GPUVendor::AMD: return "amd";
|
||||
case GameSettings::GPUVendor::Intel: return "intel";
|
||||
case GameSettings::GPUVendor::Apple: return "apple";
|
||||
case GameSettings::GPUVendor::Qualcomm: return "qualcomm";
|
||||
case GameSettings::GPUVendor::ARM: return "arm";
|
||||
case GameSettings::GPUVendor::Imagination: return "imagination";
|
||||
case GameSettings::GPUVendor::Microsoft: return "microsoft";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
#define SET_OVERRIDE(setting, new_value) \
|
||||
do { \
|
||||
s.setting.SetGlobal(false); \
|
||||
s.setting.SetValue(new_value); \
|
||||
} while(0)
|
||||
|
||||
void ApplySetting(const std::string& key, const std::string& value) {
|
||||
const std::string k = ToLower(key);
|
||||
const std::string v = ToLower(value);
|
||||
auto& s = Settings::values;
|
||||
|
||||
if (k == "backend" || k == "renderer_backend") {
|
||||
if (v == "vulkan") {
|
||||
SET_OVERRIDE(renderer_backend, Settings::RendererBackend::Vulkan);
|
||||
} else if (v == "opengl" || v == "opengl_glsl") {
|
||||
SET_OVERRIDE(renderer_backend, Settings::RendererBackend::OpenGL_GLSL);
|
||||
} else if (v == "opengl_glasm") {
|
||||
SET_OVERRIDE(renderer_backend, Settings::RendererBackend::OpenGL_GLASM);
|
||||
} else if (v == "opengl_spirv") {
|
||||
SET_OVERRIDE(renderer_backend, Settings::RendererBackend::OpenGL_SPIRV);
|
||||
} else if (v == "null") {
|
||||
SET_OVERRIDE(renderer_backend, Settings::RendererBackend::Null);
|
||||
}
|
||||
}
|
||||
else if (k == "vsync") {
|
||||
SET_OVERRIDE(vsync_mode, ToBool(v) ? Settings::VSyncMode::Fifo : Settings::VSyncMode::Immediate);
|
||||
}
|
||||
else if (k == "vsync_mode") {
|
||||
if (v == "immediate" || v == "off") {
|
||||
SET_OVERRIDE(vsync_mode, Settings::VSyncMode::Immediate);
|
||||
} else if (v == "fifo" || v == "on") {
|
||||
SET_OVERRIDE(vsync_mode, Settings::VSyncMode::Fifo);
|
||||
} else if (v == "fifo_relaxed") {
|
||||
SET_OVERRIDE(vsync_mode, Settings::VSyncMode::FifoRelaxed);
|
||||
} else if (v == "mailbox") {
|
||||
SET_OVERRIDE(vsync_mode, Settings::VSyncMode::Mailbox);
|
||||
}
|
||||
}
|
||||
else if (k == "gpu_accuracy" || k == "accuracy_level") {
|
||||
if (v == "low" || v == "fast") {
|
||||
SET_OVERRIDE(gpu_accuracy, Settings::GpuAccuracy::Low);
|
||||
} else if (v == "medium" || v == "balanced" || v == "normal") {
|
||||
SET_OVERRIDE(gpu_accuracy, Settings::GpuAccuracy::Medium);
|
||||
} else if (v == "high" || v == "accurate") {
|
||||
SET_OVERRIDE(gpu_accuracy, Settings::GpuAccuracy::High);
|
||||
}
|
||||
}
|
||||
else if (k == "cpu_accuracy") {
|
||||
if (v == "auto") {
|
||||
SET_OVERRIDE(cpu_accuracy, Settings::CpuAccuracy::Auto);
|
||||
} else if (v == "accurate") {
|
||||
SET_OVERRIDE(cpu_accuracy, Settings::CpuAccuracy::Accurate);
|
||||
} else if (v == "unsafe") {
|
||||
SET_OVERRIDE(cpu_accuracy, Settings::CpuAccuracy::Unsafe);
|
||||
} else if (v == "paranoid") {
|
||||
SET_OVERRIDE(cpu_accuracy, Settings::CpuAccuracy::Paranoid);
|
||||
}
|
||||
}
|
||||
else if (k == "astc_decode_mode" || k == "accelerate_astc") {
|
||||
if (v == "cpu") {
|
||||
SET_OVERRIDE(accelerate_astc, Settings::AstcDecodeMode::Cpu);
|
||||
} else if (v == "gpu") {
|
||||
SET_OVERRIDE(accelerate_astc, Settings::AstcDecodeMode::Gpu);
|
||||
} else if (v == "cpu_async") {
|
||||
SET_OVERRIDE(accelerate_astc, Settings::AstcDecodeMode::CpuAsynchronous);
|
||||
}
|
||||
}
|
||||
else if (k == "fast_gpu_time") {
|
||||
if (v == "off" || v == "false" || v == "0" || v == "normal") {
|
||||
SET_OVERRIDE(fast_gpu_time, Settings::GpuOverclock::Normal);
|
||||
} else if (v == "medium" || v == "true" || v == "1") {
|
||||
SET_OVERRIDE(fast_gpu_time, Settings::GpuOverclock::Medium);
|
||||
} else if (v == "high") {
|
||||
SET_OVERRIDE(fast_gpu_time, Settings::GpuOverclock::High);
|
||||
}
|
||||
}
|
||||
else if (k == "resolution" || k == "resolution_setup") {
|
||||
if (v == "0.25x") {
|
||||
SET_OVERRIDE(resolution_setup, Settings::ResolutionSetup::Res1_4X);
|
||||
} else if (v == "0.5x" || v == "half") {
|
||||
SET_OVERRIDE(resolution_setup, Settings::ResolutionSetup::Res1_2X);
|
||||
} else if (v == "0.75x") {
|
||||
SET_OVERRIDE(resolution_setup, Settings::ResolutionSetup::Res3_4X);
|
||||
} else if (v == "1x" || v == "native") {
|
||||
SET_OVERRIDE(resolution_setup, Settings::ResolutionSetup::Res1X);
|
||||
} else if (v == "1.25x") {
|
||||
SET_OVERRIDE(resolution_setup, Settings::ResolutionSetup::Res5_4X);
|
||||
} else if (v == "1.5x") {
|
||||
SET_OVERRIDE(resolution_setup, Settings::ResolutionSetup::Res3_2X);
|
||||
} else if (v == "2x") {
|
||||
SET_OVERRIDE(resolution_setup, Settings::ResolutionSetup::Res2X);
|
||||
} else if (v == "3x") {
|
||||
SET_OVERRIDE(resolution_setup, Settings::ResolutionSetup::Res3X);
|
||||
} else if (v == "4x") {
|
||||
SET_OVERRIDE(resolution_setup, Settings::ResolutionSetup::Res4X);
|
||||
} else if (v == "5x") {
|
||||
SET_OVERRIDE(resolution_setup, Settings::ResolutionSetup::Res5X);
|
||||
} else if (v == "6x") {
|
||||
SET_OVERRIDE(resolution_setup, Settings::ResolutionSetup::Res6X);
|
||||
} else if (v == "7x") {
|
||||
SET_OVERRIDE(resolution_setup, Settings::ResolutionSetup::Res7X);
|
||||
} else if (v == "8x") {
|
||||
SET_OVERRIDE(resolution_setup, Settings::ResolutionSetup::Res8X);
|
||||
}
|
||||
}
|
||||
else if (k == "async_gpu" || k == "use_asynchronous_gpu_emulation") {
|
||||
SET_OVERRIDE(use_asynchronous_gpu_emulation, ToBool(v));
|
||||
}
|
||||
else if (k == "reactive_flushing" || k == "use_reactive_flushing") {
|
||||
SET_OVERRIDE(use_reactive_flushing, ToBool(v));
|
||||
}
|
||||
else if (k == "multicore" || k == "use_multi_core") {
|
||||
SET_OVERRIDE(use_multi_core, ToBool(v));
|
||||
}
|
||||
else if (k == "async_shaders" || k == "use_asynchronous_shaders") {
|
||||
SET_OVERRIDE(use_asynchronous_shaders, ToBool(v));
|
||||
}
|
||||
else if (k == "pipeline_cache" || k == "use_vulkan_driver_pipeline_cache") {
|
||||
SET_OVERRIDE(use_vulkan_driver_pipeline_cache, ToBool(v));
|
||||
}
|
||||
else if (k == "compute_pipelines" || k == "enable_compute_pipelines") {
|
||||
SET_OVERRIDE(enable_compute_pipelines, ToBool(v));
|
||||
}
|
||||
else if (k == "use_disk_shader_cache") {
|
||||
SET_OVERRIDE(use_disk_shader_cache, ToBool(v));
|
||||
}
|
||||
else if (k == "barrier_feedback_loops") {
|
||||
SET_OVERRIDE(barrier_feedback_loops, ToBool(v));
|
||||
}
|
||||
else if (k == "async_presentation") {
|
||||
SET_OVERRIDE(async_presentation, ToBool(v));
|
||||
}
|
||||
else if (k == "use_squashed_iterated_blend") {
|
||||
s.use_squashed_iterated_blend = ToBool(v);
|
||||
}
|
||||
else if (k == "smo" || k == "sync_memory_operations") {
|
||||
SET_OVERRIDE(sync_memory_operations, ToBool(v));
|
||||
}
|
||||
else if (k == "nvdec" || k == "nvdec_emulation") {
|
||||
if (v == "off" || v == "disabled") {
|
||||
SET_OVERRIDE(nvdec_emulation, Settings::NvdecEmulation::Off);
|
||||
} else if (v == "cpu") {
|
||||
SET_OVERRIDE(nvdec_emulation, Settings::NvdecEmulation::Cpu);
|
||||
} else if (v == "gpu") {
|
||||
SET_OVERRIDE(nvdec_emulation, Settings::NvdecEmulation::Gpu);
|
||||
}
|
||||
}
|
||||
else if (k == "enable_buffer_history" || k == "buffer_history") {
|
||||
SET_OVERRIDE(enable_buffer_history, ToBool(v));
|
||||
}
|
||||
else if (k == "fix_bloom_effects" || k == "fix_bloom") {
|
||||
SET_OVERRIDE(fix_bloom_effects, ToBool(v));
|
||||
}
|
||||
else if (k == "dma_accuracy") {
|
||||
if (v == "default") {
|
||||
SET_OVERRIDE(dma_accuracy, Settings::DmaAccuracy::Default);
|
||||
} else if (v == "unsafe" || v == "fast") {
|
||||
SET_OVERRIDE(dma_accuracy, Settings::DmaAccuracy::Unsafe);
|
||||
} else if (v == "safe" || v == "stable") {
|
||||
SET_OVERRIDE(dma_accuracy, Settings::DmaAccuracy::Safe);
|
||||
}
|
||||
}
|
||||
else if (k == "airplane_mode" || k == "airplane") {
|
||||
SET_OVERRIDE(airplane_mode, ToBool(v));
|
||||
}
|
||||
else if (k == "controller_applet_mode" || k == "controller_applet") {
|
||||
if (v == "lle" || v == "real") {
|
||||
SET_OVERRIDE(controller_applet_mode, Settings::AppletMode::LLE);
|
||||
} else if (v == "hle" || v == "custom") {
|
||||
SET_OVERRIDE(controller_applet_mode, Settings::AppletMode::HLE);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG_WARNING(Core, "Unknown game override setting: {}={}", key, value);
|
||||
}
|
||||
}
|
||||
|
||||
#undef SET_OVERRIDE
|
||||
|
||||
std::optional<Section> ParseSectionHeader(std::string_view header) {
|
||||
if (header.size() < 2 || header.front() != '[' || header.back() != ']') {
|
||||
return std::nullopt;
|
||||
}
|
||||
header = header.substr(1, header.size() - 2);
|
||||
|
||||
Section section{};
|
||||
|
||||
const auto pipe_pos = header.find('|');
|
||||
std::string_view title_part = header;
|
||||
std::string_view conditions_part;
|
||||
|
||||
if (pipe_pos != std::string_view::npos) {
|
||||
title_part = header.substr(0, pipe_pos);
|
||||
conditions_part = header.substr(pipe_pos + 1);
|
||||
}
|
||||
|
||||
std::string title_str = Trim(title_part);
|
||||
if (title_str.starts_with("0x") || title_str.starts_with("0X")) {
|
||||
title_str = title_str.substr(2);
|
||||
}
|
||||
|
||||
std::uint64_t title_id = 0;
|
||||
auto [ptr, ec] = std::from_chars(title_str.data(), title_str.data() + title_str.size(),
|
||||
title_id, 16);
|
||||
if (ec != std::errc{}) {
|
||||
return std::nullopt;
|
||||
}
|
||||
section.title_id = title_id;
|
||||
|
||||
if (!conditions_part.empty()) {
|
||||
std::string cond_str(conditions_part);
|
||||
std::istringstream stream(cond_str);
|
||||
std::string token;
|
||||
|
||||
while (std::getline(stream, token, ',')) {
|
||||
token = Trim(token);
|
||||
const auto colon_pos = token.find(':');
|
||||
if (colon_pos == std::string::npos) continue;
|
||||
|
||||
std::string cond_key = ToLower(Trim(token.substr(0, colon_pos)));
|
||||
std::string cond_value = ToLower(Trim(token.substr(colon_pos + 1)));
|
||||
|
||||
if (cond_key == "vendor") {
|
||||
section.condition.vendor = cond_value;
|
||||
} else if (cond_key == "os") {
|
||||
section.condition.os.push_back(cond_value);
|
||||
} else if (cond_key == "cpu" || cond_key == "cpu_backend") {
|
||||
section.condition.cpu_backend = cond_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return section;
|
||||
}
|
||||
|
||||
std::string CpuBackendToString(Settings::CpuBackend backend) {
|
||||
switch (backend) {
|
||||
case Settings::CpuBackend::Dynarmic: return "jit";
|
||||
case Settings::CpuBackend::Nce: return "nce";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
bool ConditionMatches(const Condition& cond, const GameSettings::EnvironmentInfo& env) {
|
||||
if (cond.vendor.has_value() && cond.vendor.value() != VendorToString(env.vendor)) {
|
||||
return false;
|
||||
}
|
||||
if (!cond.os.empty()) {
|
||||
const std::string current_os = OSToString(env.os);
|
||||
bool os_match = std::any_of(cond.os.begin(), cond.os.end(),
|
||||
[¤t_os](const std::string& os) { return os == current_os; });
|
||||
if (!os_match) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (cond.cpu_backend.has_value()) {
|
||||
const std::string current_cpu = CpuBackendToString(Settings::values.cpu_backend.GetValue());
|
||||
const std::string& required = cond.cpu_backend.value();
|
||||
if (required == "jit" || required == "dynarmic") {
|
||||
if (current_cpu != "jit") return false;
|
||||
} else if (required == "nce") {
|
||||
if (current_cpu != "nce") return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<Section> ParseOverridesFile(const std::filesystem::path& path) {
|
||||
std::vector<Section> sections;
|
||||
std::ifstream file(path);
|
||||
if (!file.is_open()) {
|
||||
return sections;
|
||||
}
|
||||
|
||||
Section* current_section = nullptr;
|
||||
std::string line;
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
line = Trim(line);
|
||||
|
||||
if (line.empty() || line.front() == ';' || line.front() == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.front() == '[' && line.back() == ']') {
|
||||
auto section = ParseSectionHeader(line);
|
||||
if (section) {
|
||||
sections.push_back(std::move(*section));
|
||||
current_section = §ions.back();
|
||||
} else {
|
||||
current_section = nullptr;
|
||||
LOG_WARNING(Core, "Invalid section header in overrides.ini: {}", line);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current_section) {
|
||||
const auto eq_pos = line.find('=');
|
||||
if (eq_pos != std::string::npos) {
|
||||
std::string setting_key = Trim(line.substr(0, eq_pos));
|
||||
std::string setting_value = Trim(line.substr(eq_pos + 1));
|
||||
current_section->settings.emplace_back(std::move(setting_key), std::move(setting_value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sections;
|
||||
}
|
||||
|
||||
bool IsEarlySetting(const std::string& key) {
|
||||
std::array early_settings = {
|
||||
"backend",
|
||||
"renderer_backend",
|
||||
"multicore",
|
||||
"use_multi_core",
|
||||
};
|
||||
const std::string k = ToLower(key);
|
||||
return std::ranges::any_of(early_settings,
|
||||
[&k](const char* s) { return k == s; });
|
||||
}
|
||||
|
||||
bool EarlyConditionMatches(const Condition& cond, GameSettings::OS current_os,
|
||||
const std::optional<GameSettings::GPUVendor>& detected_vendor) {
|
||||
if (!cond.os.empty()) {
|
||||
const std::string current_os_str = OSToString(current_os);
|
||||
bool os_match = std::any_of(cond.os.begin(), cond.os.end(),
|
||||
[¤t_os_str](const std::string& os) { return os == current_os_str; });
|
||||
if (!os_match) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (cond.vendor.has_value()) {
|
||||
if (!detected_vendor.has_value()) {
|
||||
return false;
|
||||
}
|
||||
if (cond.vendor.value() != VendorToString(*detected_vendor)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (cond.cpu_backend.has_value()) {
|
||||
const std::string current_cpu = CpuBackendToString(Settings::values.cpu_backend.GetValue());
|
||||
const std::string& required = cond.cpu_backend.value();
|
||||
if (required == "jit" || required == "dynarmic") {
|
||||
if (current_cpu != "jit") return false;
|
||||
} else if (required == "nce" /* || required == "native" */) {
|
||||
if (current_cpu != "nce") return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int GetSpecificity(const Section& s) {
|
||||
int score = 0;
|
||||
if (s.condition.vendor.has_value()) score++;
|
||||
if (!s.condition.os.empty()) score++;
|
||||
if (s.condition.cpu_backend.has_value()) score++;
|
||||
return score;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
std::filesystem::path GetOverridesPath() {
|
||||
return Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) / "overrides.ini";
|
||||
}
|
||||
|
||||
bool OverridesFileExists() {
|
||||
return std::filesystem::exists(GetOverridesPath());
|
||||
}
|
||||
|
||||
std::optional<std::uint32_t> GetOverridesFileVersion() {
|
||||
const auto path = GetOverridesPath();
|
||||
std::ifstream file(path);
|
||||
if (!file.is_open()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
if (std::getline(file, line)) {
|
||||
line = Trim(line);
|
||||
if (line.starts_with(kVersionPrefix)) {
|
||||
const auto version_str = line.substr(std::strlen(kVersionPrefix));
|
||||
std::uint32_t version = 0;
|
||||
auto [ptr, ec] = std::from_chars(version_str.data(),
|
||||
version_str.data() + version_str.size(), version);
|
||||
if (ec == std::errc{}) {
|
||||
return version;
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void ApplyEarlyOverrides(std::uint64_t program_id, const std::string& gpu_vendor) {
|
||||
const auto path = GetOverridesPath();
|
||||
if (!std::filesystem::exists(path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto current_os = DetectOS();
|
||||
std::optional<GameSettings::GPUVendor> detected_vendor;
|
||||
if (!gpu_vendor.empty()) {
|
||||
detected_vendor = DetectGPUVendor(gpu_vendor);
|
||||
}
|
||||
|
||||
auto sections = ParseOverridesFile(path);
|
||||
|
||||
// Filter matching sections
|
||||
std::vector<Section> matching;
|
||||
for (auto& section : sections) {
|
||||
if (section.title_id != program_id) continue;
|
||||
if (!EarlyConditionMatches(section.condition, current_os, detected_vendor)) continue;
|
||||
matching.push_back(std::move(section));
|
||||
}
|
||||
|
||||
// Sort by base first, then os, then vendor, then both
|
||||
std::sort(matching.begin(), matching.end(),
|
||||
[](const Section& a, const Section& b) {
|
||||
return GetSpecificity(a) < GetSpecificity(b);
|
||||
});
|
||||
|
||||
// Apply only early settings, only apply before renderer overrides
|
||||
for (const auto& section : matching) {
|
||||
for (const auto& [setting_key, setting_value] : section.settings) {
|
||||
if (IsEarlySetting(setting_key)) {
|
||||
LOG_INFO(Core, "Game override {:016X}: {}={}",
|
||||
program_id, setting_key, setting_value);
|
||||
ApplySetting(setting_key, setting_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyLateOverrides(std::uint64_t program_id, const VideoCore::RendererBase& renderer) {
|
||||
const auto path = GetOverridesPath();
|
||||
if (!std::filesystem::exists(path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto env = GameSettings::DetectEnvironment(renderer);
|
||||
auto sections = ParseOverridesFile(path);
|
||||
|
||||
// Filter matching sections
|
||||
std::vector<Section> matching;
|
||||
for (auto& section : sections) {
|
||||
if (section.title_id != program_id) continue;
|
||||
if (!ConditionMatches(section.condition, env)) continue;
|
||||
matching.push_back(std::move(section));
|
||||
}
|
||||
|
||||
// Sort by base first, then single condition, then multiple
|
||||
std::sort(matching.begin(), matching.end(),
|
||||
[](const Section& a, const Section& b) {
|
||||
return GetSpecificity(a) < GetSpecificity(b);
|
||||
});
|
||||
|
||||
// Apply settings (skip early ones, they were already applied, like renderr_backend)
|
||||
for (const auto& section : matching) {
|
||||
for (const auto& [setting_key, setting_value] : section.settings) {
|
||||
if (!IsEarlySetting(setting_key)) {
|
||||
LOG_INFO(Core, "Game override {:016X}: {}={}",
|
||||
program_id, setting_key, setting_value);
|
||||
ApplySetting(setting_key, setting_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace Core::GameOverrides
|
||||
@@ -1,40 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace VideoCore {
|
||||
class RendererBase;
|
||||
}
|
||||
|
||||
namespace Core::GameOverrides {
|
||||
// used to check against GitHub Release Version
|
||||
// repo at: https://github.com/eden-emulator/eden-overrides
|
||||
inline constexpr std::uint32_t kOverridesVersion = 1;
|
||||
inline constexpr const char* kVersionPrefix = "; version=";
|
||||
|
||||
struct Condition {
|
||||
std::optional<std::string> vendor;
|
||||
std::vector<std::string> os;
|
||||
std::optional<std::string> cpu_backend;
|
||||
};
|
||||
|
||||
struct Section {
|
||||
std::uint64_t title_id{};
|
||||
Condition condition;
|
||||
std::vector<std::pair<std::string, std::string>> settings;
|
||||
};
|
||||
|
||||
std::filesystem::path GetOverridesPath();
|
||||
bool OverridesFileExists();
|
||||
std::optional<std::uint32_t> GetOverridesFileVersion();
|
||||
|
||||
void ApplyEarlyOverrides(std::uint64_t program_id, const std::string& gpu_vendor);
|
||||
void ApplyLateOverrides(std::uint64_t program_id, const VideoCore::RendererBase& renderer);
|
||||
} // namespace Core::GameOverrides
|
||||
+17
-23
@@ -1,8 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/game_settings.h"
|
||||
#include "core/game_overrides.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
@@ -61,26 +60,15 @@ static GPUVendor GetGPU(const std::string& gpu_vendor_string) {
|
||||
}
|
||||
}
|
||||
|
||||
// legacy (shouldn't be needed anymore, but just in case)
|
||||
std::string gpu = gpu_vendor_string;
|
||||
std::transform(gpu.begin(), gpu.end(), gpu.begin(), [](unsigned char c){ return (char)std::tolower(c); });
|
||||
if (gpu.find("geforce") != std::string::npos) {
|
||||
return GPUVendor::Nvidia;
|
||||
}
|
||||
if (gpu.find("amd") != std::string::npos || gpu.find("radeon") != std::string::npos || gpu.find("ati") != std::string::npos) {
|
||||
if (gpu.find("radeon") != std::string::npos || gpu.find("ati") != std::string::npos) {
|
||||
return GPUVendor::AMD;
|
||||
}
|
||||
if (gpu.find("intel") != std::string::npos) {
|
||||
return GPUVendor::Intel;
|
||||
}
|
||||
if (gpu.find("apple") != std::string::npos) {
|
||||
return GPUVendor::Apple;
|
||||
}
|
||||
if (gpu.find("qualcomm") != std::string::npos || gpu.find("adreno") != std::string::npos) {
|
||||
return GPUVendor::Qualcomm;
|
||||
}
|
||||
if (gpu.find("mali") != std::string::npos) {
|
||||
return GPUVendor::ARM;
|
||||
}
|
||||
|
||||
return GPUVendor::Unknown;
|
||||
}
|
||||
@@ -131,16 +119,22 @@ EnvironmentInfo DetectEnvironment(const VideoCore::RendererBase& renderer) {
|
||||
return env;
|
||||
}
|
||||
|
||||
void LoadEarlyOverrides(std::uint64_t program_id, const std::string& gpu_vendor, bool enabled) {
|
||||
if (enabled && GameOverrides::OverridesFileExists()) {
|
||||
GameOverrides::ApplyEarlyOverrides(program_id, gpu_vendor);
|
||||
}
|
||||
}
|
||||
void LoadOverrides(std::uint64_t program_id, const VideoCore::RendererBase& renderer) {
|
||||
const auto env = DetectEnvironment(renderer);
|
||||
|
||||
void LoadOverrides(std::uint64_t program_id, const VideoCore::RendererBase& renderer, bool enabled) {
|
||||
if (enabled && GameOverrides::OverridesFileExists()) {
|
||||
GameOverrides::ApplyLateOverrides(program_id, renderer);
|
||||
switch (static_cast<TitleID>(program_id)) {
|
||||
case TitleID::NinjaGaidenRagebound:
|
||||
Settings::values.use_squashed_iterated_blend = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_INFO(Core, "Applied game settings for title ID {:016X} on OS {}, GPU vendor {} ({})",
|
||||
program_id,
|
||||
static_cast<int>(env.os),
|
||||
static_cast<int>(env.vendor),
|
||||
env.vendor_string);
|
||||
}
|
||||
|
||||
} // namespace Core::GameSettings
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
@@ -55,8 +55,6 @@ struct EnvironmentInfo {
|
||||
|
||||
EnvironmentInfo DetectEnvironment(const VideoCore::RendererBase& renderer);
|
||||
|
||||
void LoadEarlyOverrides(std::uint64_t program_id, const std::string& gpu_vendor, bool enabled = true);
|
||||
|
||||
void LoadOverrides(std::uint64_t program_id, const VideoCore::RendererBase& renderer, bool enabled = true);
|
||||
void LoadOverrides(std::uint64_t program_id, const VideoCore::RendererBase& renderer);
|
||||
|
||||
} // namespace Core::GameSettings
|
||||
|
||||
@@ -443,15 +443,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent)
|
||||
check_for_updates,
|
||||
tr("Check for updates"),
|
||||
tr("Whether or not to check for updates upon startup."));
|
||||
INSERT(UISettings,
|
||||
enable_global_overrides,
|
||||
tr("Enable global game overrides"),
|
||||
tr("When enabled, per-game settings from the global overrides.ini file will be applied."));
|
||||
INSERT(UISettings,
|
||||
auto_update_overrides,
|
||||
tr("Auto-update game overrides"),
|
||||
tr("Automatically download the latest game overrides list from GitHub on startup."));
|
||||
INSERT(UISettings, overrides_consent_given, QString(), QString());
|
||||
|
||||
// Linux
|
||||
INSERT(UISettings, enable_gamemode, tr("Enable Gamemode"), QString());
|
||||
|
||||
@@ -142,10 +142,6 @@ struct Values {
|
||||
|
||||
Setting<bool> check_for_updates{linkage, true, "check_for_updates", Category::UiGeneral};
|
||||
|
||||
Setting<bool> enable_global_overrides{linkage, true, "enable_global_overrides", Category::UiGeneral};
|
||||
Setting<bool> auto_update_overrides{linkage, false, "auto_update_overrides", Category::UiGeneral};
|
||||
Setting<bool> overrides_consent_given{linkage, false, "overrides_consent_given", Category::UiGeneral};
|
||||
|
||||
// Linux/MinGW may support (requires libdl support)
|
||||
SwitchableSetting<bool> enable_gamemode{linkage,
|
||||
#ifndef _MSC_VER
|
||||
|
||||
@@ -739,12 +739,18 @@ void BufferCache<P>::BindHostIndexBuffer() {
|
||||
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
|
||||
if (!draw_state.inline_index_draw_indexes.empty()) [[unlikely]] {
|
||||
if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) {
|
||||
auto upload_staging = runtime.UploadStagingBuffer(size);
|
||||
std::array<BufferCopy, 1> copies{
|
||||
{BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}};
|
||||
std::memcpy(upload_staging.mapped_span.data(),
|
||||
draw_state.inline_index_draw_indexes.data(), size);
|
||||
runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true);
|
||||
if (buffer.IsHostVisible()) {
|
||||
// write directly to mapped buffer
|
||||
std::memcpy(buffer.Mapped().data(),
|
||||
draw_state.inline_index_draw_indexes.data(), size);
|
||||
} else {
|
||||
auto upload_staging = runtime.UploadStagingBuffer(size);
|
||||
std::array<BufferCopy, 1> copies{
|
||||
{BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}};
|
||||
std::memcpy(upload_staging.mapped_span.data(),
|
||||
draw_state.inline_index_draw_indexes.data(), size);
|
||||
runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true);
|
||||
}
|
||||
} else {
|
||||
buffer.ImmediateUpload(0, draw_state.inline_index_draw_indexes);
|
||||
}
|
||||
@@ -1590,6 +1596,15 @@ void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
|
||||
[[maybe_unused]] u64 total_size_bytes,
|
||||
[[maybe_unused]] std::span<BufferCopy> copies) {
|
||||
if constexpr (USE_MEMORY_MAPS) {
|
||||
if (buffer.IsHostVisible() && runtime.CanReorderUpload(buffer, copies)) {
|
||||
const std::span<u8> mapped_span = buffer.Mapped();
|
||||
for (const BufferCopy& copy : copies) {
|
||||
u8* const dst_pointer = mapped_span.data() + copy.dst_offset;
|
||||
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
|
||||
device_memory.ReadBlockUnsafe(device_addr, dst_pointer, copy.size);
|
||||
}
|
||||
return;
|
||||
}
|
||||
auto upload_staging = runtime.UploadStagingBuffer(total_size_bytes);
|
||||
const std::span<u8> staging_pointer = upload_staging.mapped_span;
|
||||
for (BufferCopy& copy : copies) {
|
||||
@@ -1634,16 +1649,22 @@ void BufferCache<P>::InlineMemoryImplementation(DAddr dest_address, size_t copy_
|
||||
SynchronizeBuffer(buffer, dest_address, static_cast<u32>(copy_size));
|
||||
|
||||
if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) {
|
||||
auto upload_staging = runtime.UploadStagingBuffer(copy_size);
|
||||
std::array copies{BufferCopy{
|
||||
.src_offset = upload_staging.offset,
|
||||
.dst_offset = buffer.Offset(dest_address),
|
||||
.size = copy_size,
|
||||
}};
|
||||
u8* const src_pointer = upload_staging.mapped_span.data();
|
||||
std::memcpy(src_pointer, inlined_buffer.data(), copy_size);
|
||||
const bool can_reorder = runtime.CanReorderUpload(buffer, copies);
|
||||
runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true, can_reorder);
|
||||
const u32 buffer_offset = buffer.Offset(dest_address);
|
||||
if (buffer.IsHostVisible()) {
|
||||
// write directly to mapped buffer
|
||||
std::memcpy(buffer.Mapped().data() + buffer_offset, inlined_buffer.data(), copy_size);
|
||||
} else {
|
||||
auto upload_staging = runtime.UploadStagingBuffer(copy_size);
|
||||
std::array copies{BufferCopy{
|
||||
.src_offset = upload_staging.offset,
|
||||
.dst_offset = buffer_offset,
|
||||
.size = copy_size,
|
||||
}};
|
||||
u8* const src_pointer = upload_staging.mapped_span.data();
|
||||
std::memcpy(src_pointer, inlined_buffer.data(), copy_size);
|
||||
const bool can_reorder = runtime.CanReorderUpload(buffer, copies);
|
||||
runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true, can_reorder);
|
||||
}
|
||||
} else {
|
||||
buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size));
|
||||
}
|
||||
|
||||
@@ -34,6 +34,14 @@ public:
|
||||
|
||||
void MarkUsage(u64 offset, u64 size) {}
|
||||
|
||||
[[nodiscard]] bool IsHostVisible() const noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::span<u8> Mapped() noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] GLuint View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format);
|
||||
|
||||
[[nodiscard]] GLuint64EXT HostGpuAddr() const noexcept {
|
||||
|
||||
@@ -43,6 +43,14 @@ public:
|
||||
return tracker.IsUsed(offset, size);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsHostVisible() const noexcept {
|
||||
return buffer.IsHostVisible();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::span<u8> Mapped() noexcept {
|
||||
return buffer.Mapped();
|
||||
}
|
||||
|
||||
void MarkUsage(u64 offset, u64 size) noexcept {
|
||||
tracker.Track(offset, size);
|
||||
}
|
||||
|
||||
@@ -798,6 +798,10 @@ public:
|
||||
return must_emulate_scaled_formats;
|
||||
}
|
||||
|
||||
bool HasUnifiedMemory() const {
|
||||
return is_integrated;
|
||||
}
|
||||
|
||||
bool HasNullDescriptor() const {
|
||||
return features.robustness2.nullDescriptor;
|
||||
}
|
||||
|
||||
@@ -259,7 +259,7 @@ namespace Vulkan {
|
||||
vk::Buffer
|
||||
MemoryAllocator::CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsage usage) const
|
||||
{
|
||||
const VmaAllocationCreateInfo alloc_ci = {
|
||||
VmaAllocationCreateInfo alloc_ci = {
|
||||
.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage),
|
||||
.usage = MemoryUsageVma(usage),
|
||||
.requiredFlags = 0,
|
||||
@@ -270,6 +270,11 @@ namespace Vulkan {
|
||||
.priority = 0.f,
|
||||
};
|
||||
|
||||
if (device.HasUnifiedMemory() && usage == MemoryUsage::DeviceLocal) {
|
||||
alloc_ci.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||
alloc_ci.preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||
}
|
||||
|
||||
VkBuffer handle{};
|
||||
VmaAllocationInfo alloc_info{};
|
||||
VmaAllocation allocation{};
|
||||
|
||||
@@ -239,9 +239,6 @@ add_executable(yuzu
|
||||
ryujinx_dialog.h ryujinx_dialog.cpp ryujinx_dialog.ui
|
||||
main_window.h main_window.cpp main.ui
|
||||
|
||||
overrides_updater.h
|
||||
overrides_updater.cpp
|
||||
|
||||
configuration/system/new_user_dialog.h configuration/system/new_user_dialog.cpp configuration/system/new_user_dialog.ui
|
||||
configuration/system/profile_avatar_dialog.h configuration/system/profile_avatar_dialog.cpp
|
||||
configuration/addon/mod_select_dialog.h configuration/addon/mod_select_dialog.cpp configuration/addon/mod_select_dialog.ui
|
||||
@@ -364,7 +361,6 @@ target_sources(yuzu
|
||||
|
||||
if (ENABLE_OPENSSL)
|
||||
target_link_libraries(yuzu PRIVATE OpenSSL::SSL OpenSSL::Crypto)
|
||||
target_compile_definitions(yuzu PRIVATE CPPHTTPLIB_OPENSSL_SUPPORT)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
|
||||
@@ -217,7 +217,6 @@
|
||||
<addaction name="menuInstall_Firmware"/>
|
||||
<addaction name="action_Verify_installed_contents"/>
|
||||
<addaction name="action_Data_Manager"/>
|
||||
<addaction name="action_Download_Override_Settings"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="menu_cabinet_applet"/>
|
||||
<addaction name="menu_Applets"/>
|
||||
@@ -578,11 +577,6 @@
|
||||
<string>&Data Manager</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Download_Override_Settings">
|
||||
<property name="text">
|
||||
<string>Download &Override Settings</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Tree_View">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
|
||||
@@ -45,8 +45,6 @@
|
||||
#include "configuration/configure_per_game.h"
|
||||
#include "configuration/configure_tas.h"
|
||||
|
||||
#include "overrides_updater.h"
|
||||
|
||||
#include "util/clickable_label.h"
|
||||
#include "util/overlay_dialog.h"
|
||||
#include "util/controller_navigation.h"
|
||||
@@ -125,7 +123,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
#include "core/frontend/applets/mii_edit.h"
|
||||
#include "core/frontend/applets/general.h"
|
||||
#include "core/game_settings.h"
|
||||
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
@@ -550,17 +547,6 @@ MainWindow::MainWindow(bool has_broken_vulkan)
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check for game overrides updates
|
||||
overrides_updater = new OverridesUpdater(this);
|
||||
connect(overrides_updater, &OverridesUpdater::ConfigChanged, this, [this]() {
|
||||
config->SaveAllValues();
|
||||
});
|
||||
connect(overrides_updater, &OverridesUpdater::UpdateCompleted, this,
|
||||
[this](bool success, const QString& message) {
|
||||
QMessageBox::information(this, tr("Game Overrides"), message);
|
||||
});
|
||||
overrides_updater->CheckAndUpdate();
|
||||
|
||||
QtCommon::system->SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
||||
QtCommon::system->RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
|
||||
QtCommon::provider.get());
|
||||
@@ -1730,11 +1716,6 @@ void MainWindow::ConnectMenuEvents() {
|
||||
connect_menu(ui->action_About, &MainWindow::OnAbout);
|
||||
connect_menu(ui->action_Eden_Dependencies, &MainWindow::OnEdenDependencies);
|
||||
connect_menu(ui->action_Data_Manager, &MainWindow::OnDataDialog);
|
||||
connect(ui->action_Download_Override_Settings, &QAction::triggered, this, [this] {
|
||||
if (overrides_updater) {
|
||||
overrides_updater->DownloadOverrides();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void MainWindow::UpdateMenuState() {
|
||||
@@ -1899,18 +1880,6 @@ bool MainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletPar
|
||||
ShutdownGame();
|
||||
}
|
||||
|
||||
const bool overrides_enabled = UISettings::values.enable_global_overrides.GetValue();
|
||||
Settings::values.enable_global_overrides = overrides_enabled;
|
||||
|
||||
std::string gpu_vendor;
|
||||
if (!vk_device_records.empty()) {
|
||||
const int device_index = Settings::values.vulkan_device.GetValue();
|
||||
const int safe_index = std::clamp(device_index, 0,
|
||||
static_cast<int>(vk_device_records.size()) - 1);
|
||||
gpu_vendor = vk_device_records[safe_index].name;
|
||||
}
|
||||
Core::GameSettings::LoadEarlyOverrides(params.program_id, gpu_vendor, overrides_enabled);
|
||||
|
||||
if (!render_window->InitRenderTarget()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -64,7 +64,6 @@ class QtControllerSelectorDialog;
|
||||
class QtProfileSelectionDialog;
|
||||
class QtSoftwareKeyboardDialog;
|
||||
class QtNXWebEngineView;
|
||||
class OverridesUpdater;
|
||||
|
||||
enum class StartGameType {
|
||||
Normal, // Can use custom configuration
|
||||
@@ -565,9 +564,6 @@ private:
|
||||
QtSoftwareKeyboardDialog* software_keyboard = nullptr;
|
||||
QtNXWebEngineView* web_applet = nullptr;
|
||||
|
||||
// Overrides updater
|
||||
OverridesUpdater* overrides_updater = nullptr;
|
||||
|
||||
// True if amiibo file select is visible
|
||||
bool is_amiibo_file_select_active{};
|
||||
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "overrides_updater.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QtConcurrent>
|
||||
#include <fstream>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/game_overrides.h"
|
||||
#include "qt_common/config/uisettings.h"
|
||||
|
||||
#include <httplib.h>
|
||||
|
||||
#ifdef YUZU_BUNDLED_OPENSSL
|
||||
#include <openssl/cert.h>
|
||||
#endif
|
||||
|
||||
OverridesUpdater::OverridesUpdater(QWidget* parent)
|
||||
: QObject(parent), parent_widget(parent) {}
|
||||
|
||||
OverridesUpdater::~OverridesUpdater() = default;
|
||||
|
||||
void OverridesUpdater::CheckAndUpdate() {
|
||||
if (!UISettings::values.enable_global_overrides.GetValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!UISettings::values.overrides_consent_given.GetValue()) {
|
||||
ShowConsentDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
if (UISettings::values.auto_update_overrides.GetValue()) {
|
||||
(void)QtConcurrent::run([this] {
|
||||
DownloadAndSave();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void OverridesUpdater::ShowConsentDialog() {
|
||||
QMessageBox msgBox(parent_widget);
|
||||
msgBox.setWindowTitle(tr("Game Overrides Database"));
|
||||
msgBox.setIcon(QMessageBox::Question);
|
||||
msgBox.setText(tr("Would you like to update the game overrides database?"));
|
||||
msgBox.setInformativeText(
|
||||
tr("This will download a config file from GitHub to enable critical per-game settings.\n\n"
|
||||
"Your choice will be saved. You can change this later in:\n"
|
||||
"Settings -> General -> Enable global game overrides"));
|
||||
|
||||
QCheckBox autoUpdateCheckbox(tr("Automatically update the list"), &msgBox);
|
||||
autoUpdateCheckbox.setChecked(true);
|
||||
msgBox.setCheckBox(&autoUpdateCheckbox);
|
||||
|
||||
auto* yesButton = msgBox.addButton(tr("Yes, Enable"), QMessageBox::AcceptRole);
|
||||
msgBox.addButton(tr("No Thanks"), QMessageBox::RejectRole);
|
||||
|
||||
msgBox.setDefaultButton(yesButton);
|
||||
msgBox.exec();
|
||||
|
||||
auto* clicked = msgBox.clickedButton();
|
||||
|
||||
UISettings::values.overrides_consent_given.SetValue(true);
|
||||
|
||||
if (clicked == yesButton) {
|
||||
UISettings::values.auto_update_overrides.SetValue(autoUpdateCheckbox.isChecked());
|
||||
UISettings::values.enable_global_overrides.SetValue(true);
|
||||
emit ConfigChanged();
|
||||
DownloadOverrides();
|
||||
} else {
|
||||
UISettings::values.auto_update_overrides.SetValue(false);
|
||||
UISettings::values.enable_global_overrides.SetValue(false);
|
||||
emit ConfigChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void OverridesUpdater::DownloadOverrides() {
|
||||
(void)QtConcurrent::run([this] {
|
||||
const bool success = DownloadAndSave();
|
||||
emit UpdateCompleted(success, success
|
||||
? tr("Game overrides updated successfully.")
|
||||
: tr("Failed to download game overrides."));
|
||||
});
|
||||
}
|
||||
|
||||
std::optional<std::string> OverridesUpdater::FetchOverridesFile() {
|
||||
try {
|
||||
constexpr std::size_t timeout_seconds = 3;
|
||||
|
||||
std::unique_ptr<httplib::Client> client = std::make_unique<httplib::Client>(kOverridesUrl);
|
||||
client->set_connection_timeout(timeout_seconds);
|
||||
client->set_read_timeout(timeout_seconds);
|
||||
client->set_write_timeout(timeout_seconds);
|
||||
|
||||
#ifdef YUZU_BUNDLED_OPENSSL
|
||||
client->load_ca_cert_store(kCert, sizeof(kCert));
|
||||
#endif
|
||||
|
||||
httplib::Request request{
|
||||
.method = "GET",
|
||||
.path = kOverridesPath,
|
||||
};
|
||||
|
||||
client->set_follow_location(true);
|
||||
httplib::Result result = client->send(request);
|
||||
|
||||
if (!result) {
|
||||
LOG_ERROR(Frontend, "GET to {}{} returned null", kOverridesUrl, kOverridesPath);
|
||||
return {};
|
||||
}
|
||||
|
||||
return result.value().body;
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(Frontend, "Failed to fetch overrides: {}", e.what());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
bool OverridesUpdater::DownloadAndSave() {
|
||||
LOG_INFO(Frontend, "Checking for game overrides updates...");
|
||||
|
||||
const auto response = FetchOverridesFile();
|
||||
if (!response) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& data = *response;
|
||||
|
||||
auto remote_version = ParseVersion(data);
|
||||
if (!remote_version) {
|
||||
LOG_ERROR(Frontend, "Downloaded overrides file has invalid format (no version header)");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto local_version = Core::GameOverrides::GetOverridesFileVersion();
|
||||
if (local_version && *local_version >= *remote_version) {
|
||||
LOG_INFO(Frontend, "Game overrides are up to date (v{})", *local_version);
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto path = Core::GameOverrides::GetOverridesPath();
|
||||
std::filesystem::create_directories(path.parent_path());
|
||||
|
||||
std::ofstream file(path, std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
LOG_ERROR(Frontend, "Failed to write overrides file: {}", path.string());
|
||||
return false;
|
||||
}
|
||||
|
||||
file.write(data.data(), static_cast<std::streamsize>(data.size()));
|
||||
file.close();
|
||||
|
||||
LOG_INFO(Frontend, "Game overrides updated to version {}", *remote_version);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<std::uint32_t> OverridesUpdater::ParseVersion(const std::string& data) {
|
||||
// Look for "; version=X" on first line
|
||||
const auto newline_pos = data.find('\n');
|
||||
const std::string first_line = (newline_pos != std::string::npos)
|
||||
? data.substr(0, newline_pos)
|
||||
: data;
|
||||
|
||||
constexpr const char* prefix = "; version=";
|
||||
const auto prefix_pos = first_line.find(prefix);
|
||||
if (prefix_pos == std::string::npos) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto version_str = first_line.substr(prefix_pos + std::strlen(prefix));
|
||||
try {
|
||||
return static_cast<std::uint32_t>(std::stoul(version_str));
|
||||
} catch (...) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <optional>
|
||||
|
||||
class QWidget;
|
||||
|
||||
class OverridesUpdater : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OverridesUpdater(QWidget* parent = nullptr);
|
||||
~OverridesUpdater() override;
|
||||
|
||||
void CheckAndUpdate();
|
||||
void DownloadOverrides();
|
||||
|
||||
signals:
|
||||
void ConfigChanged();
|
||||
void UpdateCompleted(bool success, const QString& message);
|
||||
|
||||
private:
|
||||
void ShowConsentDialog();
|
||||
bool DownloadAndSave();
|
||||
static std::optional<std::uint32_t> ParseVersion(const std::string& data);
|
||||
static std::optional<std::string> FetchOverridesFile();
|
||||
|
||||
QWidget* parent_widget{};
|
||||
|
||||
static constexpr const char* kOverridesUrl = "https://raw.githubusercontent.com";
|
||||
static constexpr const char* kOverridesPath = "/eden-emulator/eden-overrides/refs/heads/master/overrides.ini";
|
||||
};
|
||||
Reference in New Issue
Block a user