Files
nxnative-easy/dm/games/0100000000010000/README.md
T
seedds 8334fdfb50 Compile translated shaders with the Metal front end at -O1
At -O2, CorrelatedValuePropagation and SimplifyCFG fold a translated
shader's repeated tests of one value into switches whose cases jump past
their own join. Apple's GPU backend then runs out of control-flow IDs
("Max Unstruct ID 758 ... exceeds supported limit", a note: metal-tt
still exits 0) and lowers the function a far slower way. Odyssey's
alRenderMaterialUber took 24.6 minutes and 17.3 MB of applegpu_g13s code;
at -O1 it takes 31 s and 1.3 MB.

Sampled archive bases from seven games compile to code within a few
percent of -O2. Odyssey's full g13s archive builds in 15.6 minutes
(exit 0), its Cap Kingdom frames match -O2 within 3/255 on scattered
pixels, and GPU time per frame is within run-to-run noise.

Document the mechanism, and correct the Odyssey note: the 758 line never
failed the build; the reporter's exit 1 came from the BAHS link errors
fixed in ff44c59.

Refs #276
2026-10-27 10:16:49 +08:00

14 KiB
Raw Blame History

Super Mario Odyssey (0100000000010000)

Update 1.4.1 (v393216) over the base game, SDK 7.3.2. Nintendo EPD's sead / agl engine with NintendoWare (G3d / Ui2d / Vfx), on NVN. Modules main, subsdk0 (the movie module) and sdk; DRAGON QUEST XI S 1.0.4 runs the same sdk build. Plays on the iPhone. The README lists its IPA at about 6 GB.

What it needs

Setting Where Why
pad_layout full game.tsv the 3D gamepad layout
4 GB layout: mem_reserve 0x100000000, mem_alias 0x10000000, mem_stack 0x10000000, app_pool 0xCD500000 game.tsv from the NSP script's provisional profile
Node area cut and 32 MB alignment of large regions dm/tools/sdk_patches.py, sdk build fcf93a41… registered for DRAGON QUEST XI S on the same build; Odyssey has no virtual address memory and never allocates an address region

No patches.py. op_mode stays docked: Odyssey only takes its motion moves in its default docked mode.

Input

  • Resolved: buttons and menus worked, the sticks did nothing in play (its first hint, "Look Around", waited forever). The game reads all 16 entries of the controller history (GetNpadStates) and ignores the returned count; the host filled only the newest. It polls every controller slot each frame, which moves the shared sampling number on by about 16, so the stick samples it combined were mostly empty, while buttons and the stick's direction bits (which menus use) still came through. The host now fills every requested entry with the current state under consecutive sampling numbers.
  • Resolved: motion moves never happened; shaking did nothing (cap throws by shaking). The host handed out six-axis handles but reported no sensor; it now reports one per connected controller. The game reads the six-axis history the same way (16 entries per sensor, the count ignored, main+0x93fd84). Its motion analyzer (main+0x5d5590) turns the newest acceleration, seen against the controller's pose, into a swing value per hand and sets the swing flags above 0.2. A still controller gives 0, and the host's shake (±3 G side to side) gives up to 1.5, so a Shake sets the left, right and any-swing flags for its length. To find the swing detector, probe the accelerometer value it reads (DM_PROBE=main:626248@x8+10 in 1.4.1). On the iPhone, shake the phone (a sharp flick); on the Mac, Space.

