Integrate fast shared-event wakeups, per-segment publication, array-slice pass merging, and independent compute dispatch hoisting from the PRs. Serialize CPU completion effects before publishing completed fences and validate recycled post records. Preserve submission order for early waits. Guard scheduling against attachment aliases, first-draw and bindless reads, indirect ranges, stencil writes, and explicit barriers. Add direct Metal regression coverage and a gated CPU completion race test with TSAN. Record final BotW and Metroid Dread gameplay validation with optimizations on and off. BotW retains fewer render passes, but the measured 60 Hz M4 Pro runs do not establish a timing speedup. Keep the original higher-load and iOS performance claims explicitly unverified. Co-authored-by: v0lumehi <aljic.zijad@gmail.com>
10 KiB
10 KiB
13 Sentinels: Aegis Rim (01003FC01670C000)
Update 1.10 (v65536) over the base game, SDK 11.4.3. Vanillaware's own engine on NVN. Modules main, subsdk0 (NintendoSDK_movie 11.4.3) and sdk; sdk and subsdk0 are byte-identical to Metroid Dread's (010093801237C000). Playable on the iPhone; on the Mac a new game gets through the prologue into the first battle.
The Asian release (Japanese, Korean, Chinese) has its own title ID and main build, and its own profile: 01008D7016438000.
What it needs
| Setting | Where | Why |
|---|---|---|
alloc_big_align on |
game.tsv |
the engine cuts ring heaps out of a memory pool at 64 KB GPU alignment, and the pool's storage comes from malloc: without 64 KB-aligned large allocations the third ring heap keeps base 0 and the prologue's first battle writes through a null pointer; see Memory |
4 GB layout: mem_reserve 0x100000000, mem_alias 0x10000000, mem_stack 0x10000000, app_pool 0xCD500000 |
game.tsv |
256 MB in front + 256 MB alias region + 256 MB stack region + 3.25 GB heap region. No system resource: the game sets a heap |
depth_mode zero |
game.tsv |
the game sets the near = 0 depth mode, so the pipeline bases made before it runs (the NSP script's Mac prebuild, and a pipeline archive made offline for the iPhone when one is built with DM_PSO_GPUS) take its vertex shaders' main0_z entries (the host itself follows the mode the game sets) |
No patches.py, no sdk_patches.py cut for its sdk build (2d369d99…), no pad_layout (the iPhone pad starts in the 2D layout).
Memory
- The NPDM declares no system resource (META +0x14 = 0): the game uses
SetHeapSize.main's bss is only about 21 MB. Measured withDM_SVC=1: the heap request is pool − used =0xc9400000(3.14 GB); the alias region gets one page during SDK initialization; thread stacks go into the stack region throughMapMemory. On the Mac about 2.2 GB is resident in the first battle. - Resolved: a null write as soon as the main thread starts recording a command buffer. A constructor at
main+0x2fa9a0ran onthis = 0when the prologue entered its first battle. The object came from one of the engine's ring heaps (main+0x2d83ec), and that heap had base 0. At startup the engine takes one GPU memory pool, sized as exactly the sum of three ring heaps, and cuts the heaps out at 64 KB alignment relative to the pool's GPU address (main+0x30c118). On the Switch a pool's GPU address is big-page aligned; the host's GPU address is the CPU address, and the storage came from the game'smalloc(nn::mem::StandardAllocator::Allocate(size)) rounded only to 4 KB. Up to 60 KB was lost at the front, the third heap (16 MB) did not fit, and the sub-allocator returned 0. Probing the heap object at the allocation (DM_PROBE=main:2bec60@x0+8) showed the zero base directly. Current:alloc_big_alignroutes allocations of 64 KB or more toAllocate(size, 0x10000)(hle_os.c).
Services
- Resolved: SDK 11 calls that reach a service:
hid::GetMouseState(the first boot aborted in it: the SDK's mouse resource was never initialized),fs::CleanDirectoryRecursively,fs::EnableGlobalFileDataCache,oe::SetUserInactivityDetectionTimeExtendedUnsafe,time::ToPosixTime(hostmktime) andaudio::GetAudioOutName(Mouse, keyboard and key code maps, Directory, file data cache and save-thumbnail calls, oe calls answered without an applet proxy, Time zone rules, calendar conversion and the network clock, Audio device output volume and names).
Files and shaders
- Everything is in six CRI CPK archives (
ROBO_*.CPK, 4.3 GB;ROBO_US.CPKis the profile'sromfs_marker) that CRI File System opens throughnn::fs; they hold textures, scripts, CRI ADX2 audio (acb / awb) and CRI USM movies. The host needs nothing but plain file access for them. - To look inside one: the header and TOC are
@UTFtables (big-endian columns), masked with CRI's byte stream (m = 0x655f, thenm *= 0x4115per byte); file offsets in the TOC are relative to the smaller ofTocOffsetandContentOffset, and files whoseExtractSizediffers fromFileSizeare CRILAYLA-compressed. The listing showed no shader container, which is how the shaders were found inmaininstead (02, Data containers). - Shaders: no shader file in RomFS. 838 GLSLC compiler outputs (magic
0x19866891) sit inmain's read-only segment, each referenced from a{pointer, size, 0}table in.data(shader_split.pyglslc_shaders). Each output holds one control section with the code area right after it; a debug section with the NV assembly and the GLSL source follows. The control sections are all at least 0x880 bytes.
Rendering
- Synchronization menu smoke check (2026-10-26, Mac M4 Pro): #158 and #213 with review fixes and current
8393d85a4eaeMac libraries. Isolated copies of the existing save ran 50 s with fast wake/pass optimizations/deferred encoding (2,853 frames) and with all three optimizations disabled/direct encoding (2,968 frames), without a red line or crash. The wait ledger records one samples-passed report and one timestamp report per frame in both modes. Screenshots show the audio-language selection menu; a 65 s extended route still stopped there (3,864 frames). This validates that menu and its report traffic, not battle/gameplay or a performance gain; neither pass optimization activated. Logs, copied saves and shots are in the NSP work folder'srun/perf-order-on/,perf-order-off/andperf-scene-on/. See the general SyncWait completion, slice merging and compute hoisting entries. iOS runtime validation remains open. - Single-stage programs: the game builds one NVN program per GLSLC output (838
ProgramSetShadersat startup) and combines vertex and pixel programs per draw withBindProgramstage bits, so the real pairs exist only at draw time. Every pixel shader input is a plainfloat4at locations 1–9 (the SPH input map, words 6–13, matches the translated MSL inputs for all 375 pixel shaders), so the cover that pairs every vertex shader here is one pixel shader. - Depth mode: the game sets near = 0 (
nvnDeviceSetDepthMode), as Link's Awakening and Engage do. - Resolved: an assertion a minute and a half into the prologue,
buffer index (31) must be < 31: the game binds vertex streams 14 and 15, and the host bound NVN stream n to Metal vertex buffer 16 + n. - Every texture is created with
TextureBuilderSetPackagedTextureData. Current: the host copies that data atTextureInitializeand uploads it like a packaged texture; the linear ones (the save thumbnails) are excluded and, in a CPU-visible pool, read straight from the pool buffer. - Attachment loads and stores per frame on the Mac went down about 15% once an inline uniform update inside a render pass stopped ending the pass (development.md, Power).
Found on this game
General host changes first needed here; each applies to every game:
- Synchronization review menu check: Rendering records the #158/#213 comparison, samples-passed/timestamp traffic and its limits; the general fixes and synthetic regressions are linked there.
- 64 KB-aligned large allocations (
alloc_big_align,hle_os.c; shared with Unicorn Overlord, 010069401ADB8000): Ring heaps cut from a pool at 64 KB alignment and the profile keys. - GLSLC outputs in
main(shader_split.pyglslc_shaders, the control / code / length check): GLSLC outputs inmainand in loose files. - Single-stage programs paired with a cover (
vs_pad), a shader cache key that covers the pairs (shader_key,pair_keyed), triangle fans and indexed quads (a shared fan index buffer; CPU expansion at replay innvn_mtl.m), vertex streams 14 and 15 at slots 14 and 15 (VB_SLOT) and packaged texture data copied atTextureInitialize: Single-stage programs paired with a cover, Shader cache key covering the pairs, Triangle fans and indexed quads, Vertex streams 14 and 15 at Metal slots 14 and 15, Packaged texture data copied atTextureInitialize. - Finding service calls before the first boot (
main'snn::imports filtered againstHLE_FORCEnames): Listing a game's unhooked imports before they are met.
Open
game.tsvstill describes the game as "In bring-up" (the Mac reaching the first battle), while AGENTS.md lists it as playable on the iPhone.game.tsv: only the merged 1.10 copy is extracted; the base game's own build IDs are added when needed.