From a6cf81223ba758c77a1aaeba6fd6f6f732a343a8 Mon Sep 17 00:00:00 2001 From: Ribbit Date: Mon, 29 Sep 2025 18:01:08 -0700 Subject: [PATCH 1/4] [nce] Fix NCE signal chaining and cache invalidation --- src/core/arm/nce/arm_nce.cpp | 96 +++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 28 deletions(-) diff --git a/src/core/arm/nce/arm_nce.cpp b/src/core/arm/nce/arm_nce.cpp index 877e8ac3c7..e676035b72 100644 --- a/src/core/arm/nce/arm_nce.cpp +++ b/src/core/arm/nce/arm_nce.cpp @@ -12,6 +12,7 @@ #include "core/arm/nce/interpreter_visitor.h" #include "core/arm/nce/patcher.h" #include "core/core.h" +#include "core/device_memory.h" #include "core/memory.h" #include "core/hle/kernel/k_process.h" @@ -173,12 +174,40 @@ bool ArmNce::HandleGuestAccessFault(GuestContext* guest_ctx, void* raw_info, voi return HandleFailedGuestFault(guest_ctx, raw_info, raw_context); } +namespace { + +void ChainSignalHandler(const struct sigaction& action, int sig, void* raw_info, + void* raw_context) { + if ((action.sa_flags & SA_SIGINFO) != 0) { + if (action.sa_sigaction) { + action.sa_sigaction(sig, static_cast(raw_info), raw_context); + } + return; + } + + if (action.sa_handler == SIG_IGN) { + return; + } + + if (action.sa_handler == SIG_DFL) { + signal(sig, SIG_DFL); + raise(sig); + return; + } + + if (action.sa_handler) { + action.sa_handler(sig); + } +} + +} // namespace + void ArmNce::HandleHostAlignmentFault(int sig, void* raw_info, void* raw_context) { - return g_orig_bus_action.sa_sigaction(sig, static_cast(raw_info), raw_context); + ChainSignalHandler(g_orig_bus_action, sig, raw_info, raw_context); } void ArmNce::HandleHostAccessFault(int sig, void* raw_info, void* raw_context) { - return g_orig_segv_action.sa_sigaction(sig, static_cast(raw_info), raw_context); + ChainSignalHandler(g_orig_segv_action, sig, raw_info, raw_context); } void ArmNce::LockThread(Kernel::KThread* thread) { @@ -322,7 +351,7 @@ void ArmNce::Initialize() { alignment_fault_action.sa_sigaction = reinterpret_cast(&ArmNce::GuestAlignmentFaultSignalHandler); alignment_fault_action.sa_mask = signal_mask; - Common::SigAction(GuestAlignmentFaultSignal, &alignment_fault_action, nullptr); + Common::SigAction(GuestAlignmentFaultSignal, &alignment_fault_action, &g_orig_bus_action); struct sigaction access_fault_action {}; access_fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART; @@ -385,41 +414,52 @@ void ArmNce::SignalInterrupt(Kernel::KThread* thread) { } } -const std::size_t CACHE_PAGE_SIZE = 4096; - void ArmNce::ClearInstructionCache() { -#if defined(__GNUC__) || defined(__clang__) - void* start = (void*)((uintptr_t)__builtin_return_address(0) & ~(CACHE_PAGE_SIZE - 1)); - void* end = - (void*)((uintptr_t)start + CACHE_PAGE_SIZE * 2); // Clear two pages for better coverage - // Prefetch next likely pages - __builtin_prefetch((void*)((uintptr_t)end), 1, 3); - __builtin___clear_cache(static_cast(start), static_cast(end)); -#endif -#ifdef __aarch64__ - // Ensure all previous memory operations complete - asm volatile("dmb ish" ::: "memory"); +#if defined(__aarch64__) + // Invalidate the entire instruction cache to the point of unification. + asm volatile("ic iallu" ::: "memory"); asm volatile("dsb ish" ::: "memory"); asm volatile("isb" ::: "memory"); +#else + // Fallback: nothing to do on unsupported architectures since NCE is AArch64-only. #endif } void ArmNce::InvalidateCacheRange(u64 addr, std::size_t size) { - #if defined(__GNUC__) || defined(__clang__) - // Align the start address to cache line boundary for better performance - const size_t CACHE_LINE_SIZE = 64; - addr &= ~(CACHE_LINE_SIZE - 1); + if (size == 0) { + return; + } - // Round up size to nearest cache line - size = (size + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1); +#if defined(__aarch64__) + constexpr std::size_t CACHE_LINE_SIZE = 64; - // Prefetch the range to be invalidated - for (size_t offset = 0; offset < size; offset += CACHE_LINE_SIZE) { - __builtin_prefetch((void*)(addr + offset), 1, 3); + const u64 start = addr & ~(static_cast(CACHE_LINE_SIZE) - 1ULL); + const u64 end = (addr + size + CACHE_LINE_SIZE - 1ULL) & + ~(static_cast(CACHE_LINE_SIZE) - 1ULL); + + auto* const virtual_base = m_system.DeviceMemory().buffer.VirtualBasePointer(); + if (virtual_base == nullptr) { + // Fall back to full invalidation if the direct mapping is unavailable. + ClearInstructionCache(); + return; + } + + for (u64 line = start; line < end; line += CACHE_LINE_SIZE) { + if (line < Core::DramMemoryMap::Base) { + continue; } - #endif - this->ClearInstructionCache(); + const u64 offset = line - Core::DramMemoryMap::Base; + const void* line_ptr = virtual_base + offset; + asm volatile("ic ivau, %0" : : "r"(line_ptr) : "memory"); + } + + asm volatile("dsb ish" ::: "memory"); + asm volatile("isb" ::: "memory"); +#else + (void)addr; + (void)size; +#endif } -} // namespace Core +} // namespace Core \ No newline at end of file From 1a5b3fb23934cfb9647120fc2a055bc116e67460 Mon Sep 17 00:00:00 2001 From: MaranBr Date: Thu, 2 Oct 2025 01:30:05 +0200 Subject: [PATCH 2/4] [audio_core] Fix audio reverb effect (#2646) This fixes the audio reverb effect that was causing loud noise in some games and on some platforms. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2646 Co-authored-by: MaranBr Co-committed-by: MaranBr --- src/audio_core/renderer/behavior/info_updater.cpp | 12 ------------ src/audio_core/renderer/command/effect/reverb.cpp | 2 -- 2 files changed, 14 deletions(-) diff --git a/src/audio_core/renderer/behavior/info_updater.cpp b/src/audio_core/renderer/behavior/info_updater.cpp index 48fe1f8975..20f6cda3a2 100644 --- a/src/audio_core/renderer/behavior/info_updater.cpp +++ b/src/audio_core/renderer/behavior/info_updater.cpp @@ -165,12 +165,6 @@ Result InfoUpdater::UpdateEffectsVersion1(EffectContext& effect_context, const b reinterpret_cast(output), effect_count}; for (u32 i = 0; i < effect_count; i++) { -#ifdef _WIN32 - // There's a bug in Windows where using this effect causes extreme noise. So let's skip it. - if (in_params[i].type == EffectInfoBase::Type::Reverb) { - continue; - } -#endif auto effect_info{&effect_context.GetInfo(i)}; if (effect_info->GetType() != in_params[i].type) { effect_info->ForceUnmapBuffers(pool_mapper); @@ -218,12 +212,6 @@ Result InfoUpdater::UpdateEffectsVersion2(EffectContext& effect_context, const b reinterpret_cast(output), effect_count}; for (u32 i = 0; i < effect_count; i++) { -#ifdef _WIN32 - // There's a bug in Windows where using this effect causes extreme noise. So let's skip it. - if (in_params[i].type == EffectInfoBase::Type::Reverb) { - continue; - } -#endif auto effect_info{&effect_context.GetInfo(i)}; if (effect_info->GetType() != in_params[i].type) { effect_info->ForceUnmapBuffers(pool_mapper); diff --git a/src/audio_core/renderer/command/effect/reverb.cpp b/src/audio_core/renderer/command/effect/reverb.cpp index 46b85b8945..87eab1adda 100644 --- a/src/audio_core/renderer/command/effect/reverb.cpp +++ b/src/audio_core/renderer/command/effect/reverb.cpp @@ -191,8 +191,6 @@ static void InitializeReverbEffect(const ReverbInfo::ParameterVersion2& params, const auto center_delay_time{(5 * delay).to_uint_floor()}; state.center_delay_line.Initialize(center_delay_time, 1.0f); - UpdateReverbEffectParameter(params, state); - for (u32 i = 0; i < ReverbInfo::MaxDelayLines; i++) { std::ranges::fill(state.fdn_delay_lines[i].buffer, 0); std::ranges::fill(state.decay_delay_lines[i].buffer, 0); From 990a43a48c77816f0f9f5edad40ec905f05df62b Mon Sep 17 00:00:00 2001 From: Ribbit Date: Thu, 2 Oct 2025 20:00:34 +0200 Subject: [PATCH 3/4] [vk] Add missing flush per spec (#2624) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We copy pixels into a CPU-side staging buffer and then ask the GPU to read from it. On some systems those CPU writes aren’t automatically visible to the GPU unless explicitly flushed, so the GPU can sometimes read stale data. By calling buffer.Flush() immediately after writing, we force those CPU changes to become visible to the device, ensuring the GPU sees the latest frame. However, this is an emulator, so sometimes what spec says may not work cause reasons. Co-authored-by: Ribbit Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2624 Reviewed-by: Shinmegumi Reviewed-by: MaranBr Co-authored-by: Ribbit Co-committed-by: Ribbit --- src/video_core/renderer_vulkan/present/layer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/video_core/renderer_vulkan/present/layer.cpp b/src/video_core/renderer_vulkan/present/layer.cpp index fa7c457573..5676dfe62a 100644 --- a/src/video_core/renderer_vulkan/present/layer.cpp +++ b/src/video_core/renderer_vulkan/present/layer.cpp @@ -280,6 +280,7 @@ void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t i Tegra::Texture::UnswizzleTexture( mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size), bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); + buffer.Flush(); // Ensure host writes are visible before the GPU copy. } const VkBufferImageCopy copy{ From 80bcc6fb1236122cd5591971ae99826d4f69c220 Mon Sep 17 00:00:00 2001 From: Ribbit Date: Mon, 29 Sep 2025 18:01:08 -0700 Subject: [PATCH 4/4] [nce] Fix NCE signal chaining and cache invalidation --- src/core/arm/nce/arm_nce.cpp | 96 +++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 28 deletions(-) diff --git a/src/core/arm/nce/arm_nce.cpp b/src/core/arm/nce/arm_nce.cpp index 877e8ac3c7..e676035b72 100644 --- a/src/core/arm/nce/arm_nce.cpp +++ b/src/core/arm/nce/arm_nce.cpp @@ -12,6 +12,7 @@ #include "core/arm/nce/interpreter_visitor.h" #include "core/arm/nce/patcher.h" #include "core/core.h" +#include "core/device_memory.h" #include "core/memory.h" #include "core/hle/kernel/k_process.h" @@ -173,12 +174,40 @@ bool ArmNce::HandleGuestAccessFault(GuestContext* guest_ctx, void* raw_info, voi return HandleFailedGuestFault(guest_ctx, raw_info, raw_context); } +namespace { + +void ChainSignalHandler(const struct sigaction& action, int sig, void* raw_info, + void* raw_context) { + if ((action.sa_flags & SA_SIGINFO) != 0) { + if (action.sa_sigaction) { + action.sa_sigaction(sig, static_cast(raw_info), raw_context); + } + return; + } + + if (action.sa_handler == SIG_IGN) { + return; + } + + if (action.sa_handler == SIG_DFL) { + signal(sig, SIG_DFL); + raise(sig); + return; + } + + if (action.sa_handler) { + action.sa_handler(sig); + } +} + +} // namespace + void ArmNce::HandleHostAlignmentFault(int sig, void* raw_info, void* raw_context) { - return g_orig_bus_action.sa_sigaction(sig, static_cast(raw_info), raw_context); + ChainSignalHandler(g_orig_bus_action, sig, raw_info, raw_context); } void ArmNce::HandleHostAccessFault(int sig, void* raw_info, void* raw_context) { - return g_orig_segv_action.sa_sigaction(sig, static_cast(raw_info), raw_context); + ChainSignalHandler(g_orig_segv_action, sig, raw_info, raw_context); } void ArmNce::LockThread(Kernel::KThread* thread) { @@ -322,7 +351,7 @@ void ArmNce::Initialize() { alignment_fault_action.sa_sigaction = reinterpret_cast(&ArmNce::GuestAlignmentFaultSignalHandler); alignment_fault_action.sa_mask = signal_mask; - Common::SigAction(GuestAlignmentFaultSignal, &alignment_fault_action, nullptr); + Common::SigAction(GuestAlignmentFaultSignal, &alignment_fault_action, &g_orig_bus_action); struct sigaction access_fault_action {}; access_fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART; @@ -385,41 +414,52 @@ void ArmNce::SignalInterrupt(Kernel::KThread* thread) { } } -const std::size_t CACHE_PAGE_SIZE = 4096; - void ArmNce::ClearInstructionCache() { -#if defined(__GNUC__) || defined(__clang__) - void* start = (void*)((uintptr_t)__builtin_return_address(0) & ~(CACHE_PAGE_SIZE - 1)); - void* end = - (void*)((uintptr_t)start + CACHE_PAGE_SIZE * 2); // Clear two pages for better coverage - // Prefetch next likely pages - __builtin_prefetch((void*)((uintptr_t)end), 1, 3); - __builtin___clear_cache(static_cast(start), static_cast(end)); -#endif -#ifdef __aarch64__ - // Ensure all previous memory operations complete - asm volatile("dmb ish" ::: "memory"); +#if defined(__aarch64__) + // Invalidate the entire instruction cache to the point of unification. + asm volatile("ic iallu" ::: "memory"); asm volatile("dsb ish" ::: "memory"); asm volatile("isb" ::: "memory"); +#else + // Fallback: nothing to do on unsupported architectures since NCE is AArch64-only. #endif } void ArmNce::InvalidateCacheRange(u64 addr, std::size_t size) { - #if defined(__GNUC__) || defined(__clang__) - // Align the start address to cache line boundary for better performance - const size_t CACHE_LINE_SIZE = 64; - addr &= ~(CACHE_LINE_SIZE - 1); + if (size == 0) { + return; + } - // Round up size to nearest cache line - size = (size + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1); +#if defined(__aarch64__) + constexpr std::size_t CACHE_LINE_SIZE = 64; - // Prefetch the range to be invalidated - for (size_t offset = 0; offset < size; offset += CACHE_LINE_SIZE) { - __builtin_prefetch((void*)(addr + offset), 1, 3); + const u64 start = addr & ~(static_cast(CACHE_LINE_SIZE) - 1ULL); + const u64 end = (addr + size + CACHE_LINE_SIZE - 1ULL) & + ~(static_cast(CACHE_LINE_SIZE) - 1ULL); + + auto* const virtual_base = m_system.DeviceMemory().buffer.VirtualBasePointer(); + if (virtual_base == nullptr) { + // Fall back to full invalidation if the direct mapping is unavailable. + ClearInstructionCache(); + return; + } + + for (u64 line = start; line < end; line += CACHE_LINE_SIZE) { + if (line < Core::DramMemoryMap::Base) { + continue; } - #endif - this->ClearInstructionCache(); + const u64 offset = line - Core::DramMemoryMap::Base; + const void* line_ptr = virtual_base + offset; + asm volatile("ic ivau, %0" : : "r"(line_ptr) : "memory"); + } + + asm volatile("dsb ish" ::: "memory"); + asm volatile("isb" ::: "memory"); +#else + (void)addr; + (void)size; +#endif } -} // namespace Core +} // namespace Core \ No newline at end of file