Repository navigation
Conversation
…, and surveys a dump with no profile Fire Emblem Fortune's Weave's dump holds updates/131072 alone, and the update is the whole game, but the script took base/ as the mark of a dump and refused one with no profile. pack_dump.py now takes a dump with base/ or with updates/<version>/ that hold code and a RomFS, and reads the title ID from the base game, else from the newest update; version 0 without base/ is refused with the versions there. The script lists v0 only when there is a base, looks up the build by the newest version's main, and surveys a dump with no profile from its own files: the code modules are copied, the RomFS is read in place in place of the manifest, and the folder names the game, a dump having no control data. The provisional profile was wrong for a Switch 2 game in two ways. The SDK version string names a variant (nnSdk-Generic_22_2_0), which the pattern did not take, so the survey showed no SDK. And the pool was the Switch 1 one; an sdk built for the Switch 2 (spec: Ounce, as loader.c's dm_sdk_ounce reads it) now gets a 9 GB pool and a heap region that holds it, as Donkey Kong Bananza's profile has. Checked: test_nsp_dump.py (a dump with only an update, and a dump with no profile surveyed until its 32-bit NPDM stops it) and test_profile_guess.py (the pool and the variant) pass. The script built Fire Emblem Fortune's Weave from its dump on the Mac, survey and provisional profile included. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fire Emblem Fortune's Weave's shader split took 12 minutes, 6 of them in one 9.8 MB SARC (Layout/Common.arc.zst). sarc_members took each member's name by splitting the whole rest of the archive at every NUL and keeping the first piece, so the work grew with the archive's size times its member count. It now finds the name's own NUL and decodes only that. The names are the same; the split of that game took 1:51. Checked: test_split_nesting.py's new test splits an 800-member archive and compares the names and data, in 1 ms against the old code's 47 s. Donkey Kong Bananza's split gives the same 23,638 shaders. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fire Emblem Fortune's Weave stopped before nnMain: nninitStartup's StandardAllocator::Initialize(nullptr, 36 GB), which the host lowers to 4.06 GB on the assumption of a 62 MB node area, failed in CentralHeap::Init, because none of the six reviewed implementations of the node area's constructor matched its sdk (nnSdk-Generic_22_2_0, build 5b1602f3…) and the node area stayed 1 GB. Its constructor keeps its result on the stack and gives each failing result its own abort (328 bytes), with the unit heap made inline: the limit at +0x18, the occupied length at +0xa4. A seventh rule in sdk_patches.py's MATCH_CUTS cuts it as the others do, by function fingerprint, so any game shipping this sdk build gets it. Checked: on the six sdk builds on this drive the new rule matches Fire Emblem Fortune's Weave's only, at sdk+0x16ba48 and +0x16bad4, and no other rule matches it; with the cut the region lies at alias +0.06 GB and the game reaches nnMain and NVN2's device. test_patches.py and test_nso_match.py pass. game-patches.md is regenerated by docs_patches.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h no mouse and no device change Fire Emblem Fortune's Weave, on SDK 22.2.0, aborted twice at start with Result 0xe15, each time on a service the host does not serve. hid::InitializeNmouse maps the hid service's transfer memory for the Switch 2's mouse pointer (a Joy-Con 2 used as a mouse, or a USB mouse). And audio::AcquireAudioOutputDeviceNotification, SDK 22's name for the output device notification, opens a user session with aud:u. hle_hid.c takes over the 19 calls of the application Nmouse API and answers as with no mouse attached: GetNmouseHandles lists none, and every call given a handle returns false and leaves its output untouched, as the SDK does for a handle it does not know. The pointer ranges are kept, so GetNmousePointerRanges reads back what InitializeNmouse and SetNmousePointerRanges set. For the notification, the host already takes over the same name for SDK 21 (Splatoon Raiders): the event is bound, never signaled, and the Result is 0. SDK 22.2.0's version (sdk+0x1f1e00) keeps no per-process flag, where that takeover returns 0x499 on a second acquire, as the older names do; no game has been seen to acquire twice, so the comment and audio.md record the difference and the one takeover serves both The system overlay calls and ReleaseAudioOutputDeviceNotification are not taken over: no game seen imports them. Checked: with both, the game runs on the Mac to its first autosave, where its first movie (AV1, served later) stopped it. Entries in services.md and audio.md, and the symptom index, describe both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…here VideoToolbox has none Fire Emblem Fortune's Weave's 91 movies are AV1, and VideoToolbox decodes AV1 only on Apple M3 and A17 Pro and later; the supported Macs and iPads go back to M1. dav1d is VideoLAN's AV1 decoder (BSD 2-Clause), with NEON assembly for arm64. This adds release 1.5.4 (upstream 54706fc), unmodified, as the host's other third-party code is shipped, so that nothing is fetched at build time. The next commit builds and uses it. Only what an arm64 build needs is kept: the public headers, the C sources of src/, and src/arm/ with the 64-bit assembly; the other architectures, tools, tests and meson files are left out. config/ holds config.h as meson writes it for an arm64 Apple target, verbatim, the version header of a release archive build and one wrapper per template and bit depth (config/tmpl/), where meson passes -DBITDEPTH per object, so that a project with one set of defines for all its files builds them too. dav1d/README.md lists the files compiled and how to check the configuration against a new release; dm/third_party/README.md has its row. Checked: the 130 files are byte-identical to the 1.5.4 tag (54706fc). config.h is the one meson 1.11.2 writes on this Mac, and an arm64-apple-ios cross build writes the same file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, and decoded pictures as NVN native images Fire Emblem Fortune's Weave (SDK 22.2.0) aborted with Result 0xe15 when its first movie started, in nn::codec::InitializeAv1DecoderByTransferMemory, from its movie module. SDK 22 calls the decoder API under other names than SDK 20, whose names the host took over: Initialize…DecoderByTransferMemory and …ByRemoteMemory (the old overloads' arguments plus an output mode), Get…DecoderSubmissionReadyEvent returning the event, MakeDecodeImagePreference with an ImageTilingMode, InitializeDecoderImagePool without a codec mode, and ReadDecoderOutputImageRgbaHdr. Each went to the decoder service, which the host does not serve. The movies are AV1, which the host had no decoder for. With those served, the player aborted again in nvnNativeImageInitialize: it draws decoded pictures from the GPU, wrapping the pool's native image handle in an NVNnativeImage and making a texture of it with nvnTextureInitializeFromNativeImage. hle_codec_video.c takes the SDK 22 names over with the SDK 20 implementations (CV_SDK22), and AV1 joins VP8, VP9 and H.264. hle_movie_av1.c decodes it with VideoToolbox where the chip has an AV1 decoder (Apple M3 and A17 Pro on), building the av1C record from the stream's sequence header since the SDK passes no codec private data, and with dav1d elsewhere, or with DM_AV1_DAV1D=1; 10- and 12-bit streams are shifted to 8 bits. For the GPU route, nvnNativeImageInitialize gives a native image a CPU-uncached memory pool that the host makes through its own NVN entry points (nvn_host_proc, new in hle_nvn.c), holding one picture in RGBA8 rows, and nvnTextureInitializeFromNativeImage makes a linear texture over it, so the GPU reads each picture the decoder converts into it. dm/host/Makefile builds dav1d into gen/dav1d/libdav1d.a and links it, and the NSP script's Mac host build key includes it. Checked: t_codec_video decodes a 64x64 AV1 key frame (SVT-AV1) through the SDK 22 calls on both backends and reads it back, and checks a native image's pool, texture and picture against recorded NVN entry points (AddressSanitizer and UBSan). On Fire Emblem Fortune's Weave's opening movie (2560x1440, 8-bit), VideoToolbox and dav1d gave the same pictures, bit for bit, over its first 600 (about 450 pictures a second each on an M3 Pro), and the movie plays in the game. Open: the game's second movie waits for a handler thread that never starts, not yet traced; iOS untested. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…put into its output After its opening movie, Fire Emblem Fortune's Weave's first map drew its 2D overlays over a black 3D scene, with red lines for nvnCommandBufferEvaluateDlss and the nvnDlssNetworkBuilder… / nvnDlssParameters… calls. NVN2 has DLSS: the game makes a network for an output size, asks it for the size to render at, and every frame fills a parameters object (input colour, depth, motion vectors, output colour, their sub-rectangles, jitter, motion-vector scale, history reset) and records the evaluation, which writes the upscaled picture into the output texture. Unserved, the output was never written. hle_nvn.c serves the API. The networks' and parameters' state is kept in a table of its own beside the game's objects; GetOptimalSettings answers six ints in NGX's order (the optimal render size, 2/3 of the output, then the largest, the output, and the smallest, a third); and the evaluation is recorded as a bilinear scaled copy of the input rectangle into the output, through the copy the NVN layer already has (OP_COPY_T2T). The setters' arguments were read from the game's calls: textures with a view, sub-rectangles as two ints, jitter and motion-vector scale as two floats. Checked: the map's 640x360 scene now reaches the 1280x720 output, evaluated every frame. It is a spatial scale only: the jitter, depth and motion vectors are kept but unused, so detail is softer than DLSS's and the game's dithered fades show as a fine grid; a temporal upscale (MetalFX's temporal scaler takes the same inputs) is open. The graphics tests pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Weave's shaders use, and IMAD.X and IMAD.HI add their carries
Fire Emblem Fortune's Weave's first map drew its characters as black silhouettes before a flat red sky: 84 of its shaders (its characters', rock's and sky's materials) did not translate. Read against Mesa NAK's SM70 encoder where it has the instruction, the translator now decodes BREV and UBREV; bindless TEX, TLD, TLD4, TMML and TXQ, and TEX with its handle in a uniform register (each gives its handle as the first source with TextureFlags.Bindless, as the Maxwell bindless forms do, so BindlessElimination traces it to its constant buffer); ATOMS and ATOMS.CAS; MEMBAR; F2I to and I2F from a 64-bit integer; and the carry out of IMAD.WIDE and IMAD.HI. LDS, STS and ATOMS scale their address register by its stride (bits 78..80): STS [R0.X4] stored to the word index itself. 83 of the 84 translate; the last stops at 0x9ab, not identified.
The map was then about a hundred times too dark, its sky black. The auto exposure (a0c632d8b5fa925b) averages its luminance histogram with an inlined u64 division, and the result clamped the exposure to the top of its range: 5273600 / 25600 gave 659001, not 206. Two multiply-adds of the division's reciprocal refinement were translated wrong. IMAD.X (.X at bit 74, the carry predicate at 87..90) dropped its carry in. IMAD.HI added only Rc, not the pair {Rc+1:Rc}: the compiler chains IMAD.WIDE R8, a, b, R8 then IMAD.HI R8, a', b', R8, so IMAD.HI returns the high word of a * b + {R9:R8}, its carry out that of the 64-bit sum; only with the pair do the code's three carries meet at the same bit. IMAD, IMAD.WIDE and IMAD.HI now add the carry with .X, as IADD3.X does (a negated addend is then its bitwise not), and IMAD.HI adds the pair.
Checked: the translated division, run on the GPU, matches 64-bit division on eight cases, dividends up to 2^44. The map now renders lit as in a capture of the game: its characters, the red rock cliff, the sky. test_dmsh_ampere_ops.py translates each instruction and checks IMAD.HI's pair and IMAD.X's carry (6 tests). Chain f6328472c831 (dmsh_chain.h regenerated). Fire Emblem Fortune's Weave: 16,887 of 16,888 shaders. Donkey Kong Bananza, rebuilt from an empty cache: 23,638 of 23,638 shaders, 0 regressions against its previous libraries. regress.py was not run: no other Switch 2 game here has a profile, and Switch 1 games take the Maxwell path, which this does not touch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Weave shader translates One of Fire Emblem Fortune's Weave's shaders (35d12d6f98fa05e2) still did not translate: it stopped at 0x9ab, an instruction with no operands after its BAR and MEMBAR. It is ERRBAR, the error barrier the compiler puts after a device-scope MEMBAR (MEMBAR.SC.GPU; ERRBAR is its fence). It waits for earlier memory errors to be reported, which a translation has none of, so it decodes as a no-op; the MEMBAR before it stays a barrier. Checked: test_dmsh_ampere_ops.py translates MEMBAR.GPU then ERRBAR (7 tests), and every dm/gfx/tests/test_dmsh_*.py passes against the rebuilt translator. Chain 959b0e43e889 (dmsh_chain.h regenerated, tool_lock.py --check-tools clean). Fire Emblem Fortune's Weave: 16,888 of 16,888 shaders. regress.py was not run: the change reaches only Ampere shaders that held 0x9ab, which failed before; Donkey Kong Bananza, the other Switch 2 game here, translated all of its shaders already, and Switch 1 games take the Maxwell path, which this does not touch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… holds its 9 GB pool Fire Emblem Fortune's Weave went black at its second movie with the game still running. It sets no heap and takes its memory from StandardAllocator::Initialize(nullptr, 36 GB), which the host fits to the alias region: 4.06 GB in Engage's 4.375 GB region, which the provisional profile gave every game with virtual address memory. By the second movie it had mapped 4.03 GB, and the movie prepare (main+0x1906510) returned 0x11, its result for an allocation that came back null (the movie's 200 MB buffer at main+0x19065f8, or the track configuration before it), before it started its MovieEventHandler thread. profile_guess.py now gives a Switch 2 sdk (spec: Ounce) with virtual address memory an alias region of the pool + 512 MB (9.5 GB) in a 10.25 GB block with 256 MB stack and heap regions; Switch 1 games keep Engage's block. The allocator then gets 9.19 GB, and the game plays past that movie to its character creation. The block is larger than the 6 GB reservation an iPhone allows without the extended-virtual-addressing entitlement (open, as for Donkey Kong Bananza's 9.75 GB block; neither is tested on iOS). Checked: test_profile_guess.py checks the Switch 2 alias region and block and that the Switch 1 result is unchanged; test_docs.py passes. Existing profiles are not regenerated, so no game with a profile is reached. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e Emblem Fortune's Weave's battle maps no longer shake Fire Emblem Fortune's Weave's battle maps shook all the time with the camera still: the whole picture moved by up to a pixel from frame to frame, softer than on the console. The game moves its projection by a sub-pixel offset each frame (nvnDlssParametersSetJitterOffset: 0, -0.167; -0.25, 0.167; -0.375, -0.056; ...) for DLSS to accumulate into a still picture; the host served the evaluation as a bilinear scaled copy of the 640x360 input into the 1280x720 output, which showed each frame's offset. nvnCommandBufferEvaluateDlss now records the parameters as they are (OP_DLSS: input colour, depth, motion vectors, output and exposure textures with their views, copied when set, and sub-rectangles; jitter, motion-vector scale, pre-exposure, history reset and the network's NGX flags), and the replay runs them through MetalFX's Metal 4 temporal scaler (dlss_eval), which takes the same inputs: the jitter as its jitter offset, the motion-vector scale as its scale (both in input pixels, vectors pointing to where the pixel was), the history reset as reset, NGX's DepthInverted (0x8) as depthReversed. The inputs are copied into textures of the scaler's own and its output into the game's output rectangle. Without a Metal 4 temporal scaler, with formats or rectangles it does not take, and while Metal calls are recorded, the evaluation is the scaled copy as before; DM_NVN_DLSS=bilinear asks for it. The new op is a barrier wherever a copy or dispatch is (the depth-stencil scan, the hoist, defer and fuse look-aheads, the event walk), and the capture records its five textures. Checked on the Mac on the game's battle map, camera still: six consecutive frames, phase correlation over the scene, no shift between any two (the copy moved up to a pixel). The scaler: 640x360 -> 1280x720, colour RG11B10Float, depth Depth32Float (inverted), motion RG16Float, made once in 0.7 s. The GPU tests (make -C dm/gfx/tests run), the tool and docs tests pass. regress.py was not run: no other local game calls DLSS (Donkey Kong Bananza, Hello Kitty Party Land), and the op is absent from their command streams. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… whole, so Fire Emblem Fortune's Weave's lit scene loses its 2x2 dot grid Fire Emblem Fortune's Weave's battle maps had one bright pixel in every 2x2 block, already in the 640x360 lit scene. Its ambient occlusion is rendered deinterleaved: b6726be4afc44691 draws four full-screen triangles in an attachment-less pass and stores into the layer of a 4-layer array its primitive ID names, and b6c18be9a0c582cc puts the layers back together at full resolution. Layers 1 to 3 stayed empty: every primitive read primitive ID 0. The cause is Metal. A pipeline built from a base whose colour state is left unspecialized, then specialized with the pass's colour state (newRenderPipelineStateBySpecializationWithDescriptor), gives [[primitive_id]] 0 in every primitive; the same pipeline built whole gives 0, 1, 2, 3. Probes on macOS 27 with an Apple M3 Pro: with the game's own two libraries, a minimal shader, a base from the offline archive or compiled on the device, specialized to no colour attachment or to one. The host builds every draw's pipeline from such a base. lib_from_file notes a library whose bytes name air.primitive_id (the builtin's AIR metadata), as it notes render_target_array_index; fn4_note marks the functions made from it, and pso_build_ex builds a pipeline with such a fragment function whole through the Metal 4 compiler (the run's archive captures it). pre_desc and pso_offline.py leave their pairs out of the prebuild and the offline archive. The pipeline bases line counts them. For the search, the pass dump's image lines and DM_CTL texlist now print the view (type, layers) behind a binding. Checked: t_image_primitive_id draws the case through the host from a metallib loaded as game shaders are; each layer holds its primitive's value, and through a base layers 1 to 3 stayed empty. test_pso_offline.py leaves such a pair out. In the game the 2x2 pattern is gone from every target (the lit scene's even and odd pixels 185.8 / 185.8, before 180 / 115), and the map renders smooth and lit. The GPU tests, the tool, docs and lint checks pass. Of the local games' shader libraries, only Fortune's Weave's b6726be4afc44691 reads the primitive ID (Donkey Kong Bananza 0 of 23,638, Hello Kitty Party Land 0 of 12,739), so regress.py was not run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… Emblem Fortune's Weave's title is lit Fire Emblem Fortune's Weave's title came out nearly black where its wheel should glow warm gold. The title is lit by many small lights that a compute shader (35d12d6f98fa05e2) sorts into a 3D grid of clusters, which the lighting pass reads back. The culling shader computes each cluster as (z c0[8] + y) c0[4] + x: NVN2 compute keeps the dispatch's workgroup counts at the start of its driver segment, x at c0 + 4 and y at c0 + 8, below the per-shader layout that begins at 0x10. The translator leaves c0 below 0x10 in place, and the host wrote the counts only at c0 + 0x200, Maxwell's place, so the shader read zeros. Every cluster of a column wrote the same entry, and the lighting pass found lights in its grid but zeros in their entries. Debug copies of the lighting shader that wrote out the grid lookup, the light index and its entry showed each step. With a Switch 2 sdk (dm_sdk_ounce), encode_args_ now also writes the counts at c0 + 4, and an indirect dispatch copies its GPU-written counts there too. Switch 1 games are unchanged: their layout has nothing there, but no Switch 1 shader was checked for reads of those bytes. No shader needs rebuilding. Donkey Kong Bananza's e697fe655b1e24bd and c5c746915e9a3265 read c0[4] the same way. Checked: on the title with the same save, the picture's mean brightness over six frames went from 8.4-8.7 without the fix to 19.8-21.9 with it, the wheel lit. Donkey Kong Bananza, rebuilt (23,638 of 23,638 shaders), plays its opening and reaches its first tutorial with no red line. Host tests, the GPU graphics tests (make -C dm/gfx/tests run) and test_docs pass. shaders.md has the entry, and the symptom index is refreshed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The DM_CTL command "texraw <serial> <path>" saved a 3D texture's first depth slice only: it copied one slice per array element and cube face, and a 3D texture has one. It now copies each depth slice, one after another, and writes their count in the header as it does for arrays. Used to read Fire Emblem Fortune's Weave's light cluster grid (an R32Uint 3D texture) while tracing its dark title. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ne's Weave scene no longer opens washed out Fire Emblem Fortune's Weave's battle map opened near white after the title and faded to its normal colours over a second or more. The game resets its auto exposure on a scene change by clearing the exposure image (a 3x1 render target) to (1, 0, 0, 0), then dispatches the histogram (6d0f6db3f0d396a3) and exposure (a0c632d8b5fa925b) shaders, which read and write it as an image. A full clear with no draw after it waits on the current targets for the next pass that attaches them, as its load action. Copies and the DLSS evaluation land such clears first; a compute dispatch only ended the open pass. So the clear landed after both dispatches: the exposure shader started from the dark title's value, and the reset then overwrote the exposure it had just written. DM_NVN_PASSDUMP with DM_NVN_PASSDUMP_ALL over the scene change showed it: the clear recorded in pass 148710 before both dispatches, landed in pass 148711. dispatch() now lands pending clears (rp_flush) before the dispatch, as copy_t2t does. A dispatch moved ahead of a pass (cs_hoist_try) already waits for no pending clear. The cost is one clear-only pass where a game clears targets and dispatches before drawing to them. Checked: same save and input, frames every 12 from the title through the map's first minute. The map's first frames go from mean brightness 230-233 to 172-176 of 255, with normal colours from the first frame (the rest is the game's movement overlay); the title frames are unchanged, and from frame 1640 on the runs match within 0.4. Donkey Kong Bananza boots through its opening to its first tutorial with no red line (frames 4200 and 6000 identical to the previous run; 2400 shows another shot of its cutscene). Host tests and the GPU graphics tests pass. rendering.md has the entry; the symptom index is refreshed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…as "?" Fire Emblem Fortune's Weave's title shows "Ver. ?". oe::GetDisplayVersion answers the display version of the profile's build row for main's build ID, which the NSP script fills from the version's control data (control.nacp). A dump folder holds exefs and RomFS only, and nothing else in the game files records the version: main has no version string, only the GetDisplayVersion import, and an update's number (131072) does not map to a display version. So the provisional profile writes "?", which the host returns as it is. Left as is, as the user chose: the game shows what the profile knows, and the host's fallback for an unknown build (1.0.0) would show a version the files do not have. The entry says how a profile can record the version by hand, and that reading control data from a dump is not done. Checked: test_docs passes and the symptom index is refreshed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o Fire Emblem Fortune's Weave renders for it instead of for 720p Fire Emblem Fortune's Weave looked soft and blurry next to the console: it rendered its scene at 640x360, upscaled it with its 1280x720 DLSS network, and the host scaled that to the window. The game sizes its scene from vi::QueryDisplayBufferMaxResolution, a Switch 2 call that forwards to vi2 through the SDK's function table (sdk+0x5e10c0), a service the host does not serve: it returned Result 0x272 and left the output as the game had set it, 0, which the game's own table (main+0x1abc2c0 / +0x1abc050) reads as 1280x720. A probe after the call (main+0x1818150) showed the result. dm/gfx/hle_vi.c takes it over and answers as a Switch 2 would: in handheld mode its own 1920x1080 screen; docked, the default, the largest of 1280x720, 1920x1080, 2560x1440 and 3840x2160 that the display the game is shown on holds, 1920x1080 when that is not known. The display's size comes from the front end, front_display_pixels: the Mac's main display in its current mode (front_mac.m), an iPhone's or iPad's screen (front_ios.m), and a weak default for builds without one. Checked: docked on a 3456x2234 MacBook Pro screen the answer is 2560x1440, and the game renders at 1024x576 into a 2048x1152 DLSS output, at the same 30 fps on its battle map (p95 33.8 ms). The same battle map frames at 1920x1080, without and with the change, have 43% more edge energy (6.6-6.9 to 9.6-9.8), the bricks and cobblestones visibly sharper. Donkey Kong Bananza does not call it. Host tests, the GPU graphics tests, the iOS compile check (258 files) and test_docs pass; no takeover name is duplicated. services.md has the entry, and the symptom index is refreshed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fire Emblem Fortune's Weave (04007EF01F8AE000, Switch 2, update v131072, SDK 22.2.0 built for the Switch 2, NVN2; Intelligent Systems' engine on NintendoWare): a profile with its note, its row in the games table and its gallery screenshot. Played on the Mac (M3 Pro) for about 30 minutes, to the first dungeon, Heretics' Cave, through the opening movie, the first map scenes, the world map, character creation and the first battle maps; the battle maps hold the game's 30 fps. The profile is the provisional one the NSP script made from the dump, checked by hand: virtual address memory with a 9.5 GB alias region in a 10.25 GB block (the game sets no heap and takes its memory from a StandardAllocator there), the Switch 2's 9 GB pool, depth_mode zero (the game sets near = 0 at startup), pad_layout full, and the modules main, subsdk2 (movie) and sdk. Its shader tables are those the NSP script split from the game, 16,888 shaders, all of which build. The prewarm list is empty until recorded on the Mac, as Splatoon Raiders'. The display version stays "?": a dump has no control data. The reference entries found on this game now link its note in place of "no profile yet". The screenshot is the first dungeon from the play session, cropped to the game picture (no title bar, border or shadow) and scaled to 960x540 JPEG as the other gallery images are; it follows the other Switch 2 games, with their note "Switch 2, Mac only, from a dump" (the block is larger than an iPhone allows without the extended-virtual-addressing entitlement). Open, in the note: AV1 decoding through dav1d on Macs before M3 is being tried by a tester (seedds#927, where the AV1 change's scope is being settled), iOS, the prewarm list, DLSS inputs kept but unused, and anything past the first dungeon. Checked: test_docs, test_game_profile, test_settings, test_launcher_settings, test_profile_guess, test_nsp_chain, test_regress, test_nsp_mac_app, test_nsp_incremental, test_usage and test_bench pass; docs_gallery.py --check is clean; the host builds from the repository's profile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first crop stayed 15 pixels inside the window's edges to clear its rounded corners, and cut into the HUD at the edges (the date dial's "12/16", "Show Path"). This one, from a second capture of the first dungeon, takes the whole game picture: 1838x1033 from below the title bar (the window's columns 81 to 1918, rows 101 to 1133), 16:9. Only the window's two rounded bottom corners were masked, about 14x13 pixels each with the window's shadow under them; those 192 pixels are filled from their neighbours, and the edge pixels that are nearly opaque keep their own colours. Scaled to 960x540 JPEG as the other gallery images are. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…vie module aborts on a refused Initialize A tester built the branch on an M1 Pro (macOS 26.6.2), which has no AV1 decoder in VideoToolbox, from a dump with the base game only (seedds#927). The host picked dav1d by itself; the opening movie (3,301 pictures) and the second (1,501) played smooth and in sync with their sound, in 14.5 minutes of play with no crash. movies.md's check and the note's open item record it, with the M1 Pro's GPU time in busy scenes (36-38 ms a frame at 640x360 into 1280x720, from a build before the host answered the display size: its log names commit c496aba). The tester also found that refusing AV1 is no fallback: movie::detail::InitializeDecoderByTransferMemory aborts on any non-zero result (subsdk2+0xf9b4, checked here by disassembly), so a host without a decoder ends the game rather than skipping its movies. movies.md's cause says so. Checked: test_docs passes; the symptom index is current. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…drop a shell call and a nullable buffer clang-tidy (ratchet) found four findings above main's baseline on PR seedds#957. hle_movie_av1.c from_nv12 read VideoToolbox's planes without checking CVPixelBufferGetBaseAddressOfPlane's result, which is null when the lock fails (clang-analyzer-nullability.NullableDereferenced): it now returns no picture then, as when the allocation fails, which its callers already handle. t_image_primitive_id.m compiled its test shader with system() (bugprone-command-processor): it now runs xcrun with an argument list through NSTask, as t_pso_warm.m does. t_geometry_hazards.m: the branch's host changes changed how the analyzer walks the nvn_mtl.m it compiles in, and two findings appeared, as in 97f1514. The readback buffer from newBufferWithLength went unchecked into copyFromTexture (clang-analyzer-nullability.NullablePassedToNonnull): checked now, failing the test cleanly. The RetainCount leak on the program is a false positive (serial_put hands it to g_by_serial, whose CFBridgingRetain owns it): // NOLINT with that reason. Checked: t_image_primitive_id (6 checks) and t_geometry_hazards (57 checks) pass, and so do the host tests (t_codec_video among them). clang-tidy itself was not run here (no LLVM 23.1.4 on this Mac); CI's ratchet will. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rts the program's leak at clang-tidy (ratchet) on cbc2501 still found the RetainCount report on the program in t_geometry_hazards.m: the analyzer reports it at line 141, the posix_memalign statement after serial_put, where it takes pr to be no longer referenced, not at the line that makes pr, where the NOLINT was. The NOLINT, with the same reason (serial_put hands pr to g_by_serial, whose CFBridgingRetain owns it), now sits on that line. The other three findings of the last run are gone (CI: 272 findings, one pair above the baseline of 271). Checked: t_geometry_hazards passes (57 checks). clang-tidy itself was not run here (no LLVM 23.1.4 on this Mac). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he test archive Tool tests on PR seedds#957 failed in MemberNames.test_names_and_time: 1.44 s, not less than 1.0. The timed section also built the 800-member, 12.8 MB test archive with the fuzz builder, which takes 0.34 s on an M3 Pro and over a second on the CI runner, so the limit measured the builder more than the code it guards. The archive is now built before the timing starts. Reading its members takes about 1.5 ms here, against 47 s for the per-member split the test guards against, so the 1 s limit keeps a wide margin both ways. Checked: test_split_nesting.py passes; the two parts timed separately (0.336 s building, 0.0015 s reading). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Switch 2, v131072, SDK 22.2.0 (Ounce), NVN2 231.40, NintendoWare G3d / Ui2d / Vfx / Atk, a deferred
renderer. The opening movie, the dialogue, the first battle map and its battle scenes, compared with
published footage and in a playtest. All 16,888 shaders translate. Mac only: the 16 GB alias region is
over iOS's limit. Note: dm/games/04007EF01F8AE000/README.md.
Host:
- NVN2's DLSS through MetalFX's temporal scaler (OP_DLSS, dlss_eval), with a bilinear fallback.
- AV1 movies through VideoToolbox (hle_movie_av1.c), SDK 22's decoder forms, and pictures as native
images. A Mac without a hardware AV1 decoder shows black movies with a red line, a platform limit.
- vi::QueryDisplayBufferMaxResolution answers (unanswered, the game rendered for 720p); Nmouse.
- A fragment shader that reads the primitive ID gets its pipeline built whole: Metal 4 gives
[[primitive_id]] 0 in a pipeline specialized from a base with unspecialized colour state, which left
three of four ambient occlusion layers empty (a dot lattice).
- Pending clears land before a compute dispatch (every game). A clear before a dispatch landed after
it, so the game's exposure reset overwrote what its exposure shaders had just written: 7 s after boot
on the same route, exposure 3.035 and climbing with 63 % of the frame near white, then 5.555 settled
with 0.1 %.
- NVN2 compute reads its workgroup counts at c0 + 4 (Switch 2 only): a light culling shader's clusters
collided in a column at zero. Read from the shader; its effect was not measured on its own.
- SDK 22.2.0's node area cut (by fingerprint).
Translator (Ampere only, the chain changes): bindless TEX forms (0x361, 0xf60, 0x370), BREV, F2I / I2F
of 64 bits, ATOMS and ATOMS.CAS, MEMBAR, ERRBAR. Shared memory addresses take their stride (the exposure
histogram's bins read back at about 4x their index). IMAD.HI adds the pair {c + 1:c} and IMAD.X its carry
(the exposure's 64-bit division gave 893011 for 218; run from its translated MSL on 208 inputs up to
2^44, none wrong now). A table of handles past the array's length starts the array at its base (black
characters; also a Maxwell path, BindlessToArray).
Checked: t_codec_video, test_dmsh_ampere_mem.py (13 tests), test_docs, test_nsp_chain, test_patches,
`make -C dm/gfx/tests run` and `make -C dm/host/tests run`, on Mario's profile, before the rebase; a
playtest on the Mac (M6). No test here covers the primitive ID pipelines, the clear ordering, or c0 + 4.
Regression: uncertain reach, a broad chain and host change, so 2 games: Pikmin 4 (the most compute
shaders of those with routes, for the clear ordering) and Breath of the Wild (compute and bindless). The
run had not finished at this push, by the maintainer's decision. No Switch 2 game is in the local
harness, so Donkey Kong Bananza and Splatoon Raiders have not been rebuilt or run with these translator
and host changes.
The diagnoses of the clear ordering, IMAD.X, c0 + 4 and the stride's field come from capibawa's PR #957,
each checked here before use.
Co-authored-by: James <12531452+capibawa@users.noreply.github.com>
|
Thank you for this. Fire Emblem Fortune's Weave landed on Taken from your PR, each checked here before use and then written here, not copied:
Reached independently here, so not from your PR: IMAD.HI's pair, the primitive ID pipelines, DLSS on MetalFX, AV1 on VideoToolbox, the display query, the bindless forms. Not in that commit, and still open if you'd like to send them as separate PRs, as you offered:
Your testing also went further than mine: you reached the first dungeon, while this port has only been played on the first map. Anything you see past that on Each of those would need rebasing onto 0a759de, since the overlapping files have changed. |
VideoToolbox decodes AV1 only in hardware, on Apple M3 and A17 Pro and later. On M1 and M2 the host gave black pictures with a red line. Here a session with the hardware decoder forbidden opens for H.264 and not for AV1, so VideoToolbox has no software path to fall back on. hle_movie_av1.c gets dav1d (vendored in the previous commit) as a second backend behind the same interface. It is used on chips with no hardware AV1 decoder, for a stream VideoToolbox refuses (from that temporal unit on), and with DM_AV1_DAV1D=1 on any chip. Its planar pictures are handed over as the NV12 the interface gives. 10- and 12-bit streams are shifted to 8 bits, and a stream with no colour matrix gets BT.601 below 720 lines and BT.709 from there, as VideoToolbox gives its pictures. hle_codec_video.c drops the black-picture path, names the backend in its log line, and keeps a full-range stream's range. dm/host/Makefile builds dav1d into gen/dav1d/libdav1d.a and links it, the host tests link it for t_codec_video, and the NSP script's Mac host build key includes it. The iOS build takes the same files from the Makefile's compile lines. Checked: - t_codec_video decodes a 64x64 AV1 key frame (SVT-AV1) on VideoToolbox and on dav1d, and reads both back as the same NV12 (AddressSanitizer and UBSan). - Fire Emblem Fortune's Weave's 82 movies through hle_movie_av1.c, the first 300 pictures of each on an M6: 23,219 pictures with the same size, matrix, range and hashes on both decoders (every 7th row hashed), no stream refused. dav1d ran at about 190 pictures a second with its two task threads. - With DM_AV1_DAV1D=1 and no window on the M6, the game plays the opening's 3,301 pictures, right on screen, and goes on into the prologue with no crash. - An M1 Pro played the first two movies through an earlier version of this code, on #957's branch (#927). This commit has not itself run on a chip without hardware AV1, and iOS has only been compile-checked. Regression, on main plus this work against 54ab7b1: uncertain reach (it includes the clear ordering and the chain change of 0a759de), so 2 games, in quick mode, as each game has at most 5 minutes. Pikmin 4 for its compute shaders, Blasphemous as a small NVN game on another engine, in place of Breath of the Wild, whose translation takes far longer than that. No new red lines, crashes or hangs, and no translated shader changed (Blasphemous's reflections gain only primitive_id 0 and the chain version). Blasphemous's screenshots match, 59.4 to 59.9 fps. Pikmin's quick route has no screenshots or timing window, so only its boot and frame count (2815 to 2817) were compared. Peak memory rose 1.4 % and about 4 % in single runs, not looked into. From PR #976. Co-authored-by: James <12531452+capibawa@users.noreply.github.com>
What and why
Fire Emblem Fortune's Weave (
04007EF01F8AE000, Switch 2, update v131072, SDK 22.2.0 built for the Switch 2, NVN2) plays on the Mac: about 30 minutes to the first dungeon, Heretics' Cave. This adds its profile, note (dm/games/04007EF01F8AE000/README.md), games table row and gallery screenshot, with the host, translator and tool changes it needed.This is more than one change. CONTRIBUTING asks for one change per PR, and the AV1 part is waiting for an agreed scope in #927. I'm opening it whole so the game's support is visible and testable in one place (a tester on #927 is building it on an M1 Pro for the dav1d path). Each commit is self-contained, with its own symptom, cause, fix and checks in the message, so I'm happy to split it into separate PRs, in this order or any other you prefer, and to rework or drop the AV1 commits once #927 is settled.
The commits, by reach:
sdkbuild only, by fingerprintsdk)vi::QueryDisplayBufferMaxResolutionanswers the Switch 2's displaytexrawsaves every depth slice of a 3D texture; services.md: why a dump shows "Ver. ?"Checked
make -C dm/host/tests GAME=010015100B514000 runand itsasan/tsan-threadsbuilds,make -C dm/gfx/tests GAME=010015100B514000 run(Apple silicon, Metal 4), everydm/tools/tests,dm/ios/testsanddm/gfx/testsPython file,dmhostfor the CI games and both Switch 2 games,dm/ios/tools/compile_check.py(258 files). Each run after the commit it covers; the last full set on the branch before the latest rebase ontomain, thentest_docs,test_nsp_chainand the Ampere translator tests after it.scripts/regress.py check --games: not run. There is no regression root set up on this Mac, and the only other local games are Switch 2 or provisional. The changes that reach Switch 1 games, and so most need it, are the clear-before-dispatch ordering, the primitive-ID pipelines, the SDK 22 takeovers and the shader rebuild from the chain change. I can run it on games you name, or you may prefer to run it on yours.Docs
docs/reference/<subsystem>.mdentries (memory, services, audio, movies, rendering, shaders), linked from the game's note, andpython3 dm/tools/docs_symptoms.pywas rundmsh_chain.py --headerwas run anddm/gfx/nvn/dmsh_chain.his part of the changeRelated: #927 (the AV1 scope).
🤖 Generated with Claude Code