8 Commits

Author SHA1 Message Date
chrg6
222a025c52 Give back what a host thread keeps for itself when it ends.
Every guest thread is a host thread, and the game makes and ends them all through play. A thread that slept through plat_wait_ns kept a waitable timer, one that read or wrote a file an event for each, and one that decoded zstd through the host a decoder context of about 100 KiB; nothing closed or freed them when the thread ended, and a detached thread's own record stayed allocated. They are now given back when the thread's function returns: the handles in win32.c thread_main, the decoder context through plat_thread_end_hook, and the record by whichever of the thread and its detach comes last.

tests/thread_end_check.py runs 416 such threads and compares the process's handle count before and after. tests/sync_wake_check.c gets the two stand-ins kernel.c has needed since the service wait log, so that check builds and passes again. The Linux file only gains the hook call and was not compiled here.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 15:18:25 -05:00
chrg6
61e1ca8bad Drop a zstd stream's slot by its stream state under one hold of the lock, not by a pointer found earlier.
Between the lookup and the drop another thread could take the spent slot for its own stream; the drop then cleared that thread's slot, and a third thread could take it as free and reset the decoder context still in use.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 23:24:34 -05:00
chrg6
ded13958de Log the sizes of the caches, pools and tables under the perf trace, every 600 presents and with each memory snapshot.
Four "mem: sizes" lines give the entries and bytes of what no other line counted: the guest heap, physical maps, kernel region, lazily written ranges and the fixed kernel, setjmp, save-file, RomFS and zstd tables; pool records, mirrors, deferred destructions, upload and staging buffers, the device address table and the texture, sampler and slot caches; image views, descriptor spares, the completion queue, uniform snapshot arenas, recording copies and pins; and pipelines, layouts, stage libraries, job queues, the shader memory cache and the translator processes. Each subsystem reads its own counters under its lock; settings now count the replaced values they keep. Segment and scene lines say "-" for the world clock before it starts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 19:51:36 -05:00
chrg6
66aedffcdd Name the work behind each frame on any thread under the perf trace: RomFS reads, zstd decodes, pipeline creates, guest memory maps, pool initialize and finalize, and device memory allocations.
A slow frame's line and the 60-frame window's line now say how long each kind took on any thread, how often, how many bytes and the longest single one, so a hitch outside the render thread's own waits has a named cause. While NX2NAVI_TRACE=perf is off each site pays one branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 19:27:58 -05:00
chrg6
f17f4f9bcc Decode the game's zstd files on the host by default on 1.1.1 as well.
That build kept the game's own decoder until its sites had been through a check run. The check has now run over a village and a field: 3,072 streams and 31.7 GiB of output compared byte for byte, none different. The game's decoder took about a fifth of the running time of the game's threads in a village and about a second of each area load. zstd_native = 0 keeps the game's decoder.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 15:33:28 -05:00
chrg6
f63b6796ee Decode Ocarina 1.1.1's loading streams on the host decoder, behind the same zstd_native = 1 switch.
The loading threads feed each chunk they read to the game's stream decoder, which is where most of the
decompression time goes. That call now reaches a host stream that writes straight into the game's output
buffer and keeps the game's state fields and results. A stream is taken from its first chunk or left to
the game; zstd_check = 1 runs both and compares the outputs. --probe-zstd checks the host streams on
frames it makes itself.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-09 21:21:48 -05:00
chrg6
c32b9c40a8 Send Ocarina 1.1.1's one-shot zstd decodes to the host decoder, switched on by zstd_native = 1 until a check run.
A profile of a play session showed the game's own zstd decoder, lifted from ARM64, taking 16 to 19% of all running CPU time on the loading threads: the patch set that sends decodes to the host's libzstd had sites for 1.4.3 only. recomp/patches-v111.txt now has the three calls of ZSTD_decompressMultiFrame (main+0xeb380) of this build, checked against its main by the generator; a lift with them changes three chunks. ov_zstd.c takes the guest function's address by game version.

The sites have not been through a check run on this build, and 1.4.3 has a caller that relies on the game decoder's own result, so on 1.1.1 the host function passes every call on to the game's decoder unless zstd_native = 1 is set or zstd_check = 1 runs both and logs what differs. The loading threads' stream feed (main+0xf5c80), which decodes most of the data, is not replaced yet.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-09 20:52:48 -05:00
Valiantsin Dzerakh
0478c13e45 Import the base tree (MIT, see LICENSE) 2026-11-07 05:04:38 -05:00