aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-28 16:44:24 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-28 16:44:24 -0400
commitcc3d73762a41dba370aadd3468cb510dcbc2b74d (patch)
tree32629e503c2cd1d64ce2353c76fb02585ce1d1cb
parent82a5b520bc6dc4a77acca9b3ddd0bc577ada5676 (diff)
Performance diagnostics and small wins for title 1
- eden-cli: SDL called SDL_AppIterate back to back, keeping a host core at 100% polling input; run the main callbacks at 240 Hz. - EDEN_NX2_IDLE_SPIN_US: optional spin before an idle core blocks (measured: no gain on title 1, so off by default). - EDEN_NX2_SVC_TRACE=<first>:<last>: trace SVCs (with thread ids) for a range of presents only. - MME DMA reads flush GPU-written memory first (never triggered on title 1, kept for correctness); render-enable override histogram instead of the first-400 log. Measured on title 1's title screen (RTX 4070 Laptop): GPU ~20% busy, the six emulated cores ~0.9 host cores in total, Eden's GPU thread 45-70% busy and largely waiting: on host fences for semaphore acquires (GPU accuracy High delays every release to host completion), on ASTC upload Finish(), and in a 1 ms poll sleep. gpu_accuracy=Low: ~12.5 -> ~16 fps.
-rw-r--r--src/core/hle/kernel/physical_core.cpp19
-rw-r--r--src/core/hle/kernel/physical_core.h3
-rw-r--r--src/core/hle/kernel/svc.cpp22
-rw-r--r--src/video_core/macro.cpp23
-rw-r--r--src/yuzu_cmd/yuzu.cpp3
5 files changed, 61 insertions, 9 deletions
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index c5236c64e3..c3ae7629aa 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -4,6 +4,9 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include <chrono>
+#include <cstdlib>
+#include <thread>
#include "common/scope_exit.h"
#include "common/settings.h"
#include "core/core.h"
@@ -222,8 +225,22 @@ void PhysicalCore::LogBacktrace(KernelCore& kernel) {
}
void PhysicalCore::Idle() {
+ // Guest job systems hand work between cores many times per frame; blocking at once adds the
+ // host's thread wake-up latency to every handoff. EDEN_NX2_IDLE_SPIN_US=<n>: poll for that
+ // many microseconds before blocking.
+ static const auto spin = std::chrono::microseconds{[] {
+ const char* text = std::getenv("EDEN_NX2_IDLE_SPIN_US");
+ return text ? std::strtoll(text, nullptr, 10) : 0LL;
+ }()};
+ if (spin.count() > 0) {
+ const auto deadline = std::chrono::steady_clock::now() + spin;
+ while (!m_is_interrupted.load(std::memory_order_acquire) &&
+ std::chrono::steady_clock::now() < deadline) {
+ std::this_thread::yield();
+ }
+ }
std::unique_lock lk{m_guard};
- m_on_interrupt.wait(lk, [this] { return m_is_interrupted; });
+ m_on_interrupt.wait(lk, [this] { return m_is_interrupted.load(); });
}
bool PhysicalCore::IsInterrupted() const {
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h
index b92201204a..20371d6717 100644
--- a/src/core/hle/kernel/physical_core.h
+++ b/src/core/hle/kernel/physical_core.h
@@ -6,6 +6,7 @@
#pragma once
+#include <atomic>
#include <condition_variable>
#include <cstddef>
#include <memory>
@@ -71,7 +72,7 @@ private:
std::condition_variable m_on_interrupt;
Core::ArmInterface* m_arm_interface{};
KThread* m_current_thread{};
- bool m_is_interrupted{};
+ std::atomic<bool> m_is_interrupted{};
bool m_is_single_core{};
};
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index e458a5057d..53d964ecbc 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -7,6 +7,7 @@
// DO NOT MODIFY IT MANUALLY
#include <cstdlib>
+#include <cstring>
#include <type_traits>
#include "core/arm/arm_interface.h"
@@ -2506,10 +2507,20 @@ void Call(Core::System& system, u32 imm) {
LOG_TRACE(Kernel_SVC, "{} [0]={:#x} [1]={:#x} [2]={:#x} [3]={:#x} [4]={:#x} [5]={:#x} [6]={:#x}",
imm, GetArg64(args, 0), GetArg64(args, 1), GetArg64(args, 2),
GetArg64(args, 3), GetArg64(args, 4), GetArg64(args, 5), GetArg64(args, 6));
- // Set EDEN_NX2_SVC_TRACE=1 to log every SVC with its arguments and results.
- static const bool svc_trace = std::getenv("EDEN_NX2_SVC_TRACE") != nullptr;
+ // Set EDEN_NX2_SVC_TRACE=1 to log every SVC with its arguments and results, or
+ // EDEN_NX2_SVC_TRACE=<first>:<last> for presents first..last only (a frame-time profile).
+ static const std::pair<u64, u64> trace_range = [] {
+ const char* text = std::getenv("EDEN_NX2_SVC_TRACE");
+ if (text == nullptr) return std::pair<u64, u64>{1, 0};
+ const char* colon = std::strchr(text, ':');
+ if (colon == nullptr) return std::pair<u64, u64>{0, ~u64{}};
+ return std::pair<u64, u64>{std::strtoull(text, nullptr, 10), std::strtoull(colon + 1, nullptr, 10)};
+ }();
+ const u64 presents = Core::g_nx2_present_count.load(std::memory_order_relaxed);
+ const bool svc_trace = presents >= trace_range.first && presents <= trace_range.second;
if (svc_trace) {
- LOG_INFO(Kernel_SVC, "svc {:#04x} in [0]={:#x} [1]={:#x} [2]={:#x} [3]={:#x}", imm,
+ LOG_INFO(Kernel_SVC, "svc {:#04x} in t{} [0]={:#x} [1]={:#x} [2]={:#x} [3]={:#x}", imm,
+ GetCurrentThread(kernel).GetThreadId(),
GetArg64(args, 0), GetArg64(args, 1), GetArg64(args, 2), GetArg64(args, 3));
}
if (imm == 0x80) {
@@ -2546,8 +2557,9 @@ void Call(Core::System& system, u32 imm) {
Call32(system, imm, args);
}
if (svc_trace) {
- LOG_INFO(Kernel_SVC, "svc {:#04x} out [0]={:#x} [1]={:#x} [2]={:#x}", imm,
- GetArg64(args, 0), GetArg64(args, 1), GetArg64(args, 2));
+ LOG_INFO(Kernel_SVC, "svc {:#04x} out t{} [0]={:#x} [1]={:#x} [2]={:#x}", imm,
+ GetCurrentThread(kernel).GetThreadId(), GetArg64(args, 0), GetArg64(args, 1),
+ GetArg64(args, 2));
}
//kernel.ExitSVCProfile();
kernel.CurrentPhysicalCore().LoadSvcArguments(process, args);
diff --git a/src/video_core/macro.cpp b/src/video_core/macro.cpp
index 59396cb58b..d70cfefd2f 100644
--- a/src/video_core/macro.cpp
+++ b/src/video_core/macro.cpp
@@ -4,6 +4,7 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include <array>
#include <cstdlib>
#include <unordered_set>
#include <cstring>
@@ -1360,8 +1361,15 @@ void MacroEngine::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, u
[&](u32 address, u32 value) {
if (address >= 0xe00) return false;
if (address == 0x556) {
- static u32 enable_logs{};
- if (enable_logs++ < 400) LOG_INFO(HW_GPU, "Native MME start={:#x} render enable mode={}", method, value);
+ // SET_RENDER_ENABLE_OVERRIDE: 0 use the render-enable condition, 1 always,
+ // 2 never. Log a histogram every 20000 macro writes (pop-in diagnostics).
+ static std::array<u64, 4> modes{};
+ static u64 enable_writes{};
+ ++modes[value & 3];
+ if (++enable_writes % 20000 == 0) {
+ LOG_INFO(HW_GPU, "Native MME render enable modes: use {} always {} never {} other {}",
+ modes[0], modes[1], modes[2], modes[3]);
+ }
}
maxwell3d.CallMethod(system, address, value, true);
// Turing MME memory reads (methods 0x550 SET_MME_MEM_ADDRESS_A/B, 0x558 data RAM
@@ -1374,6 +1382,17 @@ void MacroEngine::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, u
maxwell3d.GetRegisterValue(MemAddressB);
auto& memory = maxwell3d.GetMemoryManager();
const u32 ram = maxwell3d.GetRegisterValue(DataRamAddress);
+ // The words may have just been written by the GPU (e.g. indirect draw arguments
+ // from a culling dispatch) and still be only in host buffers: flush them first,
+ // or the macro draws with an older frame's arguments (objects pop in and out).
+ const size_t read_bytes = size_t{std::min<u32>(value, 0x1000)} * 4;
+ if (memory.IsMemoryDirty(source, read_bytes, VideoCommon::CacheType::BufferCache)) {
+ memory.FlushRegion(source, read_bytes, VideoCommon::CacheType::BufferCache);
+ static u64 flushes{};
+ if (++flushes % 2000 == 1) {
+ LOG_INFO(HW_GPU, "Native MME DMA read flushed GPU-written memory ({} times)", flushes);
+ }
+ }
static u32 dma_logs{};
if (dma_logs < 400) {
++dma_logs;
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index f55da5fd61..f44916a2e3 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -172,6 +172,9 @@ struct SdlState {
};
extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
+ // SDL calls SDL_AppIterate back to back by default, which kept a host core at 100% polling
+ // input; 240 Hz is plenty for input and the key script.
+ SDL_SetHint(SDL_HINT_MAIN_CALLBACK_RATE, "240");
SdlState* state = new SdlState();
*appstate = state;