From 8a185ac1be8b47fb69cb59b6e997fdac107f1090 Mon Sep 17 00:00:00 2001 From: lizzie Date: Mon, 29 Sep 2025 08:30:12 +0000 Subject: [PATCH 1/4] [dynarmic] backport WAITPKG based spinlocks Signed-off-by: lizzie --- .../dynarmic/backend/x64/block_of_code.cpp | 2 + .../backend/x64/emit_x64_memory.cpp.inc | 3 +- .../dynarmic/backend/x64/emit_x64_memory.h | 5 ++- .../src/dynarmic/backend/x64/host_feature.h | 3 +- .../src/dynarmic/common/spin_lock_x64.cpp | 37 +++++++++++++++++-- .../src/dynarmic/common/spin_lock_x64.h | 5 ++- 6 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp b/src/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp index d5d5f089ff..4a8de6475e 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp @@ -188,6 +188,8 @@ HostFeature GetHostFeatures() { features |= HostFeature::LZCNT; if (cpu_info.has(Cpu::tGFNI)) features |= HostFeature::GFNI; + if (cpu_info.has(Cpu::tWAITPKG)) + features |= HostFeature::WAITPKG; if (cpu_info.has(Cpu::tBMI2)) { // BMI2 instructions such as pdep and pext have been very slow up until Zen 3. diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc index 34f77b0446..36a2d40de6 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc @@ -430,10 +430,11 @@ void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* i const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[1]); const Xbyak::Reg32 status = ctx.reg_alloc.ScratchGpr().cvt32(); const Xbyak::Reg64 tmp = ctx.reg_alloc.ScratchGpr(); + const Xbyak::Reg64 tmp2 = ctx.reg_alloc.ScratchGpr(); const auto wrapped_fn = exclusive_write_fallbacks[std::make_tuple(ordered, bitsize, vaddr.getIdx(), value.getIdx())]; - EmitExclusiveLock(code, conf, tmp, eax); + EmitExclusiveLock(code, conf, tmp, tmp2); SharedLabel end = GenSharedLabel(); diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.h b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.h index 75a47c6a80..c363ea1b6b 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.h +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* This file is part of the dynarmic project. * Copyright (c) 2022 MerryMage * SPDX-License-Identifier: 0BSD @@ -343,7 +346,7 @@ void EmitExclusiveLock(BlockOfCode& code, const UserConfig& conf, Xbyak::Reg64 p } code.mov(pointer, mcl::bit_cast(GetExclusiveMonitorLockPointer(conf.global_monitor))); - EmitSpinLockLock(code, pointer, tmp); + EmitSpinLockLock(code, pointer, tmp, code.HasHostFeature(HostFeature::WAITPKG)); } template diff --git a/src/dynarmic/src/dynarmic/backend/x64/host_feature.h b/src/dynarmic/src/dynarmic/backend/x64/host_feature.h index 7246ed18d4..34dca971cb 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/host_feature.h +++ b/src/dynarmic/src/dynarmic/backend/x64/host_feature.h @@ -35,9 +35,10 @@ enum class HostFeature : u64 { BMI2 = 1ULL << 19, LZCNT = 1ULL << 20, GFNI = 1ULL << 21, + WAITPKG = 1ULL << 22, // Zen-based BMI2 - FastBMI2 = 1ULL << 22, + FastBMI2 = 1ULL << 23, // Orthographic AVX512 features on 128 and 256 vectors AVX512_Ortho = AVX512F | AVX512VL, diff --git a/src/dynarmic/src/dynarmic/common/spin_lock_x64.cpp b/src/dynarmic/src/dynarmic/common/spin_lock_x64.cpp index da50179de9..5307672bfe 100644 --- a/src/dynarmic/src/dynarmic/common/spin_lock_x64.cpp +++ b/src/dynarmic/src/dynarmic/common/spin_lock_x64.cpp @@ -22,17 +22,46 @@ static const auto default_cg_mode = nullptr; //Allow RWE namespace Dynarmic { -void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp) { +void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp, bool waitpkg) { + // TODO: this is because we lack regalloc - so better to be safe :( + if (waitpkg) { + code.push(Xbyak::util::eax); + code.push(Xbyak::util::ebx); + code.push(Xbyak::util::edx); + } Xbyak::Label start, loop; - code.jmp(start, code.T_NEAR); code.L(loop); - code.pause(); + if (waitpkg) { + // TODO: This clobbers EAX and EDX did we tell the regalloc? + // ARM ptr for address-monitoring + code.umonitor(ptr); + // tmp.bit[0] = 0: C0.1 | Slow Wakup | Better Savings + // tmp.bit[0] = 1: C0.2 | Fast Wakup | Lesser Savings + // edx:eax is implicitly used as a 64-bit deadline timestamp + // Use the maximum so that we use the operating system's maximum + // allowed wait time within the IA32_UMWAIT_CONTROL register + // Enter power state designated by tmp and wait for a write to lock_ptr + code.mov(Xbyak::util::eax, 0xFFFFFFFF); + code.mov(Xbyak::util::edx, Xbyak::util::eax); + // TODO: We can only be here because tmp is 1 already - however we repeatedly overwrite it... + code.mov(Xbyak::util::ebx, 1); + code.umwait(Xbyak::util::ebx); + // CF == 1 if we hit the OS-timeout in IA32_UMWAIT_CONTROL without a write + // CF == 0 if we exited the wait for any other reason + } else { + code.pause(); + } code.L(start); code.mov(tmp, 1); /*code.lock();*/ code.xchg(code.dword[ptr], tmp); code.test(tmp, tmp); code.jnz(loop, code.T_NEAR); + if (waitpkg) { + code.pop(Xbyak::util::edx); + code.pop(Xbyak::util::ebx); + code.pop(Xbyak::util::eax); + } } void EmitSpinLockUnlock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp) { @@ -60,7 +89,7 @@ void SpinLockImpl::Initialize() { code.align(); lock = code.getCurr(); - EmitSpinLockLock(code, ABI_PARAM1, code.eax); + EmitSpinLockLock(code, ABI_PARAM1, code.eax, false); code.ret(); code.align(); diff --git a/src/dynarmic/src/dynarmic/common/spin_lock_x64.h b/src/dynarmic/src/dynarmic/common/spin_lock_x64.h index df6a3d7407..df6860e2f2 100644 --- a/src/dynarmic/src/dynarmic/common/spin_lock_x64.h +++ b/src/dynarmic/src/dynarmic/common/spin_lock_x64.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* This file is part of the dynarmic project. * Copyright (c) 2022 MerryMage * SPDX-License-Identifier: 0BSD @@ -9,7 +12,7 @@ namespace Dynarmic { -void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp); +void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp, bool waitpkg); void EmitSpinLockUnlock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp); } // namespace Dynarmic From dfca07f4e3df72b243828268d0a3c4a49279ff9f Mon Sep 17 00:00:00 2001 From: xbzk Date: Wed, 1 Oct 2025 00:10:59 +0200 Subject: [PATCH 2/4] Initial a9 (minsdk=28) support (#2600) Minimal changes to make android 10 installable and emulationFragment not immediately crashable. Testers (mainly android 10) NEEDED!!! Co-authored-by: Allison Cunha Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2600 Reviewed-by: Lizzie Co-authored-by: xbzk Co-committed-by: xbzk --- src/android/app/build.gradle.kts | 2 +- .../org/yuzu/yuzu_emu/views/CarouselRecyclerView.kt | 5 +++-- src/common/host_memory.cpp | 10 ++++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index d3a05cf3e2..e8d8141711 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -59,7 +59,7 @@ android { defaultConfig { // TODO If this is ever modified, change application_id in strings.xml applicationId = "dev.eden.eden_emulator" - minSdk = 30 + minSdk = 28 targetSdk = 36 versionName = getGitVersion() diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/CarouselRecyclerView.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/CarouselRecyclerView.kt index 8a66ebf11f..2c35e7349a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/CarouselRecyclerView.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/CarouselRecyclerView.kt @@ -19,6 +19,7 @@ import org.yuzu.yuzu_emu.adapters.GameAdapter import androidx.core.view.doOnNextLayout import org.yuzu.yuzu_emu.YuzuApplication import androidx.preference.PreferenceManager +import androidx.core.view.WindowInsetsCompat /** * CarouselRecyclerView encapsulates all carousel logic for the games UI. @@ -205,8 +206,8 @@ class CarouselRecyclerView @JvmOverloads constructor( if (enabled) { useCustomDrawingOrder = true - val insets = rootWindowInsets - val bottomInset = insets?.getInsets(android.view.WindowInsets.Type.systemBars())?.bottom ?: 0 + val insets = rootWindowInsets?.let { WindowInsetsCompat.toWindowInsetsCompat(it, this) } + val bottomInset = insets?.getInsets(WindowInsetsCompat.Type.systemBars())?.bottom ?: 0 val internalFactor = resources.getFraction(R.fraction.carousel_card_size_factor, 1, 1) val userFactor = preferences.getFloat(CAROUSEL_CARD_SIZE_FACTOR, internalFactor).coerceIn( 0f, diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index 2e36d59569..3838c12903 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -56,6 +56,16 @@ #include "common/host_memory.h" #include "common/logging/log.h" +#if defined(__ANDROID__) && __ANDROID_API__ < 30 +#include +#ifndef MFD_CLOEXEC +#define MFD_CLOEXEC 0x0001U +#endif +static int memfd_create(const char* name, unsigned int flags) { + return syscall(__NR_memfd_create, name, flags); +} +#endif + namespace Common { constexpr size_t PageAlignment = 0x1000; From 43a7470a7d09ad412c7d9815ae23402f4bb7acb0 Mon Sep 17 00:00:00 2001 From: Gamer64 Date: Wed, 1 Oct 2025 01:21:12 +0200 Subject: [PATCH 3/4] [Maxwell]: Fix shaders compilation memory leak (#2606) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: collecting "The ObjectPool was never being cleared after use. When compiling complex shaders, this would allocate gigabytes of memory, causing the emulator to run out of RAM and be killed by the operating system. This is a critical fix that prevents out-of-memory crashes on all operating systems when playing games with complex shaders." Co-authored-by: Gamer64 <76565986+Gamer64ytb@users.noreply.github.com> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2606 Reviewed-by: MaranBr Reviewed-by: Shinmegumi Co-authored-by: Gamer64 Co-committed-by: Gamer64 --- .../frontend/maxwell/structured_control_flow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp index b5e1e70b4c..6d325b4aad 100644 --- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp +++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp @@ -991,6 +991,7 @@ IR::AbstractSyntaxList BuildASL(ObjectPool& inst_pool, ObjectPool Date: Mon, 29 Sep 2025 08:30:12 +0000 Subject: [PATCH 4/4] [dynarmic] backport WAITPKG based spinlocks Signed-off-by: lizzie --- .../dynarmic/backend/x64/block_of_code.cpp | 2 + .../backend/x64/emit_x64_memory.cpp.inc | 3 +- .../dynarmic/backend/x64/emit_x64_memory.h | 5 ++- .../src/dynarmic/backend/x64/host_feature.h | 3 +- .../src/dynarmic/common/spin_lock_x64.cpp | 37 +++++++++++++++++-- .../src/dynarmic/common/spin_lock_x64.h | 5 ++- 6 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp b/src/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp index d5d5f089ff..4a8de6475e 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp @@ -188,6 +188,8 @@ HostFeature GetHostFeatures() { features |= HostFeature::LZCNT; if (cpu_info.has(Cpu::tGFNI)) features |= HostFeature::GFNI; + if (cpu_info.has(Cpu::tWAITPKG)) + features |= HostFeature::WAITPKG; if (cpu_info.has(Cpu::tBMI2)) { // BMI2 instructions such as pdep and pext have been very slow up until Zen 3. diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc index 34f77b0446..36a2d40de6 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc @@ -430,10 +430,11 @@ void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* i const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[1]); const Xbyak::Reg32 status = ctx.reg_alloc.ScratchGpr().cvt32(); const Xbyak::Reg64 tmp = ctx.reg_alloc.ScratchGpr(); + const Xbyak::Reg64 tmp2 = ctx.reg_alloc.ScratchGpr(); const auto wrapped_fn = exclusive_write_fallbacks[std::make_tuple(ordered, bitsize, vaddr.getIdx(), value.getIdx())]; - EmitExclusiveLock(code, conf, tmp, eax); + EmitExclusiveLock(code, conf, tmp, tmp2); SharedLabel end = GenSharedLabel(); diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.h b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.h index 75a47c6a80..c363ea1b6b 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.h +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* This file is part of the dynarmic project. * Copyright (c) 2022 MerryMage * SPDX-License-Identifier: 0BSD @@ -343,7 +346,7 @@ void EmitExclusiveLock(BlockOfCode& code, const UserConfig& conf, Xbyak::Reg64 p } code.mov(pointer, mcl::bit_cast(GetExclusiveMonitorLockPointer(conf.global_monitor))); - EmitSpinLockLock(code, pointer, tmp); + EmitSpinLockLock(code, pointer, tmp, code.HasHostFeature(HostFeature::WAITPKG)); } template diff --git a/src/dynarmic/src/dynarmic/backend/x64/host_feature.h b/src/dynarmic/src/dynarmic/backend/x64/host_feature.h index 7246ed18d4..34dca971cb 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/host_feature.h +++ b/src/dynarmic/src/dynarmic/backend/x64/host_feature.h @@ -35,9 +35,10 @@ enum class HostFeature : u64 { BMI2 = 1ULL << 19, LZCNT = 1ULL << 20, GFNI = 1ULL << 21, + WAITPKG = 1ULL << 22, // Zen-based BMI2 - FastBMI2 = 1ULL << 22, + FastBMI2 = 1ULL << 23, // Orthographic AVX512 features on 128 and 256 vectors AVX512_Ortho = AVX512F | AVX512VL, diff --git a/src/dynarmic/src/dynarmic/common/spin_lock_x64.cpp b/src/dynarmic/src/dynarmic/common/spin_lock_x64.cpp index da50179de9..5307672bfe 100644 --- a/src/dynarmic/src/dynarmic/common/spin_lock_x64.cpp +++ b/src/dynarmic/src/dynarmic/common/spin_lock_x64.cpp @@ -22,17 +22,46 @@ static const auto default_cg_mode = nullptr; //Allow RWE namespace Dynarmic { -void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp) { +void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp, bool waitpkg) { + // TODO: this is because we lack regalloc - so better to be safe :( + if (waitpkg) { + code.push(Xbyak::util::eax); + code.push(Xbyak::util::ebx); + code.push(Xbyak::util::edx); + } Xbyak::Label start, loop; - code.jmp(start, code.T_NEAR); code.L(loop); - code.pause(); + if (waitpkg) { + // TODO: This clobbers EAX and EDX did we tell the regalloc? + // ARM ptr for address-monitoring + code.umonitor(ptr); + // tmp.bit[0] = 0: C0.1 | Slow Wakup | Better Savings + // tmp.bit[0] = 1: C0.2 | Fast Wakup | Lesser Savings + // edx:eax is implicitly used as a 64-bit deadline timestamp + // Use the maximum so that we use the operating system's maximum + // allowed wait time within the IA32_UMWAIT_CONTROL register + // Enter power state designated by tmp and wait for a write to lock_ptr + code.mov(Xbyak::util::eax, 0xFFFFFFFF); + code.mov(Xbyak::util::edx, Xbyak::util::eax); + // TODO: We can only be here because tmp is 1 already - however we repeatedly overwrite it... + code.mov(Xbyak::util::ebx, 1); + code.umwait(Xbyak::util::ebx); + // CF == 1 if we hit the OS-timeout in IA32_UMWAIT_CONTROL without a write + // CF == 0 if we exited the wait for any other reason + } else { + code.pause(); + } code.L(start); code.mov(tmp, 1); /*code.lock();*/ code.xchg(code.dword[ptr], tmp); code.test(tmp, tmp); code.jnz(loop, code.T_NEAR); + if (waitpkg) { + code.pop(Xbyak::util::edx); + code.pop(Xbyak::util::ebx); + code.pop(Xbyak::util::eax); + } } void EmitSpinLockUnlock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp) { @@ -60,7 +89,7 @@ void SpinLockImpl::Initialize() { code.align(); lock = code.getCurr(); - EmitSpinLockLock(code, ABI_PARAM1, code.eax); + EmitSpinLockLock(code, ABI_PARAM1, code.eax, false); code.ret(); code.align(); diff --git a/src/dynarmic/src/dynarmic/common/spin_lock_x64.h b/src/dynarmic/src/dynarmic/common/spin_lock_x64.h index df6a3d7407..df6860e2f2 100644 --- a/src/dynarmic/src/dynarmic/common/spin_lock_x64.h +++ b/src/dynarmic/src/dynarmic/common/spin_lock_x64.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* This file is part of the dynarmic project. * Copyright (c) 2022 MerryMage * SPDX-License-Identifier: 0BSD @@ -9,7 +12,7 @@ namespace Dynarmic { -void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp); +void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp, bool waitpkg); void EmitSpinLockUnlock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp); } // namespace Dynarmic