aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-28 07:47:45 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-28 07:47:45 -0400
commit32d876ded1aefc52f839d65570e3e3c43ec04415 (patch)
tree9030b5090a857d943b5b0341c499af7184abb192
parent598bb7750c71ea1378724ddfe9418f27330fa279 (diff)
nx2: read debug switches once; key scripts from a file
Several debug switches were read with std::getenv on every macro call, QMD, semaphore write or draw. The UCRT getenv locks and scans the environment, thousands of times a frame; a long EDEN_NX2_KEY_SCRIPT made title 4 crawl at 2 fps. Each is now read once. EDEN_NX2_KEY_SCRIPT=@<file> reads the script from a file. gdbrsp.py gains 'hits' (log registers, frame chain and object words at breakpoints, following the stopped thread).
-rw-r--r--src/video_core/engines/kepler_compute.cpp11
-rw-r--r--src/video_core/macro.cpp6
-rw-r--r--src/video_core/native_query_trace.h3
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp8
-rw-r--r--src/yuzu_cmd/yuzu.cpp10
-rw-r--r--tools/nx2/gdbrsp.py43
6 files changed, 70 insertions, 11 deletions
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index dc3f9b25b6..4954fe613a 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -134,7 +134,8 @@ bool KeplerCompute::ProcessNativeMethod(Core::System& system, u32 method, u32 ar
});
if (!result.success) {
native_macro->native_execution_failed = true;
- if (std::getenv("EDEN_NATIVE_DRAW_DIAGNOSTICS")) {
+ if (static const bool diagnostics = std::getenv("EDEN_NATIVE_DRAW_DIAGNOSTICS") != nullptr;
+ diagnostics) {
std::ofstream dump("native-compute-mme.bin", std::ios::binary);
dump.write(reinterpret_cast<const char*>(native_macro->native_code.data()), sizeof(native_macro->native_code));
}
@@ -162,7 +163,8 @@ bool KeplerCompute::ProcessNativeMethod(Core::System& system, u32 method, u32 ar
LOG_DEBUG(HW_GPU, "Native inline QMD: address={:#x}, program={:#x}, control={:#x}, grid={}/{}/{}",
address, program, control, native_registers[0xc8 + 12],
native_registers[0xc8 + 13] & 0xffff, native_registers[0xc8 + 14] & 0xffff);
- if (std::getenv("EDEN_NATIVE_DRAW_DIAGNOSTICS")) {
+ if (static const bool diagnostics = std::getenv("EDEN_NATIVE_DRAW_DIAGNOSTICS") != nullptr;
+ diagnostics) {
std::ofstream dump("native-inline-qmd.bin", std::ios::binary);
dump.write(reinterpret_cast<const char*>(native_registers.data() + 0xc8), 256);
std::ofstream registers("native-inline-compute-registers.bin", std::ios::binary);
@@ -203,7 +205,8 @@ bool KeplerCompute::ProcessNativeMethod(Core::System& system, u32 method, u32 ar
memory_manager.GpuToCpuAddress(qmd_address).value_or(0), method, argument, 0);
if (method == 0xae || (method == 0xaf && !(argument & 2)) ||
(method == 0xb0 && ((argument & 15) == 0 || (argument & 15) == 1))) return true;
- if ((argument & 15) == 12 && std::getenv("EDEN_NATIVE_DRAW_DIAGNOSTICS")) {
+ static const bool qmd_diagnostics = std::getenv("EDEN_NATIVE_DRAW_DIAGNOSTICS") != nullptr;
+ if ((argument & 15) == 12 && qmd_diagnostics) {
static u32 captured{};
if (captured++ == 0 && memory_manager.IsFullyMappedRange(qmd_address, 256)) {
std::array<u32, 64> descriptor{};
@@ -288,7 +291,7 @@ bool KeplerCompute::ProcessNativeMethod(Core::System& system, u32 method, u32 ar
if (memory_manager.IsFullyMappedRange(qmd_address, sizeof(qmd))) {
memory_manager.ReadBlockUnsafe(qmd_address, qmd.data(), sizeof(qmd));
}
- if (std::getenv("EDEN_NATIVE_DRAW_DIAGNOSTICS") && memory_manager.IsFullyMappedRange(qmd_address, sizeof(qmd))) {
+ if (qmd_diagnostics && memory_manager.IsFullyMappedRange(qmd_address, sizeof(qmd))) {
std::ofstream dump("native-qmd.bin", std::ios::binary);
dump.write(reinterpret_cast<const char*>(qmd.data()), sizeof(qmd));
std::ofstream macros("native-dispatch-mme.bin", std::ios::binary);
diff --git a/src/video_core/macro.cpp b/src/video_core/macro.cpp
index 06c649260d..e6279deab4 100644
--- a/src/video_core/macro.cpp
+++ b/src/video_core/macro.cpp
@@ -1346,7 +1346,8 @@ void MacroEngine::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, u
maxwell3d.RefreshParameters();
const auto context = reinterpret_cast<std::uintptr_t>(this);
static std::unordered_set<std::uintptr_t> captured_contexts;
- if (std::getenv("EDEN_NATIVE_FRAME_CAPTURE") && captured_contexts.insert(context).second) {
+ static const bool capture = std::getenv("EDEN_NATIVE_FRAME_CAPTURE") != nullptr;
+ if (capture && captured_contexts.insert(context).second) {
std::ofstream dump("native-graphics-mme-" + std::to_string(context) + ".bin", std::ios::binary);
dump.write(reinterpret_cast<const char*>(native_code.data()), sizeof(native_code));
}
@@ -1393,7 +1394,8 @@ void MacroEngine::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, u
});
if (!result.success) {
native_execution_failed = true;
- if (std::getenv("EDEN_NATIVE_DRAW_DIAGNOSTICS")) {
+ if (static const bool diagnostics = std::getenv("EDEN_NATIVE_DRAW_DIAGNOSTICS") != nullptr;
+ diagnostics) {
std::ofstream dump("native-mme-" + std::to_string(method) + ".bin", std::ios::binary);
dump.write(reinterpret_cast<const char*>(native_code.data()), sizeof(native_code));
}
diff --git a/src/video_core/native_query_trace.h b/src/video_core/native_query_trace.h
index eae258dc04..036c3de936 100644
--- a/src/video_core/native_query_trace.h
+++ b/src/video_core/native_query_trace.h
@@ -30,7 +30,8 @@ inline State& GetState() {
// 10=DMA destination (value=source, flags=line bytes), 11=inline write.
// Keep this opt-in and bounded.
inline void TraceNativeQuery(u32 event, u64 asid, u64 gpu, u64 cpu, u32 type, u64 value, u32 flags) {
- if (!std::getenv("EDEN_NATIVE_DRAW_DIAGNOSTICS")) return;
+ static const bool diagnostics = std::getenv("EDEN_NATIVE_DRAW_DIAGNOSTICS") != nullptr;
+ if (!diagnostics) return;
auto& state = NativeQueryDiagnostics::GetState();
const std::scoped_lock lock{state.mutex};
if (state.records++ < 1000000 || event == 4 || event == 5) {
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 1b0493c4b5..609ac12fb4 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -288,7 +288,10 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
submitted = true;
static u32 native_capture_count{};
if (maxwell3d->GetNativeMacroEngine().native_turing &&
- std::getenv("EDEN_NATIVE_DRAW_CAPTURE") && native_capture_count < 16) {
+ native_capture_count < 16 && [] {
+ static const bool capture = std::getenv("EDEN_NATIVE_DRAW_CAPTURE") != nullptr;
+ return capture;
+ }()) {
const u32 capture = native_capture_count++;
const auto prefix = fmt::format("native-draw-{}", capture);
const auto dump = [&](const std::string& name, GPUVAddr address, size_t size) {
@@ -1252,7 +1255,8 @@ std::optional<FramebufferTextureInfo> RasterizerVulkan::AccelerateDisplay(
const auto [image_view, scaled] =
texture_cache.TryFindFramebufferImageView(config, framebuffer_addr);
static u32 display_trace_count{};
- if (std::getenv("EDEN_NATIVE_FRAME_CAPTURE") && display_trace_count++ < 64) {
+ static const bool frame_capture = std::getenv("EDEN_NATIVE_FRAME_CAPTURE") != nullptr;
+ if (frame_capture && display_trace_count++ < 64) {
LOG_INFO(HW_GPU, "Native display source: address={:#x}, offset={:#x}, dimensions={}x{}, stride={}, accelerated={}, mask={:#x}",
framebuffer_addr, config.offset, config.width, config.height, pixel_stride,
image_view != nullptr, config.layer_stack_mask);
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 96146abec3..f55da5fd61 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -4,6 +4,8 @@
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include <iterator>
+#include <fstream>
#include <vector>
#include <cstdlib>
#include <chrono>
@@ -331,7 +333,7 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
state->system.InitializeDebugger();
return SDL_APP_CONTINUE;
}
-// EDEN_NX2_KEY_SCRIPT="seconds:code,seconds:-code,...": press (code) or release (-code) keyboard
+// EDEN_NX2_KEY_SCRIPT="seconds:code,seconds:-code,..." (or "@<file>"): press (code) or release (-code) keyboard
// keys, by SDL scancode, that many seconds after start. Lets hidden test runs drive menus.
double Nx2KeyClockSeconds(); // emu_window_sdl3.cpp
@@ -344,7 +346,13 @@ static void RunKeyScript(InputCommon::InputSubsystem& input_subsystem) {
static const std::vector<KeyEvent> script = [] {
std::vector<KeyEvent> events;
if (const char* text = std::getenv("EDEN_NX2_KEY_SCRIPT")) {
+ // "@<file>" reads the list from a file: long scripts in the environment slow down
+ // every getenv in the process.
std::string list{text};
+ if (!list.empty() && list[0] == '@') {
+ std::ifstream file{list.substr(1)};
+ list.assign(std::istreambuf_iterator<char>{file}, {});
+ }
size_t start = 0;
while (start < list.size()) {
const size_t end = std::min(list.find(',', start), list.size());
diff --git a/tools/nx2/gdbrsp.py b/tools/nx2/gdbrsp.py
index 51968254fa..f7e70c8fbe 100644
--- a/tools/nx2/gdbrsp.py
+++ b/tools/nx2/gdbrsp.py
@@ -15,7 +15,7 @@ class Rsp:
import time
for _ in range(240): # wait up to ~2 min for the stub to listen
try:
- self.s = socket.create_connection(("127.0.0.1", port), timeout=600)
+ self.s = socket.create_connection(("127.0.0.1", port), timeout=3600)
break
except OSError:
time.sleep(0.5)
@@ -139,6 +139,47 @@ def main():
prev = pc
if pc == stop_at:
break
+ elif op == "hits":
+ # hits <max> <addr>...: break at the addresses and, on each hit, print the registers,
+ # the frame-pointer chain and the 0x40 bytes at x0, then step past and continue.
+ # Stops after <max> hits or when the target stops anywhere else.
+ limit, addrs = int(parts[1]), [int(a, 16) for a in parts[2:]]
+ for a in addrs:
+ rsp.cmd(f"Z0,{a:x},4")
+ for hit in range(limit):
+ reason = rsp.cmd("c")
+ # Several threads can stop at once: read and step the one this reply names.
+ tid = re.search(r"thread:([0-9a-f]+);", reason)
+ if tid:
+ rsp.cmd(f"Hg{tid.group(1)}")
+ r = rsp.regs()
+ pc = r.get("pc", 0)
+ stamp = time.strftime("%H:%M:%S")
+ label = (f"hit {hit}" if pc in addrs else f"stopped elsewhere ({reason})") + (f" thread {tid.group(1)}" if tid else "")
+ print(f"[{stamp}] {label} pc={pc:#x} " + " ".join(
+ f"{k}={r.get(k, 0):#x}" for k in ("x0", "x1", "x2", "x19", "x20", "x21", "x30", "sp")), flush=True)
+ obj = rsp.mem(r.get("x0", 0), 0x40)
+ if len(obj) == 0x40:
+ print(" *x0: " + " ".join(f"{w:016x}" for w in struct.unpack("<8Q", obj)), flush=True)
+ fp = r.get("x29", 0)
+ chain = []
+ for _ in range(16):
+ d = rsp.mem(fp, 16)
+ if len(d) < 16:
+ break
+ nfp, ret = struct.unpack("<QQ", d)
+ chain.append(f"{ret & 0xFFFFFFFFFFFF:x}")
+ if nfp <= fp:
+ break
+ fp = nfp
+ print(" bt " + " ".join(chain), flush=True)
+ if pc not in addrs:
+ break
+ rsp.cmd(f"z0,{pc:x},4")
+ rsp.cmd("s")
+ rsp.cmd(f"Z0,{pc:x},4")
+ for a in addrs:
+ rsp.cmd(f"z0,{a:x},4")
elif op == "continue":
print("stop:", rsp.cmd("c"))
elif op == "continue_async":