mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-21 07:18:33 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f40ebd305c |
@@ -84,11 +84,7 @@ function(SystemPackageViable JSON_NAME)
|
||||
parse_object(${object})
|
||||
|
||||
string(REPLACE " " ";" find_args "${find_args}")
|
||||
if (${package}_FORCE_BUNDLED)
|
||||
set(${package}_FOUND OFF)
|
||||
else()
|
||||
find_package(${package} ${version} ${find_args} QUIET NO_POLICY_SCOPE)
|
||||
endif()
|
||||
find_package(${package} ${version} ${find_args} QUIET NO_POLICY_SCOPE)
|
||||
|
||||
set(${pkg}_VIABLE ${${package}_FOUND} PARENT_SCOPE)
|
||||
set(${pkg}_PACKAGE ${package} PARENT_SCOPE)
|
||||
|
||||
Vendored
+3
-1
@@ -150,7 +150,7 @@
|
||||
"package": "SDL2",
|
||||
"name": "SDL2",
|
||||
"repo": "crueter-ci/SDL2",
|
||||
"version": "2.32.10-cf5dabd6ea",
|
||||
"version": "2.32.10-a65111bd2d",
|
||||
"min_version": "2.26.4"
|
||||
},
|
||||
"catch2": {
|
||||
@@ -184,6 +184,7 @@
|
||||
"repo": "libsdl-org/SDL",
|
||||
"tag": "release-%VERSION%",
|
||||
"hash": "d5622d6bb7266f7942a7b8ad43e8a22524893bf0c2ea1af91204838d9b78d32768843f6faa248757427b8404b8c6443776d4afa6b672cd8571a4e0c03a829383",
|
||||
"key": "generic",
|
||||
"bundled": true,
|
||||
"git_version": "2.32.10",
|
||||
"skip_updates": true
|
||||
@@ -193,6 +194,7 @@
|
||||
"repo": "libsdl-org/SDL",
|
||||
"sha": "cc016b0046",
|
||||
"hash": "b8d9873446cdb922387471df9968e078714683046674ef0d0edddf8e25da65a539a3bae83d635496b970237f90b07b36a69f8d7855d450de59311d6d6e8c3dbc",
|
||||
"key": "steamdeck",
|
||||
"bundled": true,
|
||||
"skip_updates": "true"
|
||||
},
|
||||
|
||||
@@ -507,7 +507,7 @@
|
||||
<string name="skip_cpu_inner_invalidation">Skip CPU Inner Invalidation</string>
|
||||
<string name="skip_cpu_inner_invalidation_description">Skips certain CPU-side cache invalidations during memory updates, reducing CPU usage and improving it\'s performance. This may cause glitches or crashes on some games.</string>
|
||||
<string name="fix_bloom_effects">Fix Bloom Effects</string>
|
||||
<string name="fix_bloom_effects_description">Reduces bloom blur in LA/EOW (Adreno 700), removes bloom in Burnout. Warning: may cause graphical artifacts in other games.</string>
|
||||
<string name="fix_bloom_effects_description">Reduces bloom blur in LA/EOW (Adreno 700), removes bloom in Burnout</string>
|
||||
<string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
|
||||
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches.</string>
|
||||
<string name="gpu_unswizzle_settings">GPU Unswizzle Settings</string>
|
||||
|
||||
@@ -568,7 +568,13 @@ struct Values {
|
||||
Category::RendererHacks};
|
||||
|
||||
SwitchableSetting<ExtendedDynamicState> dyna_state{linkage,
|
||||
#if defined (ANDROID) || defined (__APPLE__)
|
||||
#if defined (_WIN32)
|
||||
ExtendedDynamicState::EDS3,
|
||||
#elif defined (__FreeBSD__)
|
||||
ExtendedDynamicState::EDS3,
|
||||
#elif defined (ANDROID)
|
||||
ExtendedDynamicState::Disabled,
|
||||
#elif defined (__APPLE__)
|
||||
ExtendedDynamicState::Disabled,
|
||||
#else
|
||||
ExtendedDynamicState::EDS2,
|
||||
|
||||
+14
-11
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 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
|
||||
|
||||
@@ -13,16 +10,27 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifdef __ANDROID__
|
||||
template <typename T>
|
||||
T* LookupLibcSymbol(const char* name) {
|
||||
#if defined(__BIONIC__)
|
||||
Common::DynamicLibrary provider("libc.so");
|
||||
ASSERT_MSG(provider.IsOpen(), "Failed to open libc!");
|
||||
if (!provider.IsOpen()) {
|
||||
UNREACHABLE_MSG("Failed to open libc!");
|
||||
}
|
||||
#else
|
||||
// For other operating environments, we assume the symbol is not overridden.
|
||||
const char* base = nullptr;
|
||||
Common::DynamicLibrary provider(base);
|
||||
#endif
|
||||
|
||||
void* sym = provider.GetSymbolAddress(name);
|
||||
if (sym == nullptr) {
|
||||
sym = dlsym(RTLD_DEFAULT, name);
|
||||
}
|
||||
ASSERT_MSG(sym != nullptr, "Unable to find symbol {}!", name);
|
||||
if (sym == nullptr) {
|
||||
UNREACHABLE_MSG("Unable to find symbol {}!", name);
|
||||
}
|
||||
|
||||
return reinterpret_cast<T*>(sym);
|
||||
}
|
||||
|
||||
@@ -30,10 +38,5 @@ int SigAction(int signum, const struct sigaction* act, struct sigaction* oldact)
|
||||
static auto libc_sigaction = LookupLibcSymbol<decltype(sigaction)>("sigaction");
|
||||
return libc_sigaction(signum, act, oldact);
|
||||
}
|
||||
#else
|
||||
int SigAction(int signum, const struct sigaction* act, struct sigaction* oldact) {
|
||||
return sigaction(signum, act, oldact);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
@@ -38,16 +38,10 @@ u64 DynarmicCallbacks32::MemoryRead64(u32 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
|
||||
return m_memory.Read64(vaddr);
|
||||
}
|
||||
|
||||
std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
|
||||
if (!m_memory.IsValidVirtualAddressRange(vaddr, sizeof(u32)))
|
||||
return std::nullopt;
|
||||
auto const aligned_vaddr = vaddr & ~Core::Memory::YUZU_PAGEMASK;
|
||||
if (last_code_addr != aligned_vaddr) {
|
||||
m_memory.ReadBlock(aligned_vaddr, &cached_code_page, sizeof(cached_code_page));
|
||||
last_code_addr = aligned_vaddr;
|
||||
}
|
||||
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
||||
return m_memory.Read32(vaddr);
|
||||
}
|
||||
|
||||
void DynarmicCallbacks32::MemoryWrite8(u32 vaddr, u8 value) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
@@ -7,7 +7,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <dynarmic/interface/A32/a32.h>
|
||||
#include <dynarmic/interface/code_page.h>
|
||||
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
||||
@@ -50,9 +49,6 @@ public:
|
||||
u64 GetTicksRemaining() override;
|
||||
bool CheckMemoryAccess(u64 addr, u64 size, Kernel::DebugWatchpointType type);
|
||||
void ReturnException(u32 pc, Dynarmic::HaltReason hr);
|
||||
//
|
||||
Dynarmic::CodePage cached_code_page;
|
||||
u64 last_code_addr = 0;
|
||||
ArmDynarmic32& m_parent;
|
||||
Core::Memory::Memory& m_memory;
|
||||
Kernel::KProcess* m_process{};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
@@ -41,17 +41,10 @@ Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
|
||||
return {m_memory.Read64(vaddr), m_memory.Read64(vaddr + 8)};
|
||||
}
|
||||
|
||||
std::optional<u32> DynarmicCallbacks64::MemoryReadCode(u64 vaddr) {
|
||||
if (!m_memory.IsValidVirtualAddressRange(vaddr, sizeof(u32)))
|
||||
return std::nullopt;
|
||||
// return m_memory.Read32(vaddr);
|
||||
auto const aligned_vaddr = vaddr & ~Core::Memory::YUZU_PAGEMASK;
|
||||
if (last_code_addr != aligned_vaddr) {
|
||||
m_memory.ReadBlock(aligned_vaddr, &cached_code_page, sizeof(cached_code_page));
|
||||
last_code_addr = aligned_vaddr;
|
||||
}
|
||||
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
||||
return m_memory.Read32(vaddr);
|
||||
}
|
||||
|
||||
void DynarmicCallbacks64::MemoryWrite8(u64 vaddr, u8 value) {
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include <dynarmic/interface/A64/a64.h>
|
||||
#include <dynarmic/interface/code_page.h>
|
||||
#include "common/common_types.h"
|
||||
#include "common/hash.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
@@ -62,8 +61,6 @@ public:
|
||||
bool CheckMemoryAccess(u64 addr, u64 size, Kernel::DebugWatchpointType type);
|
||||
void ReturnException(u64 pc, Dynarmic::HaltReason hr);
|
||||
|
||||
Dynarmic::CodePage cached_code_page;
|
||||
u64 last_code_addr = 0;
|
||||
ArmDynarmic64& m_parent;
|
||||
Core::Memory::Memory& m_memory;
|
||||
u64 m_tpidrro_el0{};
|
||||
|
||||
@@ -13,14 +13,12 @@
|
||||
#include "core/arm/nce/patcher.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
|
||||
#include "dynarmic/common/context.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
@@ -35,70 +33,100 @@ static_assert(offsetof(NativeExecutionParameters, native_context) == TpidrEl0Nat
|
||||
static_assert(offsetof(NativeExecutionParameters, lock) == TpidrEl0Lock);
|
||||
static_assert(offsetof(NativeExecutionParameters, magic) == TpidrEl0TlsMagic);
|
||||
|
||||
fpsimd_context* GetFloatingPointState(mcontext_t& host_ctx) {
|
||||
_aarch64_ctx* header = reinterpret_cast<_aarch64_ctx*>(&host_ctx.__reserved);
|
||||
while (header->magic != FPSIMD_MAGIC) {
|
||||
header = reinterpret_cast<_aarch64_ctx*>(reinterpret_cast<char*>(header) + header->size);
|
||||
}
|
||||
return reinterpret_cast<fpsimd_context*>(header);
|
||||
}
|
||||
|
||||
using namespace Common::Literals;
|
||||
constexpr u32 StackSize = 128_KiB;
|
||||
|
||||
} // namespace
|
||||
|
||||
void* ArmNce::RestoreGuestContext(void* raw_context) {
|
||||
CTX_DECLARE(raw_context);
|
||||
// Restore all guest state except tpidr_el0.
|
||||
// Retrieve the host context.
|
||||
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
|
||||
|
||||
// Thread-local parameters will be located in x9.
|
||||
auto* tpidr = reinterpret_cast<NativeExecutionParameters*>(CTX_X(9));
|
||||
auto* tpidr = reinterpret_cast<NativeExecutionParameters*>(host_ctx.regs[9]);
|
||||
auto* guest_ctx = static_cast<GuestContext*>(tpidr->native_context);
|
||||
|
||||
// Retrieve the host floating point state.
|
||||
auto* fpctx = GetFloatingPointState(host_ctx);
|
||||
|
||||
// Save host callee-saved registers.
|
||||
std::memcpy(guest_ctx->host_ctx.host_saved_regs.data(), &CTX_X(19), sizeof(guest_ctx->host_ctx.host_saved_regs));
|
||||
std::memcpy(guest_ctx->host_ctx.host_saved_vregs.data(), &CTX_Q(8), sizeof(guest_ctx->host_ctx.host_saved_vregs));
|
||||
std::memcpy(guest_ctx->host_ctx.host_saved_vregs.data(), &fpctx->vregs[8],
|
||||
sizeof(guest_ctx->host_ctx.host_saved_vregs));
|
||||
std::memcpy(guest_ctx->host_ctx.host_saved_regs.data(), &host_ctx.regs[19],
|
||||
sizeof(guest_ctx->host_ctx.host_saved_regs));
|
||||
|
||||
// Save stack pointer.
|
||||
guest_ctx->host_ctx.host_sp = CTX_SP;
|
||||
CTX_PC = guest_ctx->sp;
|
||||
CTX_SP = guest_ctx->pc;
|
||||
CTX_PSTATE = guest_ctx->pstate;
|
||||
CTX_FPCR = guest_ctx->fpcr;
|
||||
CTX_FPSR = guest_ctx->fpsr;
|
||||
std::memcpy(&CTX_X(0), guest_ctx->cpu_registers.data(), sizeof(guest_ctx->cpu_registers));
|
||||
std::memcpy(&CTX_Q(0), guest_ctx->vector_registers.data(), sizeof(guest_ctx->vector_registers));
|
||||
guest_ctx->host_ctx.host_sp = host_ctx.sp;
|
||||
|
||||
// Restore all guest state except tpidr_el0.
|
||||
host_ctx.sp = guest_ctx->sp;
|
||||
host_ctx.pc = guest_ctx->pc;
|
||||
host_ctx.pstate = guest_ctx->pstate;
|
||||
fpctx->fpcr = guest_ctx->fpcr;
|
||||
fpctx->fpsr = guest_ctx->fpsr;
|
||||
std::memcpy(host_ctx.regs, guest_ctx->cpu_registers.data(), sizeof(host_ctx.regs));
|
||||
std::memcpy(fpctx->vregs, guest_ctx->vector_registers.data(), sizeof(fpctx->vregs));
|
||||
|
||||
// Return the new thread-local storage pointer.
|
||||
return tpidr;
|
||||
}
|
||||
|
||||
void ArmNce::SaveGuestContext(GuestContext* guest_ctx, void* raw_context) {
|
||||
CTX_DECLARE(raw_context);
|
||||
// Retrieve the host context.
|
||||
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
|
||||
|
||||
// Retrieve the host floating point state.
|
||||
auto* fpctx = GetFloatingPointState(host_ctx);
|
||||
|
||||
// Save all guest registers except tpidr_el0.
|
||||
std::memcpy(guest_ctx->cpu_registers.data(), &CTX_X(0), sizeof(guest_ctx->cpu_registers));
|
||||
std::memcpy(guest_ctx->vector_registers.data(), &CTX_Q(0), sizeof(guest_ctx->vector_registers));
|
||||
guest_ctx->fpsr = CTX_FPSR;
|
||||
guest_ctx->fpcr = CTX_FPCR;
|
||||
guest_ctx->pc = CTX_PC;
|
||||
guest_ctx->sp = CTX_SP;
|
||||
guest_ctx->pstate = u32(CTX_PSTATE);
|
||||
std::memcpy(guest_ctx->cpu_registers.data(), host_ctx.regs, sizeof(host_ctx.regs));
|
||||
std::memcpy(guest_ctx->vector_registers.data(), fpctx->vregs, sizeof(fpctx->vregs));
|
||||
guest_ctx->fpsr = fpctx->fpsr;
|
||||
guest_ctx->fpcr = fpctx->fpcr;
|
||||
guest_ctx->pstate = static_cast<u32>(host_ctx.pstate);
|
||||
guest_ctx->pc = host_ctx.pc;
|
||||
guest_ctx->sp = host_ctx.sp;
|
||||
|
||||
// Restore stack pointer.
|
||||
CTX_SP = guest_ctx->host_ctx.host_sp;
|
||||
host_ctx.sp = guest_ctx->host_ctx.host_sp;
|
||||
|
||||
// Restore host callee-saved registers.
|
||||
std::memcpy(&CTX_X(19), guest_ctx->host_ctx.host_saved_regs.data(), sizeof(guest_ctx->host_ctx.host_saved_regs));
|
||||
std::memcpy(&CTX_Q(8), guest_ctx->host_ctx.host_saved_vregs.data(), sizeof(guest_ctx->host_ctx.host_saved_vregs));
|
||||
std::memcpy(&host_ctx.regs[19], guest_ctx->host_ctx.host_saved_regs.data(),
|
||||
sizeof(guest_ctx->host_ctx.host_saved_regs));
|
||||
std::memcpy(&fpctx->vregs[8], guest_ctx->host_ctx.host_saved_vregs.data(),
|
||||
sizeof(guest_ctx->host_ctx.host_saved_vregs));
|
||||
|
||||
// Return from the call on exit by setting pc to x30.
|
||||
CTX_PC = guest_ctx->host_ctx.host_saved_regs[11];
|
||||
host_ctx.pc = guest_ctx->host_ctx.host_saved_regs[11];
|
||||
|
||||
// Clear esr_el1 and return it.
|
||||
CTX_X(0) = guest_ctx->esr_el1.exchange(0);
|
||||
host_ctx.regs[0] = guest_ctx->esr_el1.exchange(0);
|
||||
}
|
||||
|
||||
bool ArmNce::HandleFailedGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
|
||||
CTX_DECLARE(raw_context);
|
||||
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
|
||||
auto* info = static_cast<siginfo_t*>(raw_info);
|
||||
|
||||
// We can't handle the access, so determine why we crashed.
|
||||
auto const is_prefetch_abort = CTX_PC == reinterpret_cast<u64>(info->si_addr);
|
||||
const bool is_prefetch_abort = host_ctx.pc == reinterpret_cast<u64>(info->si_addr);
|
||||
|
||||
// For data aborts, skip the instruction and return to guest code.
|
||||
// This will allow games to continue in many scenarios where they would otherwise crash.
|
||||
if (!is_prefetch_abort) {
|
||||
CTX_PC += 4;
|
||||
host_ctx.pc += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is a prefetch abort.
|
||||
guest_ctx->esr_el1.fetch_or(u64(HaltReason::PrefetchAbort));
|
||||
guest_ctx->esr_el1.fetch_or(static_cast<u64>(HaltReason::PrefetchAbort));
|
||||
|
||||
// Forcibly mark the context as locked. We are still running.
|
||||
// We may race with SignalInterrupt here:
|
||||
@@ -114,13 +142,17 @@ bool ArmNce::HandleFailedGuestFault(GuestContext* guest_ctx, void* raw_info, voi
|
||||
}
|
||||
|
||||
bool ArmNce::HandleGuestAlignmentFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
|
||||
CTX_DECLARE(raw_context);
|
||||
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
|
||||
auto* fpctx = GetFloatingPointState(host_ctx);
|
||||
auto& memory = guest_ctx->parent->m_running_thread->GetOwnerProcess()->GetMemory();
|
||||
|
||||
// Match and execute an instruction.
|
||||
if (auto next_pc = MatchAndExecuteOneInstruction(memory, raw_context); next_pc) {
|
||||
CTX_PC = *next_pc;
|
||||
auto next_pc = MatchAndExecuteOneInstruction(memory, &host_ctx, fpctx);
|
||||
if (next_pc) {
|
||||
host_ctx.pc = *next_pc;
|
||||
return true;
|
||||
}
|
||||
|
||||
// We couldn't handle the access.
|
||||
return HandleFailedGuestFault(guest_ctx, raw_info, raw_context);
|
||||
}
|
||||
@@ -166,7 +198,7 @@ void ArmNce::UnlockThread(Kernel::KThread* thread) {
|
||||
HaltReason ArmNce::RunThread(Kernel::KThread* thread) {
|
||||
// Check if we're already interrupted.
|
||||
// If we are, we can just return immediately.
|
||||
auto hr = HaltReason(m_guest_ctx.esr_el1.exchange(0));
|
||||
HaltReason hr = static_cast<HaltReason>(m_guest_ctx.esr_el1.exchange(0));
|
||||
if (True(hr)) {
|
||||
return hr;
|
||||
}
|
||||
@@ -244,51 +276,9 @@ ArmNce::ArmNce(System& system, bool uses_wall_clock, std::size_t core_index)
|
||||
|
||||
ArmNce::~ArmNce() = default;
|
||||
|
||||
// Borrowed from libusb
|
||||
static unsigned int posix_gettid(void) {
|
||||
static thread_local unsigned int tl_tid;
|
||||
int tid;
|
||||
if (tl_tid)
|
||||
return tl_tid;
|
||||
#if defined(__ANDROID__)
|
||||
tid = gettid();
|
||||
#elif defined(__APPLE__)
|
||||
#ifdef HAVE_PTHREAD_THREADID_NP
|
||||
uint64_t thread_id;
|
||||
if (pthread_threadid_np(NULL, &thread_id) == 0)
|
||||
tid = (int)thread_id;
|
||||
else
|
||||
tid = -1;
|
||||
#else
|
||||
tid = (int)pthread_mach_thread_np(pthread_self());
|
||||
#endif
|
||||
#elif defined(__HAIKU__)
|
||||
tid = get_pthread_thread_id(pthread_self());
|
||||
#elif defined(__linux__)
|
||||
tid = (int)syscall(SYS_gettid);
|
||||
#elif defined(__NetBSD__)
|
||||
tid = _lwp_self();
|
||||
#elif defined(__OpenBSD__)
|
||||
/* The following only works with OpenBSD > 5.1 as it requires
|
||||
* real thread support. For 5.1 and earlier, -1 is returned. */
|
||||
tid = syscall(SYS_getthrid);
|
||||
#elif defined(__sun__)
|
||||
tid = _lwp_self();
|
||||
#else
|
||||
tid = -1;
|
||||
#endif
|
||||
if (tid == -1) {
|
||||
/* If we don't have a thread ID, at least return a unique
|
||||
* value that can be used to distinguish individual
|
||||
* threads. */
|
||||
tid = (int)(intptr_t)pthread_self();
|
||||
}
|
||||
return tl_tid = (unsigned int)tid;
|
||||
}
|
||||
|
||||
void ArmNce::Initialize() {
|
||||
if (m_thread_id == -1) {
|
||||
m_thread_id = posix_gettid();
|
||||
m_thread_id = gettid();
|
||||
}
|
||||
|
||||
// Configure signal stack.
|
||||
@@ -319,7 +309,7 @@ void ArmNce::Initialize() {
|
||||
&ArmNce::ReturnToRunCodeByExceptionLevelChangeSignalHandler);
|
||||
return_to_run_code_action.sa_mask = signal_mask;
|
||||
Common::SigAction(ReturnToRunCodeByExceptionLevelChangeSignal, &return_to_run_code_action,
|
||||
nullptr);
|
||||
nullptr);
|
||||
|
||||
struct sigaction break_from_run_code_action {};
|
||||
break_from_run_code_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
@@ -380,7 +370,7 @@ void ArmNce::SetContext(const Kernel::Svc::ThreadContext& ctx) {
|
||||
|
||||
void ArmNce::SignalInterrupt(Kernel::KThread* thread) {
|
||||
// Add break loop condition.
|
||||
m_guest_ctx.esr_el1.fetch_or(u64(HaltReason::BreakLoop));
|
||||
m_guest_ctx.esr_el1.fetch_or(static_cast<u64>(HaltReason::BreakLoop));
|
||||
|
||||
auto* params = &thread->GetNativeExecutionParameters();
|
||||
LockThreadParameters(params);
|
||||
@@ -391,11 +381,7 @@ void ArmNce::SignalInterrupt(Kernel::KThread* thread) {
|
||||
if (params->is_running) {
|
||||
// We should signal to the running thread.
|
||||
// The running thread will unlock the thread context.
|
||||
#ifdef __linux__
|
||||
syscall(SYS_tkill, m_thread_id, BreakFromRunCodeSignal);
|
||||
#else
|
||||
pthread_kill(pthread_t(m_thread_id), int(BreakFromRunCodeSignal));
|
||||
#endif
|
||||
} else {
|
||||
// If the thread is no longer running, we have nothing to do.
|
||||
UnlockThreadParameters(params);
|
||||
@@ -405,11 +391,12 @@ void ArmNce::SignalInterrupt(Kernel::KThread* thread) {
|
||||
const std::size_t CACHE_PAGE_SIZE = 4096;
|
||||
|
||||
void ArmNce::ClearInstructionCache() {
|
||||
#ifdef __aarch64__
|
||||
// Ensure all previous memory operations complete
|
||||
asm volatile(
|
||||
"dsb ish\r\n"
|
||||
"dsb ish\r\n"
|
||||
"isb\r\n" ::: "memory");
|
||||
asm volatile("dsb ish\n"
|
||||
"dsb ish\n"
|
||||
"isb" ::: "memory");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ArmNce::InvalidateCacheRange(u64 addr, std::size_t size) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
@@ -7,8 +7,6 @@
|
||||
|
||||
#include <numeric>
|
||||
#include "core/arm/nce/interpreter_visitor.h"
|
||||
#include "core/memory.h"
|
||||
#include "dynarmic/common/context.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
@@ -61,7 +59,7 @@ static u64 SignExtend(u64 value, u64 bitsize, u64 regsize) {
|
||||
if (regsize == 64) {
|
||||
return SignExtendToLong(value, bitsize);
|
||||
} else {
|
||||
return SignExtendToWord(u32(value), bitsize);
|
||||
return SignExtendToWord(static_cast<u32>(value), bitsize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,11 +147,11 @@ u64 InterpreterVisitor::ExtendReg(size_t bitsize, Reg reg, Imm<3> option, u8 shi
|
||||
}
|
||||
|
||||
u128 InterpreterVisitor::GetVec(Vec v) {
|
||||
return m_fpsimd_regs[u32(v)];
|
||||
return m_fpsimd_regs[static_cast<u32>(v)];
|
||||
}
|
||||
|
||||
u64 InterpreterVisitor::GetReg(Reg r) {
|
||||
return m_regs[u32(r)];
|
||||
return m_regs[static_cast<u32>(r)];
|
||||
}
|
||||
|
||||
u64 InterpreterVisitor::GetSp() {
|
||||
@@ -165,11 +163,11 @@ u64 InterpreterVisitor::GetPc() {
|
||||
}
|
||||
|
||||
void InterpreterVisitor::SetVec(Vec v, u128 value) {
|
||||
m_fpsimd_regs[u32(v)] = value;
|
||||
m_fpsimd_regs[static_cast<u32>(v)] = value;
|
||||
}
|
||||
|
||||
void InterpreterVisitor::SetReg(Reg r, u64 value) {
|
||||
m_regs[u32(r)] = value;
|
||||
m_regs[static_cast<u32>(r)] = value;
|
||||
}
|
||||
|
||||
void InterpreterVisitor::SetSp(u64 value) {
|
||||
@@ -676,7 +674,7 @@ bool InterpreterVisitor::STRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> opti
|
||||
Reg Rt) {
|
||||
const Imm<1> opc_0{0};
|
||||
const size_t scale = size.ZeroExtend<size_t>();
|
||||
const u8 shift = S ? u8(scale) : 0;
|
||||
const u8 shift = S ? static_cast<u8>(scale) : 0;
|
||||
if (!option.Bit<1>()) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
@@ -688,7 +686,7 @@ bool InterpreterVisitor::LDRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> opti
|
||||
Reg Rt) {
|
||||
const Imm<1> opc_0{1};
|
||||
const size_t scale = size.ZeroExtend<size_t>();
|
||||
const u8 shift = S ? u8(scale) : 0;
|
||||
const u8 shift = S ? static_cast<u8>(scale) : 0;
|
||||
if (!option.Bit<1>()) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
@@ -739,7 +737,7 @@ bool InterpreterVisitor::STR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
const u8 shift = S ? u8(scale) : 0;
|
||||
const u8 shift = S ? static_cast<u8>(scale) : 0;
|
||||
if (!option.Bit<1>()) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
@@ -755,7 +753,7 @@ bool InterpreterVisitor::LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
const u8 shift = S ? u8(scale) : 0;
|
||||
const u8 shift = S ? static_cast<u8>(scale) : 0;
|
||||
if (!option.Bit<1>()) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
@@ -763,25 +761,24 @@ bool InterpreterVisitor::LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3
|
||||
return this->SIMDOffset(scale, shift, opc_0, Rm, option, Rn, Vt);
|
||||
}
|
||||
|
||||
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, void* raw_context) {
|
||||
CTX_DECLARE(raw_context);
|
||||
std::span<u64, 31> regs(reinterpret_cast<u64*>(&CTX_X(0)), 31);
|
||||
std::span<u128, 32> vregs(reinterpret_cast<u128*>(&CTX_Q(0)), 32);
|
||||
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context,
|
||||
fpsimd_context* fpsimd_context) {
|
||||
std::span<u64, 31> regs(reinterpret_cast<u64*>(context->regs), 31);
|
||||
std::span<u128, 32> vregs(reinterpret_cast<u128*>(fpsimd_context->vregs), 32);
|
||||
u64& sp = *reinterpret_cast<u64*>(&context->sp);
|
||||
const u64& pc = *reinterpret_cast<u64*>(&context->pc);
|
||||
|
||||
// Store temporal to not break aliasing rules :)
|
||||
u64 tmp_sp = CTX_SP;
|
||||
u64 tmp_pc = CTX_PC;
|
||||
u32 instruction = memory.Read32(tmp_pc);
|
||||
InterpreterVisitor visitor(memory, regs, vregs, sp, pc);
|
||||
u32 instruction = memory.Read32(pc);
|
||||
bool was_executed = false;
|
||||
InterpreterVisitor visitor(memory, regs, vregs, tmp_sp, tmp_pc);
|
||||
|
||||
if (auto decoder = Dynarmic::A64::Decode<VisitorBase>(instruction)) {
|
||||
was_executed = decoder->get().call(visitor, instruction);
|
||||
} else {
|
||||
LOG_ERROR(Core_ARM, "Unallocated encoding: {:#x}", instruction);
|
||||
}
|
||||
CTX_SP = tmp_sp;
|
||||
CTX_PC = tmp_pc;
|
||||
return was_executed ? std::optional<u64>(tmp_pc + 4) : std::nullopt;
|
||||
|
||||
return was_executed ? std::optional<u64>(pc + 4) : std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
@@ -105,6 +105,7 @@ private:
|
||||
const u64& m_pc;
|
||||
};
|
||||
|
||||
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, void* raw_context);
|
||||
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context,
|
||||
fpsimd_context* fpsimd_context);
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include <dynarmic/interface/A64/a64.h>
|
||||
#include <dynarmic/interface/A64/config.h>
|
||||
#include <dynarmic/interface/code_page.h>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/common_funcs.h"
|
||||
@@ -47,15 +46,6 @@ public:
|
||||
: memory{memory_}, local_memory{local_memory_},
|
||||
mapped_ranges{mapped_ranges_}, parent{parent_} {}
|
||||
|
||||
std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) override {
|
||||
static_assert(Core::Memory::YUZU_PAGESIZE == Dynarmic::CODE_PAGE_SIZE);
|
||||
auto const aligned_vaddr = vaddr & ~Core::Memory::YUZU_PAGEMASK;
|
||||
if (last_code_addr != aligned_vaddr) {
|
||||
cached_code_page = ReadMemory<Dynarmic::CodePage>(aligned_vaddr);
|
||||
last_code_addr = aligned_vaddr;
|
||||
}
|
||||
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
||||
}
|
||||
u8 MemoryRead8(u64 vaddr) override {
|
||||
return ReadMemory<u8>(vaddr);
|
||||
}
|
||||
@@ -126,7 +116,8 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename T> T ReadMemory(u64 vaddr) {
|
||||
template <class T>
|
||||
T ReadMemory(u64 vaddr) {
|
||||
T ret{};
|
||||
if (boost::icl::contains(mapped_ranges, vaddr)) {
|
||||
memory.ReadBlock(vaddr, &ret, sizeof(T));
|
||||
@@ -155,9 +146,6 @@ private:
|
||||
std::vector<u8>& local_memory;
|
||||
IntervalSet& mapped_ranges;
|
||||
JITContextImpl& parent;
|
||||
|
||||
Dynarmic::CodePage cached_code_page;
|
||||
u64 last_code_addr = 0;
|
||||
};
|
||||
|
||||
class JITContextImpl {
|
||||
|
||||
+3
-2
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
|
||||
@@ -36,7 +36,8 @@
|
||||
|
||||
namespace Core::Memory {
|
||||
|
||||
static inline bool AddressSpaceContains(const Common::PageTable& table, const Common::ProcessAddress addr, const std::size_t size) {
|
||||
static inline bool AddressSpaceContains(const Common::PageTable& table, const Common::ProcessAddress addr,
|
||||
const std::size_t size) {
|
||||
const Common::ProcessAddress max_addr = 1ULL << table.GetAddressSpaceBits();
|
||||
return addr + size >= addr && addr + size <= max_addr;
|
||||
}
|
||||
|
||||
@@ -65,10 +65,11 @@ static Optimization::PolyfillOptions GenPolyfillOptions(const BlockOfCode& code)
|
||||
|
||||
struct Jit::Impl {
|
||||
Impl(Jit* jit, A32::UserConfig conf) noexcept
|
||||
: block_of_code(GenRunCodeCallbacks(conf.callbacks, &GetCurrentBlockThunk, this, conf), JitStateInfo{jit_state}, conf.code_cache_size, GenRCP(conf))
|
||||
: ir_block{LocationDescriptor(0, PSR(0), FPSCR(0), false)}
|
||||
, conf(std::move(conf))
|
||||
, block_of_code(GenRunCodeCallbacks(conf.callbacks, &GetCurrentBlockThunk, this, conf), JitStateInfo{jit_state}, conf.code_cache_size, GenRCP(conf))
|
||||
, emitter(block_of_code, conf, jit)
|
||||
, polyfill_options(GenPolyfillOptions(block_of_code))
|
||||
, conf(std::move(conf))
|
||||
, jit_interface(jit)
|
||||
{}
|
||||
|
||||
@@ -203,7 +204,8 @@ private:
|
||||
}
|
||||
|
||||
A32EmitX64::BlockDescriptor GetBasicBlock(IR::LocationDescriptor descriptor) {
|
||||
if (auto block = emitter.GetBasicBlock(descriptor))
|
||||
auto block = emitter.GetBasicBlock(descriptor);
|
||||
if (block)
|
||||
return *block;
|
||||
|
||||
constexpr size_t MINIMUM_REMAINING_CODESIZE = 1 * 1024 * 1024;
|
||||
@@ -213,10 +215,8 @@ private:
|
||||
}
|
||||
block_of_code.EnsureMemoryCommitted(MINIMUM_REMAINING_CODESIZE);
|
||||
|
||||
// LocationDescriptor ctor() does important ops (like tflags) do not skip
|
||||
auto const arch_descriptor = A32::LocationDescriptor{descriptor};
|
||||
ir_block.Reset(arch_descriptor);
|
||||
A32::Translate(ir_block, arch_descriptor, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
||||
ir_block.Reset(descriptor);
|
||||
A32::Translate(ir_block, A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
||||
Optimization::Optimize(ir_block, conf, polyfill_options);
|
||||
return emitter.Emit(ir_block);
|
||||
}
|
||||
@@ -243,13 +243,12 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
IR::Block ir_block = {LocationDescriptor(0, PSR(0), FPSCR(0), false)};
|
||||
IR::Block ir_block;
|
||||
const A32::UserConfig conf;
|
||||
A32JitState jit_state;
|
||||
BlockOfCode block_of_code;
|
||||
A32EmitX64 emitter;
|
||||
Optimization::PolyfillOptions polyfill_options;
|
||||
// Keep it here, you don't wanna mess with the fuckery that's initializer lists
|
||||
const A32::UserConfig conf;
|
||||
Jit* jit_interface;
|
||||
|
||||
// Requests made during execution to invalidate the cache are queued up here.
|
||||
|
||||
@@ -62,7 +62,8 @@ static Optimization::PolyfillOptions GenPolyfillOptions(const BlockOfCode& code)
|
||||
struct Jit::Impl final {
|
||||
public:
|
||||
Impl(Jit* jit, UserConfig conf)
|
||||
: conf(conf)
|
||||
: ir_block{LocationDescriptor(0, FP::FPCR(0), false)}
|
||||
, conf(conf)
|
||||
, block_of_code(GenRunCodeCallbacks(conf.callbacks, &GetCurrentBlockThunk, this, conf), JitStateInfo{jit_state}, conf.code_cache_size, GenRCP(conf))
|
||||
, emitter(block_of_code, conf, jit)
|
||||
, polyfill_options(GenPolyfillOptions(block_of_code))
|
||||
@@ -256,8 +257,8 @@ private:
|
||||
return GetBlock(A64::LocationDescriptor{GetCurrentLocation()}.SetSingleStepping(true));
|
||||
}
|
||||
|
||||
CodePtr GetBlock(IR::LocationDescriptor descriptor) {
|
||||
if (auto block = emitter.GetBasicBlock(descriptor))
|
||||
CodePtr GetBlock(IR::LocationDescriptor current_location) {
|
||||
if (auto block = emitter.GetBasicBlock(current_location))
|
||||
return block->entrypoint;
|
||||
|
||||
constexpr size_t MINIMUM_REMAINING_CODESIZE = 1 * 1024 * 1024;
|
||||
@@ -270,10 +271,8 @@ private:
|
||||
|
||||
// JIT Compile
|
||||
const auto get_code = [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); };
|
||||
// LocationDescriptor ctor() does important ops (like tflags) do not skip
|
||||
auto const arch_descriptor = A64::LocationDescriptor{descriptor};
|
||||
ir_block.Reset(arch_descriptor);
|
||||
A64::Translate(ir_block, arch_descriptor, get_code, {conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
|
||||
ir_block.Reset(current_location);
|
||||
A64::Translate(ir_block, A64::LocationDescriptor{current_location}, get_code, {conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
|
||||
Optimization::Optimize(ir_block, conf, polyfill_options);
|
||||
return emitter.Emit(ir_block).entrypoint;
|
||||
}
|
||||
@@ -300,7 +299,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
IR::Block ir_block = {LocationDescriptor(0, FP::FPCR(0), false)};
|
||||
IR::Block ir_block;
|
||||
const UserConfig conf;
|
||||
A64JitState jit_state;
|
||||
BlockOfCode block_of_code;
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Dynarmic {
|
||||
|
||||
/// @brief Smallest valid page (may change for Apple?)
|
||||
constexpr inline uint64_t CODE_PAGE_SIZE = 4096;
|
||||
struct CodePage {
|
||||
uint32_t inst[CODE_PAGE_SIZE / sizeof(uint32_t)];
|
||||
};
|
||||
|
||||
}
|
||||
@@ -22,10 +22,13 @@
|
||||
|
||||
namespace Dynarmic::IR {
|
||||
|
||||
Block::Block(LocationDescriptor location) noexcept
|
||||
: location{location}
|
||||
, end_location{location}
|
||||
{}
|
||||
Block::Block(const LocationDescriptor& location)
|
||||
: location{location},
|
||||
end_location{location},
|
||||
cond{Cond::AL}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// Prepends a new instruction to this basic block before the insertion point,
|
||||
/// handling any allocations necessary to do so.
|
||||
@@ -57,21 +60,6 @@ Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode opcode, s
|
||||
return instructions.insert_before(insertion_point, inst);
|
||||
}
|
||||
|
||||
void Block::Reset(LocationDescriptor location_) noexcept {
|
||||
mcl::intrusive_list<IR::Inst> tmp = {};
|
||||
instructions.swap(tmp);
|
||||
inlined_inst.clear();
|
||||
pooled_inst.clear();
|
||||
cond_failed.reset();
|
||||
location = location_;
|
||||
end_location = location_;
|
||||
cond = Cond::AL;
|
||||
terminal = Term::Invalid{};
|
||||
cond_failed_cycle_count = 0;
|
||||
cycle_count = 0;
|
||||
ASSERT(instructions.size() == 0);
|
||||
}
|
||||
|
||||
static std::string TerminalToString(const Terminal& terminal_variant) noexcept {
|
||||
struct : boost::static_visitor<std::string> {
|
||||
std::string operator()(const Term::Invalid&) const {
|
||||
@@ -135,11 +123,11 @@ std::string DumpBlock(const IR::Block& block) noexcept {
|
||||
case Type::A32ExtReg: return A32::ExtRegToString(arg.GetA32ExtRegRef());
|
||||
case Type::A64Reg: return A64::RegToString(arg.GetA64RegRef());
|
||||
case Type::A64Vec: return A64::VecToString(arg.GetA64VecRef());
|
||||
case Type::CoprocInfo: return fmt::format("$coproc{}", arg.GetCoprocInfo()[0]);
|
||||
case Type::NZCVFlags: return fmt::format("$nzcv");
|
||||
case Type::Cond: return fmt::format("$cond={}", A32::CondToString(arg.GetCond()));
|
||||
case Type::Table: return fmt::format("$table");
|
||||
case Type::AccType: return fmt::format("$acc-type={}", u32(arg.GetAccType()));
|
||||
case Type::CoprocInfo: return fmt::format("#<coproc>");
|
||||
case Type::NZCVFlags: return fmt::format("#<NZCV flags>");
|
||||
case Type::Cond: return fmt::format("#<cond={}>", A32::CondToString(arg.GetCond()));
|
||||
case Type::Table: return fmt::format("#<table>");
|
||||
case Type::AccType: return fmt::format("#<acc-type={}>", u32(arg.GetAccType()));
|
||||
default: return fmt::format("<unknown immediate type {}>", arg.GetType());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
using reverse_iterator = instruction_list_type::reverse_iterator;
|
||||
using const_reverse_iterator = instruction_list_type::const_reverse_iterator;
|
||||
|
||||
Block(LocationDescriptor location) noexcept;
|
||||
explicit Block(const LocationDescriptor& location);
|
||||
~Block() = default;
|
||||
Block(const Block&) = delete;
|
||||
Block& operator=(const Block&) = delete;
|
||||
@@ -58,7 +58,6 @@ public:
|
||||
return PrependNewInst(instructions.end(), opcode, args);
|
||||
}
|
||||
iterator PrependNewInst(iterator insertion_point, Opcode op, std::initializer_list<Value> args) noexcept;
|
||||
void Reset(LocationDescriptor location_) noexcept;
|
||||
|
||||
/// Gets a mutable reference to the instruction list for this basic block.
|
||||
inline instruction_list_type& Instructions() noexcept {
|
||||
@@ -141,6 +140,18 @@ public:
|
||||
return cycle_count;
|
||||
}
|
||||
|
||||
inline void Reset(LocationDescriptor location_) noexcept {
|
||||
inlined_inst.clear();
|
||||
pooled_inst.clear();
|
||||
cond_failed.reset();
|
||||
location = location_;
|
||||
terminal = Term::Invalid{};
|
||||
cond_failed_cycle_count = 0;
|
||||
cycle_count = 0;
|
||||
instruction_list_type tmp{};
|
||||
instructions.swap(tmp);
|
||||
}
|
||||
|
||||
/// "Hot cache" for small blocks so we don't call global allocator
|
||||
boost::container::static_vector<Inst, 30> inlined_inst;
|
||||
/// List of instructions in this block.
|
||||
@@ -154,7 +165,7 @@ public:
|
||||
/// Description of the end location of this block
|
||||
LocationDescriptor end_location;
|
||||
/// Conditional to pass in order to execute this block
|
||||
Cond cond = Cond::AL;
|
||||
Cond cond;
|
||||
/// Terminal instruction of this block.
|
||||
Terminal terminal = Term::Invalid{};
|
||||
/// Number of cycles this block takes to execute if the conditional fails.
|
||||
|
||||
@@ -1042,11 +1042,6 @@ static void FoldZeroExtendXToLong(IR::Inst& inst) {
|
||||
static void ConstantPropagation(IR::Block& block) {
|
||||
for (auto& inst : block.instructions) {
|
||||
auto const opcode = inst.GetOpcode();
|
||||
// skip NZCV so we dont end up discarding side effects :)
|
||||
// TODO(lizzie): hey stupid maybe fix the A64 codegen for folded constants AND
|
||||
// redirect the mfer properly?!??! just saying :)
|
||||
if (IR::MayGetNZCVFromOp(opcode) && inst.GetAssociatedPseudoOperation(IR::Opcode::GetNZCVFromOp))
|
||||
continue;
|
||||
switch (opcode) {
|
||||
case Op::LeastSignificantWord:
|
||||
FoldLeastSignificantWord(inst);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
@@ -20,7 +20,7 @@
|
||||
namespace Vulkan {
|
||||
|
||||
// Prefer small grow rates to avoid saturating the descriptor pool with barely used pipelines
|
||||
constexpr size_t SETS_GROW_RATE = 16;
|
||||
constexpr size_t SETS_GROW_RATE = 32; //test difference between 16 and 32
|
||||
constexpr s32 SCORE_THRESHOLD = 3;
|
||||
|
||||
struct DescriptorBank {
|
||||
@@ -29,9 +29,12 @@ struct DescriptorBank {
|
||||
};
|
||||
|
||||
bool DescriptorBankInfo::IsSuperset(const DescriptorBankInfo& subset) const noexcept {
|
||||
return uniform_buffers >= subset.uniform_buffers && storage_buffers >= subset.storage_buffers &&
|
||||
texture_buffers >= subset.texture_buffers && image_buffers >= subset.image_buffers &&
|
||||
textures >= subset.textures && images >= subset.images;
|
||||
return uniform_buffers >= subset.uniform_buffers &&
|
||||
storage_buffers >= subset.storage_buffers &&
|
||||
texture_buffers >= subset.texture_buffers &&
|
||||
image_buffers >= subset.image_buffers &&
|
||||
textures >= subset.textures &&
|
||||
images >= subset.images;
|
||||
}
|
||||
|
||||
template <typename Descriptors>
|
||||
@@ -45,6 +48,19 @@ static u32 Accumulate(const Descriptors& descriptors) {
|
||||
|
||||
static DescriptorBankInfo MakeBankInfo(std::span<const Shader::Info> infos) {
|
||||
DescriptorBankInfo bank;
|
||||
if (infos.size() == 1) {
|
||||
const auto& info = infos.front();
|
||||
const auto acc = [](const auto& ds){ u32 c=0; for (const auto& d: ds) c+=d.count; return c; };
|
||||
bank.uniform_buffers += acc(info.constant_buffer_descriptors);
|
||||
bank.storage_buffers += acc(info.storage_buffers_descriptors);
|
||||
bank.texture_buffers += acc(info.texture_buffer_descriptors);
|
||||
bank.image_buffers += acc(info.image_buffer_descriptors);
|
||||
bank.textures += acc(info.texture_descriptors);
|
||||
bank.images += acc(info.image_descriptors);
|
||||
bank.score = bank.uniform_buffers + bank.storage_buffers + bank.texture_buffers +
|
||||
bank.image_buffers + bank.textures + bank.images;
|
||||
return bank;
|
||||
}
|
||||
for (const Shader::Info& info : infos) {
|
||||
bank.uniform_buffers += Accumulate(info.constant_buffer_descriptors);
|
||||
bank.storage_buffers += Accumulate(info.storage_buffers_descriptors);
|
||||
@@ -102,13 +118,22 @@ void DescriptorAllocator::Allocate(size_t begin, size_t end) {
|
||||
}
|
||||
|
||||
vk::DescriptorSets DescriptorAllocator::AllocateDescriptors(size_t count) {
|
||||
const std::vector<VkDescriptorSetLayout> layouts(count, layout);
|
||||
std::array<VkDescriptorSetLayout, 64> stack{};
|
||||
const VkDescriptorSetLayout* p_layouts = nullptr;
|
||||
std::vector<VkDescriptorSetLayout> heap;
|
||||
if (count <= stack.size()) {
|
||||
stack.fill(layout);
|
||||
p_layouts = stack.data();
|
||||
} else {
|
||||
heap.assign(count, layout);
|
||||
p_layouts = heap.data();
|
||||
}
|
||||
VkDescriptorSetAllocateInfo allocate_info{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.descriptorPool = *bank->pools.back(),
|
||||
.descriptorSetCount = static_cast<u32>(count),
|
||||
.pSetLayouts = layouts.data(),
|
||||
.pSetLayouts = p_layouts,
|
||||
};
|
||||
vk::DescriptorSets new_sets = bank->pools.back().Allocate(allocate_info);
|
||||
if (!new_sets.IsOutOfPoolMemory()) {
|
||||
@@ -146,21 +171,58 @@ DescriptorAllocator DescriptorPool::Allocator(VkDescriptorSetLayout layout,
|
||||
}
|
||||
|
||||
DescriptorBank& DescriptorPool::Bank(const DescriptorBankInfo& reqs) {
|
||||
{
|
||||
std::scoped_lock lk(cache_mutex);
|
||||
DescriptorBank* best = nullptr; u64 best_stamp = 0;
|
||||
for (const auto& e : cache_) {
|
||||
if (!e.bank) continue;
|
||||
if (std::abs(e.info.score - reqs.score) < SCORE_THRESHOLD && e.info.IsSuperset(reqs)) {
|
||||
if (e.stamp >= best_stamp) { best_stamp = e.stamp; best = e.bank; }
|
||||
}
|
||||
}
|
||||
if (best) return *best;
|
||||
}
|
||||
std::shared_lock read_lock{banks_mutex};
|
||||
const auto it = std::ranges::find_if(bank_infos, [&reqs](const DescriptorBankInfo& bank) {
|
||||
return std::abs(bank.score - reqs.score) < SCORE_THRESHOLD && bank.IsSuperset(reqs);
|
||||
});
|
||||
if (it != bank_infos.end()) {
|
||||
return *banks[std::distance(bank_infos.begin(), it)].get();
|
||||
DescriptorBank& found = *banks[std::distance(bank_infos.begin(), it)].get();
|
||||
read_lock.unlock();
|
||||
// update cache
|
||||
std::scoped_lock lk(cache_mutex);
|
||||
size_t victim = 0; u64 oldest = UINT64_MAX;
|
||||
for (size_t i=0;i<cache_.size();++i) if (cache_[i].stamp < oldest) { oldest = cache_[i].stamp; victim = i; }
|
||||
cache_[victim] = CacheEntry{found.info, &found, ++cache_tick_};
|
||||
return found;
|
||||
}
|
||||
read_lock.unlock();
|
||||
|
||||
std::unique_lock write_lock{banks_mutex};
|
||||
auto it2 = std::ranges::find_if(bank_infos, [&reqs](const DescriptorBankInfo& bank) {
|
||||
return std::abs(bank.score - reqs.score) < SCORE_THRESHOLD && bank.IsSuperset(reqs);
|
||||
});
|
||||
if (it2 != bank_infos.end()) {
|
||||
DescriptorBank& found = *banks[std::distance(bank_infos.begin(), it2)].get();
|
||||
// update cache
|
||||
std::scoped_lock lk(cache_mutex);
|
||||
size_t victim = 0; u64 oldest = UINT64_MAX;
|
||||
for (size_t i=0;i<cache_.size();++i) if (cache_[i].stamp < oldest) { oldest = cache_[i].stamp; victim = i; }
|
||||
cache_[victim] = CacheEntry{found.info, &found, ++cache_tick_};
|
||||
return found;
|
||||
}
|
||||
bank_infos.push_back(reqs);
|
||||
|
||||
auto& bank = *banks.emplace_back(std::make_unique<DescriptorBank>());
|
||||
bank.info = reqs;
|
||||
AllocatePool(device, bank);
|
||||
// update cache
|
||||
{
|
||||
std::scoped_lock lk(cache_mutex);
|
||||
size_t victim = 0; u64 oldest = UINT64_MAX;
|
||||
for (size_t i=0;i<cache_.size();++i) if (cache_[i].stamp < oldest) { oldest = cache_[i].stamp; victim = i; }
|
||||
cache_[victim] = CacheEntry{bank.info, &bank, ++cache_tick_};
|
||||
}
|
||||
return bank;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -6,7 +9,8 @@
|
||||
#include <shared_mutex>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include <array>
|
||||
#include <mutex>
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
#include "video_core/renderer_vulkan/vk_resource_pool.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
@@ -75,6 +79,14 @@ public:
|
||||
|
||||
private:
|
||||
DescriptorBank& Bank(const DescriptorBankInfo& reqs);
|
||||
struct CacheEntry {
|
||||
DescriptorBankInfo info{};
|
||||
DescriptorBank* bank{nullptr};
|
||||
u64 stamp{0};
|
||||
};
|
||||
std::mutex cache_mutex{};
|
||||
std::array<CacheEntry, 8> cache_{}; //test and then adjust
|
||||
u64 cache_tick_{0};
|
||||
|
||||
const Device& device;
|
||||
MasterSemaphore& master_semaphore;
|
||||
|
||||
Reference in New Issue
Block a user