Compare commits

...

7 Commits

Author SHA1 Message Date
lizzie
795a52c75a [qt] remove full/empty filters, replace with public lobby filter"
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-02 21:08:56 -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
22 changed files with 172 additions and 228 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

@@ -170,14 +170,10 @@ struct Values {
// multiplayer settings
Setting<std::string> multiplayer_nickname{linkage, {}, "nickname", Category::Multiplayer};
Setting<std::string> multiplayer_filter_text{linkage, {}, "filter_text", Category::Multiplayer};
Setting<bool> multiplayer_filter_games_owned{linkage, false, "filter_games_owned",
Category::Multiplayer};
Setting<bool> multiplayer_filter_hide_empty{linkage, false, "filter_games_hide_empty",
Category::Multiplayer};
Setting<bool> multiplayer_filter_hide_full{linkage, false, "filter_games_hide_full",
Category::Multiplayer};
Setting<bool> multiplayer_filter_games_owned{linkage, true, "filter_games_owned", Category::Multiplayer};
Setting<bool> multiplayer_filter_public_rooms{linkage, true, "filter_games_public_room", Category::Multiplayer};
Setting<std::string> multiplayer_ip{linkage, {}, "ip", Category::Multiplayer};
Setting<u16, true> multiplayer_port{linkage, 24872, 0,
Setting<u16, true> multiplayer_port{linkage, 24872, 0,
UINT16_MAX, "port", Category::Multiplayer};
Setting<std::string> multiplayer_room_nickname{
linkage, {}, "room_nickname", Category::Multiplayer};

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

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

@@ -62,14 +62,12 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
ui->room_list->setContextMenuPolicy(Qt::CustomContextMenu);
ui->nickname->setValidator(validation.GetNickname());
ui->nickname->setText(
QString::fromStdString(UISettings::values.multiplayer_nickname.GetValue()));
ui->nickname->setText(QString::fromStdString(UISettings::values.multiplayer_nickname.GetValue()));
// Try find the best nickname by default
if (ui->nickname->text().isEmpty() || ui->nickname->text() == QStringLiteral("Eden")) {
if (!Settings::values.eden_username.GetValue().empty()) {
ui->nickname->setText(
QString::fromStdString(Settings::values.eden_username.GetValue()));
if (auto const username = Settings::values.eden_username.GetValue(); !username.empty()) {
ui->nickname->setText(QString::fromStdString(username));
} else if (!GetProfileUsername().empty()) {
ui->nickname->setText(QString::fromStdString(GetProfileUsername()));
} else {
@@ -81,21 +79,17 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby);
connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch);
connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned);
connect(ui->hide_empty, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterEmpty);
connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull);
connect(ui->public_rooms, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterPublic);
connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom);
connect(ui->room_list, &QTreeView::clicked, this, &Lobby::OnExpandRoom);
// Actions
connect(&room_list_watcher, &QFutureWatcher<AnnounceMultiplayerRoom::RoomList>::finished, this,
&Lobby::OnRefreshLobby);
connect(&room_list_watcher, &QFutureWatcher<AnnounceMultiplayerRoom::RoomList>::finished, this, &Lobby::OnRefreshLobby);
// Load persistent filters after events are connected to make sure they apply
ui->search->setText(
QString::fromStdString(UISettings::values.multiplayer_filter_text.GetValue()));
ui->search->setText(QString::fromStdString(UISettings::values.multiplayer_filter_text.GetValue()));
ui->games_owned->setChecked(UISettings::values.multiplayer_filter_games_owned.GetValue());
ui->hide_empty->setChecked(UISettings::values.multiplayer_filter_hide_empty.GetValue());
ui->hide_full->setChecked(UISettings::values.multiplayer_filter_hide_full.GetValue());
ui->public_rooms->setChecked(UISettings::values.multiplayer_filter_public_rooms.GetValue());
}
Lobby::~Lobby() = default;
@@ -215,12 +209,9 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
UISettings::values.multiplayer_nickname = ui->nickname->text().toStdString();
UISettings::values.multiplayer_filter_text = ui->search->text().toStdString();
UISettings::values.multiplayer_filter_games_owned = ui->games_owned->isChecked();
UISettings::values.multiplayer_filter_hide_empty = ui->hide_empty->isChecked();
UISettings::values.multiplayer_filter_hide_full = ui->hide_full->isChecked();
UISettings::values.multiplayer_ip =
proxy->data(connection_index, LobbyItemHost::HostIPRole).value<QString>().toStdString();
UISettings::values.multiplayer_port =
proxy->data(connection_index, LobbyItemHost::HostPortRole).toInt();
UISettings::values.multiplayer_filter_public_rooms = ui->public_rooms->isChecked();
UISettings::values.multiplayer_ip = proxy->data(connection_index, LobbyItemHost::HostIPRole).value<QString>().toStdString();
UISettings::values.multiplayer_port = proxy->data(connection_index, LobbyItemHost::HostPortRole).toInt();
emit SaveConfig();
}
@@ -228,8 +219,8 @@ void Lobby::ResetModel() {
model->clear();
model->insertColumns(0, Column::TOTAL);
model->setHeaderData(Column::MEMBER, Qt::Horizontal, tr("Players"), Qt::DisplayRole);
model->setHeaderData(Column::ROOM_NAME, Qt::Horizontal, tr("Room Name"), Qt::DisplayRole);
model->setHeaderData(Column::GAME_NAME, Qt::Horizontal, tr("Preferred Game"), Qt::DisplayRole);
model->setHeaderData(Column::ROOM_NAME, Qt::Horizontal, tr("Name"), Qt::DisplayRole);
model->setHeaderData(Column::GAME_NAME, Qt::Horizontal, tr("Game"), Qt::DisplayRole);
model->setHeaderData(Column::HOST, Qt::Horizontal, tr("Host"), Qt::DisplayRole);
}
@@ -345,26 +336,12 @@ bool LobbyFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& s
return true;
}
// filter by empty rooms
if (filter_empty) {
QModelIndex member_list = sourceModel()->index(sourceRow, Column::MEMBER, sourceParent);
int player_count =
sourceModel()->data(member_list, LobbyItemMemberList::MemberListRole).toList().size();
if (player_count == 0) {
// filter by non-password protected
if (filter_public) {
QModelIndex password_index = sourceModel()->index(sourceRow, Column::ROOM_NAME);
bool has_password = sourceModel()->data(password_index, LobbyItemName::PasswordRole).toBool();
if (has_password)
return false;
}
}
// filter by filled rooms
if (filter_full) {
QModelIndex member_list = sourceModel()->index(sourceRow, Column::MEMBER, sourceParent);
int player_count =
sourceModel()->data(member_list, LobbyItemMemberList::MemberListRole).toList().size();
int max_players =
sourceModel()->data(member_list, LobbyItemMemberList::MaxPlayerRole).toInt();
if (player_count >= max_players) {
return false;
}
}
// filter by search parameters
@@ -372,18 +349,9 @@ bool LobbyFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& s
QModelIndex game_name = sourceModel()->index(sourceRow, Column::GAME_NAME, sourceParent);
QModelIndex room_name = sourceModel()->index(sourceRow, Column::ROOM_NAME, sourceParent);
QModelIndex host_name = sourceModel()->index(sourceRow, Column::HOST, sourceParent);
bool preferred_game_match = sourceModel()
->data(game_name, LobbyItemGame::GameNameRole)
.toString()
.contains(filter_search, filterCaseSensitivity());
bool room_name_match = sourceModel()
->data(room_name, LobbyItemName::NameRole)
.toString()
.contains(filter_search, filterCaseSensitivity());
bool username_match = sourceModel()
->data(host_name, LobbyItemHost::HostUsernameRole)
.toString()
.contains(filter_search, filterCaseSensitivity());
bool preferred_game_match = sourceModel()->data(game_name, LobbyItemGame::GameNameRole).toString().contains(filter_search, filterCaseSensitivity());
bool room_name_match = sourceModel()->data(room_name, LobbyItemName::NameRole).toString().contains(filter_search, filterCaseSensitivity());
bool username_match = sourceModel()->data(host_name, LobbyItemHost::HostUsernameRole).toString().contains(filter_search, filterCaseSensitivity());
if (!preferred_game_match && !room_name_match && !username_match) {
return false;
}
@@ -425,13 +393,8 @@ void LobbyFilterProxyModel::SetFilterOwned(bool filter) {
invalidate();
}
void LobbyFilterProxyModel::SetFilterEmpty(bool filter) {
filter_empty = filter;
invalidate();
}
void LobbyFilterProxyModel::SetFilterFull(bool filter) {
filter_full = filter;
void LobbyFilterProxyModel::SetFilterPublic(bool filter) {
filter_public = filter;
invalidate();
}

View File

@@ -126,14 +126,13 @@ public:
public slots:
void SetFilterOwned(bool);
void SetFilterEmpty(bool);
void SetFilterFull(bool);
void SetFilterPublic(bool);
void SetFilterSearch(const QString&);
private:
QStandardItemModel* game_list;
bool filter_owned = false;
bool filter_empty = false;
bool filter_full = false;
bool filter_public = false;
QString filter_search;
};

View File

@@ -78,16 +78,9 @@
</widget>
</item>
<item>
<widget class="QCheckBox" name="hide_empty">
<widget class="QCheckBox" name="public_rooms">
<property name="text">
<string>Hide Empty Rooms</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="hide_full">
<property name="text">
<string>Hide Full Rooms</string>
<string>Public Rooms</string>
</property>
</widget>
</item>