Rendering

  • Resolved: hard-edged dark boxes behind the HUD's layout panes (the coin counters' solid white cards, a dark box behind the "Look Around" hint; layout captures, as in Breath of the Wild). The game creates each capture with its per-format default mapping (an R8 capture reads (R, 0, 0, 1)), rebuilds it on the same storage with the capture's own mapping, (1, 1, 1, R), and replaces the registered descriptor only if nvnTextureCompare reports a difference (main+0x982320). The host compared everything but the mapping, so the captures kept alpha 1. Fixed by the general nvnTextureCompare compares the channel mapping. The profile's earlier workaround, alpha_from_red (all 26 layout pixel shaders, main's BNSH at 0x132c000, read such targets as (R, 0, 0, R)), is gone with its host code. Checked on the Mac from the "Look Around" hint with a probe host on the comparison: 40 rebuilds in the first 11,700 frames change only the mapping, R8 captures (640×80, 400×120, 390×80, 378×80, 300×40, 250×50, 128×128, 64×64, 48×57) from (R, 0, 0, 1) to (1, 1, 1, R) and a 320×180 RGBA one from (R, G, B, 1) to (R, G, B, A). Ignoring the mapping again brings back a dark box behind the hint (about 12,800 pixels from frame 4,200 on); with the old list the workaround converted the 378×80 capture and the frames matched the fixed host's; with the fix, the list converted nothing and changed nothing (the frames 4,200 to 6,600 equal to the pixel but for the hint video's corner). The comparison and the views run the same on the iPhone (no platform branch), so the rebuilt captures leave no (R, 0, 0, 1) target for the list to find there either.
  • Resolved: the Cap Kingdom fog never parted around Mario (reported on an iPhone; fix checked on the Mac). The fog's shading reads velocity fields that a small fluid simulation updates every frame: two 256×256 and two 128×128 RG16F targets, with a two-pass blur and a blended composite. Its only input is a point-sprite splat per mover, drawn with alPointSpriteShader: one vertex per mover, its size in the vertex data (Mario's main splat about 18 px, others 3–5 px) and a soft disc in the fragment shader (clamp(1 - 4r², 0, 1) × velocity). The vertex shader writes gl_PointSize = min(size, c0[0x610]), and the host had nothing at that offset of the driver segment, so every splat had size 0 and the fields stayed exactly zero (raw dumps with DM_NVN_TEXDUMP + DM_NVN_SNAPRAW). The host now writes the largest point size, 2047, at c0 + 0x610 for the graphics stages. Writing the SetPointSize value instead (always 1 here) left one-pixel splats whose disc weight came out 0 (the fields held -0.0). After the fix the fields hold velocities up to ±44 and the fog clears around Mario.
  • Resolved: the Cap Kingdom ground showed 32×32 blocks of a stale image on every fourth frame (Mac). Presents 4k were brighter and blocky, 4k+1 and 4k+2 clean, 4k+3 partly blocked, repeating to the decimal across runs; only with the ground's material (b6384a85afd3ef94) drawn. In some frames the game first draws a depth pre-pass (alRenderDepthOnly) for objects its occlusion queries report visible (the queries rotate through three slots, hence the cycle), then the G-buffer with other vertex shaders and depth test less-or-equal, without clearing the colour targets. Metal compiled the two vertex shaders' identical position math to slightly different depths, the G-buffer fragments failed the test and kept the previous content. Every Metal compile now uses -fpreserve-invariance. Ruled out on the way: the TBDR optimizations, same-pass feedback, the present path; evaluating the game's conditional rendering on the CPU from earlier frames' results made it worse (the condition belongs to the same frame on the GPU), so the host still ignores the condition here and draws (no render_cond).
  • Its three geometry shaders (a thunder effect) do not translate.

Movies

  • MP4 / H.264: the videos in its tutorial hint boxes and the kingdom travel videos, 41 of 44 High or Main profile with B-frames. The host decodes them with VideoToolbox; the SDK's demuxer passes the parameter sets as the avcc record (the MP4 avcC box), and pictures leave in presentation order through a small queue.

Services

  • Start-up calls no game before it made, each answered by the host: the aborting account::OpenPreselectedUser and GetLastOpenedUser (a null read), news delivery (bcat: the delivery storage does not mount, which the game treats as "nothing delivered"), HOME-button blocking, the parental 3D-mode setting, vi::SetLayerScalingMode, time::ToPosixTimeFromUtc, the debug pad, and the SDK 7 names of two audio-device calls (audio::AcquireAudioDeviceSwitchNotification aborted). Each aborted with no message in the SDK's IPC; frames resolved with sdis.py --sym.
  • Command memory: it hands out many 512-byte command memory grants before recording; with no recording to reclaim them by, 256 KB host segments each reached the cap entering the Wooded Kingdom, so an empty-pool grant of at most 4 KB outside a recording gets a 4 KB segment (hostseg_size). The GetCommandMemoryUsed count from the last AddCommandMemory (found on Three Houses) left it unchanged.
  • Shared fonts: SDK 7, so the address is the plain TTF. Focus handling mode 3 (no focus notifications; the system suspends the game out of focus).
  • Its SDK 7 driver's GPU code range (1.5–1.14, nvnDeviceGetInteger 0x2E–0x31) was measured here; the host now reports newer versions for newer SDKs.

Audio

  • It is silent for its first minute with either audio renderer update lock: the old SDK 16 offset and the one update_lock now reads off the SDK's own Update (MutexType at +0x1d8 on SDK 6.4–8.2) (The renderer's update lock).

