Skip to content

Ocarina of Time on RDNA 4 and Zen 5: 224 fps VRR cap, AMD FSR 4 and frame generation, pacing, loads, HDR, 21:9 (draft, tracking) - #831

Draft
JuliusBairaktaris wants to merge 351 commits into
zortos293:nx2from
JuliusBairaktaris:oot-rdna4-zen5
Draft

JuliusBairaktaris wants to merge 351 commits into
zortos293:nx2from
JuliusBairaktaris:oot-rdna4-zen5

Conversation

@JuliusBairaktaris

@JuliusBairaktaris JuliusBairaktaris commented Nov 8, 2026 •

Copy link
Copy Markdown

Draft, opened early to track progress in the open. The branch is the consolidated build (nx2 + every finished item, 240 commits; native work in progress sits behind runtime switches that default off). The checklist below is the whole plan; ticked items are committed. Commits titled EXPERIMENT (do not merge) hold A/B switches and are dropped before this leaves draft.

Goal

Ocarina of Time (0400486024DEC000) on an RX 9070 XT (RDNA 4, Adrenalin 26.10.2) and a Ryzen 7 9800X3D (Zen 5), on a 3440x1440 240 Hz OLED HDR display with VRR:

  • unlocked, capped at floor(R − R²/3600) = 224 fps so presents stay inside the VRR window, with the emulator able to render 240 fps natively uncapped (4.17 ms per frame for every critical thread) so the cap has headroom for flat pacing; frame generation optional on top
  • AMD FSR 4 upscaling / Native AA, AMD frame generation through AMD's frame-generation swapchain (driver frame pacing), Anti-Lag 2, lowest input latency
  • everything that can run natively runs natively: GPU work as native GPU work (bindless textures, GPU page table, GPU-driven commands), guest code translated once into optimised native code
  • full use of the hardware: GPU ≥ 95% busy when uncapped, the emulator's front/back GPU threads and guest cores not waiting on each other
  • native 21:9 with correct field of view, HUD and cutscenes; 16:9 movies and 2D screens pillarboxed, never stretched
  • no VRR flicker on the OLED: the refresh interval stays steady through gameplay, loads, menus and movies
  • correct HDR and SDR colours, no rendering artefacts, flat frame times, no shader or traversal stutter, short loads, movies at their own speed
  • one-hour sessions without crash, device loss or memory growth; saves load back

Every change is benched on Windows with the game; the before/after table below is updated as items land.

Numbers so far

Updated 2026-11-08 23:00. Field (balcony above Kokiri village, camera pan) and walk (ladder, cutscene, village route) scenes at 16:9, one run per column. base = this branch's nx2 rebase before the native work (allr); native (cons2) = the consolidated build at 994ba77 (GPU page table not yet active, bindless off); native (cons7) = the consolidated build at 6ce1332a03 with the GPU page table, the front-thread wait cuts (QMD view, GPU-kept compute list words, no drain behind narrowed semaphore reads), shader objects and the dynamic rasterization state, indirect-branch cache off. The base and cons2 columns were measured while the machine compiled (load 67–100%); cons7 ran with every compile paused, and the base build gets the same paused run in the final sweep.

metric base native (cons2) native (cons7)
field real fps 45.5 44.6 62.4
walk real fps 42.4 43.5 61.7
GPU busy %, field 69 69 80
HostTiming busy %, field 56 69 67
GPUBack busy %, field 54 58 62
boot s 9.6 12.6 –
save load s 13.5 14.9 –

With the steady VRR presenter on, PresentMon sees the re-presented frames at the 224 Hz cap, so its lows and spikes describe the presentation, not the game frames; those cells are left out.

Where the frame goes now (cons7, field 62 fps = 16 ms/frame): the GPU is 80% busy (~13 ms/frame), and the guest CPU core 0, HostTiming and the GPU back thread each take ~10–11 ms/frame. Every one of them has to come down to 4.17 ms for 240 fps; the GPU per-pass profile and the shared translation cache are the next items.

Plan and progress

Native architecture (the main work now)

