Compare commits

..

8 Commits

Author SHA1 Message Date
lizzie
72bcc4c43a [qt] remove duplicate applet launch code
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-02 20:43:18 -05:00
lizzie
e22756160c [qt] configure network per game (#3113)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3113
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-12-01 07:44:47 +01:00
lizzie
a33956f738 [hle] remove settings server thread (#3118)
test no perf regressions for games when saving settings

ALSO:
- the old logic made some settings not save
- if stop_token is true
- if the time was less than 1 minute

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3118
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-12-01 07:19:45 +01:00
lizzie
4642e82ca7 [logging] disable windows-only debugger backend on non-windows platforms (#3032)
Signed-off-by: lizzie lizzie@eden-emu.dev
Co-authored-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3032
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-12-01 05:42:02 +01:00
lizzie
97054357d2 [common] asserts now display expression of assert (#2997)
Easiest change in the world, will help to pinpoint asserts quicker, it's just a relatively small thing so doesn't even need testing.

Signed-off-by: lizzie lizzie@eden-emu.dev

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2997
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-12-01 05:06:57 +01:00
lizzie
941caf31ce [port] NetBSD and improper ctor for SpinLock fixes (#3092)
So when libc starts it has to start at an entry point located into crt0, now most OSes will do "enough" setup to allow mprotect() and mmap() to be called in static ctors (remember they're called BEFORE main)
By some stupid miracle, NetBSD doesn't; this means that using those functions on NetBSD will result in spurious results
The reason why is still unknown to me, but this is also combined with the fact that allocating a big chunk of memory for the JIT will make NetBSD refuse to mprotect()/mmap() it in low memory situations (even when space is available); so I take the same approach as with solaris
Also I now make it so fastmem handlers are NOT registered for OSes that disabled fastmem, this is because they pollute sigsegv and makes debugging stupidier

Signed-off-by: lizzie lizzie@eden-emu.dev

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3092
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-11-30 06:05:53 +01:00
lizzie
c72144abad [port] windows 7 support (#3105)
Signed-off-by: lizzie lizzie@eden-emu.dev
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3105
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-11-30 06:04:36 +01:00
lizzie
d656e347c8 [vk] bump cache to v14 (#3114)
invalidates all vulkan caches now, sorry not sorry, but will fix your broken games :)
Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3114
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-11-29 02:59:24 +01:00
30 changed files with 291 additions and 611 deletions

View File

@@ -27,7 +27,7 @@ set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL ON)
# Xbyak (also used by Dynarmic, so needs to be added first)
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
if (PLATFORM_SUN OR PLATFORM_OPENBSD)
if (PLATFORM_SUN OR PLATFORM_OPENBSD OR PLATFORM_NETBSD OR PLATFORM_DRAGONFLY)
AddJsonPackage(xbyak_sun)
else()
AddJsonPackage(xbyak)

View File

@@ -22,33 +22,21 @@ void AssertFailSoftImpl();
#define YUZU_NO_INLINE __attribute__((noinline))
#endif
#define ASSERT(_a_) \
([&]() YUZU_NO_INLINE { \
if (!(_a_)) [[unlikely]] { \
LOG_CRITICAL(Debug, "Assert"); \
AssertFailSoftImpl(); \
} \
}())
#define ASSERT_MSG(_a_, ...) \
([&]() YUZU_NO_INLINE { \
if (!(_a_)) [[unlikely]] { \
LOG_CRITICAL(Debug, "Assert\n" __VA_ARGS__); \
AssertFailSoftImpl(); \
LOG_CRITICAL(Debug, __FILE__ ": assert\n" __VA_ARGS__); \
AssertFailSoftImpl(); \
} \
}())
#define UNREACHABLE() \
do { \
LOG_CRITICAL(Debug, "Unreachable"); \
AssertFatalImpl(); \
} while (0)
#define ASSERT(_a_) ASSERT_MSG(_a_, "{}", #_a_)
#define UNREACHABLE_MSG(...) \
do { \
LOG_CRITICAL(Debug, "Unreachable\n" __VA_ARGS__); \
AssertFatalImpl(); \
LOG_CRITICAL(Debug, __FILE__ ": unreachable\n" __VA_ARGS__); \
AssertFatalImpl(); \
} while (0)
#define UNREACHABLE() UNREACHABLE_MSG("")
#ifdef _DEBUG
#define DEBUG_ASSERT(_a_) ASSERT(_a_)
@@ -68,20 +56,12 @@ void AssertFailSoftImpl();
#define UNIMPLEMENTED_IF(cond) ASSERT_MSG(!(cond), "Unimplemented code!")
#define UNIMPLEMENTED_IF_MSG(cond, ...) ASSERT_MSG(!(cond), __VA_ARGS__)
// If the assert is ignored, execute _b_
#define ASSERT_OR_EXECUTE(_a_, _b_) \
do { \
ASSERT(_a_); \
if (!(_a_)) [[unlikely]] { \
_b_ \
} \
} while (0)
// If the assert is ignored, execute _b_
#define ASSERT_OR_EXECUTE_MSG(_a_, _b_, ...) \
do { \
ASSERT_MSG(_a_, __VA_ARGS__); \
if (!(_a_)) [[unlikely]] { \
_b_ \
} \
if (!(_a_)) { _b_ } \
} while (0)
// If the assert is ignored, execute _b_
#define ASSERT_OR_EXECUTE(_a_, _b_) ASSERT_OR_EXECUTE_MSG(_a_, _b_, "{}", #_a_)

View File

@@ -50,8 +50,7 @@ constexpr const char* TrimSourcePath(std::string_view source) {
}
/// @brief Interface for logging backends.
class Backend {
public:
struct Backend {
virtual ~Backend() = default;
virtual void Write(const Entry& entry) = 0;
virtual void EnableForStacktrace() = 0;
@@ -59,8 +58,7 @@ public:
};
/// @brief Backend that writes to stderr and with color
class ColorConsoleBackend final : public Backend {
public:
struct ColorConsoleBackend final : public Backend {
explicit ColorConsoleBackend() = default;
~ColorConsoleBackend() override = default;
@@ -86,16 +84,15 @@ private:
};
/// @brief Backend that writes to a file passed into the constructor
class FileBackend final : public Backend {
public:
struct FileBackend final : public Backend {
explicit FileBackend(const std::filesystem::path& filename) {
auto old_filename = filename;
old_filename += ".old.txt";
// Existence checks are done within the functions themselves.
// We don't particularly care if these succeed or not.
static_cast<void>(FS::RemoveFile(old_filename));
static_cast<void>(FS::RenameFile(filename, old_filename));
void(FS::RemoveFile(old_filename));
void(FS::RenameFile(filename, old_filename));
file = std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, FS::FileType::TextFile);
}
@@ -165,51 +162,34 @@ private:
bool enabled = true;
};
/**
* Backend that writes to Visual Studio's output window
*/
class DebuggerBackend final : public Backend {
public:
explicit DebuggerBackend() = default;
~DebuggerBackend() override = default;
void Write(const Entry& entry) override {
#ifdef _WIN32
/// @brief Backend that writes to Visual Studio's output window
struct DebuggerBackend final : public Backend {
explicit DebuggerBackend() = default;
~DebuggerBackend() override = default;
void Write(const Entry& entry) override {
::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
#endif
}
void Flush() override {}
void EnableForStacktrace() override {}
};
#endif
#ifdef ANDROID
/**
* Backend that writes to the Android logcat
*/
class LogcatBackend : public Backend {
public:
/// @brief Backend that writes to the Android logcat
struct LogcatBackend : public Backend {
explicit LogcatBackend() = default;
~LogcatBackend() override = default;
void Write(const Entry& entry) override {
PrintMessageToLogcat(entry);
}
void Flush() override {}
void EnableForStacktrace() override {}
};
#endif
bool initialization_in_progress_suppress_logging = true;
/**
* Static state as a singleton.
*/
/// @brief Static state as a singleton.
class Impl {
public:
static Impl& Instance() {
@@ -228,8 +208,7 @@ public:
void(CreateDir(log_dir));
Filter filter;
filter.ParseFilterString(Settings::values.log_filter.GetValue());
instance = std::unique_ptr<Impl, decltype(&Deleter)>(new Impl(log_dir / LOG_FILE, filter),
Deleter);
instance = std::unique_ptr<Impl, decltype(&Deleter)>(new Impl(log_dir / LOG_FILE, filter), Deleter);
initialization_in_progress_suppress_logging = false;
}
@@ -276,13 +255,14 @@ private:
Common::SetCurrentThreadName("Logger");
Entry entry;
const auto write_logs = [this, &entry]() {
ForEachBackend([&entry](Backend& backend) { backend.Write(entry); });
ForEachBackend([&entry](Backend& backend) {
backend.Write(entry);
});
};
while (!stop_token.stop_requested()) {
do {
message_queue.PopWait(entry, stop_token);
if (entry.filename != nullptr)
write_logs();
}
write_logs();
} while (!stop_token.stop_requested());
// Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a
// case where a system is repeatedly spamming logs even on close.
int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100;
@@ -315,9 +295,11 @@ private:
}
void ForEachBackend(auto lambda) {
lambda(static_cast<Backend&>(debugger_backend));
lambda(static_cast<Backend&>(color_console_backend));
lambda(static_cast<Backend&>(file_backend));
#ifdef _WIN32
lambda(static_cast<Backend&>(debugger_backend));
#endif
#ifdef ANDROID
lambda(static_cast<Backend&>(lc_backend));
#endif
@@ -330,9 +312,11 @@ private:
static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter};
Filter filter;
DebuggerBackend debugger_backend{};
ColorConsoleBackend color_console_backend{};
FileBackend file_backend;
#ifdef _WIN32
DebuggerBackend debugger_backend{};
#endif
#ifdef ANDROID
LogcatBackend lc_backend{};
#endif

View File

@@ -1,3 +1,6 @@
// 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
@@ -25,15 +28,21 @@ static s64 WindowsQueryPerformanceCounter() {
}
static s64 GetSystemTimeNS() {
// GetSystemTimePreciseAsFileTime returns the file time in 100ns units.
static constexpr s64 Multiplier = 100;
// Convert Windows epoch to Unix epoch.
static constexpr s64 WindowsEpochToUnixEpoch = 0x19DB1DED53E8000LL;
FILETIME filetime;
GetSystemTimePreciseAsFileTime(&filetime);
return Multiplier * ((static_cast<s64>(filetime.dwHighDateTime) << 32) +
static_cast<s64>(filetime.dwLowDateTime) - WindowsEpochToUnixEpoch);
static auto pf = (decltype(&GetSystemTimePreciseAsFileTime))(void*)GetProcAddress(GetModuleHandle(TEXT("Kernel32.dll")), "GetSystemTimePreciseAsFileTime"); // Windows 8+
if (pf) {
// GetSystemTimePreciseAsFileTime returns the file time in 100ns units.
constexpr s64 Multiplier = 100;
// Convert Windows epoch to Unix epoch.
constexpr s64 WindowsEpochToUnixEpoch = 0x19DB1DED53E8000LL;
FILETIME filetime;
pf(&filetime);
return Multiplier * ((s64(filetime.dwHighDateTime) << 32) + s64(filetime.dwLowDateTime) - WindowsEpochToUnixEpoch);
} else {
// Only Windows XP and below error out here
LARGE_INTEGER ticks;
QueryPerformanceCounter(&ticks);
return ticks.QuadPart;
}
}
#endif

View File

@@ -81,10 +81,9 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
// Sets the debugger-visible name of the current thread.
void SetCurrentThreadName(const char* name) {
if (auto pf = (decltype(&SetThreadDescription))(void*)GetProcAddress(GetModuleHandle(TEXT("KernelBase.dll")), "SetThreadDescription"); pf)
static auto pf = (decltype(&SetThreadDescription))(void*)GetProcAddress(GetModuleHandle(TEXT("KernelBase.dll")), "SetThreadDescription");
if (pf)
pf(GetCurrentThread(), UTF8ToUTF16W(name).data()); // Windows 10+
else
; // No-op
}
#else // !MSVC_VER, so must be POSIX threads

View File

@@ -1,3 +1,6 @@
// 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
@@ -62,14 +65,15 @@ TimerResolution GetTimerResolution() {
void SetHighQoS() {
// https://learn.microsoft.com/en-us/windows/win32/procthread/quality-of-service
PROCESS_POWER_THROTTLING_STATE PowerThrottling{
.Version{PROCESS_POWER_THROTTLING_CURRENT_VERSION},
.ControlMask{PROCESS_POWER_THROTTLING_EXECUTION_SPEED |
PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION},
.StateMask{},
};
SetProcessInformation(GetCurrentProcess(), ProcessPowerThrottling, &PowerThrottling,
sizeof(PROCESS_POWER_THROTTLING_STATE));
static auto pf = (decltype(&SetProcessInformation))(void*)GetProcAddress(GetModuleHandle(TEXT("Kernel32.dll")), "SetProcessInformation");
if (pf) {
PROCESS_POWER_THROTTLING_STATE PowerThrottling{
.Version = PROCESS_POWER_THROTTLING_CURRENT_VERSION,
.ControlMask = PROCESS_POWER_THROTTLING_EXECUTION_SPEED | PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION,
.StateMask = 0,
};
pf(GetCurrentProcess(), ProcessPowerThrottling, &PowerThrottling, sizeof(PROCESS_POWER_THROTTLING_STATE)); // Windows 7+
}
}
} // Anonymous namespace

View File

@@ -211,7 +211,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ArmDynarmic32::MakeJit(Common::PageTable* pa
config.enable_cycle_counting = !m_uses_wall_clock;
// Code cache size
#if defined(ARCHITECTURE_arm64) || defined(__sun__)
#if defined(ARCHITECTURE_arm64) || defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
config.code_cache_size = std::uint32_t(128_MiB);
#else
config.code_cache_size = std::uint32_t(512_MiB);
@@ -295,7 +295,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ArmDynarmic32::MakeJit(Common::PageTable* pa
// Curated optimizations
case Settings::CpuAccuracy::Auto:
config.unsafe_optimizations = true;
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__DragonFly__)
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__DragonFly__) || defined(__NetBSD__)
config.fastmem_pointer = std::nullopt;
config.fastmem_exclusive_access = false;
#endif

View File

@@ -270,7 +270,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
config.enable_cycle_counting = !m_uses_wall_clock;
// Code cache size
#if defined(ARCHITECTURE_arm64) || defined(__sun__)
#if defined(ARCHITECTURE_arm64) || defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
config.code_cache_size = std::uint32_t(128_MiB);
#else
config.code_cache_size = std::uint32_t(512_MiB);

View File

@@ -339,14 +339,10 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
};
m_system_settings.eula_versions[0] = eula_version;
m_system_settings.eula_version_count = 1;
m_save_thread =
std::jthread([this](std::stop_token stop_token) { StoreSettingsThreadFunc(stop_token); });
}
ISystemSettingsServer::~ISystemSettingsServer() {
SetSaveNeeded();
m_save_thread.request_stop();
}
bool ISystemSettingsServer::LoadSettingsFile(std::filesystem::path& path, auto&& default_func) {
@@ -1393,20 +1389,9 @@ void ISystemSettingsServer::StoreSettings() {
}
}
void ISystemSettingsServer::StoreSettingsThreadFunc(std::stop_token stop_token) {
Common::SetCurrentThreadName("SettingsStore");
while (Common::StoppableTimedWait(stop_token, std::chrono::minutes(1))) {
std::scoped_lock l{m_save_needed_mutex};
if (!std::exchange(m_save_needed, false))
continue;
StoreSettings();
}
}
void ISystemSettingsServer::SetSaveNeeded() {
std::scoped_lock l{m_save_needed_mutex};
m_save_needed = true;
StoreSettings();
}
Result ISystemSettingsServer::GetSettingsItemValueImpl(std::span<u8> out_value, u64& out_size,

View File

@@ -167,7 +167,6 @@ private:
bool StoreSettingsFile(std::filesystem::path& path, auto& settings);
void SetupSettings();
void StoreSettings();
void StoreSettingsThreadFunc(std::stop_token stop_token);
void SetSaveNeeded();
Core::System& m_system;
@@ -176,7 +175,6 @@ private:
DeviceSettings m_device_settings{};
ApplnSettings m_appln_settings{};
std::mutex m_save_needed_mutex;
std::jthread m_save_thread;
bool m_save_needed{false};
};

View File

@@ -18,7 +18,12 @@ endif()
# Dynarmic project options
option(DYNARMIC_ENABLE_CPU_FEATURE_DETECTION "Turning this off causes dynarmic to assume the host CPU doesn't support anything later than SSE3" ON)
option(DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT "Enables support for systems that require W^X" ${PLATFORM_OPENBSD})
if (PLATFORM_OPENBSD OR PLATFORM_DRAGONFLY OR PLATFORM_NETBSD)
set(REQUIRE_WX ON)
else()
set(REQUIRE_WX OFF)
endif()
option(DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT "Enables support for systems that require W^X" ${REQUIRE_WX})
option(DYNARMIC_IGNORE_ASSERTS "Ignore asserts" OFF)
option(DYNARMIC_TESTS_USE_UNICORN "Enable fuzzing tests against unicorn" OFF)

View File

@@ -87,9 +87,11 @@ A32EmitX64::A32EmitX64(BlockOfCode& code, A32::UserConfig conf, A32::Jit* jit_in
code.PreludeComplete();
ClearFastDispatchTable();
exception_handler.SetFastmemCallback([this](u64 rip_) {
return FastmemCallback(rip_);
});
if (conf.fastmem_pointer.has_value()) {
exception_handler.SetFastmemCallback([this](u64 rip_) {
return FastmemCallback(rip_);
});
}
}
A32EmitX64::~A32EmitX64() = default;

View File

@@ -61,9 +61,11 @@ A64EmitX64::A64EmitX64(BlockOfCode& code, A64::UserConfig conf, A64::Jit* jit_in
code.PreludeComplete();
ClearFastDispatchTable();
exception_handler.SetFastmemCallback([this](u64 rip_) {
return FastmemCallback(rip_);
});
if (conf.fastmem_pointer.has_value()) {
exception_handler.SetFastmemCallback([this](u64 rip_) {
return FastmemCallback(rip_);
});
}
}
A64EmitX64::~A64EmitX64() = default;

View File

@@ -87,18 +87,24 @@ public:
// Waste a page to store the size
size += DYNARMIC_PAGE_SIZE;
# if defined(MAP_ANONYMOUS)
int mode = MAP_PRIVATE | MAP_ANONYMOUS;
# elif defined(MAP_ANON)
int mode = MAP_PRIVATE | MAP_ANON;
# else
# error "not supported"
# endif
# ifdef MAP_JIT
int mode = MAP_PRIVATE;
#if defined(MAP_ANONYMOUS)
mode |= MAP_ANONYMOUS;
#elif defined(MAP_ANON)
mode |= MAP_ANON;
#else
# error "not supported"
#endif
#ifdef MAP_JIT
mode |= MAP_JIT;
# endif
void* p = mmap(nullptr, size, PROT_READ | PROT_WRITE, mode, -1, 0);
#endif
int prot = PROT_READ | PROT_WRITE;
#ifdef PROT_MPROTECT
// https://man.netbsd.org/mprotect.2 specifies that an mprotect() that is LESS
// restrictive than the original mapping MUST fail
prot |= PROT_MPROTECT(PROT_READ) | PROT_MPROTECT(PROT_WRITE) | PROT_MPROTECT(PROT_EXEC);
#endif
void* p = mmap(nullptr, size, prot, mode, -1, 0);
if (p == MAP_FAILED) {
using Xbyak::Error;
XBYAK_THROW(Xbyak::ERR_CANT_ALLOC);

View File

@@ -7,7 +7,7 @@
*/
#include <mutex>
#include <optional>
#include <xbyak/xbyak.h>
#include "dynarmic/backend/x64/abi.h"
@@ -42,43 +42,46 @@ void EmitSpinLockUnlock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg
}
namespace {
struct SpinLockImpl {
void Initialize();
void Initialize() noexcept;
static void GlobalInitialize() noexcept;
Xbyak::CodeGenerator code = Xbyak::CodeGenerator(4096, default_cg_mode);
void (*lock)(volatile int*);
void (*unlock)(volatile int*);
void (*lock)(volatile int*) = nullptr;
void (*unlock)(volatile int*) = nullptr;
};
std::once_flag flag;
SpinLockImpl impl;
void SpinLockImpl::Initialize() {
const Xbyak::Reg64 ABI_PARAM1 = Backend::X64::HostLocToReg64(Backend::X64::ABI_PARAM1);
/// @brief Bear in mind that initializing the variable as-is on ctor time will trigger bugs
/// because some OSes do not prepare mprotect() properly at static ctor time
/// We can't really do anything about it, so just live with this fact
std::optional<SpinLockImpl> impl;
void SpinLockImpl::Initialize() noexcept {
Xbyak::Reg64 const ABI_PARAM1 = Backend::X64::HostLocToReg64(Backend::X64::ABI_PARAM1);
code.align();
lock = code.getCurr<void (*)(volatile int*)>();
EmitSpinLockLock(code, ABI_PARAM1, code.eax);
code.ret();
code.align();
unlock = code.getCurr<void (*)(volatile int*)>();
EmitSpinLockUnlock(code, ABI_PARAM1, code.eax);
code.ret();
}
void SpinLockImpl::GlobalInitialize() noexcept {
impl.emplace();
impl->Initialize();
}
} // namespace
void SpinLock::Lock() noexcept {
std::call_once(flag, &SpinLockImpl::Initialize, impl);
impl.lock(&storage);
std::call_once(flag, &SpinLockImpl::GlobalInitialize);
impl->lock(&storage);
}
void SpinLock::Unlock() noexcept {
std::call_once(flag, &SpinLockImpl::Initialize, impl);
impl.unlock(&storage);
std::call_once(flag, &SpinLockImpl::GlobalInitialize);
impl->unlock(&storage);
}
} // namespace Dynarmic

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -198,34 +195,6 @@ Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR
}
}
Id TextureColorResultType(EmitContext& ctx, const TextureDefinition& def) {
switch (def.component_type) {
case SamplerComponentType::Float:
case SamplerComponentType::Depth:
return ctx.F32[4];
case SamplerComponentType::Sint:
case SamplerComponentType::Stencil:
return ctx.S32[4];
case SamplerComponentType::Uint:
return ctx.U32[4];
}
throw InvalidArgument("Invalid sampler component type {}", def.component_type);
}
Id TextureSampleResultToFloat(EmitContext& ctx, const TextureDefinition& def, Id color) {
switch (def.component_type) {
case SamplerComponentType::Float:
case SamplerComponentType::Depth:
return color;
case SamplerComponentType::Sint:
case SamplerComponentType::Stencil:
return ctx.OpConvertSToF(ctx.F32[4], color);
case SamplerComponentType::Uint:
return ctx.OpConvertUToF(ctx.F32[4], color);
}
throw InvalidArgument("Invalid sampler component type {}", def.component_type);
}
Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& index) {
if (!index.IsImmediate() || index.U32() != 0) {
throw NotImplementedException("Indirect image indexing");
@@ -480,39 +449,31 @@ Id EmitBoundImageWrite(EmitContext&) {
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id bias_lc, const IR::Value& offset) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
const Id color_type{TextureColorResultType(ctx, def)};
const Id texture{Texture(ctx, info, index)};
Id color{};
if (ctx.stage == Stage::Fragment) {
const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
bias_lc, offset);
color = Emit(&EmitContext::OpImageSparseSampleImplicitLod,
&EmitContext::OpImageSampleImplicitLod, ctx, inst, color_type, texture,
coords, operands.MaskOptional(), operands.Span());
return Emit(&EmitContext::OpImageSparseSampleImplicitLod,
&EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4],
Texture(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
} else {
// We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as
// if the lod was explicitly zero. This may change on Turing with implicit compute
// derivatives
const Id lod{ctx.Const(0.0f)};
const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset);
color = Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, color_type, texture,
coords, operands.Mask(), operands.Span());
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
}
return TextureSampleResultToFloat(ctx, def, color);
}
Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id lod, const IR::Value& offset) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
const Id color_type{TextureColorResultType(ctx, def)};
const ImageOperands operands(ctx, false, true, false, lod, offset);
const Id color{Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, color_type,
Texture(ctx, info, index), coords, operands.Mask(), operands.Span())};
return TextureSampleResultToFloat(ctx, def, color);
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
}
Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
@@ -548,18 +509,13 @@ Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va
Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
const IR::Value& offset, const IR::Value& offset2) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
const Id color_type{TextureColorResultType(ctx, def)};
const ImageOperands operands(ctx, offset, offset2);
const Id texture{Texture(ctx, info, index)};
if (ctx.profile.need_gather_subpixel_offset) {
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
}
const Id color{
Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst, color_type,
texture, coords, ctx.Const(info.gather_component), operands.MaskOptional(),
operands.Span())};
return TextureSampleResultToFloat(ctx, def, color);
return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst,
ctx.F32[4], Texture(ctx, info, index), coords, ctx.Const(info.gather_component),
operands.MaskOptional(), operands.Span());
}
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
@@ -577,9 +533,6 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
Id lod, Id ms) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
const TextureDefinition* def =
info.type == TextureType::Buffer ? nullptr : &ctx.textures.at(info.descriptor_index);
const Id result_type{def ? TextureColorResultType(ctx, *def) : ctx.F32[4]};
AddOffsetToCoordinates(ctx, info, coords, offset);
if (info.type == TextureType::Buffer) {
lod = Id{};
@@ -589,13 +542,8 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
lod = Id{};
}
const ImageOperands operands(lod, ms);
Id color{Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst,
result_type, TextureImage(ctx, info, index), coords, operands.MaskOptional(),
operands.Span())};
if (def) {
color = TextureSampleResultToFloat(ctx, *def, color);
}
return color;
return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
}
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
@@ -640,17 +588,14 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id derivatives, const IR::Value& offset, Id lod_clamp) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
const Id color_type{TextureColorResultType(ctx, def)};
const auto operands = info.num_derivatives == 3
? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
ctx.Def(offset), {}, lod_clamp)
: ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
info.num_derivatives, offset, lod_clamp);
const Id color{Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, color_type,
Texture(ctx, info, index), coords, operands.Mask(), operands.Span())};
return TextureSampleResultToFloat(ctx, def, color);
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
}
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {

View File

@@ -28,40 +28,27 @@ enum class Operation {
FPMax,
};
Id ComponentScalarType(EmitContext& ctx, SamplerComponentType component_type) {
switch (component_type) {
case SamplerComponentType::Float:
case SamplerComponentType::Depth:
return ctx.F32[1];
case SamplerComponentType::Sint:
case SamplerComponentType::Stencil:
return ctx.S32[1];
case SamplerComponentType::Uint:
return ctx.U32[1];
}
throw InvalidArgument("Invalid sampler component type {}", component_type);
}
Id ImageType(EmitContext& ctx, const TextureDescriptor& desc, Id sampled_type) {
Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
const spv::ImageFormat format{spv::ImageFormat::Unknown};
const Id type{ctx.F32[1]};
const bool depth{desc.is_depth};
const bool ms{desc.is_multisample};
switch (desc.type) {
case TextureType::Color1D:
return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, depth, false, false, 1, format);
return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format);
case TextureType::ColorArray1D:
return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, depth, true, false, 1, format);
return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format);
case TextureType::Color2D:
case TextureType::Color2DRect:
return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, depth, false, ms, 1, format);
return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, ms, 1, format);
case TextureType::ColorArray2D:
return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, depth, true, ms, 1, format);
return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, ms, 1, format);
case TextureType::Color3D:
return ctx.TypeImage(sampled_type, spv::Dim::Dim3D, depth, false, false, 1, format);
return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format);
case TextureType::ColorCube:
return ctx.TypeImage(sampled_type, spv::Dim::Cube, depth, false, false, 1, format);
return ctx.TypeImage(type, spv::Dim::Cube, depth, false, false, 1, format);
case TextureType::ColorArrayCube:
return ctx.TypeImage(sampled_type, spv::Dim::Cube, depth, true, false, 1, format);
return ctx.TypeImage(type, spv::Dim::Cube, depth, true, false, 1, format);
case TextureType::Buffer:
break;
}
@@ -328,9 +315,6 @@ void DefineSsbos(EmitContext& ctx, StorageTypeDefinition& type_def,
ctx.Decorate(id, spv::Decoration::Binding, binding);
ctx.Decorate(id, spv::Decoration::DescriptorSet, 0U);
ctx.Name(id, fmt::format("ssbo{}", index));
if (!desc.is_written) {
ctx.Decorate(id, spv::Decoration::NonWritable);
}
if (ctx.profile.supported_spirv >= 0x00010400) {
ctx.interfaces.push_back(id);
}
@@ -1375,8 +1359,7 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_index) {
textures.reserve(info.texture_descriptors.size());
for (const TextureDescriptor& desc : info.texture_descriptors) {
const Id result_type{ComponentScalarType(*this, desc.component_type)};
const Id image_type{ImageType(*this, desc, result_type)};
const Id image_type{ImageType(*this, desc)};
const Id sampled_type{TypeSampledImage(image_type)};
const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, sampled_type)};
const Id desc_type{DescType(*this, sampled_type, pointer_type, desc.count)};
@@ -1389,10 +1372,8 @@ void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_in
.sampled_type = sampled_type,
.pointer_type = pointer_type,
.image_type = image_type,
.result_type = result_type,
.count = desc.count,
.is_multisample = desc.is_multisample,
.component_type = desc.component_type,
});
if (profile.supported_spirv >= 0x00010400) {
interfaces.push_back(id);
@@ -1451,9 +1432,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
}
if (info.uses_sample_id) {
sample_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SampleId);
if (stage == Stage::Fragment) {
Decorate(sample_id, spv::Decoration::Flat);
}
}
if (info.uses_is_helper_invocation) {
is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation);
@@ -1464,21 +1442,14 @@ void EmitContext::DefineInputs(const IR::Program& program) {
subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR);
subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR);
subgroup_mask_ge = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGeMaskKHR);
if (stage == Stage::Fragment) {
Decorate(subgroup_mask_eq, spv::Decoration::Flat);
Decorate(subgroup_mask_lt, spv::Decoration::Flat);
Decorate(subgroup_mask_le, spv::Decoration::Flat);
Decorate(subgroup_mask_gt, spv::Decoration::Flat);
Decorate(subgroup_mask_ge, spv::Decoration::Flat);
}
}
if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
(profile.warp_size_potentially_larger_than_guest &&
(profile.warp_size_potentially_larger_than_guest &&
(info.uses_subgroup_vote || info.uses_subgroup_mask))) {
AddCapability(spv::Capability::GroupNonUniform);
subgroup_local_invocation_id =
DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId);
Decorate(subgroup_local_invocation_id, spv::Decoration::Flat);
Decorate(subgroup_local_invocation_id, spv::Decoration::Flat);
}
if (info.uses_fswzadd) {
const Id f32_one{Const(1.0f)};
@@ -1490,9 +1461,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
}
if (loads[IR::Attribute::PrimitiveId]) {
primitive_id = DefineInput(*this, U32[1], false, spv::BuiltIn::PrimitiveId);
if (stage == Stage::Fragment) {
Decorate(primitive_id, spv::Decoration::Flat);
}
}
if (loads[IR::Attribute::Layer]) {
AddCapability(spv::Capability::Geometry);
@@ -1584,21 +1552,17 @@ void EmitContext::DefineInputs(const IR::Program& program) {
if (stage != Stage::Fragment) {
continue;
}
const bool is_integer = input_type == AttributeType::SignedInt ||
input_type == AttributeType::UnsignedInt;
if (is_integer) {
switch (info.interpolation[index]) {
case Interpolation::Smooth:
// Default
// Decorate(id, spv::Decoration::Smooth);
break;
case Interpolation::NoPerspective:
Decorate(id, spv::Decoration::NoPerspective);
break;
case Interpolation::Flat:
Decorate(id, spv::Decoration::Flat);
} else {
switch (info.interpolation[index]) {
case Interpolation::Smooth:
break;
case Interpolation::NoPerspective:
Decorate(id, spv::Decoration::NoPerspective);
break;
case Interpolation::Flat:
Decorate(id, spv::Decoration::Flat);
break;
}
break;
}
}
if (stage == Stage::TessellationEval) {

View File

@@ -36,10 +36,8 @@ struct TextureDefinition {
Id sampled_type;
Id pointer_type;
Id image_type;
Id result_type;
u32 count;
bool is_multisample;
SamplerComponentType component_type;
};
struct TextureBufferDefinition {

View File

@@ -22,8 +22,6 @@ public:
[[nodiscard]] virtual TextureType ReadTextureType(u32 raw_handle) = 0;
[[nodiscard]] virtual SamplerComponentType ReadTextureComponentType(u32 raw_handle) = 0;
[[nodiscard]] virtual TexturePixelFormat ReadTexturePixelFormat(u32 raw_handle) = 0;
[[nodiscard]] virtual bool IsTexturePixelFormatInteger(u32 raw_handle) = 0;

View File

@@ -396,10 +396,6 @@ bool IsTexturePixelFormatInteger(Environment& env, const ConstBufferAddr& cbuf)
return env.IsTexturePixelFormatInteger(GetTextureHandle(env, cbuf));
}
SamplerComponentType ReadTextureComponentType(Environment& env, const ConstBufferAddr& cbuf) {
return env.ReadTextureComponentType(GetTextureHandle(env, cbuf));
}
class Descriptors {
public:
explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_,
@@ -437,9 +433,7 @@ public:
u32 Add(const TextureDescriptor& desc) {
const u32 index{Add(texture_descriptors, desc, [&desc](const auto& existing) {
return desc.type == existing.type &&
desc.component_type == existing.component_type &&
desc.is_depth == existing.is_depth &&
return desc.type == existing.type && desc.is_depth == existing.is_depth &&
desc.has_secondary == existing.has_secondary &&
desc.cbuf_index == existing.cbuf_index &&
desc.cbuf_offset == existing.cbuf_offset &&
@@ -676,7 +670,6 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
} else {
index = descriptors.Add(TextureDescriptor{
.type = flags.type,
.component_type = ReadTextureComponentType(env, cbuf),
.is_depth = flags.is_depth != 0,
.is_multisample = is_multisample,
.has_secondary = cbuf.has_secondary,

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -154,14 +151,6 @@ enum class ImageFormat : u32 {
R32G32B32A32_UINT,
};
enum class SamplerComponentType : u8 {
Float,
Sint,
Uint,
Depth,
Stencil,
};
enum class Interpolation {
Smooth,
Flat,
@@ -215,7 +204,6 @@ using ImageBufferDescriptors = boost::container::small_vector<ImageBufferDescrip
struct TextureDescriptor {
TextureType type;
SamplerComponentType component_type;
bool is_depth;
bool is_multisample;
bool has_secondary;

View File

@@ -44,10 +44,20 @@ std::bitset<32> PersistentCallerSavedRegs() {
return PERSISTENT_REGISTERS & Common::X64::ABI_ALL_CALLER_SAVED;
}
/// @brief Must enforce W^X constraints, as we yet don't havea global "NO_EXECUTE" support flag
/// the speed loss is minimal, and in fact may be negligible, however for your peace of mind
/// I simply included known OSes whom had W^X issues
#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
static const auto default_cg_mode = Xbyak::DontSetProtectRWE;
#else
static const auto default_cg_mode = nullptr; //Allow RWE
#endif
class MacroJITx64Impl final : public Xbyak::CodeGenerator, public CachedMacro {
public:
explicit MacroJITx64Impl(Engines::Maxwell3D& maxwell3d_, const std::vector<u32>& code_)
: CodeGenerator{MAX_CODE_SIZE}, code{code_}, maxwell3d{maxwell3d_} {
: Xbyak::CodeGenerator(MAX_CODE_SIZE, default_cg_mode)
, code{code_}, maxwell3d{maxwell3d_} {
Compile();
}

View File

@@ -55,7 +55,7 @@ using VideoCommon::FileEnvironment;
using VideoCommon::GenericEnvironment;
using VideoCommon::GraphicsEnvironment;
constexpr u32 CACHE_VERSION = 13;
constexpr u32 CACHE_VERSION = 14;
constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
template <typename Container>

View File

@@ -70,59 +70,6 @@ static Shader::TexturePixelFormat ConvertTexturePixelFormat(const Tegra::Texture
entry.a_type, entry.srgb_conversion));
}
static Shader::SamplerComponentType ConvertSamplerComponentType(
const Tegra::Texture::TICEntry& entry) {
const auto pixel_format = PixelFormatFromTextureInfo(entry.format, entry.r_type, entry.g_type,
entry.b_type, entry.a_type,
entry.srgb_conversion);
const auto surface_type = VideoCore::Surface::GetFormatType(pixel_format);
if (entry.depth_texture != 0 || surface_type == VideoCore::Surface::SurfaceType::Depth) {
return Shader::SamplerComponentType::Depth;
}
if (surface_type == VideoCore::Surface::SurfaceType::Stencil) {
return Shader::SamplerComponentType::Stencil;
}
if (surface_type == VideoCore::Surface::SurfaceType::DepthStencil) {
return entry.depth_texture != 0 ? Shader::SamplerComponentType::Depth
: Shader::SamplerComponentType::Stencil;
}
const auto accumulate = [](const Tegra::Texture::ComponentType component,
bool& has_signed, bool& has_unsigned) {
switch (component) {
case Tegra::Texture::ComponentType::SINT:
has_signed = true;
break;
case Tegra::Texture::ComponentType::UINT:
has_unsigned = true;
break;
default:
break;
}
};
bool has_signed{};
bool has_unsigned{};
accumulate(entry.r_type, has_signed, has_unsigned);
accumulate(entry.g_type, has_signed, has_unsigned);
accumulate(entry.b_type, has_signed, has_unsigned);
accumulate(entry.a_type, has_signed, has_unsigned);
if (has_signed && !has_unsigned) {
return Shader::SamplerComponentType::Sint;
}
if (has_unsigned && !has_signed) {
return Shader::SamplerComponentType::Uint;
}
if (has_signed) {
return Shader::SamplerComponentType::Sint;
}
if (has_unsigned) {
return Shader::SamplerComponentType::Uint;
}
return Shader::SamplerComponentType::Float;
}
static std::string_view StageToPrefix(Shader::Stage stage) {
switch (stage) {
case Shader::Stage::VertexB:
@@ -253,7 +200,6 @@ void GenericEnvironment::Serialize(std::ofstream& file) const {
const u64 code_size{static_cast<u64>(CachedSizeBytes())};
const u64 num_texture_types{static_cast<u64>(texture_types.size())};
const u64 num_texture_pixel_formats{static_cast<u64>(texture_pixel_formats.size())};
const u64 num_texture_component_types{static_cast<u64>(texture_component_types.size())};
const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())};
const u64 num_cbuf_replacement_values{static_cast<u64>(cbuf_replacements.size())};
@@ -261,8 +207,6 @@ void GenericEnvironment::Serialize(std::ofstream& file) const {
.write(reinterpret_cast<const char*>(&num_texture_types), sizeof(num_texture_types))
.write(reinterpret_cast<const char*>(&num_texture_pixel_formats),
sizeof(num_texture_pixel_formats))
.write(reinterpret_cast<const char*>(&num_texture_component_types),
sizeof(num_texture_component_types))
.write(reinterpret_cast<const char*>(&num_cbuf_values), sizeof(num_cbuf_values))
.write(reinterpret_cast<const char*>(&num_cbuf_replacement_values),
sizeof(num_cbuf_replacement_values))
@@ -279,10 +223,6 @@ void GenericEnvironment::Serialize(std::ofstream& file) const {
file.write(reinterpret_cast<const char*>(&key), sizeof(key))
.write(reinterpret_cast<const char*>(&type), sizeof(type));
}
for (const auto& [key, component] : texture_component_types) {
file.write(reinterpret_cast<const char*>(&key), sizeof(key))
.write(reinterpret_cast<const char*>(&component), sizeof(component));
}
for (const auto& [key, format] : texture_pixel_formats) {
file.write(reinterpret_cast<const char*>(&key), sizeof(key))
.write(reinterpret_cast<const char*>(&format), sizeof(format));
@@ -434,21 +374,6 @@ Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) {
ReadTextureInfo(regs.tex_header.Address(), regs.tex_header.limit, via_header_index, handle);
const Shader::TextureType result{ConvertTextureType(entry)};
texture_types.emplace(handle, result);
texture_component_types.emplace(handle, ConvertSamplerComponentType(entry));
return result;
}
Shader::SamplerComponentType GraphicsEnvironment::ReadTextureComponentType(u32 handle) {
const auto it{texture_component_types.find(handle)};
if (it != texture_component_types.end()) {
return it->second;
}
const auto& regs{maxwell3d->regs};
const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding};
auto entry =
ReadTextureInfo(regs.tex_header.Address(), regs.tex_header.limit, via_header_index, handle);
const Shader::SamplerComponentType result{ConvertSamplerComponentType(entry)};
texture_component_types.emplace(handle, result);
return result;
}
@@ -505,20 +430,6 @@ Shader::TextureType ComputeEnvironment::ReadTextureType(u32 handle) {
auto entry = ReadTextureInfo(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle);
const Shader::TextureType result{ConvertTextureType(entry)};
texture_types.emplace(handle, result);
texture_component_types.emplace(handle, ConvertSamplerComponentType(entry));
return result;
}
Shader::SamplerComponentType ComputeEnvironment::ReadTextureComponentType(u32 handle) {
const auto it{texture_component_types.find(handle)};
if (it != texture_component_types.end()) {
return it->second;
}
const auto& regs{kepler_compute->regs};
const auto& qmd{kepler_compute->launch_description};
auto entry = ReadTextureInfo(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle);
const Shader::SamplerComponentType result{ConvertSamplerComponentType(entry)};
texture_component_types.emplace(handle, result);
return result;
}
@@ -544,15 +455,12 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
u64 code_size{};
u64 num_texture_types{};
u64 num_texture_pixel_formats{};
u64 num_texture_component_types{};
u64 num_cbuf_values{};
u64 num_cbuf_replacement_values{};
file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size))
.read(reinterpret_cast<char*>(&num_texture_types), sizeof(num_texture_types))
.read(reinterpret_cast<char*>(&num_texture_pixel_formats),
.read(reinterpret_cast<char*>(&num_texture_pixel_formats),
sizeof(num_texture_pixel_formats))
.read(reinterpret_cast<char*>(&num_texture_component_types),
sizeof(num_texture_component_types))
.read(reinterpret_cast<char*>(&num_cbuf_values), sizeof(num_cbuf_values))
.read(reinterpret_cast<char*>(&num_cbuf_replacement_values),
sizeof(num_cbuf_replacement_values))
@@ -572,13 +480,6 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
.read(reinterpret_cast<char*>(&type), sizeof(type));
texture_types.emplace(key, type);
}
for (size_t i = 0; i < num_texture_component_types; ++i) {
u32 key;
Shader::SamplerComponentType component;
file.read(reinterpret_cast<char*>(&key), sizeof(key))
.read(reinterpret_cast<char*>(&component), sizeof(component));
texture_component_types.emplace(key, component);
}
for (size_t i = 0; i < num_texture_pixel_formats; ++i) {
u32 key;
Shader::TexturePixelFormat format;
@@ -633,15 +534,6 @@ u32 FileEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) {
return it->second;
}
Shader::SamplerComponentType FileEnvironment::ReadTextureComponentType(u32 handle) {
const auto it{texture_component_types.find(handle)};
if (it == texture_component_types.end()) {
LOG_WARNING(Render_Vulkan, "Texture component descriptor {:08x} not found", handle);
return Shader::SamplerComponentType::Float;
}
return it->second;
}
Shader::TextureType FileEnvironment::ReadTextureType(u32 handle) {
const auto it{texture_types.find(handle)};
if (it == texture_types.end()) {

View File

@@ -80,7 +80,6 @@ protected:
std::vector<u64> code;
std::unordered_map<u32, Shader::TextureType> texture_types;
std::unordered_map<u32, Shader::SamplerComponentType> texture_component_types;
std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats;
std::unordered_map<u64, u32> cbuf_values;
std::unordered_map<u64, Shader::ReplaceConstant> cbuf_replacements;
@@ -117,8 +116,6 @@ public:
Shader::TextureType ReadTextureType(u32 handle) override;
Shader::SamplerComponentType ReadTextureComponentType(u32 handle) override;
Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
bool IsTexturePixelFormatInteger(u32 handle) override;
@@ -145,8 +142,6 @@ public:
Shader::TextureType ReadTextureType(u32 handle) override;
Shader::SamplerComponentType ReadTextureComponentType(u32 handle) override;
Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
bool IsTexturePixelFormatInteger(u32 handle) override;
@@ -181,8 +176,6 @@ public:
[[nodiscard]] Shader::TextureType ReadTextureType(u32 handle) override;
[[nodiscard]] Shader::SamplerComponentType ReadTextureComponentType(u32 handle) override;
[[nodiscard]] Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
[[nodiscard]] bool IsTexturePixelFormatInteger(u32 handle) override;
@@ -209,7 +202,6 @@ public:
private:
std::vector<u64> code;
std::unordered_map<u32, Shader::TextureType> texture_types;
std::unordered_map<u32, Shader::SamplerComponentType> texture_component_types;
std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats;
std::unordered_map<u64, u32> cbuf_values;
std::unordered_map<u64, Shader::ReplaceConstant> cbuf_replacements;

View File

@@ -41,6 +41,7 @@
#include "yuzu/configuration/configure_per_game.h"
#include "yuzu/configuration/configure_per_game_addons.h"
#include "yuzu/configuration/configure_system.h"
#include "yuzu/configuration/configure_network.h"
#include "qt_common/config/uisettings.h"
#include "yuzu/util/util.h"
#include "yuzu/vk_device_info.h"
@@ -69,6 +70,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
input_tab = std::make_unique<ConfigureInputPerGame>(system_, game_config.get(), this);
linux_tab = std::make_unique<ConfigureLinuxTab>(system_, tab_group, *builder, this);
system_tab = std::make_unique<ConfigureSystem>(system_, tab_group, *builder, this);
network_tab = std::make_unique<ConfigureNetwork>(system_, this);
ui->setupUi(this);
@@ -80,6 +82,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
ui->tabWidget->addTab(graphics_extensions_tab.get(), tr("GPU Extensions"));
ui->tabWidget->addTab(audio_tab.get(), tr("Audio"));
ui->tabWidget->addTab(input_tab.get(), tr("Input Profiles"));
ui->tabWidget->addTab(network_tab.get(), tr("Network"));
// Only show Linux tab on Unix
linux_tab->setVisible(false);
@@ -113,6 +116,7 @@ void ConfigurePerGame::ApplyConfiguration() {
}
addons_tab->ApplyConfiguration();
input_tab->ApplyConfiguration();
network_tab->ApplyConfiguration();
if (Settings::IsDockedMode() && Settings::values.players.GetValue()[0].controller_type ==
Settings::ControllerType::Handheld) {

View File

@@ -38,6 +38,7 @@ class ConfigureGraphicsExtensions;
class ConfigureInputPerGame;
class ConfigureLinuxTab;
class ConfigureSystem;
class ConfigureNetwork;
class QGraphicsScene;
class QStandardItem;
@@ -93,4 +94,5 @@ private:
std::unique_ptr<ConfigureInputPerGame> input_tab;
std::unique_ptr<ConfigureLinuxTab> linux_tab;
std::unique_ptr<ConfigureSystem> system_tab;
std::unique_ptr<ConfigureNetwork> network_tab;
};

View File

@@ -160,20 +160,20 @@
<property name="title">
<string>Am&amp;iibo</string>
</property>
<addaction name="action_Load_Cabinet_Nickname_Owner"/>
<addaction name="action_Load_Cabinet_Eraser"/>
<addaction name="action_Load_Cabinet_Restorer"/>
<addaction name="action_Load_Cabinet_Formatter"/>
<addaction name="action_Launch_Cabinet_Nickname_Owner"/>
<addaction name="action_Launch_Cabinet_Eraser"/>
<addaction name="action_Launch_Cabinet_Restorer"/>
<addaction name="action_Launch_Cabinet_Formatter"/>
</widget>
<widget class="QMenu" name="menu_Applets">
<property name="title">
<string>&amp;Applets</string>
<string>Launch &amp;Applet</string>
</property>
<addaction name="action_Load_Home_Menu"/>
<addaction name="action_Load_Album"/>
<addaction name="action_Load_Mii_Edit"/>
<addaction name="action_Open_Controller_Menu"/>
<addaction name="action_Open_Setup"/>
<addaction name="action_Launch_QLaunch"/>
<addaction name="action_Launch_MiiEdit"/>
<addaction name="action_Launch_Controller"/>
<addaction name="action_Launch_Setup"/>
<addaction name="action_Launch_PhotoViewer"/>
</widget>
<widget class="QMenu" name="menuTAS">
<property name="title">
@@ -420,34 +420,34 @@
<string>&amp;Capture Screenshot</string>
</property>
</action>
<action name="action_Load_Album">
<action name="action_Launch_PhotoViewer">
<property name="text">
<string>Open &amp;Album</string>
<string>&amp;Album</string>
</property>
</action>
<action name="action_Load_Cabinet_Nickname_Owner">
<action name="action_Launch_Cabinet_Nickname_Owner">
<property name="text">
<string>&amp;Set Nickname and Owner</string>
</property>
</action>
<action name="action_Load_Cabinet_Eraser">
<action name="action_Launch_Cabinet_Eraser">
<property name="text">
<string>&amp;Delete Game Data</string>
</property>
</action>
<action name="action_Load_Cabinet_Restorer">
<action name="action_Launch_Cabinet_Restorer">
<property name="text">
<string>&amp;Restore Amiibo</string>
</property>
</action>
<action name="action_Load_Cabinet_Formatter">
<action name="action_Launch_Cabinet_Formatter">
<property name="text">
<string>&amp;Format Amiibo</string>
</property>
</action>
<action name="action_Load_Mii_Edit">
<action name="action_Launch_MiiEdit">
<property name="text">
<string>Open &amp;Mii Editor</string>
<string>&amp;Mii Editor</string>
</property>
</action>
<action name="action_Configure_Tas">
@@ -493,7 +493,7 @@
<string>R&amp;ecord</string>
</property>
</action>
<action name="action_Open_Controller_Menu">
<action name="action_Launch_Controller">
<property name="text">
<string>Open &amp;Controller Menu</string>
</property>
@@ -503,14 +503,14 @@
<string>Install Decryption &amp;Keys</string>
</property>
</action>
<action name="action_Load_Home_Menu">
<action name="action_Launch_QLaunch">
<property name="text">
<string>Open &amp;Home Menu</string>
<string>&amp;Home Menu</string>
</property>
</action>
<action name="action_Open_Setup">
<action name="action_Launch_Setup">
<property name="text">
<string>Open &amp;Setup</string>
<string>&amp;Setup</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::TextHeuristicRole</enum>

View File

@@ -705,13 +705,13 @@ MainWindow::MainWindow(bool has_broken_vulkan)
}
if (should_launch_setup) {
OnInitialSetup();
LaunchFirmwareApplet(Service::AM::AppletProgramId::Starter, std::nullopt);
} else {
if (!game_path.isEmpty()) {
BootGame(game_path, ApplicationAppletParameters());
} else {
if (should_launch_qlaunch) {
OnHomeMenu();
LaunchFirmwareApplet(Service::AM::AppletProgramId::QLaunch, std::nullopt);
}
}
}
@@ -1661,19 +1661,35 @@ void MainWindow::ConnectMenuEvents() {
connect(multiplayer_state, &MultiplayerState::SaveConfig, this, &MainWindow::OnSaveConfig);
// Tools
connect_menu(ui->action_Load_Album, &MainWindow::OnAlbum);
connect_menu(ui->action_Load_Cabinet_Nickname_Owner,
[this]() { OnCabinet(Service::NFP::CabinetMode::StartNicknameAndOwnerSettings); });
connect_menu(ui->action_Load_Cabinet_Eraser,
[this]() { OnCabinet(Service::NFP::CabinetMode::StartGameDataEraser); });
connect_menu(ui->action_Load_Cabinet_Restorer,
[this]() { OnCabinet(Service::NFP::CabinetMode::StartRestorer); });
connect_menu(ui->action_Load_Cabinet_Formatter,
[this]() { OnCabinet(Service::NFP::CabinetMode::StartFormatter); });
connect_menu(ui->action_Load_Mii_Edit, &MainWindow::OnMiiEdit);
connect_menu(ui->action_Open_Controller_Menu, &MainWindow::OnOpenControllerMenu);
connect_menu(ui->action_Load_Home_Menu, &MainWindow::OnHomeMenu);
connect_menu(ui->action_Open_Setup, &MainWindow::OnInitialSetup);
connect_menu(ui->action_Launch_PhotoViewer, [this]{
LaunchFirmwareApplet(Service::AM::AppletProgramId::PhotoViewer, std::nullopt);
});
connect_menu(ui->action_Launch_MiiEdit, [this]{
LaunchFirmwareApplet(Service::AM::AppletProgramId::MiiEdit, std::nullopt);
});
connect_menu(ui->action_Launch_Controller, [this]{
LaunchFirmwareApplet(Service::AM::AppletProgramId::Controller, std::nullopt);
});
connect_menu(ui->action_Launch_QLaunch, [this]{
LaunchFirmwareApplet(Service::AM::AppletProgramId::QLaunch, std::nullopt);
});
connect_menu(ui->action_Launch_Setup, [this]{
LaunchFirmwareApplet(Service::AM::AppletProgramId::Starter, std::nullopt);
});
// Tools (cabinet)
connect_menu(ui->action_Launch_Cabinet_Nickname_Owner, [this]{
LaunchFirmwareApplet(Service::AM::AppletProgramId::Cabinet, {Service::NFP::CabinetMode::StartNicknameAndOwnerSettings});
});
connect_menu(ui->action_Launch_Cabinet_Eraser, [this]{
LaunchFirmwareApplet(Service::AM::AppletProgramId::Cabinet, {Service::NFP::CabinetMode::StartGameDataEraser});
});
connect_menu(ui->action_Launch_Cabinet_Restorer, [this]{
LaunchFirmwareApplet(Service::AM::AppletProgramId::Cabinet, {Service::NFP::CabinetMode::StartRestorer});
});
connect_menu(ui->action_Launch_Cabinet_Formatter, [this]{
LaunchFirmwareApplet(Service::AM::AppletProgramId::Cabinet, {Service::NFP::CabinetMode::StartFormatter});
});
connect_menu(ui->action_Desktop, &MainWindow::OnCreateHomeMenuDesktopShortcut);
connect_menu(ui->action_Application_Menu,
&MainWindow::OnCreateHomeMenuApplicationMenuShortcut);
@@ -1714,14 +1730,16 @@ void MainWindow::UpdateMenuState() {
ui->action_Pause,
};
const std::array applet_actions{ui->action_Load_Album,
ui->action_Load_Cabinet_Nickname_Owner,
ui->action_Load_Cabinet_Eraser,
ui->action_Load_Cabinet_Restorer,
ui->action_Load_Cabinet_Formatter,
ui->action_Load_Mii_Edit,
ui->action_Load_Home_Menu,
ui->action_Open_Controller_Menu};
const std::array applet_actions{
ui->action_Launch_PhotoViewer,
ui->action_Launch_Cabinet_Nickname_Owner,
ui->action_Launch_Cabinet_Eraser,
ui->action_Launch_Cabinet_Restorer,
ui->action_Launch_Cabinet_Formatter,
ui->action_Launch_MiiEdit,
ui->action_Launch_QLaunch,
ui->action_Launch_Controller
};
for (QAction* action : running_actions) {
action->setEnabled(emulation_running);
@@ -3960,157 +3978,61 @@ void MainWindow::OnGameListRefresh()
SetFirmwareVersion();
}
void MainWindow::OnAlbum() {
constexpr u64 AlbumId = static_cast<u64>(Service::AM::AppletProgramId::PhotoViewer);
auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install firmware to use the Album applet."));
return;
}
auto album_nca = bis_system->GetEntry(AlbumId, FileSys::ContentRecordType::Program);
if (!album_nca) {
QMessageBox::warning(this, tr("Album Applet"),
tr("Album applet is not available. Please reinstall firmware."));
return;
}
QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::PhotoViewer);
const auto filename = QString::fromStdString(album_nca->GetFullPath());
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
BootGame(filename, LibraryAppletParameters(AlbumId, Service::AM::AppletId::PhotoViewer));
}
void MainWindow::OnCabinet(Service::NFP::CabinetMode mode) {
constexpr u64 CabinetId = static_cast<u64>(Service::AM::AppletProgramId::Cabinet);
auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install firmware to use the Cabinet applet."));
return;
}
auto cabinet_nca = bis_system->GetEntry(CabinetId, FileSys::ContentRecordType::Program);
if (!cabinet_nca) {
QMessageBox::warning(this, tr("Cabinet Applet"),
tr("Cabinet applet is not available. Please reinstall firmware."));
return;
}
QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::Cabinet);
QtCommon::system->GetFrontendAppletHolder().SetCabinetMode(mode);
const auto filename = QString::fromStdString(cabinet_nca->GetFullPath());
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
BootGame(filename, LibraryAppletParameters(CabinetId, Service::AM::AppletId::Cabinet));
}
void MainWindow::OnMiiEdit() {
constexpr u64 MiiEditId = static_cast<u64>(Service::AM::AppletProgramId::MiiEdit);
auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install firmware to use the Mii editor."));
return;
}
auto mii_applet_nca = bis_system->GetEntry(MiiEditId, FileSys::ContentRecordType::Program);
if (!mii_applet_nca) {
QMessageBox::warning(this, tr("Mii Edit Applet"),
tr("Mii editor is not available. Please reinstall firmware."));
return;
}
QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::MiiEdit);
const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath()));
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
BootGame(filename, LibraryAppletParameters(MiiEditId, Service::AM::AppletId::MiiEdit));
}
void MainWindow::OnOpenControllerMenu() {
constexpr u64 ControllerAppletId = static_cast<u64>(Service::AM::AppletProgramId::Controller);
auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install firmware to use the Controller Menu."));
return;
}
auto controller_applet_nca =
bis_system->GetEntry(ControllerAppletId, FileSys::ContentRecordType::Program);
if (!controller_applet_nca) {
QMessageBox::warning(this, tr("Controller Applet"),
tr("Controller Menu is not available. Please reinstall firmware."));
return;
}
QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::Controller);
const auto filename = QString::fromStdString((controller_applet_nca->GetFullPath()));
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
BootGame(filename,
LibraryAppletParameters(ControllerAppletId, Service::AM::AppletId::Controller));
}
void MainWindow::OnHomeMenu() {
void MainWindow::LaunchFirmwareApplet(Service::AM::AppletProgramId program_id, std::optional<Service::NFP::CabinetMode> cabinet_mode) {
auto result = FirmwareManager::VerifyFirmware(*QtCommon::system.get());
using namespace QtCommon::StringLookup;
switch (result) {
case FirmwareManager::ErrorFirmwareMissing:
QMessageBox::warning(this, tr("No firmware available"),
Lookup(FwCheckErrorFirmwareMissing));
QMessageBox::warning(this, tr("No firmware available"), Lookup(FwCheckErrorFirmwareMissing));
return;
case FirmwareManager::ErrorFirmwareCorrupted:
QMessageBox::warning(this, tr("Firmware Corrupted"),
Lookup(FwCheckErrorFirmwareCorrupted));
QMessageBox::warning(this, tr("Firmware Corrupted"), Lookup(FwCheckErrorFirmwareCorrupted));
return;
default:
break;
}
constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch);
auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
if (!qlaunch_applet_nca) {
QMessageBox::warning(this, tr("Home Menu Applet"),
tr("Home Menu is not available. Please reinstall firmware."));
return;
if (auto applet_nca = bis_system->GetEntry(u64(program_id), FileSys::ContentRecordType::Program); applet_nca) {
if (auto const applet_id = [program_id] {
using namespace Service::AM;
switch (program_id) {
case AppletProgramId::OverlayDisplay: return AppletId::OverlayDisplay;
case AppletProgramId::QLaunch: return AppletId::QLaunch;
case AppletProgramId::Starter: return AppletId::Starter;
case AppletProgramId::Auth: return AppletId::Auth;
case AppletProgramId::Cabinet: return AppletId::Cabinet;
case AppletProgramId::Controller: return AppletId::Controller;
case AppletProgramId::DataErase: return AppletId::DataErase;
case AppletProgramId::Error: return AppletId::Error;
case AppletProgramId::NetConnect: return AppletId::NetConnect;
case AppletProgramId::ProfileSelect: return AppletId::ProfileSelect;
case AppletProgramId::SoftwareKeyboard: return AppletId::SoftwareKeyboard;
case AppletProgramId::MiiEdit: return AppletId::MiiEdit;
case AppletProgramId::Web: return AppletId::Web;
case AppletProgramId::Shop: return AppletId::Shop;
case AppletProgramId::PhotoViewer: return AppletId::PhotoViewer;
case AppletProgramId::Settings: return AppletId::Settings;
case AppletProgramId::OfflineWeb: return AppletId::OfflineWeb;
case AppletProgramId::LoginShare: return AppletId::LoginShare;
case AppletProgramId::WebAuth: return AppletId::WebAuth;
case AppletProgramId::MyPage: return AppletId::MyPage;
default: return AppletId::None;
}
QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::QLaunch);
const auto filename = QString::fromStdString((qlaunch_applet_nca->GetFullPath()));
}(); applet_id != Service::AM::AppletId::None) {
QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(applet_id);
if (cabinet_mode)
QtCommon::system->GetFrontendAppletHolder().SetCabinetMode(*cabinet_mode);
// ?
auto const filename = QString::fromStdString((applet_nca->GetFullPath()));
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
BootGame(filename, LibraryAppletParameters(QLaunchId, Service::AM::AppletId::QLaunch));
}
void MainWindow::OnInitialSetup() {
constexpr u64 Starter = static_cast<u64>(Service::AM::AppletProgramId::Starter);
auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install firmware to use Starter."));
return;
BootGame(filename, LibraryAppletParameters(u64(program_id), applet_id));
} else {
QMessageBox::warning(this, tr("Unknown applet"), tr("Applet doesn't map to a known value."));
}
} else {
QMessageBox::warning(this, tr("Record not found"), tr("Applet not found. Please reinstall firmware."));
}
auto qlaunch_nca = bis_system->GetEntry(Starter, FileSys::ContentRecordType::Program);
if (!qlaunch_nca) {
QMessageBox::warning(this, tr("Starter Applet"),
tr("Starter is not available. Please reinstall firmware."));
return;
}
QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::Starter);
const auto filename = QString::fromStdString((qlaunch_nca->GetFullPath()));
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
BootGame(filename, LibraryAppletParameters(Starter, Service::AM::AppletId::Starter));
}
void MainWindow::OnCreateHomeMenuDesktopShortcut() {

View File

@@ -101,6 +101,7 @@ class InputSubsystem;
namespace Service::AM {
struct FrontendAppletParameters;
enum class AppletId : u32;
enum class AppletProgramId : u64;
} // namespace Service::AM
namespace Service::AM::Frontend {
@@ -399,12 +400,7 @@ private slots:
void ResetWindowSize720();
void ResetWindowSize900();
void ResetWindowSize1080();
void OnAlbum();
void OnCabinet(Service::NFP::CabinetMode mode);
void OnMiiEdit();
void OnOpenControllerMenu();
void OnHomeMenu();
void OnInitialSetup();
void LaunchFirmwareApplet(Service::AM::AppletProgramId program_id, std::optional<Service::NFP::CabinetMode> mode);
void OnCreateHomeMenuDesktopShortcut();
void OnCreateHomeMenuApplicationMenuShortcut();
void OnCaptureScreenshot();
@@ -428,7 +424,6 @@ private:
bool SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id,
u64* selected_title_id, u8* selected_content_record_type);
ContentManager::InstallResult InstallNCA(const QString& filename);
void MigrateConfigFiles();
void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {},
std::string_view gpu_vendor = {});
void UpdateDockedButton();