Files and shaders

  • Resolved in the shader builder (#276): BAHS pipeline interfaces. The local 1.4.1 libraries have 290 pairs with missing vertex outputs, involving 29 vertex shaders. All are among the split's 12,124 BAHS pairs; the profile's 20,740 other pairs have no missing outputs. The shared fix makes padding read both tables; see Pixel shader inputs the vertex shader does not write. The report's Max Unstruct ID 758 ... exceeds supported limit lines did not fail the build: see the next item. Targeted validation: the affected vertex sources compile for Mac and iOS, and all 290 pairs build into g13s and g16s Metal archives with both depth entry points (Metal 32023.921). Full Mac rebuild: 27,984 of 27,984 shaders successful, no binding or pulling-library failures, no regressions, and no interface mismatches across all 32,864 pairs. The Mac host also rebuilt with the regenerated uniform tables. No new gameplay or iPhone run.
  • Resolved in the shader builder (#276): the 25-minute pipeline. Max Unstruct ID 758 is a note from Apple's GPU compiler, which then lowers the function a slower way; left to finish, metal-tt exits 0 with the pipeline in the archive. The report's exit 1 came from the BAHS link errors above. The function is the float body of alRenderMaterialUber (fragment 007d7e411ef14ebd, BNSH variation 0, paired with vertex 3c3435a62e05975b): alone on applegpu_g13s it took 24.6 minutes and 17.3 MB of code, which is also what held the reporter's startup prebuild at 7,008 of 32,864. With the front end at -O1 (Front end at -O1) it takes 31 s and 1.3 MB; the note remains, at 642. Full Mac app pack with that chain (Metal 32023.921): 27,984 of 27,984 shaders in 32:50, the g18g archive in 10:50 with no note, and the reporter's g13s archive of 32,864 bases in 15.6 minutes (exit 0, 912 MB). From save_new1, frames 4,200 to 6,600 (every 300) against an -O2 build of the same commit: six identical or within one pixel, frame 5,700 differs by the same 4,191 pixels between two -O2 runs too, and frames 4,800 and 6,300 by 1,140 and 2,059 grass pixels of at most 3 of 255. GPU time per frame over the same frames (one in 150, three runs each, alternating order): medians 5.05, 4.79 and 4.76 ms at -O1 (the 5.05 ms run was the first of the series), 4.76, 4.75 and 4.83 ms at -O2. No iPhone run.
  • 27,984 shaders: agl sharcb and bfsha in ShaderData, particles in EffectData; groups per RomFS folder in game.tsv.
  • The spin-wait rewrite leaves its modules byte for byte as before the lock and store rules.

Mac routes

  • A fresh save wakes Mario in the Cap Kingdom with DM_KEYS="f1000:A,f8200:RLeft~6,f9200:LUp~4": the "Look Around" hint waits for the right stick held about 6 s one way (left then right cancels out). Play starts at about frame 9,900, and the game saves there.
  • From that save, f1000:A,f1300:A resumes into play at about frame 1,500, and f1600:LUp~7 walks straight across the fog to the hills by the bridge, where Cappy flees from Mario.
  • DM_KEYS takes RLeft / RUp / RRight / RDown for the right stick and Shake for the controller.

Found on this game

General host and tool changes first needed here; each applies to every game:

Open

  • The three thunder geometry shaders do not translate.
  • The silent first minute of sound.
  • development.md calls SDK 7.3.2 "the oldest so far"; Mario Kart 8 Deluxe (SDK 4.4.0) is older now.
  • game.tsv says values marked "Mario's" are not measured yet, but none is marked.