Everything that can run natively runs natively; items the new designs supersede are dropped instead of finished.

  • Front-thread waits on the GPU (measured limiter): guest semaphore waits resolved on the GPU timeline, GPU-written command lists without front waits (video_core: decouple the split GPU threads — Ocarina of Time holds 60 fps on AMD, plus fixes for three older races #805's design, draw/dispatch indirect), fewer submits per frame
  • Native GPU backend — done: texture/sampler pool mirror; GPU page table for storage, global memory and constant buffers (it never ran before: IsBufferDeviceAddressSupported() stayed false on Vulkan 1.2+, fixed); draw-input watch; exact barriers; per-draw state as dynamic-state deltas, with a shader-object path to A/B against pipelines. Open: the descriptor heap (bindless) fails device setup on 26.10.2 (VK_ERROR_INITIALIZATION_FAILED, being root-caused), then removing the binding memo. Dropped on measurement: GPU-generated draw runs (draw recording is 3% of the frame budget). Design: notes/native-gpu-backend.md
  • Native guest code — done: guest calls/returns as host call/ret (−13.5% on a 544-site call mix), per-call-site indirect-branch cache (−30% on virtual calls; a boot crash it caused is fixed and Xbyak errors now report the guest PC), core-independent code, three FP-state correctness fixes from a new instruction-level compare mode; a whole-game compile test and Lichii-config fuzzing gate every JIT change. In progress: one translation cache shared by the six cores, then persisted per game build (removes ~9–11 s of compile per save load)
  • Native OS layer: idle cores, the address arbiter and HLE service threads wait natively (no spin loops; a service round trip is ~0.1 µs awake, ~8 µs from sleep). Dropped with evidence: direct service-call dispatch (host-core services can block and would deadlock the calling core) and guest threads as host threads (guest-visible scheduling rules; fiber switches are 0.5% of a guest core)
  • Load-time work measured: game file read through a memory mapping; a texture transcode cache and pipeline warm-up are not needed (loads wait 0 ms on ASTC transcodes; the disk pipeline cache with driver binaries is already loaded at boot before the save is picked)
  • Superseded and dropped: draw replay memo, binding memo, uniform reuse/compare, buffer_cache: upload each byte of native uniform banks once a frame #802 per-frame uniform banks, storage binding memos, back-side DrawRun folding, a second recording thread, video_core: decouple the split GPU threads — Ocarina of Time holds 60 fps on AMD, plus fixes for three older races #805's QMD view/list-indirect port, block extension / RSB / indirect-branch cache micro-work in the JIT

Bench and tooling (outside the repo)

  • Windows bench harness: debug-server driven scenes, PresentMon frame times (avg, 1%, 0.1%), GPU busy, per-thread CPU busy, real fps, live log watch and hang watchdog, movie-speed check, flicker / shadow-flicker / tile-artefact metrics
  • Bench saves: Kokiri balcony, plus other locations
  • Symbolized profiling builds (PDB + sampled thread stacks)
  • Walk + camera route with per-spike stutter attribution; several scenes in one game session; presentation-side checks (white/black frames from the real desktop output); runs refused when a GPU profiler layer is attached
  • In-game performance overlay (real fps, displayed fps, 1% low, front/back/GPU ms) for the bench builds
  • Idle-machine A/B campaign (≥ 3 runs per side) replacing the loaded-machine numbers above
  • ThinLTO bench builds matching release codegen

Loading, stutter

CPU / real fps

GPU / RDNA 4

  • Render-pass-end barrier only where a hazard exists; transform-feedback barrier only after passes that ran transform feedback (~140 fewer barriers per frame)
  • Query sync: one fill, one barrier and one resolve per sync on AMD
  • Task/mesh pipelines no longer treated as tessellation
  • The stalling AMD compute shader is rebuilt optimized in the background and swapped in
  • EDEN_NX2_GPU_MARKERS: named passes/draws for RGP captures
  • Memory priority + pageable device-local memory
  • ASTC re-compressed to BC7 on the GPU (DirectXTex encoder port) instead of BC3
  • float16 on AMD's driver from 26.10.2 (bit-exact check on the 9070 XT); HMUL2/HFMA2 .FMZ with half operands translate
  • Uniform banks bound only as far as their mapping reaches (reads past the mapping)
  • Barrier before a guest compute dispatch only on a real hazard (was a full GPU drain ~300×/frame); render-pass hazard check by image-set generation
  • Hierarchical depth (HiZ/HiS) back on: depth attachments out of GENERAL + TRANSFER_DST (A/B switch in the build)
  • Static vertex input on AMD (dynamic vertex input forces the driver's generic vertex fetch); AMD's fast-linked pipelines relinked optimized in the background
  • "Patch topology not used with tessellation" warning at boot: fix its cause
  • GPU time per frame: AA network fully replaced, RGP-guided pass cuts, async compute where it pays
  • Not done on purpose: host image copy (moves tiling onto the CPU, which is the limit).

Present, pacing, latency

  • Qt fullscreen window exactly screen-sized (independent flip)
  • VRR present policy: IMMEDIATE + 224 cap with VRR, FIFO_LATEST_READY without; present_wait2 pacing; "Variable Refresh Rate Display" setting
  • Built-in FPS unlock and Switch 2 frame-rate limit up to 240 Hz (port of Ocarina: FPS Unlock built-in and Switch 2 frame rate limit slider #655)
  • Guest frames started from the host display; Anti-Lag 2 (VK_AMD_anti_lag) at that point
  • AMD FSR frame generation through AMD's frame-generation swapchain; driver-assisted frame pacing (HW flip) prerequisites checked and logged
  • Input published as soon as it changes; keys read on SDL's raw input thread; input-to-present latency measured
  • Gamepads through Microsoft GameInput's reading callbacks when its runtime is installed, SDL otherwise
  • VRR flicker: panel VRR range read from EDID, refresh-interval steadiness metrics, steady-cadence presenter (re-presents the last image while the guest has no new frame); measured in the sweep
  • Input-to-present latency A/B and frame-generation pacing check on the PC
  • AMD's hardware-flip FSR 4 frame-generation swapchain on RDNA 4 (driver 26.10.1 or newer, enabled automatically): the FG context reports Mode 2 (HW flip) and PresentMon frame times stay flat (a thick frame-time band means the software pacing path); Lichii logs the FG mode and the Windows hardware-accelerated GPU scheduling (HAGS) state at startup, since HW flip needs HAGS on; no driver VSync (Lichii presents IMMEDIATE under the 224 VRR cap)

Image quality

Ultrawide 21:9

  • The game really renders 3440x1440 (it stayed 2560x1440 and was stretched before): picture/scene size sites from Hookshot
  • Camera, HUD, menus, movies and speech bubbles at 21:9 inside the game, ported from Ivan0xFF's oot-mod (MIT)
  • Checked in game: native 3440x1440 render with Hor+ field of view, HUD and menus 16:9 in the centre, pause menu opens without a crash
  • HUD at the real screen edges (hearts and magic left, item buttons right, menus centred) by rewriting the HUD layout's edge anchors; Z-target reticle and markers corrected at all three projection sites; texture heap grown for the wider picture. The game has no cutscene letterbox
  • 21:9 checked in game on the consolidated build (HUD edges, Z-targeting, minimap/area name/save icon still centred)

Movies

  • AV1 on the GPU (Vulkan Video, D3D12VA, D3D11VA, then libdav1d) with an FFmpeg package carrying four FFmpeg master fixes (the Vulkan out-of-memory was FFmpeg 9.0.2 requesting host-copyable video images); any decoder error moves on to the next path
  • Movies play at their own speed at high frame rates (nx2's nx2: keep refused decoder packets and stop the movie clock running ahead (#659 follow-up) #809 clock plus hold-before-write); presents paced at a multiple of the movie rate; speed logged every 10 s
  • RGBA movie output interpolates chroma
  • Movie frames self-checked (decoded vs written vs guest memory) and the submitted stream can be dumped
  • Stale 128×128 tiles over the title movie and the load menu: still present on the native build (51 tiles found by the new detector); root cause in progress (lead: uploads skipped for images whose pages are all marked stale)
  • Banding in the intro movie: root cause (the movie path stays a plain copy, no filter)
  • FFmpeg from a pinned public master build (BtbN) that already carries the four fixes, instead of an own package

Correctness and stability

  • nx2:gsv 903 and prepo 10500 handled; texture headers with format 0 treated as no texture; no false unmapped-read reports for sparse regions
  • robustness2 / external_memory_host detection; CPU Boost no longer speeds the game up (port of core_timing: don't scale the guest counter for CPU Boost/Overclock in Switch 2 titles (fixes #317) #785); docked by default for Switch 2 titles
  • CPMUtil: CI packages honour <package>_CUSTOM_DIR; sirit builds with libc++ 23
  • White frames and device losses traced to a GPU profiler layer left attached to every Vulkan process (not a code bug); the bench now refuses such runs
  • One-hour session without crash, device loss or memory growth; saves load back

Merge readiness

  • Linux GCC 13 -DENABLE_WERROR=ON build + ctest (first full run: two build fixes in, 4 of 1149 tests failing and being fixed), tools/clang-format.sh clean
  • EXPERIMENT commits dropped, winners kept unconditionally
  • Final before/after table from idle-machine runs

AMD driver bug found on the way

  • AMD-Gfx-Drivers#126: fast-linked graphics pipeline libraries write the colour of demoted invocations when derivatives follow the demote (standalone repro, validation clean). The workaround links only the affected pipelines optimized.

Credits

Ultrawide techniques from Ivan0xFF's oot-mod (MIT), Hookshot and NX2navi. Ported with their authors kept: #180, #243, #313, #321, #337, #354, #408, #559, #573, #579, #655, #678, #695, #717, #769 (opt-in part), #784, #785.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@MorpheusCode117

Copy link
Copy Markdown

Must be merged quick

@JuliusBairaktaris

JuliusBairaktaris commented Nov 8, 2026 •

Copy link
Copy Markdown
Author

@zortos293 is it OK if Lichii reads gamepads through Microsoft's GameInput API? It delivers each new controller reading through a callback, so input no longer waits for SDL's 1 ms pump.

Microsoft's GameInput header (API v3) is MIT-licensed, so it can go in-tree with its notice. The runtime is Microsoft's separately installed redistributable: Lichii wouldn't ship it, only load it when it's present, and keep SDL everywhere else. It's one commit in this PR, easy to drop if you'd rather stay with SDL only.

JuliusBairaktaris and others added 19 commits November 8, 2026 22:00
…g slot

Trapped writes from threads that are not guest cores used guest core 5's slot
(NUM_CPU_CORES - 1), so core 5 took sys_core_guard on every trapped write and its write path
could share a scratch buffer with them. Those threads now get their own slot after the cores'
(one more GPUDirtyMemoryManager in System, one more write area and scratch buffer in Memory),
and only that slot takes the guard.

The PR's EDEN_NX2_SYS_SLOT kill switch (default on) is gone. The PR gives only the aggregate
gain of its kernel group, no number for this change alone. zortos293#717 also adds a further dirty
memory manager for its buffer cache write handoff; that belongs with the buffer cache changes.

Ported from zortos293#717 by `stephenjayakar`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Read and write traps on GPU-cached pages now reuse the host pointer the trapping access already
looked up instead of walking the page table again. A core's trapped writes remember the device
page of their last host page while the device mappings stay the same (the new
DeviceMemoryManager::MappingGeneration), so writes to the page the core already collects into
skip the device address lookup. A core keeps its last four clean or flushed read areas instead
of one.

The PR's EDEN_NX2_TRAP_FASTPATH kill switch (default on) is gone. The PR gives only the
aggregate gain of its kernel group, no number for this change alone.

Ported from zortos293#717 by `stephenjayakar`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GetParentReference computed the member offset (OffsetOfCalculator) on every call, and the
kernel's intrusive trees call it for each node they visit, under the scheduler lock. At run
time the offset is now computed once per member and kept in a function-local static; constant
evaluation still uses the calculator.

The PR's EDEN_NX2_OFFSETOF_CACHE kill switch (default on) is gone, and with it the case where
the switch was read before its dynamic initialization. The PR gives only the aggregate gain of
its kernel group, no number for this change alone.

Ported from zortos293#717 by `stephenjayakar`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The GPU caches' SpinRecursiveMutex slept on std::atomic::wait once its spin ran out. On Windows
that goes through a winpthreads condition variable: the unlocking GPU thread blocked on a shared
mutex and woke every sleeper of the slot. Sleepers now use Common::Nx2AddressWait
(WaitOnAddress / WakeByAddressSingle on Windows), and try_lock reads the state before trying
the compare-exchange.

The PR's EDEN_NX2_CACHE_LOCK_WAIT kill switch (default on) is gone. The PR gives only the
aggregate gain, no number for this change alone. zortos293#717's owner hook in this class
(SetOwnerHook, for EDEN_NX2_WRITE_HANDOFF) belongs with the buffer cache write handoff and is
not part of this commit.

Ported from zortos293#717 by `stephenjayakar`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…x86-64

x86-64 is TSO, so LDAPR/LDAR/LDAXR need no locked read-modify-write and a DMB that only
orders loads (xxLD) or only stores (xxST) orders nothing the host does not already order.
Ordered stores keep XCHG, so STLR followed by LDAR stays ordered. Ocarina of Time has
~68k LDAPRB sites (static init guards), each a LOCK XADD that bounced the guard's cache
line between the cores. Two new UserConfig options (tso_ordered_loads,
elide_ld_st_barriers), set by ArmDynarmic64 for every CPU accuracy but Paranoid. The PR's
EDEN_NX2_TSO_LOADS kill switch is dropped.

Claimed effect: part of zortos293#717's JIT work; the PR gives only the aggregate (Kokiri Forest
1440p, Ryzen 7 3700X + RTX 3080: 34.8 -> 50.9 fps for the whole PR).

Ported from zortos293#717 by `stephenjayakar`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The JIT spilled ~9% of its instructions to stack slots in Ocarina of Time's hot code while
callee-saved GPRs sat empty. With spill_to_free_gpr an evicted GPR value goes to a free
callee-saved GPR (it survives host calls) before a stack slot; with
scratch_copy_to_free_reg an operand that an instruction overwrites but that is still
needed is copied to a free register instead of evicting the original. Both are new
UserConfig options, set by ArmDynarmic64 for every CPU accuracy but Paranoid. The PR's
EDEN_NX2_JIT_SPILL_GPR and EDEN_NX2_JIT_SCRATCH_COPY kill switches are dropped.

Claimed effect: the PR gives only the aggregate for its JIT work.

Ported from zortos293#717 by `stephenjayakar`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each A64 block saved %rbp, pointed it at its stack layout and restored it: three
instructions per block that nothing in the JIT reads and that Windows unwinding does not
use. The new omit_block_frame UserConfig option leaves them out; ArmDynarmic64 sets it for
every CPU accuracy but Paranoid. The PR's EDEN_NX2_JIT_NO_FRAME kill switch is dropped.

Claimed effect: the PR gives only the aggregate for its JIT work.

Ported from zortos293#717 by `stephenjayakar`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The A64 fast dispatch table was a fixed direct-mapped array of 2^20 entries, 16 MiB per
guest core. fast_dispatch_table_bits sizes it at run time and
fast_dispatch_set_associative makes it 4-way set associative (the four ways share one
64-byte cache line; a miss inserts at way 0 and drops the oldest). ArmDynarmic64 uses 2^16
entries, 4-way: 1 MiB per core. The PR's EDEN_NX2_JIT_DISPATCH switch (sizes 8-24 bits or
the old table) is dropped; its default is what ships.

Claimed effect: the PR gives only the aggregate for its JIT work.

Ported from zortos293#717 by `stephenjayakar`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A64JitState's return stack buffer grows from 8 to 32 entries and the new rsb_size
UserConfig option picks how many are used (power of two, 8 to 32; the default stays 8),
so returns from deeper call chains skip the dispatcher. ArmDynarmic64 uses 32 for every
CPU accuracy but Paranoid. The PR's EDEN_NX2_JIT_RSB32 kill switch is dropped.

Claimed effect: the PR gives only the aggregate for its JIT work.

Ported from zortos293#717 by `stephenjayakar`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An unlinked A64 block exit was an inline PC store plus return (22/23-byte patch sites, and
an inline exit after the halt check). With the new compact_block_exits UserConfig option
each target gets one shared exit stub, created while the first block that links to it is
emitted and kept until ClearCache; patch sites become 5/6-byte rel32 jumps to the target
block or to its stub. ArmDynarmic64 sets it for every CPU accuracy but Paranoid. The PR's
EDEN_NX2_JIT_COMPACT_EXITS kill switch is dropped.

Claimed effect: the PR gives only the aggregate for its JIT work.

Ported from zortos293#717 by `stephenjayakar`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every linked A64 block exit compared halt_reason before jumping on. A link to a higher guest
PC cannot close a loop on its own: every cycle of blocks contains a link to the same or a
lower start PC, or an indirect branch. With the new halt_check_on_back_edges UserConfig
option (without cycle counting only) forward links jump straight on, and links to the same
or a lower PC and the RSB/fast-dispatch handlers check for a halt instead, so every loop
still sees a halt request. ArmDynarmic64 sets it when it counts no cycles (multicore). The
PR's EDEN_NX2_JIT_BACKEDGE_HALT kill switch is dropped.

Claimed effect: the PR gives only the aggregate for its JIT work.

Ported from zortos293#717 by `stephenjayakar`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Memory fault and abort paths, atomic fallbacks and unlinked-exit stubs were emitted right
after each A64 block, between the hot blocks. With the new far_code UserConfig option the
last quarter of the code cache holds that cold code (EmitContext::far_deferred_emits,
ColdEmits()), so hot blocks sit densely. On Windows the far region has its own commit
front, with the same commit-and-retry loop as the main region (zortos293#310). It does nothing
where the code space needs W^X switching. ArmDynarmic64 sets it for every CPU accuracy but
Paranoid. The PR's EDEN_NX2_JIT_FAR_CODE kill switch is dropped.

The commit and far-code state is declared before BlockOfCode::constant_pool: the
ConstantPool constructor already calls EnsureMemoryCommitted, which reads it (zortos293#641's
a8b9923: with the old order the near region could be left uncommitted and JIT
creation crashed in Xbyak::CodeArray::db).

Claimed effect: the PR gives only the aggregate for its JIT work.

Ported from zortos293#717 by `stephenjayakar`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ing op

Every conditional branch, CSEL and CBZ/TBZ reloaded NZCV from the guest state and rebuilt
the host flags from it, even right after the Add/Sub that had just set them. With the new
reuse_host_flags UserConfig option (without cycle counting only) the emitters track while
the host flags still equal the stored NZCV (BlockOfCode::nzcv_live_pos, kept across the
movs of SetW/SetX and the %rbp restore) and conditions use them directly; a block's last
SetCheckBit leaves the bit in ZF for its CheckBit terminal. The guest NZCV is still
written. ArmDynarmic64 sets it when it counts no cycles. The PR's EDEN_NX2_JIT_FLAGS kill
switch is dropped.

Claimed effect: the PR gives only the aggregate for its JIT work.

Ported from zortos293#717 by `stephenjayakar`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SetW and ZeroExtendWordToLong always emitted `mov r32, r32`, also for values a 32-bit
instruction had just written (GetW, ReadMemory32, LeastSignificantWord, Add32, Sub32,
ConditionalSelect32), whose host register is already zero-extended. With the new
elide_redundant_zext UserConfig option those are used as they are. Adds
Argument::GetInst (zortos293#641's d5b8fe7). ArmDynarmic64 sets it for every CPU accuracy but
Paranoid. The PR's EDEN_NX2_JIT_ZEXT kill switch is dropped.

Claimed effect: the PR gives only the aggregate for its JIT work.

Ported from zortos293#717 by `stephenjayakar`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`ldr/str xN, [xM, #imm]` computed the address with a separate LEA (and often a register
copy) before the fastmem access. With the new fold_address_offsets UserConfig option
(unchecked 64-bit fastmem only, where the sum wraps exactly like the guest's), an
Add64(base, imm) whose only use is one fastmem load or store is not emitted and the access
adds the displacement in its addressing mode. Fault paths add the displacement before the
fallback and take it off again on return; callback and page-table paths compute the full
address. ArmDynarmic64 sets it for every CPU accuracy but Paranoid. The PR's
EDEN_NX2_JIT_FOLD_ADDR kill switch is dropped.

Claimed effect: the PR gives only the aggregate for its JIT work.

Ported from zortos293#717 by `stephenjayakar`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LDADD and the other LSE read-modify-writes, SWP and CAS were translated as an exclusive
load/store pair that ends the block. With the new native_lse_atomics UserConfig option
(fastmem exclusives with Unsafe_IgnoreGlobalMonitor only, x64 backend only) they become
new A64AtomicRmwMemory/A64AtomicCasMemory IR ops: LOCK XADD for LDADD, XCHG for SWP,
LOCK CMPXCHG for CAS and a CMPXCHG loop for the others, with a callback CAS-loop fallback
for fastmem faults and non-fastmem accesses (fallback stubs go to the far region). The
local monitor is still cleared, as the exclusive emulation did. ArmDynarmic64 sets it
where those conditions hold (every CPU accuracy but Paranoid). The PR's
EDEN_NX2_JIT_ATOMICS kill switch is dropped.

The callback path (no fastmem, or a fastmem fault: GPU-cached pages are protected in the
fastmem arena) is a loop of MemoryRead and MemoryWriteExclusive, which is a compare-and-swap
against the value read. A refused write is retried from a new read; it never counts as done
because a second read shows the old value again. The PR returned there ("unmapped or aborted
access"), but the word can change and change back between the refused write and that read: a
mutex another core locked and unlocked while the callbacks handled the GPU-cached page. The
CAS then reported success without writing, the guest thread went on owning a lock it never
took, and nn::os aborted at the unlock on finding another thread's handle in the word
(UnlockSlimMutex -> LeaveInternalCriticalSectionNoInline -> diag::Abort, a userspace panic
about 10 s into one of three Ocarina of Time boots). LDADD and the other read-modify-writes
lost updates the same way. Retrying stops only at a memory abort the frontend raised for the
access (UserConfig::memory_abort_halt_reasons, Lichii's DataAbort and PrefetchAbort), whose
abort check leaves the block before the result is used. The fallbacks live in
atomic_fallback.h; tests/core/dynarmic_atomic_fallback.cpp scripts the other core and fails
with the PR's loop (lock taken without the write, an addition lost).

Claimed effect: the PR gives only the aggregate for its JIT work.

Ported from zortos293#717 by `stephenjayakar`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A guest YIELD or WFE is a spin-wait hint; ExceptionRaised only logged it at trace level and
returned, so the host thread spun on at full speed. It now pauses twice (YIELD on an arm64
host), giving the SMT sibling, often a GPU thread, the core for a moment, as GetCNTPCT
already does for counter-polling loops. The PR's EDEN_NX2_HINT_PAUSE kill switch is
dropped.

Claimed effect: the PR gives only the aggregate (its kernel/JIT spin work as a whole).

Ported from zortos293#717 by `stephenjayakar`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ocarina of Time creates a Z-prepass occlusion query and a samples-passed query per object and
feeds them to conditional renders (macro 0xe2c), which make the split GPU front read query
results back. The built-in patch "Occlusion Off" (cLohan's community
OcclusionOff_ZPrePassAndSamplesQuery, module offsets = the IPS offsets - 0x100) turns both
render settings off where they are mirrored (`ldrb w8, [x0]` -> `mov w8, wzr`, six sites,
original bytes checked), so the game issues neither. Like the other built-in patches it
applies only to main build E0F3CCFAF50065AE4EA01EB5AEB13B1A45DA3348 of 0400486024DEC000 and
can be turned off in Add-Ons; the PR's EDEN_NX2_OOT_OCCLUSION_OFF switch is dropped.

Claimed effect: the PR gives only the aggregate (Kokiri Forest 1440p, 3700X + RTX 3080:
34.8 -> 50.9 fps for the whole PR); frame sheets matched build 116 there.

Ported from zortos293#717 by `stephenjayakar`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dictionaries

Ocarina of Time's asset streaming jobs decompress with the guest's
ZSTD_decompressMultiFrame (main+0xEB380); each of its three callers re-initialises a static
default DCtx right before the call and only uses the return value. The built-in patch
"Native zstd" turns those three bl into svc 0xF0-0xF2 (original bytes checked), and
svc_nx2_zstd.cpp decompresses on the host with libzstd: zstd1 and skippable frames only,
complete, with the guest's ZSTD_DDict copied into a host DDict of the same type and dictID
(cached, checked at its edges per call and in full once a second). Anything it does not
reproduce exactly (raw dict arguments, odd frames, unusual dictionaries, errors) runs the
game's own function, as the bl would have. The SVCs are handled only for program
0400486024DEC000 and the patch only applies to main build
E0F3CCFAF50065AE4EA01EB5AEB13B1A45DA3348; it can be turned off in Add-Ons.

The PR's EDEN_NX2_OOT_NATIVE_ZSTD (patch) and EDEN_NX2_OOT_ZSTD_DICT (dictionary calls)
switches are dropped, both on. The per-second counters line logs at debug level.

Claimed effect: the PR gives only the aggregate (wave 4 of zortos293#641, 39.4 -> 45.5 fps with the
other wave-4 changes).

Ported from zortos293#717 by `stephenjayakar`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
JuliusBairaktaris and others added 24 commits November 8, 2026 22:56
…vertex input A/B

Keeps dynamic vertex input on AMD's driver, as before. The first session on
each side builds its pipelines again (the key differs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ized relink A/B

Keeps AMD's fast-linked pipelines as they are, as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…et and the indirect-branch cache

For the integration A/B only: hcr = UserConfig::host_call_return, ibc =
UserConfig::indirect_branch_cache. Drop before the PR; the winner ships
unconditionally.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…IVE_UNIFORMS=0

Turn storage through the GPU page table and constant buffers by device address off, each on its
own, for the A/B of the test campaign. The winning side ships unconditionally.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…descriptor_heap

Nothing binds a descriptor heap yet (the heap object and heap-mode pipelines
are the next steps of the bindless texture path), so the device does not ask
for the extension unless EDEN_NX2_DESCRIPTOR_HEAP=1. Dropped once heap
pipelines use it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t code

Extends the EDEN_JIT_OFF switch for the integration A/B. Drop before the PR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and per changed state

For the A/B of the per-draw dynamic-state packet against the recording it
replaced (each changed state its own scheduler command, the same value
compare). The winning side ships unconditionally.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iour changes

- EDEN_NX2_EXP_HEAVY_DISPATCH=1: run the AA network's heavy dispatch while the output pass is
  replaced.
- EDEN_NX2_EXP_PRESENT_FILTERS=1: keep FXAA/SMAA and the scaling filter on the DLSS/FSR image.
- EDEN_NX2_EXP_START_RATE=console: the display starts frames no faster than the console's vsync
  rate even with the frame rate unlocked.
- EDEN_NX2_EXP_FG_HUDLESS=0: AMD frame generation gets no HUD-less image.

FSR 4 and frame generation follow their settings (oot_anti_aliasing, frame_gen, frame_gen_type),
Anti-Lag 2 the anti_lag setting, display-driven vsync EDEN_NX2_HOST_VSYNC=0, and the FSR 4
version pin EDEN_FFX_UPSCALER.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… objects

Graphics pipelines stay pipelines unless EDEN_NX2_SHADER_OBJECT=1, for the
pipelines vs VK_EXT_shader_object A/B on AMD. The winner ships
unconditionally.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s out for the sweep

A built-in patch record can name an environment variable that, set to 0, leaves it out:
- EDEN_OOT_UW_SIZE_TABLE=0: the size table, index and cap records. A/B whether Hookshot's picture
  and scene sites alone make the 3440x1440 frame.
- EDEN_OOT_UW_MARKERS=0: the two extra world-to-HUD marker sites, leaving Ivan0xFF's bubble site
  only.
- EDEN_OOT_UW_HUD_EDGES=0: the RomFS layer leaves the UI layouts alone, so the HUD stays centred
  16:9.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…server's switch command

The A/B switches of the OoT work are getenv reads cached in statics at first use, so every A/B
side needs its own launch (~35 s of boot and save load each). Common::Nx2Switches keeps them in
one registry instead: each switch starts from its environment variable, parsed exactly as its
read site parsed it (value[0] == '1', == "0", == "all", set at all, or the guest-CPU name list),
so a launch with the variable set behaves as before; a read is one relaxed atomic load.

The debug server's `switch` command {name, value} (null unsets) requests a value and waits until
it took effect; Latch applies requested values once a frame and logs
"switch NAME = VALUE (from -> to) at frame N". Without a name it lists the switches with their
value, state and launch state. It takes no tool lock (the registry is thread-safe), so status
polls continue while it waits. GuestCpuOff's cntpct and hostfn change emitted code and keep the
launch value (AtLaunch).

Tests: the parse rules, name lookup, request-then-latch, the launch value, and the request
parser of the command.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The cheap switches read Common::Nx2Switches instead of a static set at first use, so the debug
server's `switch` command flips them in a running game; the rasterizer's TickFrame latches
requested values at the frame boundary, so no flip lands inside per-frame work (an upload batch,
staged dynamic state). Each site was checked for state kept across frames:

- zortos293#805: EDEN_NX2_EXP_NO_PARK (per wait), NO_FENCE_QUIET (per fence release), FENCE_FLUSH_TWICE
  (per fence), COLD_ALWAYS (per hint), NO_EARLY_READ (per dispatch), NO_UPLOAD_BATCH (per
  upload; the frame-boundary latch keeps a batch from being split)
- EDEN_NX2_GUEST_CPU_OFF spin, idle, arb, svcwait (per call); callers pass the bit, not the name,
  so the spin hint path does no string search; cntpct and hostfn stay launch-only
- EDEN_NX2_EXP_DISPATCH_BARRIERS=all (per dispatch), EDEN_NX2_DYNSTATE_BATCH=0 (per draw; staged
  state is recorded within the draw), EDEN_NX2_PRESENT_CADENCE=0 (per present-thread loop),
  EDEN_NX2_TC_SKIP_STALE=0 (per image upload)
- FSR: EDEN_NX2_EXP_FG_HUDLESS=0 (per frame; hudless_ready resets), EXP_HEAVY_DISPATCH=1 (per
  dispatch), EXP_PRESENT_FILTERS=1 (per present)
- pipeline builds: EDEN_NX2_EXP_NO_CLAIM (per held draw), NO_BUILDER_BOOST (gates raising only;
  lowering follows the raised flag, so a flip during a build cannot strand a thread high),
  EVICT_9000 (per eviction pass), NO_GPL_RELINK (per link)

EDEN_INPUT_IMMEDIATE stays a launch switch: its SDL half decides at init whether to create the
GameInput source, so a runtime flip would A/B only half of it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ame's queue

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…elines off descriptor buffers

Buffer device addresses now report true on Vulkan 1.2+ devices (de4668e4fa), which starts the
descriptor buffer ring for the first time: pipelines past the push descriptor limit (32 on the
RX 9070 XT) move from descriptor sets to descriptor buffers. The switch turns that off for the A/B
against the HUD that goes missing with EDEN_NX2_NATIVE_STORAGE=0 (more descriptors per
pipeline). The ring logs whether it is on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…RECT=0 for the front-wait A/B

EDEN_NX2_QMD_VIEW=0: the front's QMD view never takes a table over, so
every QMD signal holds semaphore acquires as before the view.
EDEN_NX2_LIST_INDIRECT=0: the front reads every GPU-written compute list
itself again (waiting for its writers and the GPU's copy).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Depth-only passes (shadow maps) had nothing in a capture; the pass's depth/stencil
attachment is now dumped next to its colour targets (slot 8, named _zeta).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…de, provoking vertex, line stipple and patch control points static

For the A/B of the four rasterization states as dynamic state (fewer
pipelines, a recorded command where the register changes) against holding
them in the pipelines, on AMD's driver. The winner ships unconditionally.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…atform macros

On Linux vulkan.h is built with VK_USE_PLATFORM_XLIB_KHR, so <vulkan/vulkan.h> pulls
in Xlib, which defines Success as 0 and BadValue as 2. Since yuzu_cmd/yuzu.cpp
includes vulkan_common/driver_call.h, GCC 13 fails on
`case FileSys::Nup::PackResult::Success:` (yuzu.cpp:514, "expected unqualified-id
before numeric constant"). present/layer.cpp already undefined both by hand.

Undefine them in vulkan.h next to CreateEvent, None, True and the rest, so every file
that includes it gets the same treatment, and drop layer.cpp's copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
EventType::relaxed was fixed at creation, so an event that is a deadline only
some of the time (a fallback timer) paid a precise wait on every run. It is
now an atomic the owner sets with SetRelaxed, also from the event's own
callback; ScheduleEvent and Advance's reschedule of a looping event read it
each time, so the change applies from the next run, and the time already
queued keeps its choice.

Why it matters: the timer thread sleeps in the OS until os_wake_guard (one
timer tick + 0.1 ms, 0.6 ms) before a precise event and waits the rest in a
MWAITX monitor wait, which counts as CPU time. A high-resolution waitable
timer on this PC (Windows 11, 0.5 ms tick) ends 261 us late at p50, 523 us at
p99 and 663 us at p99.9 (4,000 waits of 0.1-3 ms, 2026-11-08), so there is no
shorter guard: the remedy is fewer precise events. EDEN_NX2_TIMING_TRACE in
the vrrtt-off field run logged 1,115 ms of precise waits per 2 s
(core_timing.cpp:145), the bulk of HostTiming's 67% busy in cons7.

os_wake_guard_override lets the new test pin the guard (Linux has none).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…events

Each precise core timing event costs the timer thread a MWAITX wait of up to
0.6 ms (one 0.5 ms OS tick + 0.1 ms) before it, counted as CPU time. In the
vrrtt-off field run (EDEN_NX2_TIMING_TRACE, 2 s windows) these events held
precise waits that meet no deadline (counts and times per 2 s window):

- HID::UpdatePadCallback: 3,075 waits, 549 ms (1,000 samples a second). A
  changed button, stick or trigger reaches the shared memory at once through
  npad_input_event (NPad::ControllerUpdate -> input_changed), so the periodic
  sample only steps the sampling ring.
- the other HID samplers (touch, default, motion, mouse/keyboard) and
  Hidbus::UpdateCallback: ~21 ms, the same kind of poll.
- nx2:vdec ready: 972 waits, 139 ms. It re-signals the decoder's auto-clearing
  ready event every 4 ms while the input queue has room; a beat that comes a
  tick late only makes the client check later by that much.

They become relaxed events: the timer thread sleeps in the OS until their due
time and runs them up to one tick late. That removes ~355 of the ~557 ms of
precise waits per second. npad_input_event, KHardwareTimer (guest timers),
sysmix output (audio) and the Switch 1 compositor stay precise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With host vsync, the presenter starts the game's frames (HostVsync::Displayed)
and the nx2:vi2 vsync timer only checks whether the display stopped doing so
(TimerVsyncSignals). It still ran as a precise event: in the vrrtt-off field
run 807 precise waits, 137 ms per 2 s (EDEN_NX2_TIMING_TRACE), for a check
that has no deadline.

The callback now sets its event relaxed while the display drives frames and
precise once it signals itself, through SetRelaxed. The first fallback run
after the display stops can be one OS tick late (it already comes two vblanks
after the last display start); from then on its runs are deadlines again.
With EDEN_NX2_HOST_VSYNC=0 LastStart stays empty and the timer stays precise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Advance computed its OS wake guard from GetCurrentTimerResolution on every
pass, a NtQueryTimerResolution system call ~5,000 times a second on the
timer thread. The frontend already tells CoreTiming the tick it set
(SetTimerResolutionNs, yuzu_cmd/yuzu.cpp:821-822 and qt_common.cpp:274-276,
the maximum resolution, so no other process can make it finer); Advance now
uses that and asks the OS only while none was set. timer_resolution_ns was
stored and never read before; it is atomic now, as the frontend can set it
while the thread runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… stop

RunInput ended an input call after a fixed total of frames / 20 + ms / 1000 + 10 seconds, which
assumes the game presents at 20 fps or more. During a slow load (cons7 render gate, 16:9 session:
"Load: 80.0 s ... 105 frames (1.3 fps)" and the pause menu at 1.2 fps in eden_log.txt) a 40-frame
press (10 frames held, 30 released) needs ~35 s, so the call failed with "step 1 of 2 was still
running after 12 s (paused or loading?)" although the game was still presenting, and the harness
aborted the session. The docs already described the intended rule ("when frames stop for
timeout_s").

timeout_s is now the stall window (default 10 s, PresentStall): the call fails only when a frames
step waits and no present arrives for that long. ms steps run on wall time and never stall. The
MCP adapter waits for the longest call the emulator can make (ms steps plus timeout_s per awaited
frame). Unit tests: PresentStall over a 0.5 fps stretch and a stop; the adapter's answer wait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DahBard pushed a commit to DahBard/Lichii that referenced this pull request Nov 9, 2026
Ports the guest JIT part of zortos293#717 (eb3553b, "Ocarina of
Time performance work squashed on build 116"). Ocarina's guest cores run
~750 ms/s of JIT code each; these cut the code each block executes and
its cache footprint. Each piece has an EDEN_NX2_* switch, on unless 0:

- EDEN_NX2_JIT_FLAGS: branches, CSEL and CBZ/TBZ use the host flags the
  Add/Sub that set NZCV left, instead of reloading them from the guest
  state (cycle counting off only).
- EDEN_NX2_JIT_DISPATCH: a 4-way set associative fast dispatch table of
  2^16 entries (1 MiB per core instead of 16 MiB); 0 = old table, 8-24 =
  table bits.
- EDEN_NX2_JIT_COMPACT_EXITS: unlinked exits jump to one shared stub per
  target, 5/6-byte patch sites instead of 22/23.
- EDEN_NX2_JIT_FAR_CODE: fault/abort paths and exit stubs go to the last
  quarter of the code cache (its own commit front on Windows).
- EDEN_NX2_JIT_FOLD_ADDR: [xN, #imm] folds into the fastmem address
  (unchecked 64-bit fastmem only); a fault adds the offset in its stub.
- EDEN_NX2_JIT_ZEXT, EDEN_NX2_JIT_SPILL_GPR, EDEN_NX2_JIT_SCRATCH_COPY:
  no second zero-extension, spills to free callee-saved GPRs, copies
  instead of evictions.
- EDEN_NX2_JIT_NO_FRAME: no per-block %rbp frame.
- EDEN_NX2_JIT_BACKEDGE_HALT: links to a higher PC skip the halt check;
  backward links and indirect branches check it.
- EDEN_NX2_JIT_RSB32: a 32-entry return stack buffer.
- EDEN_NX2_HINT_PAUSE: YIELD and WFE pause twice.

Port notes: zortos293#717's TSO loads (tso_ordered_loads, elide_ld_st_barriers,
EDEN_NX2_TSO_LOADS) are the same transformation as round 7's
x64_tso_barriers (LICHII_JIT_TSO): plain-MOV load-acquires and exclusive
loads, no DMB LD/ST. They are not added again; the new code uses
x64_tso_barriers, and the inline CNTPCT_EL0 (LICHII_JIT_INLINE_CNTPCT)
is unchanged. Native LSE atomics (EDEN_NX2_JIT_ATOMICS: opcodes, frontend
and the fastmem-fault CAS fallbacks) are left out: their fallback reports
a CAS on a write-protected page as successful without writing it, the
guest panic zortos293#831's author traced. Fix over zortos293#717: ZeroExtendWordToLong
only aliases a value that is in a GPR, since a U32 spill slot holds only
the 32 bits its spill stored and the 64-bit alias would reload all 64.
The CheckBit-in-ZF path is off with very verbose debugging output, which
emits code after every instruction. The switches are read where zortos293#717
reads them, after the accuracy settings, and only off Paranoid.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RqR9236AYzEMch9rerR4iU
ThatGuyMcd added a commit to ThatGuyMcd/Lichii-TGM that referenced this pull request Nov 9, 2026
…#831

Ported from c986b6b. Co-authored-by: Julius Bairaktaris <julius@bairaktaris.de>
ThatGuyMcd added a commit to ThatGuyMcd/Lichii-TGM that referenced this pull request Nov 9, 2026
Port the dynamic-rendering feature entry from PR zortos293#831 commit 42f55b8 so SetupAmdFfx uses one device feature chain.
lucastaonline pushed a commit to lucastaonline/Lichii that referenced this pull request Nov 9, 2026
Merge origin/nx2 without replacing the complete zortos293#805 split with its partial
ports. Conflicting areas are resolved as follows:

- gpu_split.{h,cpp}, dma_pusher.{h,cpp}, gpu_thread.cpp, engines/maxwell_3d.cpp,
  engines/kepler_compute.{h,cpp}, engines/maxwell_dma.h and their fast-method
  tests retain zortos293#805's write classification, QMD/front view, position waits,
  tick/idle hooks, fast method path and sampled method timing. Do not import
  zortos293#949's reduced acquire table, back_quiet drain scheme or duplicate zortos293#1028
  write tracking. Keep the independent scheduler-owner check on CPU/service
  writes, the new RingFull drain site and opt-in live-read comparisons.
- engines/puller.{h,cpp}, nx2.h and query_cache.h combine zortos293#805's authoritative
  front semaphore view and source-tracking unbounded waiters with zortos293#979/zortos293#1014's
  unbounded region table and direct-release shadow. Mirror releases only when
  ViewApplies is false: unsplit, back macros, or delayed/high-accuracy releases.
  The full front view must not consume a second mirrored counter absent from
  its original releases. Note both destination writes on the fallback path.
  Compute max/reduction releases retain shadow combination and zortos293#805 checks.
  Keep zortos293#874's synced payload handling and FencedPayloadLanding: synced
  callbacks never advance fenced_landed.
- gpu.cpp, rasterizer_download_area.h, rasterizer_interface.h, memory_manager.cpp,
  buffer_cache.{h,base.h}, vk_buffer_cache.{h,cpp}, vk_rasterizer.{h,cpp} and
  buffer_cache_ports keep the full zortos293#805 read-back, upload ordering and tracing.
  Bring in zortos293#1028's review fixes: ClearDownload lands reads before DMA/discard
  writes, ClearReplacedDownload cancels only bytes reaching guest memory,
  and pending query results leave newer GPU writes dirty. Retain the new
  regression cases and the Qt-safe MayHoldGpuWrites boundary. Drop the unused
  reduced nx2_async_readback diagnostic implementation instead of duplicating
  zortos293#805's richer GPU-wait/read traces.
- device_memory_manager.h/inc use zortos293#984's per-manager generation ranges with
  Map/Unmap increments and preserve DownloadLanding/write tracking.
- nx2_profile.h and video_core.cpp combine zortos293#805's fine counters, sampling,
  scope depth and runtime switch setup with zortos293#965/zortos293#1039's zone-parent tracking,
  GPU waits, contention, worker/present/submit zones and stutter sites.
- vk_pipeline_cache.cpp takes zortos293#1036's v2 cache, migration, jobs, parallel
  translation and emission. shader_environment.{h,cpp} keeps zortos293#970's unsafe TIC
  reads; also omit v2's new pool-wide pretranslation flush, since workers no
  longer flush descriptors. Keep the AMD e0a992208d87eeb8 exception in
  pipeline_optimization.h.
- vk_graphics_pipeline.cpp, uniform_stream_reuse.h, sampled_views.h and tests
  converge zortos293#921/zortos293#949 with the adapted branch ports. PrepareImageView uses the
  single PreparesImage predicate, including valid views of NULL_IMAGE_ID.
  Keep uniform/descriptor delta replay, zortos293#974 fast-draw checks, zortos293#805 profiling
  and the empty-descriptor-range guard; no parallel duplicate paths.
- vulkan_device.cpp and vulkan_memory_allocator.cpp combine cache-v2 device
  support and zortos293#880's backable-image failure policy with zortos293#836's synchronized
  recycler and zortos293#831's 50 ms memory-usage cache. Preserve all remaining branch
  AMD, query, shader, texture and fast-draw changes in the automatic merges.
- nx2_video_decoder.cpp takes nx2's final dav1d/default-thread state. CMake test
  lists combine both sides; keep zortos293#805 fast-method tests rather than tests of
  rejected reduced split internals, and adapt waiter tests to >64 owners.
  Other matching cherry-picks converge to nx2; NX archives, HostTiming,
  memory/stutter/cache tooling and their sources are retained.

Validation before commit: git diff --cached --check passed; 30 cache_metrics
and cache_bench Python unit tests passed using workspace-local test data.
The requested clang-cl ThinLTO/PGO build is in progress. BUILD_TESTING is OFF;
C++ tests and in-game semaphore/read-back/cache/fast-draw validation remain
separate. No emulator launched or stopped; no push or other branch changes.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants