Repository navigation
Conversation
* frame dump: port buffer, sampler and launch records from fix/oot-fog; capture_frame buffers option Ports the frame-dump part of 1ad14be (fix/oot-fog): the uniform and storage buffers of every draw and dispatch (kind: buffer, buffer_<hash>.bin.zst), the host sampler state and raw TSC words of sampled textures, and dispatch group counts (kind: launch). Its F2I constant-buffer abs/neg shader fix is not part of this port. EDEN_NX2_FRAME_DUMP_SAMPLED still records all of it. At runtime, capture_frame takes buffers separately from sampled, so a constant-buffer diff does not pay for texture readbacks. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * debug server: read_memory, search_memory and read_gpu_memory Read-only memory tools for the debug server, capped at 64 KiB per read. read_memory and search_memory run on the server thread. They resolve each page with Memory::GetPointerSilent, the lock-free lookup guest cores use: it does not commit the sparse page table, log, or download from the GPU. Every mapped entry points into the DeviceMemory backing, which stays mapped for the whole boot. search_memory scans the heap (or the whole address space), stops at 256 hits or its time budget, and returns where to resume. read_gpu_memory is queued to the rasterizer thread like a capture. At frame end it reads the first address space that maps the whole range (the bound channel's first) with IsFullyMappedRange and ReadBlockUnsafe, without flushing GPU caches. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * debug server: input and input_sequence Inject buttons and sticks on player 1 (or the handheld controller) for an exact number of presented frames, so agents can replay identical motion for A/B tests. EmulatedController::SetInputInjector installs a function that GetNpadButtons and GetSticks consult while it is set: injected buttons are ORed in, an injected stick replaces the real one and its direction bits. No mapping or setting changes. The script starts at the next present and picks its step from g_nx2_present_count on every read; it stops by itself after the last step, and the handler removes it on completion, timeout, shutdown or client disconnect (a ClientGone probe the transport now gives every handler). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * buffer_cache: default native CPU reads to trap-and-flush Avoid permanently preflushing GPU-written pages after native CPU reads. Retain non-native preflushing and EDEN_NX2_PREFLUSH=1 as the fallback; EDEN_NX2_PREFLUSH=0 forces trap-and-flush. Adapted from 0360f30 and ef3a246 in the OoT integration tree. Unlike that tree, keep the default preflush behavior for non-Ampere titles. * buffer_cache: download every read-back range at a guest read that must flush Ocarina of Time's guest CPU reads the output of ~30 compute storage buffers each frame, page by page, in a few bursts. With trap-and-flush reads (EDEN_NX2_PREFLUSH=0 for Switch 2 titles) each page read after a GPU write flushed only its own page: ~1180 reads/s, each with a full Finish (~170 us), 46 per frame, stalling the reading core and the GPU thread. Remember the ranges the guest CPU read after GPU writes. When a read must flush, download the GPU writes in all of them in the same Finish, so the reads that follow in the burst find nothing to flush. A GPU write after it marks its range modified again, and the next read flushes as before. DownloadMemory also clamps each download to its buffer: a range may now span several buffers. Kokiri Forest (i7-12700K, RX 7800 XT, owner settings, seconds 44-84): Finishes 1184 -> 70/s; mean 25.97/25.16 -> 30.75/30.73 FPS (median frame 38.6 -> 32.4 ms, p95 41.8 -> 34.6 ms). Grass, canopy and haze unchanged. (cherry picked from commit 23d3388) * buffer_cache: cache the preflush environment override * nx2: sampling profiler in every frontend and the debug server; fence download trace The in-process sampler moves to common and starts at boot (EDEN_NX2_SAMPLE) for the Qt frontend too; the debug server's sample method runs it on demand. EDEN_NX2_DOWNLOAD_TRACE logs which GPU writes queue fence downloads, which regions are copied back, and which CPU reads mark ranges preflushable. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * nx2_sampler: format the report with fmt Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * nx2_profile: per-method and per-macro DMA read counts with the zones Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * debug_mcp: list the sample tool in the adapter test Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * nx2_debug_tools: close SampleThreads after the merge Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * memory_manager: answer MaxContinuousRange from the run through the address's 64 KiB page The per-address cache missed in Ocarina (a new address per object): walking up to 8 MiB of pages took ~11% of the GPU back thread. EDEN_NX2_RUN_CACHE=0 restores the per-address cache and =2 checks every answer against a walk (experiment switch, not for nx2). nx2_profile: time zones with the unfenced time stamp counter; the fenced wall clock reads cost ~10% of the back thread. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * memory_manager: walk a page's run only as far as asked; count run cache hits and page table changes Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * experiments: EDEN_NX2_DMA_NO_DRAIN; run cache off by default; sampler reports hot instructions Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * memory_manager: key the page runs by 4 KiB page (Ocarina shares big pages between allocations) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * video_core: EDEN_NX2_PERF_OPT master switch and EDEN_NX2_PERF_VERIFY for the back-thread optimizations Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * vulkan, nx2_gsv: port zortos293#161's submission batching and Ocarina frame-ahead present behind EDEN_NX2_PERF_OPT From 999sian's PR zortos293#161 (3985ac2, 7422d90): a 4096-draw native submission budget outside MoltenVK, FragmentBarrier deferred to the next draw that reads what the pass wrote, a 5 ms DeferFlush interval on the split back with command list copies submitted at once, and Ocarina presenting a frame ahead (EDEN_NX2_PERF_FRAMES_AHEAD=0 turns only that off). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * buffer_cache: skip clean tracker words, refilled usage pages and repeated index binds (Nx2Perf) - word_manager: an exact per-word clean bitmask (no CPU and no Untracked page), kept on every path that writes those words. SynchronizeBuffer walks only each manager's dirty words (ForEachDirtyUploadRange), a per-word form of PR zortos293#161's upload_words skip. - usage_tracker: Track fills only the pages outside the run already known to be all ones. - vk_buffer_cache: skip a vkCmdBindIndexBuffer that repeats the bound buffer, offset and type in the same scheduler recording epoch (from PR zortos293#161, commit 5a0cdcb). - buffer_cache: check the per-frame native bank set before its GPU-modified query. All behind Nx2Perf::Enabled(); EDEN_NX2_PERF_VERIFY=1 walks every word and checks the clean bitmask against a recount, and checks skipped usage pages are all ones. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * memory_manager: answer MaxContinuousRange and GetMemoryLayoutSize from runs kept at map time Replaces the EDEN_NX2_RUN_CACHE experiment. Each page table operation rebuilds the maximal device-contiguous runs through the big pages it touched, from the tables themselves; readers query an immutable snapshot (runs plus a copy of the kind map) that each thread swaps only when map_serial changes, so the hot path takes no lock. Behind Nx2Perf::Enabled(); off is the plain walk and the kind map under its lock, and Nx2Perf::Verify() checks every answer against them. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * vk_pipeline_cache: keep sixteen sessions of used pipelines and save the list every 15 s (zortos293#161) From 999sian's PR zortos293#161 (88ffd1f), behind EDEN_NX2_PERF_OPT. The runtime eviction age stays 9000 frames: it only acts after that many frames, so it is not what builds pipelines early in play. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * video_core: cheaper Maxwell3D method dispatch on the GPU back thread (Nx2Perf) With VideoCommon::Nx2Perf::Enabled() (EDEN_NX2_PERF_OPT, default on) and no write history, first-write trace or GPU profile: - Maxwell3D::CallMethod takes CallMethodFast for state and executable writes outside macro parameter collection: no RecordWrite/trace/profile/macro checks, and draw begin/end, constant buffer data, bind groups and the four executable methods the draw manager ignores go straight to their handler. Everything else, and the switch off, runs CallMethodSlow (the old body). - GpuSplit::Execute calls Maxwell3D directly (it is final) without profile zones, and runs a row of single constant buffer data entries (macro uploads) through Maxwell3D::CallConstBufferWords: shadow RAM, dirty flags and deferred draws per word as before, one memory write, and the invalidation accumulator fed word by word as WriteBlockCached did (MemoryManager::WriteWordsCached). Rows that would be clipped, sparse memory and macro parameter collection go word by word. - EDEN_NX2_PERF_VERIFY=1 runs the old path for state writes and constant buffer rows too, logs "Nx2Perf method:" differences and keeps the old result. No new same-value skips: every method a skip is exact for (non-executable state writes) is already skipped on the front and back. Reviewed the executable-method dirty skip and front constant buffer batching of PR zortos293#161 (7422d90) and did not port them: the skip only changes index_buffer.first/count, where UpdateIndexBuffer needs Dirty::IndexBuffer after DrawIndexSmall or inline draws; the batching leaves the data registers and their shadow RAM unwritten and changes the accumulator ranges. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * video_core: split front waits only for the queued work an MME DMA read depends on Builds on PR zortos293#161's sync-point model (FrontDmaRead waiting for synced writers, the GpuSplit front_wake sleep) and closes its two holes. The front tracks what each queued entry may write (SplitWriteTracker): unknown (puller releases, I2M/inline uploads, unclassified methods), GPU work (draws, dispatches, copies, blits), or nothing besides noted ranges (constant buffer data, 3D and compute reports, inline QMDs). FrontDmaReadEarly waits for every unknown write, the GPU work before the last wait for idle (3D wait_for_idle/trigger_wfi, compute WAIT_FOR_IDLE, puller WaitForIdle/fence acquire, GP sync bit) and the noted writes to the read's pages, then reads guest memory. Pages ever written by GPU work, sparse or unmapped pages and queued GPU map changes fall back to the drain. Semaphore acquires keep their full drain and fence release; zortos293#161's CanResumeAcquire shortcut is not ported. All of it is behind EDEN_NX2_PERF_OPT; EDEN_NX2_PERF_VERIFY drains and re-reads, logs differences as "Nx2Perf split:" and uses the drained words. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * nx2_perf: EDEN_NX2_PERF_OFF=<name>,... turns single optimizations off; the state is logged once Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * video_core: one GPU-written lookup per buffer write, heap-free tracker batches, skip empty gathers (Nx2Perf) BufferCache::WriteMemory looked up gpu_modified_ranges twice, for the written range and for its pages. The range lies within its pages, so when the pages hold no GPU-written data one lookup answers both. WordManager collected and merged changed page runs in two std::vectors, and DeviceMemoryManager::UpdatePagesCachedBatch copied, sorted and merged them again into two more; the runs now stay on the stack, are merged in place, and the batch takes runs that are already sorted with gaps between them as they are. GPUDirtyMemoryManager::Gather, run at every fence, returns after its gatherer lock when nothing was collected instead of taking the collectors' lock and swapping empty buffers. All behind EDEN_NX2_PERF_OPT; EDEN_NX2_PERF_VERIFY=1 runs the old paths and logs any difference. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * video_core: preserve disabled tracking path and drop ineffective gather shortcut Keep the original std::vector when collecting tracker ranges so the off switch preserves the old path. The enabled path still eliminates the three downstream vector copies. Remove the gather shortcut: IsValid takes a 39-bit byte-address bound but its caller supplies a u32 page number, so its empty predicate was never true. Leave gather behavior unchanged rather than widening this optimization into a concurrency fix. Exercise the production tracker merge in the differential test, and run the same tests enabled, disabled and in verification mode. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * nx2_perf: independently gate write intersections and sorted page batches Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * vulkan: retain deferred fragment barriers across unrelated draws Do not consume the pending dependency while checking an unrelated draw. Clear it only when EndRenderPass records the barrier. Keep the existing performance switches and dependency policy unchanged. Add the deferred-barrier regression plus SlotVector relocation/reset and MemoryTracker manager-pool growth equivalence tests. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * video_core: keep batched CB writes within one device page Preserve per-word translation and range rejection when CB data crosses a 4 KiB device page. A bulk write can otherwise use a stale GPU big-page continuity bit after a device remap, or drop a valid prefix when the run crosses the GPU address-space limit. Exercise actual GpuSplit Method/MacroWrite/MultiMethod/Defer encoding and Execute with mapped backing, publication limits, ring wrap, engine switches, shadow replay, deferred draws, and buffer clipping. Both boundary regressions fail without the guard and pass with it, including verify mode. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * video_core: quarantine unsafe early MME DMA reads Audit the PR zortos293#161-derived split-overlap model on perf/oot-combined 7ea801a. A ring entry classified as GPU work can perform an immediate CPU copy or enqueue the first write to a previously unmarked page. Capping its target at the last sync lets both page checks pass before that entry executes. The new adversarial schedules fail on the old target. Remove the sync cap and its unused engine-sync state. The randomized oracle now requires every unlocalized writer, independent of guest sync assumptions. This fixes the demonstrated target misses, but consumption still does not prove completion of fence callbacks, and device-page identity is not a complete physical-alias proof. Keep split_overlap forcibly off, including in Verify mode, and log that state. MME DMA reads retain the full drain and existing buffer-cache flush. Remove the unsafe EDEN_NX2_DMA_NO_DRAIN bypass. FrontSleep and all unrelated optimizations remain independently selectable. This is a correctness quarantine, not a claimed fix for the reported foliage or descriptor corruption. Validation: clang-20 tests build; 7 split-write cases / 32625 assertions; 350 video_core cases / 60739787 assertions. Quarantine regressions also pass with PERF_OPT=1, PERF_VERIFY=1 and the obsolete DMA_NO_DRAIN variable present, and with PERF_OPT=0. No PC or game run. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * video_core: defer native graphics SPIR-V emission to pipeline workers Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * video_core: harden async pipeline completion and teardown Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * video_core: bound owned-input unswizzle work on the texture pool Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * video_core: promote tested perf9 paths and remove unsafe overlap experiment Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * buffer_cache: promote perf9 buffer paths and remove candidate switches Retain fe0c73f ON behavior for dirty-word scans, usage runs, index rebinds, native bank checks and sorted page batches. Keep diagnostic verification and the existing native read-trap/preflush default; remove its experimental override. Drop only switch-interface test coverage and Enabled assumptions, retaining behavior-equivalence tests. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * ci: gate nx2 pull requests on GCC production builds and tests Adapt the Linux dependency and Qt recipe proposed in PR zortos293#169 without merging that branch. Pin actions, disable checkout credentials, require GCC 13 and Werror, build the Qt frontend and unit tests, and cover nx2 pull requests, pushes and manual runs. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> Co-authored-by: nyakuoff <bertrandbridou1994@gmail.com>
…tos293#193) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
) Port the reviewed Frizti buffer, submission, pipeline-cache and FSR3 changes, including the tested host-read visibility and interrupted-migration corrections. Preserve conservative synchronization fallbacks and keep frame generation off by default. Source: Frizti/Lichii oot-gather-fix at 765659a. Original contributions and commit provenance by Matt Artist, chuppatricky, Dr. Sergio and zortos293 are recorded in PR zortos293#194 and its port commits. Co-authored-by: Frizti <FriztiMouche@gmail.com>
Port the default-on built-in Add-On from Frizti's 765659a with verified original-byte, executable-padding, BSS and transactional user-mod guards. Preserve Dark's routine and existing Add-Ons disabling. Co-authored-by: Frizti <FriztiMouche@gmail.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…mpling shader: fix AMD depth sampling, atomic retries and compiler stalls
* shader: fix AMD shader typing, atomic retries and compiler stalls Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * externals: port upstream MinGW AV1 package selection Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…ompute writes (zortos293#295) * shader: split misaligned 64 and 128-bit storage buffer accesses into words Storage bindings start at the device's minStorageBufferOffsetAlignment (4 bytes on AMD), and the shader's offsets are relative to that base. An aligned 8 or 16 byte guest access can then start at any word of the binding, and indexing the vector view (offset >> 3 or 4) read and wrote the vector before it, which shifts Ocarina's light colours (green lighting, cyan sky). Direct storage accesses keep the vector view only when the offset is known to be a multiple of the access size; global accesses find the buffer once by the start address, as the vector path did, and read or write its words. Without descriptor aliasing the per-word global path is unchanged. Ported from mja00/Lichii fix/ocarina-of-time: 5e8d409 (Matt Artist, after Selevet fix 36) and c418e4f (sharkiix, from sharkiix-v2). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * vulkan: order compute writes before later draws, indirect reads and idle waits The barrier before a dispatch covered the compute stage only, so indirect arguments written earlier were read unordered. Nothing ordered a dispatch's writes before the draws of the next render pass. The guest's wait-for-idle set an event and waited on it at TOP_OF_PIPE with no memory barrier. TOP_OF_PIPE in the second scope is ALL_COMMANDS with no access, so the first wait ordered execution but made no write visible. The event was never reset, and vkCmdSetEvent on a signalled event generates no dependency, so later wait-for-idles did not wait for work recorded after the first signal. The dispatch barrier now includes DRAW_INDIRECT, the next render pass begins after a compute-to-graphics barrier, and wait-for-idle is a pipeline barrier to ALL_COMMANDS with a memory barrier. Based on mja00/Lichii fix/ocarina-of-time c7b8715 (Matt Artist); the wait-for-idle uses a pipeline barrier instead of that commit's event wait for the reasons above. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* common: synchronize stopped-worker notifications with waiters Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * tools/nx2: report thread_worker_wake setup failures as harness errors Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…nbuilt pipelines (zortos293#224) * video_core: opt-in EDEN_NX2_ASYNC_DISCARD to skip native draws with unbuilt pipelines Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * video_core: document the async draw policy and BuiltPipeline Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * video_core: document BuiltPipeline and log skipped lookups, not draws CodeRabbit: the counter counts pipeline lookups that returned no pipeline, which can exceed the number of draws skipped. Name it accordingly; document the function and the test case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * video_core: doc comments for the async discard switch and its test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…tos293#314) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…os293#287) * frontend: fix Linux AppImage switch discovery and package defaults Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * frontend: constrain adjacent switch files and reject relative config roots Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* qt: add consent-first desktop compatibility reporting Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * tests: compare HTTP header names case-insensitively Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * qt: preserve report state and prefill active GPU identity Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * qt: prefill report title and enabled add-ons Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * qt: report the loaded NUP package version Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * qt: preserve literal characters in catalog searches Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…3#397) * core: add the run walker the pending-write port uses Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit eb6a2de) * buffer_cache: don't download GPU data over newer CPU writes Ported from Duck912/Eglantine-ducky e27fbc6b03 (Duck912). CPU writes into GPU-written memory are collected and applied later by the GPU thread; until then two paths wrote the GPU's older copy over them: the fencing thread's async download pops, and applying the collected writes one by one (WriteMemory downloads the GPU bytes elsewhere on the pages it touches). In Ocarina this made the Z-target reticle use values from two frames before on about half the frames. Every download write into guest memory now skips the collected, unapplied CPU writes (GPUDirtyMemoryManager::ForEachPending, which here also covers writes a Gather is handing out), and InvalidateGPUCache hands the collected writes to the rasterizer as one batch that is taken out of the GPU-written ranges before any is applied. This tree has more download paths than the original (DownloadMemoryRanges, DownloadRangesMemory); all eight writes go through the same check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 0c82282) * tests: downloads keep CPU writes collected and not yet applied Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: Frizti <FriztiMouche@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* shader: gather from a second descriptor buffer as TLD4.B From MateusAugustoBrito/Lichii a04f756 (MateusAugustoBrito). A gather whose handle comes from another descriptor buffer than the program's loads it with MOV and emits Maxwell TLD4.B, as TEX already does, instead of rejecting the program. Programs with one descriptor buffer lower as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 794233f) * tests: gathers from a second descriptor buffer lower to TLD4.B Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: Frizti <FriztiMouche@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…tos293#185) * texture_cache: land deferred clears before non-pass image writes A deferred clear is parked until its framebuffer's next render pass, but texture cache copies bypass render passes: the clear then realizes after the copy and wipes what the copy wrote. On hardware the clear executes at its stream position, before the copy, so the copy wins. That is the Ocarina of Time terrain layer (zortos293#101): the game clears the occlusion depth, blits the half-res scene depth over it, then the layer draw samples it. The parked clear landed after the blit from the second frame on (GetFramebufferId only flushes when it creates a framebuffer), the map read 1.0 everywhere, and the layer drew over the scene. It matches the workaround that dropping the Z-only clears before the blit removes the blotches. BlitImage, CopyImage and PrepareDmaImage now realize a parked clear before writing; draws keep the load-op fusion. * texture_cache: only land deferred clears when the write proceeds A rejected copy writes nothing and must not realize a parked clear ahead of the next draw that consumes it as its load-op. Move the flush in BlitImage after GetBlitImages (safe: framebuffer removal flushes first), after CopyImage's early returns, and gate PrepareDmaImage's on the DMA write actually happening (mark_as_modified). Review note from RomainROCH on zortos293#185.
…SURED register shadowing (zortos293#149) * vulkan: submit deferred work before waiting on pending fences On the split GPU thread, Scheduler::DeferFlush holds fence submissions back to batch them; the back submits them only when idle or asked between entries. WaitPendingFences(force) on the back queued a fence and blocked on the fencing thread, which waited for that fence's submission: Ocarina of Time froze on a black screen as gameplay began. Submit deferred work before waiting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * present: interpolate between the previous and current frame in FSR3 frame gen Process() copied the new frame into both the history and current images after the flow, so every generated frame blended the newest frame with itself; the interpolation also applied the motion vectors with the wrong sign and as full-resolution texels. Text over moving backgrounds was mixed with its own drop shadow (black title menu options in Ocarina of Time). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * shader: shadow the data registers of RED and SURED The register shadowing check reads bits 32-39 only for register-form opcodes and a short exception list. zortos293#100 added ISBEWR (0x927) for Ocarina's door NaN. RED (0x98e) and SURED (0x59f) take their data register from bits 32-39 as well, and RegisterPressure already lists operand 32 for both, but they were never asked: a data register at or above the uniform bank read Lichii's UR mirror instead of the guest value. Add them alongside ISBEWR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…e rejecting (zortos293#241) A native program whose own registers reach into the high uniform mirrors (R(216 - n) and up) has those registers shadowed by PrepareRegisterPressure, which works in aligned groups of four. n counts in pairs, so the base could be misaligned (R202 for n = 14) and the program was rejected with "native program uses lowering scratch registers" even though shadowing supports its instructions. Request one more lowering pass with n rounded up to a multiple of four instead; programs that lowered before are unchanged. Also shadow the register form of SHF (0x219): its destination, low word, shift amount and high word, like 0x210-0x212. Programs with it there were rejected with "native register shadowing mode". Shadow LOP3.LUT with a constant buffer or uniform B (0xa12, 0xc12) like its immediate form (0x812) too: Rd, A and C are its only registers. ampere_ported_uniform_vote expected the old rejection for a native R214 inside the UR28 mirrors; it now checks the realigned result instead: UR28 read at R212 and the native move written to its shadow, R210. In Ocarina of Time both rejected pixel shaders of the Gerudo Valley rock draws, so the rocks were skipped and the sky showed through. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…rray index against the innermost array (zortos293#203) * fix(file_sys): apply LayeredFS mods to NUP titles AppLoader_NUP reports the RomFS as not updatable (update already merged), so RomFSFactory returned the raw file and user/load/<title>/<mod>/romfs layers were never applied. Run PatchRomFS without an NCA: the update step is skipped and LayeredFS still runs. No mods present = same file as before. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(config): assert nested array index against the innermost array SetArrayIndex compared the index with the outermost array's size, so a nested array (DisabledAddOns/<n>/disabled with more than one entry) hit the assert on every entry past the outer size. Reading still worked; only the log filled with Critical asserts. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…arina white flashes) (zortos293#215) * video_core: flush the query cache before macro DMA reads under the GPU split Macro DMA reads of report regions flush the query cache so occlusion reports and semaphore writes land before the read, but the flush was gated on the split GPU thread being inactive. The default configuration (EDEN_NX2_SPLIT=2) therefore served stale counts to macros whose own comment warns that stale values make 'occlusion predicates read stale values' -- the guest reads a zero visibility count and culls visible objects, which matches Ocarina of Time's missing vegetation (zortos293#101) and the count=0 draws traced there. The native macro path now flushes under the same owns_caches condition as its stale-read trace, and the split front path (FrontDmaRead) flushes on the back next to its buffer cache flush. SemiFlushQueryDirty writes out finalized values directly and waits out the rest through the fence manager (WaitPendingFences, hardened in zortos293#149). Build-checked and unit-tested on Linux; gameplay validation needs the EDEN_NX2_SPLIT=0 vs 2 vegetation A/B described in zortos293#101. * video_core: wait for pending query results before macro DMA reads The query cache flush before macro DMA reads asks the fence manager to wait out pending reports, but below High GPU accuracy a fence runs its callback at once, so the wait returned before the report writes landed. The macro still read the old occlusion counts. With the GPU split's EDEN_NX2_QUERY_DMA_TRACE on, Ocarina of Time (0400486024DEC000) showed 976 such reads in 2.5 minutes, each reading a different value than the one that landed later, and the scene flashed overexposed for a few frames whenever the camera moved (cutscenes or the stick). The macro paths now flush through FlushQueryResults: when the region holds pending reports, Vulkan queues an operation behind them, signals a fence, submits work the split's back deferred, and waits until the operation has run. Other ReleaseFences callers are unchanged. Build-checked on Linux (clang, Release). Gameplay tested on NVIDIA, Low GPU accuracy, GPU split active: the flashes on camera movement are gone. Not run: unit tests, AMD, High accuracy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Gavin Schiller <gavinschiller51@gmail.com> Co-authored-by: WeslieDE <weslie@weslie.tk> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ortos293#197) * shader_cache: skip the cached rejection path for compute dispatches A rejected program fails the same way on every dispatch, but ComputeShader() rethrows the recorded exception and then records it again: two C++ throws, a mutex-guarded insert and a current_exception() each time the program is dispatched. GetGraphicsEnvironments already skips the equivalent draws for exactly this reason ("two C++ throws each time cost ~1-2% of the GPU thread"), so give the compute path the same treatment. Return a missing shader instead, which CurrentComputePipeline() in both renderers already handles, and keep the first exception recorded for an address rather than capturing a new one on every rejection. Reported against build-75 (c9ebb5c): Ocarina of Time rejects compute programs with "native texture gather mode" and "native per-tap gather control-flow proof" and then dispatches them repeatedly, so the compute path paid this cost per dispatch rather than once. * renderer_opengl: report a null native compute pipeline as a failed dispatch Native mode is enabled from the command stream (ChannelState::Init, gated by g_nx2_ampere), not by the renderer, so the OpenGL rasterizer can reach the cached-rejection path that now returns a missing ComputePipeline. Returning silently there makes KeplerCompute::DispatchNativeCompute treat the dispatch as successful, which skips its rejection bookkeeping (including dlss_chain.rejected) and reports no QMD failure. Match Vulkan and throw instead, as both backends already do for a native compute pipeline that cannot be built. Reported by CodeRabbit on zortos293#197. --------- Co-authored-by: axsddlr <axsddlr@users.noreply.github.com>
…GPU back thread (zortos293#412) * vulkan: look the draw's pipeline up once UpdateDynamicStates looked the current graphics pipeline up again for each draw (~2.5% of the split's back thread in Ocarina); PrepareDraw now passes the one it found. Ported from Frizti/Lichii 23f40d5 without its tools/nx2/sample_report.py change (nx2 has no sampling profiler report). (cherry picked from commit 23f40d5) * common, query_cache: skip range subtractions and query erases that touch nothing RangeSet::Subtract looks the range up first: the buffer cache clears several sets on every write, and most hold nothing there. IterateCache only erases a page's queries when one was in the range. About 3% of the split's back thread in Ocarina of Time (Lake Hylia). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit da215e8) * video_core: skip resetting untouched buffer usage trackers Port the tracked-state guard from sylvieistaken/selevet-eglantine d8c95fe. Keep page and recent-range clears after every Track while avoiding repeated clears for idle buffers. Preserve existing tracker ownership and reset semantics. (cherry picked from commit b16ba4f) * tests: cover RangeSet subtraction and skipped usage tracker resets RangeSet::Subtract now returns early when nothing intersects the range, and UsageTracker::Reset skips trackers nothing tracked since the last reset. Check both against reference models: a per-address bitmap for the range set, and the always-clearing tracker for resets, including back-to-back resets. --------- Co-authored-by: Frizti <FriztiMouche@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…r-GPU-written pages (zortos293#413) * vulkan: skip the buffer cache lock for dirty checks on pages the GPU never wrote The split's front asks MustFlushRegion before each macro DMA read; it waited for the buffer cache mutex the back holds through each draw (8% of the front in Ocarina). Every GPU-modified range also marks its pages GPU-written (bits that never clear), so pages without the bit are answered without the lock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 6247fd5) * video_core: answer the lock-free flush check from nx2.h and test it Move the GPU-written page walk behind MustFlushRegion's lock-free answer into Tegra::Nx2MayBeGpuWritten next to the bitmap it reads. A range past the bitmap, or one that wraps, now answers true and takes the lock: Nx2MarkGpuWritten clamps to the bitmap, so those pages were never marked. Test the page boundaries, the bitmap end and clearing. --------- Co-authored-by: Frizti <FriztiMouche@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…93#414) From Hydro74000/Edlws 034668a14e (Hydro74000). The old buffer is kept until the GPU is done with the current tick instead of finishing the GPU, and the buffer grows by at least 1.5x. (cherry picked from commit a20c9a9) Co-authored-by: Frizti <FriztiMouche@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…(OoT near foliage) (zortos293#410) * shader: bind loaded pointers that compete with a paired cbuf pointer A native global access whose address is a select or phi of a constant buffer pointer and a pointer loaded from that buffer was paired with the constant buffer pointer alone, so the native candidate search never ran. Accesses through the loaded pointer were then rebased on the constant buffer's binding and read out of bounds as zeros. Ocarina of Time reads its foliage unit placement records from c0[12] or from *c0[12]. The near-leaf pass (539ffb3f650a34ac) read every record as zero, normalised a zero vector and wrote NaN positions, so the near ShootLeaf meshes (BancLeafMesh, FoliageLeafNear) were never built and trees lost their leaves inside about 20 m. Run the candidate search for paired accesses too. If a loaded pointer competes with the pair, bind every candidate plus the paired pointer and let the backend pick the buffer at run time; otherwise keep the pair. Signed-off-by: Duck912 <350528965+Duck912@users.noreply.github.com> (cherry picked from commit a51a69f) * tests: bind both a cbuf pointer and the pointer loaded from it A native access whose address is a select or phi of c0[0x30] and a pointer loaded through c0[0x30] binds both buffers and stays global, as Ocarina of Time's near-leaf pass 539ffb3f650a34ac needs. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * shader: keep the pair when competing loaded pointers cannot be bound Binding the loaded pointers that compete with a paired cbuf pointer leaves the access global. The SPIR-V backend cannot route global subword accesses or most global atomics, so those accesses keep their pair. The loaded pointers are bound without the table budget, so a program already at 32 storage buffers overflowed the descriptor vector. When they leave too few bindings, collect again keeping the pairs. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * shader: keep the pair without 64-bit integers The SPIR-V backend routes no global access among bound buffers without shaderInt64, so a paired access left global would read zeros or fail to translate. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Signed-off-by: Duck912 <350528965+Duck912@users.noreply.github.com> Co-authored-by: Duck912 <350528965+Duck912@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
… CPUs on Windows (zortos293#232) SetCurrentThreadToPerformanceCores(), SetCurrentThreadToEfficiencyCores() and the other placement calls only did something on Android. On Windows a hybrid CPU (Intel P and E cores) could run the CPU core threads, the GPU front and back threads, the Vulkan worker, HostTiming and the fencing thread on an efficiency core, where a frame takes noticeably longer, while the texture decoder and pipeline builders competed for the fast cores. Read the CPU sets with GetSystemCpuSetInformation, take the highest EfficiencyClass as the performance group, and assign each thread its group with SetThreadSelectedCpuSets (a soft affinity the power manager respects, unlike affinity masks). Non-hybrid CPUs (one efficiency class) are unchanged; EDEN_HYBRID_CORES=0 turns it off. Both APIs are looked up at run time so older Windows builds still start. On an i9-13900K (8 P + 16 E cores) the time-critical threads now show the 16 P-core CPU sets selected and the texture decoder the 16 E-core sets (verified from outside with GetThreadSelectedCpuSets). Ocarina of Time in Kokiri Forest measured within noise of the previous placement (Windows already favoured the P-cores for those threads in that scene), so this is parity with the Android placement and a guard against the scheduler moving a hot thread, not a measured speed-up.
* video_core: fix GCC shadow error in pipeline cache The deferred shader pool local reused the name of the CreateGraphicsPipeline parameter. GCC treats this as a shadow warning, and Linux builds with ENABLE_WERROR=ON fail because -Werror=shadow promotes it to an error. Rename the local while keeping pool release and reuse unchanged. * tests: fix GCC shadow errors in Linux build Recent config, audio, and video tests introduced local names that shadow inherited members or the global Result type. With ENABLE_WERROR=ON, GCC treats those warnings as errors and stops the full build. Rename the test locals without changing their behavior.
MSVC builds with warnings as errors stop on C4388/C4389 in nx2_debug_server.cpp (std::optional<u64> == int) and buffer_cache_ports.cpp (u32 == int). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…93#167) swscale defines only the visible width of each output row: the bytes up to the stride are spill from its vector stores and can differ between two identical conversions. The scaler-path tests compared full pitch rows, so two of them fail on FFmpeg 9.0 with 24 differing bytes that are all in the chroma row padding past the output width (the visible pixels are bit-identical). Compare the visible pixels instead. The hand-built copy-path expectations stay byte-exact. Build-checked and tested on Linux with FFmpeg 9.0.2: all 674 test cases pass, including the two that failed before.
…cores (zortos293#698) zortos293#232 keeps about ten time-critical threads on the performance processors. On an i5-1235U (2 P cores, 4 logical) that crowded them: Ocarina of Time in Kokiri ran at 9.3 FPS, 11.5 FPS with EDEN_HYBRID_CORES=0. Place threads only with 8 or more performance logical processors. Claude-Session: https://claude.ai/code/session_01H8tHS3QiPU1NNDEpHaQTfr (cherry picked from commit fb0b747) Co-authored-by: AxelBcr <178737868+AxelBcr@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…uery walks early (all games) (zortos293#700) * video_core: invalidate whole blocks, stop query walks at the first dirty query From nemyu 99c953389 (Uesleizito Zin): the invalidation accumulator took a write's size before aligning its address down, so a write past the start of its 32-byte block got a range ending short of it, and storage buffers, textures and shaders over CB_DATA or inline-upload words kept stale data. EDEN_NX2_INVALIDATE_BLOCKS=0 rounds as before. From Orchid d38d025: IterateCache tested std::invoke_result (the trait, never bool), so IsRegionGpuModified never stopped early. SemiFlushRegion (Lichii zortos293#215) no longer returns from its callback, so it still writes every final value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit b3ce9f1) * video_core: drop the EDEN_NX2_INVALIDATE_BLOCKS switch, test the accumulator ranges The previous commit (cherry-picked from ProphetWild's fork) kept the old rounding behind EDEN_NX2_INVALIDATE_BLOCKS=0 for comparisons. Experiment switches don't ship in nx2, so the whole-block rounding is now unconditional. The tests pin the ranges: a lone word at 0x54 of its block, a run of words starting mid-block, and a write spanning blocks. All three failed before the fix (a word at 0x1054 gave [0x1040, 0x104c), which misses it). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: ProphetWild <133422166+ProphetWild@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…et (Ocarina of Time) (zortos293#692) EDEN_NX2_TC_EXPECTED_MIB / EDEN_NX2_TC_CRITICAL_MIB replaced the collector's thresholds outright, so a value above what the device budget allows kept the texture cache above a shrinking budget (other programs' video memory, the out-of-memory margins) and the driver paged: drops to 9 fps on an 8 GB card in Ocarina of Time's Kokiri Forest. The overrides now raise the thresholds past MaxCriticalMemory but never past the budget-derived value they replace, and expected stays at or below critical. Ported from upstream Eglantine 5d5fba1 (Ale, merged in upstream zortos293#727). Adapted: the TickFrame comment that said the overrides "still win" now says they raise the thresholds up to the budget. (cherry picked from commit 213442947234337d32040bf96cfe9e58e060445e) (cherry picked from commit 5d5fba1) Co-authored-by: Ale <ale@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…rer cmd 14 (zortos293#600) * audio: return the registered managers from nx2:aud:u, add IAudioRenderer cmd 14 Switch 2 titles open audio through nx2:aud:u, which constructed a fresh IAudioOutManager/IAudioRendererManager per call. A fresh manager owns its own AudioCore manager, but AudioManager accepts only the first registered buffer-release callback, so sessions opened through a second manager never release buffers: the title queues silent buffers and its audio thread stalls. Return the registered audin:u / audout:u / audren:u instances from the service manager instead, falling back to a private manager with an error log. GetAudioInManager gets the same shared-manager treatment. Also add IAudioRenderer command 14 (SetAsyncMemoryPoolControlEnabled, 23.0.0+) as a logged success no-op; newer-firmware titles call it. Fix diff supplied and tested by @javaflans in zortos293#539 (buffers released, audio plays). Adapted to current nx2. Not compiled or re-tested here. Assisted-by: Instinct (verification, adaptation) * audio: return the registered managers from nx2:aud:u, add IAudioRenderer cmd 14 * audio: return the registered managers from nx2:aud:u, add IAudioRenderer cmd 14
IDeliveryCacheFileService::Open checked the file name with the directory rule, which refuses a period, so every name with an extension failed with ResultInvalidArgument.
…tos293#642) * vulkan: add overflow-safe storage descriptor set budget helper * vulkan: include mesh emulation buffers in descriptor set limit * test: cover mesh fallback descriptor set budgets on constrained GPUs
…zortos293#756) HintCold runs every 30 s on the Switch 2 texture pool backing. On Linux it called madvise(MADV_PAGEOUT), which writes the pages to swap right away while holding the mmap lock. A guest core's mprotect (the JIT) then waits for that lock, and the whole emulator stops with it, audio included. Ocarina of Time on an i5-10400 with swap on a SATA SSD: the emulator stopped for 3-15 s every few minutes, with nx2:ColdHint in rq_qos_wait and a CPUCore thread in mprotect, every guest core idle. MADV_COLD only deactivates the pages, like VirtualUnlock on Windows, so the kernel pages them out first when RAM is short, as a03e816 intended. With it, no swap-outs and none of these stalls over about 30 minutes of play; the hitches left were pipeline builds. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…top reporting (zortos293#710) (zortos293#712) * input_common: compare motion mapping input with its first value Mapping a motion input ("Shake!") registers a device only when two consecutive reports differ by more than 1.5 g or 0.6 rotations/s. SDL3 delivers every sensor sample, so a controller reporting every 4 ms (the Steam Controller at ~250 Hz) never changes that much between two reports and could not be mapped through the UI. Compare each report with the first one received since mapping began instead, so a quick turn or shake crosses the thresholds at any report rate. The baselines are cleared when mapping begins and ends. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * input_common/sdl: restart motion sensors that stop reporting The SDL driver restarts a sensor only when its reports are all zero. A Steam Controller (2026, SDL's Triton driver) through its wireless dongle stops sending sensor reports altogether after it reconnects or when Steam Input, running at the same time, changes its IMU mode; SDL turns the IMU on only once, so motion stayed dead until the emulator restarted, and the motion mapping dialog had nothing to detect either. Track when each controller last reported a sensor sample and, from PumpEvents (every 250 ms), restart the sensors of a connected controller with motion that has been silent for a second, backing off to 8 s while it stays silent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… (Ocarina of Time) (zortos293#718) zortos293#121 (5b89a3d) paused the ZPassPixelCount64 occlusion counter across submissions and added a 1-bank (256-slot) reset bound in SamplesStreamer::CloseCounter() so Ocarina of Time's title screen and file select would not accumulate 16,384 slots across 64 banks before gameplay started. zortos293#309 (a08bf8f) added a power-of-two warning log to catch if a real gameplay reset-to-report window ever exceeded 256 slots. During extended Ocarina of Time (0400486024DEC000) gameplay across Hyrule Field, Kakariko Village, and the Shooting Gallery (with both Low and High GPU accuracy, EDEN_NX2_SPLIT=2, EDEN_NX2_FENCE_BATCH=1), real reset-to-report occlusion windows regularly span 257 to 346 slots across two banks: Render.Vulkan <Warning> vk_query_cache.cpp:CloseCounter:229: Samples query ran 314 slots with no reset or report, dropped its count (drop 256) Render.Vulkan <Warning> vk_query_cache.cpp:CloseCounter:229: Samples query ran 277 slots with no reset or report, dropped its count (drop 1024) Render.Vulkan <Warning> vk_query_cache.cpp:CloseCounter:229: Samples query ran 281 slots with no reset or report, dropped its count (drop 2048) Render.Vulkan <Warning> vk_query_cache.cpp:CloseCounter:229: Samples query ran 343 slots with no reset or report, dropped its count (drop 16384) Each drop zeroes a live ZPassPixelCount64 window mid-frame before the guest's report reads it. Raise the running-query bound in SamplesStreamer::CloseCounter() from 1 bank (256 slots) to 4 banks (1,024 slots), and fix the off-by-one boundary check in queries_prefix_scan_sum.comp ( otal_work < gl_SubgroupSize * LOCAL_RESULTS, since otal_work = accumulation_limit = runs_to_do - 1 is the 0-based last slot index and a 257-slot dispatch on 32-lane subgroups spans into subgroup 1 at slot index 256).
…s293#727) Registers StubReplyZeroed handlers for nx2:vi2 command 201 and IGeneralService command 101. These commands are called during early boot on newer SDKs (including The Legend of Zelda: Ocarina of Time), preventing the emulator from crashing on unimplemented function assertions. Fixes zortos293#653.
…rtos293#714) EDEN_NX2_GPU_PROFILE, EDEN_NX2_CMDPAGE_DIAG, EDEN_NX2_DOWNLOAD_TRACE, EDEN_NX2_PB_CHECK, EDEN_NX2_3D_METHODS and EDEN_NX2_SEM_WATCH were read by namespace-scope initializers. Those run before main(), so before LoadNx2Switches puts nx2_switches.txt into the environment: set in that file, the switches did nothing, and only a real environment variable turned them on. The three header flags stay plain globals (Enabled() runs several times per method write) and VideoCore::CreateGPU reads them again. The DmaPusher and Maxwell3D switches become const members read when the channel is made, like Maxwell3D's record_write_history.
) * loader: prefer the en-us NUP title over the default title Assisted-by: Claude Opus 5.5 (code changes) Signed-off-by: Kabosune <59909498+Kabosune@users.noreply.github.com> * loader: show the en-us NUP title in the library when there is one ReadTitle returns display_title before it reads the NACP, so the en-us title has to win there too. Assisted-by: Claude Opus 5.5 (code changes) Signed-off-by: Kabosune <59909498+Kabosune@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Signed-off-by: Kabosune <59909498+Kabosune@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… collecting (zortos293#268) 4d8a962 capped the collector's critical threshold at 5 GiB on every GPU. On a 16 GB card Mario Kart World's races then sit at the cap: the collector evicts render targets every frame, which are recreated and uploaded from guest memory again (~4100 uploads, 5 GiB per 600 frames). The cap is now 5/8 of the driver's memory budget, at least 5 GiB, so cards up to 8 GB are unchanged. GetDeviceLocalMemory is not used: it is capped near 7 GiB. Mario Kart World, Wario Stadium staff ghost replay, RTX 5080 (16 GB, critical 9.3 GiB), 2 runs: 53.0 -> 58.7 fps, p90 26.7 -> 18.4 ms, p99 45 -> 30 ms, GPU back thread 13.1 -> 11.0 ms a frame. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ortos293#676) (zortos293#721) * video_core: replace Ocarina's movie network by an input copy (port of Eglantine 92f1191) The movie network's emulated matrix multiplies take about 33 ms of GPU time per frame, which held the intro and cutscenes at 30 fps. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * kepler_compute: limit the movie network replacement to Ocarina and handle stale input and failed copies Addresses review feedback on the port: gate on the Ocarina program id, expire the retained input with the skip window, and fall back to the real network when the copy fails. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…euse, per-method switch reads (zortos293#778) * tests: a remembered rejection marks its page in the shader cache's filter From zortos293#408 (the shader_invalidation.cpp part of ba882cc), for the zortos293#250 port above, which takes the RememberRejection mark from the same commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 16b4992) * tests: cover the K=16 software HMMA loop's operand halves and rounded stores From zortos293#408 (the spirv_software_matrix.cpp part of ba882cc), for the zortos293#174 port above. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit e30e573) * tests: cover the page filter and the Ocarina of Time network bypass setting zortos293#250 and zortos293#319, ported above, came without tests. The page filter tests check marks across pages and 64-page words, empty marks, and the conservative answer outside its 64 GiB range. The bypass tests check the tonemap shader hashes and that the bypass follows the setting and the title. Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit ad1c0f6) Taken from zortos293#408 (ad1c0f6); applied cleanly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit c63b0b1) * fence_manager: with the GPU profile on, log why fences are made instead of skipped Ocarina's Castle Town: 6.1-7.7k fences made a second, 4.3-5.4k of them for committed cache flushes (preflush downloads) and 1.8-2.3k for query cache fence work; 6.3-8.4k skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit d6fb9f5) * debugger: speed_limit method lifts or restores the speed limit live Test scripts boot through the logos and menus unlimited, then restore it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 5b562f8) Adapted: nx2 has no live switch method to sit beside, so only speed_limit is added, and docs/nx2/debug-mcp.md documents it. * video_core: read EDEN_NX2_SKIP_SAME and EDEN_NX2_PERF_VERIFY once per Maxwell3D From zortos293#488 (9010ecd, mstampfli), second half: SkipSameValueWrites() and Nx2Perf::Verify() run for every 3D method on the back and kept their value in a function-local static, so each call checked its initialization guard (0.9% and 0.3% of the back thread in their profile). Adapted: the PR reads both into namespace-scope statics at static initialization, which runs before main() loads nx2_switches.txt (LoadNx2Switches), so a switch file setting either would be missed. Here Maxwell3D reads them in its constructor into members. Other Nx2Perf::Verify() callers are unchanged. Not taken from zortos293#488: its same-value drop of vertex_buffer.first/count (already done by our port of zortos293#178, bd27dc7) and of index_buffer.first, which the front sends on purpose (it sets Dirty::IndexBuffer; our EDEN_NX2_EXP2 is the guarded version). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 4f9a471) Adapted to zortos293#714: the two switches are const members with default initializers, read when the engine is made like trace_first_method_writes, instead of assignments in the constructor. nx2 has no FrontDmaReadEarly, so that hunk is dropped. * video_core: bind no texture for a handle word that is a storage buffer address Ocarina of Time's water material (shader 68d269bc849da37c, the Zora's River waterfall and other water) samples a light's texture in a clustered light loop: the handle is loaded per pixel from the light buffer, so no draw-time handle program can compute it. The translation then falls back to the first constant buffer word on the handle's path, c0[0x520], which is the light buffer's address. Its low bits named an unrelated texture (an HDR render target, a zero water simulation map), and the game double-buffers that buffer, so the texture changed every frame and the water flickered every other frame in bursts. A texture whose handle word is where a storage buffer of the same program keeps its address now gets the null view and sampler. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit d603d98) Adapted: unconditional. The fork's EDEN_NX2_EXP21 switch (default on) is dropped, since nx2 doesn't merge experiment switches. * video_core: keep two idle upload staging buffers per level up to 32 MiB Walking through Hyrule Field in Ocarina of Time, single uploads of new 2D images held a draw's setup for 2-68 ms. Upload staging buffers are now kept, two idle per level up to 32 MiB (under 128 MiB), instead of being freed at each 64-frame sweep: uploads over 8 MiB miss the stream buffer and got a new dedicated allocation after every pause. This is bit 1 of the fork's EDEN_NX2_EXP8 (7d75f91, on by default there), taken alone and unconditional. Its other bits (skipping uploads a join copy overwrites, banded unswizzling, no HintCold) are not taken. (cherry picked from commit 7d75f91, staging pool part only) * tests: a page filter mark past its space leaves the tracked range untouched The page filter test imported from the fork (c63b0b1) expects the first version of zortos293#250, where a mark past the 36-bit space set an overflow flag that made every query answer yes. nx2 has zortos293#250's later commit 65e3a09, which leaves such ranges untracked so images without CPU backing don't switch the filter off. Check that behaviour instead. * fence_manager: no branch hint on the fence decision profile check docs/Coding.md leaves branch likelihood to PGO. --------- Co-authored-by: Frizti <FriztiMouche@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: ZeroOneTV <wwwteko@hotmail.com>
Switch 2 compute storage pointers have no size, so a written binding was marked GPU-modified for up to 8 MiB. Ocarina of Time's skinning dispatches write tens of kilobytes; the rest of that range is the CPU's own per-object and exposure data. Fences then copied the whole range back, and the CPU's later writes to those pages waited for the next fence. The recompiler now proves an affine bound on each store, and a dispatch marks only those bytes. An unproven store still marks the whole binding. The binding size is unchanged, so reads are unchanged. Rebased onto build-117 from zortos293#415, without the Auto/Full/Cap64K setting: unknown extents already keep the previous full-binding mark. Co-authored-by: xTektor <xTektor@users.noreply.github.com>
Copy the proven nup-romfs-windows workflow onto this branch so a push packages lichii.exe without publishing a release. The binary is tagged pr4-write-extent and the job also runs for cursor/oot-kokiri-playable-*. Co-authored-by: xTektor <xTektor@users.noreply.github.com>
Outdoor Kokiri on the write-extent build still spends about 300 ms of every second in macro DMA read drains, about 30,000 reads a second of a few dozen bytes, with buffer downloads already at 0. Those reads are the occlusion report pool. The front drained the whole back thread before each one, including state methods that cannot change the report. Report and semaphore pages are now noted as the front sends them. A read of a page in that pool, which shaders and copies have not written and whose host query has already landed, is taken from guest memory plus any queued release whose payload the front knows. Anything else still drains, as before. The report address is address_high/address_low, after the compare block. EDEN_NX2_DMA_READ_CHECK=1 drains as well and logs reads that differ. Ported from zortos293#243, which measured this as the occlusion path (~30k reads/s) and saw no differing reads under the check. Co-authored-by: xTektor <xTektor@users.noreply.github.com>
Outdoor play after the occlusion-report fast path still spends about 160-490 ms of every second draining the back thread on semaphore acquires, about 1,600 blocking drains a second. zortos293#337 is not in this branch, so those drains are not the payload-fence wait that change introduced. Each poll drained the back even when the word was released by the CPU and no GPU release was queued. A read now waits only until a queued GPU release of that word has run. When none is queued, the host write has landed and the buffer cache is clean, the word is taken from guest memory. A value that already matches is not trusted while a release is still queued, which is the stale >= case. Anything the buffer cache or an unfinished host query may still hold drains and flushes as before. Co-authored-by: xTektor <xTektor@users.noreply.github.com>
…locking the front Outdoor play after the acquire fast path still spends about 200 ms/s waiting for the GPU to finish the copy of a command list, and about 275 ms/s in the writer wait before that read. Both GPU threads stand still for the copy. The lists are still only read after the GPU wrote them. A copy that is not finished suspends that channel, the same way a semaphore acquire does. The scheduler runs the other channels and the queued work, and the channel parses the entry again once the copy's tick is done. Resuming skips the writer wait: it already ran, and waiting again would also wait for work other channels queued during the suspension. If the step returns before that read, the skip is cleared. Co-authored-by: xTektor <xTektor@users.noreply.github.com>
The ec8e93f Legion outdoor Kokiri profile still spends about 378 ms/s of the front thread in the writer wait (list writers ~284 ms/s) before command entries are parsed, plus ~193 ms/s of pending sleep. Every entry on a page no GPU work had written waited for every queued compute and copy writer, and that wait usually left the back with nothing queued. An entry that begins between methods (method_count == 0) is a command buffer the CPU writes: the words GPU work writes for a later fetch are parameters of a method begun in the entry before. Such entries no longer wait for compute work. Entries that begin inside a method's data still wait, and every entry still waits while copy-engine work is queued (DmaPusher notes the last MaxwellDMA writer position), because a copy can place GPU-written words anywhere. The wait that remains stops at the work sent before the last host-level sync (NoteSync): the GPU only orders a fetch after earlier work across such a wait. Lists read from GPU-written pages wait the same way, under a separate "list copy writers" drain site, so the next profile can tell them apart from entry waits. A list whose copy is not finished still suspends the channel and resumes through the existing Pending path. Before the GPU thread sleeps on an empty queue with suspended channels it now publishes the words it has sent, so the back is not left idle for the whole sleep. Not measured on the Legion yet; no FPS claim. Ported from zortos293#805 (commit d4e8d85 by jpkto, plus the publish-before-sleep piece of that PR): zortos293#805 Tests: command_entry_wait (new), async_report_settled, semaphore_acquire, list_read_resume linked standalone against Catch2 and pass; touched translation units compile with GCC 14. Co-authored-by: jpkto <255363096+jpkto@users.noreply.github.com> Co-authored-by: xTektor <xTektor@users.noreply.github.com>
… data downloads Ported from javocsoft#17 (commit edab3cc, "texture_cache: keep transcoded textures in host memory, skip dead GPU data downloads"): javocsoft#17 - TranscodeCache keeps CPU-transcoded texture data (BC1/BC3 or RGBA8) by a hash of the guest data and layout, up to a sixteenth of host memory (256 MiB to 2 GiB), least recently used first. The synchronous upload path and the async decode path both use it; an async hit is uploaded at the next tick without the zero-filled placeholder. - The texture collector no longer downloads a GPU-modified image whose bytes newer GPU writes cover whole (HasDeadGpuData over its overlaps and aliases, RangesCover); it is evicted without the download. - EDEN_NX2_TC_LOG also reports real allocations, GPU-written and rescaled shares, transcode cache hits/misses and the collector's dead evictions. Adapted to this branch: the collector here downloads inline (no batched EvictedDownload path), so the dead-data check sits in the inline path and the batched-wait slow-frame logging of the original is not taken. The EDEN_NX2_SLOW_FRAME_MS diagnostic commit of that PR is not included. The author measured this on an RTX 3050 Laptop 4 GB under Linux; it is not measured on the Legion (RTX 2060 6 GB, Windows) yet. Our ec8e93f profile shows 0 buffer downloads, so the download half may stay quiet; the transcode half targets host CPU time. No FPS claim. Tests: range_cover and transcode_cache (from the original PR) linked standalone against Catch2 and pass; touched texture cache / Vulkan translation units compile with GCC 14. Co-authored-by: JavocSoft <javocsoft@gmail.com> Co-authored-by: xTektor <xTektor@users.noreply.github.com>
…in, EDEN_NX2_BACK_MACROS=1) The ec8e93f Legion profile still spends ~102 ms/s of the split's front in macro DMA reads. Ocarina of Time's render enable macro reads report words with MME_DMA_READ_FIFOED; on the front each read waits for the queued work before it (the 88f1b3d fast path only helps when the report words have already landed). A static pass over the macro code (TuringMacroInterpreter:: AnalyzeEffects) finds the methods a call can write and whether it uses the data RAM. A macro that reads memory into its FIFO, uses no data RAM and writes only registers the front can do without (BackWritable: not macro code/bindings, shadow RAM mode, draws, constant buffer data, reports, uploads, syncpoints, MME_DMA_READ) is sent to the back as one entry with its parameters (GpuSplit::DeferWords) and runs there with a macro engine of its own, reading the words as a drained front would. The registers it can write are the back's until the front writes them (FrontTakeOver) or needs them (FrontRegister, "back register" drain site); a macro code change waits for queued back calls ("back code"). If the back sees a write outside the predicted set, that macro runs on the front from then on and the front resynchronises (FrontVerify). Interaction with this branch: macros that stay on the front still use the 88f1b3d occlusion-report fast path (FrontDmaReadQueued), which now reads the data RAM address through FrontRegister. Back macros never write report or semaphore registers, so the 3863d03 acquire path and its queued-release tracking are unchanged. FrontMacroCountsDraw from the Fritchii port does not exist in this tree and is not added. Off by default: set EDEN_NX2_BACK_MACROS=1 to test it. Not measured on the Legion; the Fritchii author saw front MME DMA wait 218 -> 0 ms/s but no FPS change on an RX 6900 XT. No FPS claim. Ported from Frizti#24 (mkooner; commit 2f82fd2, itself a port of zortos293#805 a442c38 by jpkto), the BACK_MACROS piece only: Frizti#24 zortos293#805 Tests: turing_macro_effects (from the original) linked standalone against Catch2 and passes (6785 assertions, incl. random code checked against the interpreter). maxwell_3d.cpp, macro.cpp, gpu_split.cpp and maxwell_fast_method.cpp compile with GCC 14; the split end-to-end test needs the full tests binary and was not run here. Co-authored-by: jpkto <255363096+jpkto@users.noreply.github.com> Co-authored-by: xTektor <xTektor@users.noreply.github.com>
…included Ported from zortos293#869 (commit 4e3142e by Xiphias1337): zortos293#869 Ocarina of Time (0400486024DEC000) streams its data through a statically linked zstd 1.4 while it plays (about 130-160 MB/s in Kokiri Forest per the author); the JIT-compiled sequence decoder took about a quarter of the two busiest guest cores. Our ec8e93f Legion profile still shows host_cpu_busy ~336 ms/s. The loader patches the first instruction of ZSTD_decompressMultiFrame, ZSTD_decompressBlock_internal and ZSTD_decompressContinue in main (build E0F3CCFAF50065AE4EA01EB5AEB13B1A45DA3348, each checked against its original code) with an SVC Horizon does not have; Svc::Call hands it to Core::OotZstd::Handle, which decodes with the host's libzstd (a twin ZSTD_DCtx per guest dctx for the streamed blocks). Calls the host's zstd refuses run the game's code as before. The Legion's eden-log for the ec8e93f session shows this exact main build ID, so the hook should engage there; the log lines "Ocarina zstd: ..." confirm it. Added here: EDEN_NX2_OOT_HOST_ZSTD=0 leaves the game's decoder alone, for A/B and as a fallback if anything looks wrong. Not measured on the Legion. The author's 36 -> 56 fps (RX 7900 XTX + 9800X3D, 1.5x, stacked with zortos293#717 pieces) does not transfer; no FPS claim. The image should be unchanged (decoded bytes are identical). Tests: none upstream; oot_zstd_hook.cpp, svc.cpp and nso.cpp compile with GCC 14. Co-authored-by: Xiphias <12048705+Xiphias1337@users.noreply.github.com> Co-authored-by: xTektor <xTektor@users.noreply.github.com>
Ported from Spikol02/Nyamu#15 (commits 1ffea4b47e1d and 546c16dbb347): Spikol02/Nyamu#15 UsageTracker::Track is called on every storage buffer bind. This tree already skips the last few identical ranges and keeps one run of pages known to be full ([full_begin, full_end)); anything outside that run was rewritten page by page. Draws bind several disjoint multi-megabyte storage buffers between resets, so only one of them got the fast path. Now one bit per 4 KiB page records that the page is all ones, and pages outside the run are filled 64 at a time only where a bit is missing. Reset clears the bits with the pages. The existing run and the Nx2Perf verify path are kept. EDEN_NX2_USAGE_TRACKER_SKIP_FULL=0 writes every page again, as before. The author measured back "bind stage buffers" 137-165 -> 81-96 ms/s on an RX 9070 XT + 7800X3D (Castle Town). Not measured on the Legion; this is back-thread CPU, not the front writer wait. No FPS claim. No visual change expected: what IsUsed reports is unchanged. Tests: usage_tracker.cpp gains the original's randomized comparison against the old tracker and the reset case; all 7 cases pass standalone (with and without EDEN_NX2_USAGE_TRACKER_SKIP_FULL=0). Co-authored-by: Spiks <42014907+Spikol02@users.noreply.github.com> Co-authored-by: xTektor <xTektor@users.noreply.github.com>
Ported from Spikol02/Nyamu#21 (commit 5667042cb0a8), itself from Frizti/Fritchii 82a57c3 by Frizti; the cost was found in zortos293#101 (Kradnah's OoT frame dump issue, measured by d3zessete): Spikol02/Nyamu#21 Not already in this tree: a native Turing graphics program's storage pointer was still bound as a bare pointer, up to 8 MiB of its mapping, which every draw then synchronizes and tracks. Now the word after the pointer is taken as the size, as Fritchii ships it. Table pointers keep their own sizing. EDEN_NX2_GRAPHICS_SSBO_BARE=1 binds the mapping again. Reported upstream as about 9% in OoT; not measured on the Legion here. No FPS claim. Visual risk: if a shader reads past the size word the binding is short and the read returns zero; upstream reports vegetation looked the same. A/B with EDEN_NX2_GRAPHICS_SSBO_BARE=1 if anything goes missing. Build check: vk_buffer_cache_base.cpp compiles. Co-authored-by: Spiks <42014907+Spikol02@users.noreply.github.com> Co-authored-by: Frizti <FriztiMouche@gmail.com> Co-authored-by: xTektor <xTektor@users.noreply.github.com>
The kernel-lock part of javocsoft#16, commit e0ae6b0 by JavocSoft, itself from zortos293#717 by Stephen Jayakar (stephenjayakar). Only this commit: Occlusion Off (ed2b38a) and the rest of zortos293#16 are not taken. javocsoft#16 - KSpinLock spins for up to 20 us instead of 128 pauses before it sleeps. - KAddressArbiter watches the word for up to 8 us before a thread sleeps in WaitIfLessThan or WaitIfEqual. If the word stops satisfying the wait, it ends with ResultInvalidState, which is what the kernel returns to a thread that entered the SVC that much later. A thread whose spins seldom pay off stops spinning. - PhysicalCore guards its context with a KSpinLock, and Idle waits on the interrupt flag itself instead of a condition variable. Its 100 us idle poll pauses instead of yielding. - KThread's context guard, which the scheduler only try_locks, is an atomic flag. - The current thread and host thread ID are read from constinit thread_locals. - GetParentReference computes each member offset once. - SpinRecursiveMutex::try_lock reads before its compare-exchange. - Memory traps on GPU-cached pages reuse the host pointer the access already looked up, keep the last four clean read areas per core instead of one, and remember a core's last host page to device page translation until the device mappings change (DeviceMemoryManager:: MappingGeneration). Threads that are not guest cores get their own dirty memory slot instead of sharing, and locking, the last core's. Applied as is except src/tests/CMakeLists.txt, where kernel_locks.cpp was added by hand next to the other core tests. Upstream measured Kokiri Forest on an RTX 3050 Laptop with Occlusion Off, 27.9 -> 29.4 fps over two runs. Not measured on the Legion here, and this tree runs without Occlusion Off; no FPS claim. Visual risk: none expected; this is CPU-side scheduling and memory trap bookkeeping. Behaviour risk: KAddressArbiter's spin can end a wait with ResultInvalidState when the word changes while spinning, which is what a late SVC entry would see; watch for hangs or stutter in titles that wait on arbiter words. Tests: new kernel_locks.cpp, 2 cases pass standalone. Touched core TUs and k_scheduler/k_thread/svc/k_condition_variable compile. Co-authored-by: JavocSoft <javocsoft@gmail.com> Co-authored-by: Stephen Jayakar <aj12ay@gmail.com> Co-authored-by: xTektor <xTektor@users.noreply.github.com>
Visual commit for the OoT canopy haze, kept separate. Ported from zortos293#825 by pvns1108 (commits db2a16d and cd9e71e), fixing zortos293#745 reported by justin-wq1337: zortos293#825 zortos293#825 was taken over the overlapping zortos293#811 (janvitos) because it also fixes Select/LowerFp16ToFp32 for SelectF16. - FFMA.FMZ.SAT and HFMA2.FMZ.SAT skipped the FMZ select when SAT was set, so 0 * inf + c became sat(NaN) = 0 instead of sat(c). The select now always runs, then SAT. FMUL/HMUL2 keep the SAT shortcut. - HFMA2.FMZ and HMUL2.FMZ compared F16 operands with an F32 zero; the zero now takes the operands' type. - IREmitter::Select builds SelectF16, and LowerFp16ToFp32 maps SelectF16 to SelectF32 for hosts without float16. Applied as is except src/tests/CMakeLists.txt (line added by hand). Checked: EDEN_NX2_OOT_VISUAL_WORKAROUND is not in this tree. It lives in sylvieistaken/selevet-eglantine as an opt-in (=1) per-shader edit of the OoT fog integration and log-bloom exposure, marked temporary and unvalidated, and it also thins interior fog. Not ported. Visual risk: changes shader output wherever a title uses FFMA/HFMA2 with FMZ+SAT (expected to restore the addend, e.g. haze/fog terms) and any shader cache built before this will recompile. Not checked on the Legion yet; needs an outdoor A/B in Kokiri Forest. No FPS claim. Tests: new maxwell_fmz.cpp, 4 cases pass standalone; 3 of them fail against the old translation. Co-authored-by: pvns1108 <pvns1108@gmail.com> Co-authored-by: xTektor <xTektor@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes
Twelve commits. Commits 1–4 are unchanged and 4 is now measured. Commits 5–12 are new and unmeasured. No FPS claims for them.
1. Native compute write marks (
a47c72f3b0)A Switch 2 compute storage pointer has no size, so a written binding was marked GPU-modified for up to 8 MiB. Ocarina skinning writes tens of kilobytes; the rest of that range is the CPU's own data. Fences copied it back, and the CPU's later writes waited.
The recompiler now proves how far each store reaches. A dispatch marks only those bytes. An unproven store still marks the whole binding. Reads are unchanged.
Legion result (MSVC
5fb53b44, Vulkan, docked, 1x, GPU accuracy Low, no DLSS, no Dynamic FPS): buffer downloads are 0 KiB in every gameplay window. Outdoor Kokiri was still about 10–16 fps. The download path is no longer the limiter.2. Occlusion-report DMA reads (
88f1b3d9ae)The front was draining the whole back thread on every macro DMA read, about 27,000–30,000 reads a second. Those are occlusion reports. A read of a landed report, on a page shaders and copies have not written, now comes from guest memory. Anything else still drains.
Legion result (MSVC
88f1b3d9, same settings, two outdoor sessions including a warm cache): macro DMA read wait fell from about 270–373 ms/s to about 20–100 ms/s. Warm village under trees is typically 20–28 fps, where the previous build was 10–14. Walking is often still 12–14. Camera turns are typically 20–30. Overall outdoor is about 12–25, overlay average 15. Stalls to 0 fps remain. Buffer downloads stayed 0. The canopy haze is still there. Not playable 30.Split drain stayed high (session 2 often 250–580 ms/s) because acquire waits did not move (160–490 ms/s, mean about 280).
3. CPU semaphore acquires (
3863d03ef9)zortos293#337 is not in this branch.
CounterReportstill treats a fence-flagged report as synchronized at GPU accuracy Low with Default fences, and the fence manager can still skip an empty fence. The acquire cost in the88f1b3d9profile is not the host-GPU wait zortos293#874 removes from zortos293#337. It isDrainBackon every acquire check, about 1,600 blocking drains a second, including polls of a semaphore the CPU releases.zortos293#874 therefore does not apply here. zortos293#805 and zortos293#717 were not ported.
A read now waits only until a queued GPU release of that word has been executed. When none is queued, the host write has landed and the buffer cache is clean, the word is taken from guest memory and the back is not drained. A value that already matches is not trusted while a release of that word is still queued, which is the stale
>=case from zortos293#231. A dirty buffer cache or an unfinished host query still drains and flushes.Query ordering is unchanged, so this does not reintroduce the white flashes zortos293#337 fixed on trees that have that commit. A bug here would be a channel running past an acquire whose GPU release had not run.
Legion result (user play around Kokiri, same settings, window title
3863d03ef9): acquire site wait mean 93.6 ms/s versus 278.6 on88f1b3d9. About 2,214 acquires/s, of which about 6.5% waited. Split drain mean 214.9 ms/s versus 412. Outdoor overlay on the verified frames is typically 19–24 fps (one 10 fps village sample, one 0 fps stall). Not stable 30. Canopy haze remains. Buffer downloads stayed 0.What did not fall: writer wait mean 370 ms/s, list writers 276 ms/s, list GPU wait 199 ms/s, fence release 115 ms/s.
4. Command-list copy no longer blocks the front (
ec8e93f750)Those writer and list-GPU waits are the front standing in
WaitSubmittedTickuntil the GPU finishes the copy of a command list the channel is about to read, afterWaitForWriters. Upstream zortos293#752 measured the same pattern in Kokiri Forest: about 2,400 such waits a second, about a quarter of the time, with the back idle and more work already queued. This is that change, not the rest of zortos293#805.A copy that is not finished suspends that channel, the same way a semaphore acquire does. The scheduler runs the other channels and the queued work. The channel parses the entry again once the copy's tick is done. The list is still read only after the GPU wrote it. Resuming skips the writer wait, because that wait already ran and waiting again would also wait for other channels' work queued during the suspension. If the step returns before that read, the skip is cleared.
No fps number for this commit when it was pushed.
Legion result for
ec8e93f750(outdoor Kokiri, same settings): 12–26 fps. Profile per second: front writer wait ~378 ms/s, list writers ~284, split drain ~232, pending sleep ~193, resume pending ~103, acquire ~93, macro DMA read ~102, buffer downloads 0, host_cpu_busy ~336. The old list GPU wait counter is gone, since the front no longer stands in the copy wait. Writer wait and list writers did not fall, which led to commit 5.5. Command entries between methods skip the compute-writer wait (
62a5a42b3)Finishes the unpushed slice of the Cursor agent. The front ran
WaitForWritersat every command-list entry. Now it does that only when the entry starts a new method (method_count == 0skips it) or when a DMA copy writer is still pending (CommandEntryWaitsForWriters). The pending copy writer is tracked byNoteCopyWriter. A command-list read waits only for writers up to the last sync (WaitForSyncedWriters, a newlist copy writersdrain site). The GPU thread also publishes before the front sleeps on an empty queue. Credit: jpkto, and zortos293#805 (d4e8d85d4d4f). The-mtls-dialect=gnu2change was left out. Unmeasured.6. Transcode cache and dead-data downloads (
6dad7ae89)From javocsoft#17 (
edab3cc934). Transcoded textures are kept in host memory, and the GC skips downloads of GPU data nothing will read again. The log reportswith dead datadeletes and the transcode cache stats. Unmeasured.7. Back macros, opt-in (
82713612c)From Frizti#24 by mkooner (
2f82fd2d5755, a port of jpktoa442c38fe6). Macros that read memory run on the split's back thread, andAnalyzeEffectsproves which registers they write. Off unlessEDEN_NX2_BACK_MACROS=1. Unmeasured.8. OoT zstd on the host (
a6fe18b57)From zortos293#869 by Xiphias (
4e3142e4f912). Ocarina of Time's zstd decompression, streamed blocks included, runs on the host via an SVC hook. It is gated on the main build IDE0F3CCFA…(matches the user's log) and the expected original code. Otherwise it does nothing.EDEN_NX2_OOT_HOST_ZSTD=0turns it off. Unmeasured.9. UsageTracker full pages (
322e4ddfd)From Spikol02/Nyamu#15 by Spiks. One bit per page records pages already full, so they are skipped 64 at a time.
EDEN_NX2_USAGE_TRACKER_SKIP_FULL=0turns it off. Unmeasured.10. Native graphics SSBO sizing (
f76899f23)From Spikol02/Nyamu#21, itself from Frizti/Fritchii
82a57c333175. The cost was found in zortos293#101. A native graphics storage pointer takes the next word as its size instead of binding up to 8 MiB. This was not already in the tree.EDEN_NX2_GRAPHICS_SSBO_BARE=1restores the old binding. Unmeasured.11. Cheaper kernel locks and memory traps (
0d57a3921)The kernel-lock commit of javocsoft#16 (
e0ae6b08b99f), from zortos293#717 by Stephen Jayakar. It covers KSpinLock, the KAddressArbiter spin, PhysicalCore idle, the KThread context guard, and GPU memory trap caches. Occlusion Off and the rest of zortos293#16 are not taken. Unmeasured.12. FFMA/HFMA2 FMZ with SAT (
c684f4af8), visualFrom zortos293#825 by pvns1108 (fixes zortos293#745). zortos293#825 was picked over zortos293#811. FMZ+SAT no longer drops the addend, and the F16 FMZ select is fixed. This is the candidate for the canopy haze. Not checked in game.
EDEN_NX2_OOT_VISUAL_WORKAROUNDis not in this tree. It is a temporary opt-in per-shader edit in selevet-eglantine and was not ported. Unmeasured.New switches
EDEN_NX2_BACK_MACROS=1: enables commit 7. Off by default.EDEN_NX2_OOT_HOST_ZSTD=0: disables commit 8.EDEN_NX2_USAGE_TRACKER_SKIP_FULL=0: disables commit 9.EDEN_NX2_GRAPHICS_SSBO_BARE=1: reverts commit 10.What this does not claim
Commits 5–12 are unmeasured. For commit 5, watch writer wait (~378 ms/s) and list writers (~284 ms/s). A wrong resume would show up as a command list read before its copy finished: garbage methods, a channel stuck on a release, or popped geometry. Not a claim of sustained 30.
Testing
storage_write_extent: 13 tests, 61 assertions, passed.async_report_settled: an issued slot blocks until it completes. Passed.semaphore_acquire: a CPU word is read from memory; a queued release is waited for even when the old word would match; a dirty cache or unfinished query drains; the newer of two releases is the one waited for. 4 tests, 8 assertions, passed.list_read_resume: a resumed read skips the writer wait once; clearing the flag before the read does not skip a later wait. 2 tests, 4 assertions, passed.dma_pusher.cpp,vk_rasterizer.cpp, and the VulkanBufferCacheinstantiation build with-Werror.command_entry_wait(new),async_report_settled,semaphore_acquire,list_read_resume: passed.transcode_cache,range_cover(new): passed.turing_macro_effects(new): passed, 6,785 assertions.oot_zstd_hook.cpp,svc.cpp,nso.cpp: compile. No upstream test.usage_tracker: 7 cases, 279,715 assertions, passed with and withoutEDEN_NX2_USAGE_TRACKER_SKIP_FULL=0.kernel_locks(new): 2 cases, passed. Kernel TUs build.maxwell_fmz(new): 4 cases, passed. 3 of them fail against the old translation.nup-romfs-windows.Legion check
Same settings as the
3863d03ef9user-play run. Outdoor Kokiri, trees and walking, not the title screen.EDEN_NX2_GPU_PROFILE=1. Compare againstec8e93f750: writer wait ~378, list writers ~284, split ~232, acquire ~93, DMA ~102 ms/s, downloads 0.EDEN_NX2_GRAPHICS_SSBO_BARE=1).