Files
seedds f8642c480f Run image atomics as native Metal texture atomics, 3D images and CAS included
Below MSL 3.1, SPIRV-Cross emulates an image atomic with a raw device pointer
beside the texture, laid out like a linear image. A block-linear image's Metal
texture is a deswizzled copy, so its atomics went to a scratch buffer nothing
read (Pokemon Legends: Z-A's atomic max into texture 302), and a 3D image's
pointer was indexed with the int3 coordinate itself, which does not compile
(16 shaders of Octopath Traveler 0, 5 of Ghost Trick, 8 of FINAL FANTASY
RESONANCE).

- dmsh_atomic.py: shaders with image atomics (OpImageTexelPointer) are written
  at MSL 3.1, so the atomics run on the image's own texture; every other
  shader keeps its MSL 2.3 source byte for byte. MSL that still holds the
  pointer form fails the build.
- Translator (TranslationFlags.MetalImageAtomics, renamed from
  Msl23ImageAtomics): admits 3D images and SUATOM.CAS (Metal's texture
  compare-exchange); the IGN coordinate guard is unchanged.
- Host: the IMGA pointer binding, the scratch buffer and the layout admission
  are gone. NVN images of R32Uint / R32Sint take MTLTextureUsageShaderAtomic
  (img_usage, kept by tex_upgrade: Metal leaves that bit out of the usage
  property); any other texture in an atomic slot is replaced by an
  atomic-capable write sink with a red line.
- Chain a4e2088c2e69 (with main's vertex-output padding). Tested on
  chain 0ce4b5e8aefb, this change before that rebase: Octopath Traveler 0
  builds all 10,324 shaders, Z-A all
  28,603, Ghost Trick's five 3D-atomic shaders build. Mac runs of both games
  under the Metal API validation layer showed no validation error (Octopath
  Traveler 0 to frame 14,400, its particle atomics on buffer texture 2049;
  Z-A to frame 12,000 through the atomic max on texture 302). Synthetic
  atomics and CAS run on buffer, 2D, 2D-array and 3D textures of a Mac GPU
  (test_image_atomic_bounds.py, t_atomic_return.m).

GitHub issue #290: at 40b643b Octopath Traveler 0 already built 10,308 of
10,324 shaders (2e655b7 fixed the returning atomics); the 16 left were the 3D
ones, which build now. Not checked: an iPhone, FINAL FANTASY RESONANCE's
library, the picture of Z-A's atomic scene.
2026-10-27 03:01:45 +08:00

71 lines
9.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OCTOPATH TRAVELER (`010057D006492000`)
Update 1.0.5 (v327680) over the base game, SDK 3.5.1 (its own `sdk` build; NVN API 53). Square Enix / Acquire, Unreal Engine 4.18 (`++UE4+Release-4.18`; its project is called Kingship): two legacy version 4 paks with the shaders inline in the cooked packages, NVN, PhysX 3.4, CRIWARE (ADX2 sound banks, one USM movie). Modules `main`, `subsdk0` (the movie module) and `sdk`. 30 fps, as on the Switch.
Status: brought up on the Mac: the boot, the language notice, the title, a new game, the traveler selection (the world map, the travelers' cards), Ophilia's opening (her room, the cathedral hall and nave, the tutorials) and H'aanit's (the flashback, free play in S'warkii), at 30 fps (frame time p50 33.4 ms) with no red line but the two untranslated compute shaders below; about 2.4 GB resident with 1.2 GB of Metal allocations. Not yet seen: a battle, the sound (runs were headless, `DM_AUDIO` off), the iPhone.
## What it needs
| Setting | Where | Why |
|---|---|---|
| 4 GB layout: `mem_reserve 0x100000000`, `mem_alias 0x10000000`, `mem_stack 0x10000000`, `app_pool 0xCD500000`; `sys_resource 0x0` | [`game.tsv`](game.tsv) | no virtual address memory: `nninitStartup` sets a heap of pool − used (3.08 GB); see [Memory](#memory) |
| `mem_used 0x8000000` | `game.tsv` | the rewritten modules are larger than the default 64 MB of used memory |
| `tex_flush_upload on`, `tex_dirty_upload on` | `game.tsv` | the CPU loads textures straight into CPU-uncached pools after creating them and never flushes; see [Rendering](#rendering) |
| `pad_layout full` | `game.tsv` | menus and the battle commands take the D-pad |
| `shader_groups GlobalShaderCache=global #Engine/=engine #Kingship/=game` | `game.tsv` | the global shader cache, the engine's materials and the game's |
## Memory
- **Current:** the NPDM declares no system resource, so there is no virtual address memory and no engine address region: `nninitStartup` (`main+0x2c67cc` calls into the SDK's heap setup) sets a 3.08 GB heap, and Unreal's own allocator ("Using Standard Allocator", Binned Small Heap 3154 MB) and the NVN pools live in it. The game prints its memory setup through `nn::diag::LogImpl`, which reaches the host log with `DM_DBG=1` (`svcOutputDebugString`), as does an Unreal assertion.
- **Resolved: `SetHeapSize OOM: req 0xc5400000 > cap 0` and a `Break` in `nninitStartup`** with the provisional profile, which gave every Unreal game a 64 GB alias region and no heap region: [Unreal's engine address region](../../../docs/reference/memory.md#unreals-engine-address-region-and-its-options) (note).
- **Resolved: `Failed to allocate 1879113728 bytes from Standard Allocator`** at boot: [The sparse tile layout of a texture](../../../docs/reference/nvn-objects.md#the-sparse-tile-layout-of-a-texture).
- On the Mac the process holds about 2.4 GB (1.2 GB of Metal allocations) in the cathedral and in S'warkii; 221 memory pools.
## Rendering
- **Resolved: the language notice's pictures, the title logo and the whole traveler selection missing** (only the backdrop and the footer text drew). Unreal writes these textures from the CPU into CPU-uncached pools (flags 0xe2) after `nvnTextureInitialize`, with no flush, so the host kept them as GPU textures with no content; Slate then drew nothing for them. The profile turns on `tex_flush_upload` and `tex_dirty_upload` ([dm/games/README.md](../README.md)), which upload them from guest memory and again when their pages change.
- **Resolved: the world map on the traveler selection a patchwork of rectangles:** [Sparse textures take the block height of their 64 KB tile](../../../docs/reference/rendering.md#sparse-textures-take-the-block-height-of-their-64-kb-tile).
- **Resolved: streamed textures black or missing** (S'warkii's ground, plants and props) and `[RED] MapVirtual: non-contiguous mapping (true sparse) not implemented`: [Virtual pools of one texture each](../../../docs/reference/nvn-objects.md#virtual-pools-of-one-texture-each-mapped-before-and-after-the-texture).
- **Current (checked, not confirmed on hardware): a dark red cast at the screen edges in S'warkii.** The tonemapper is right (checked by snapshots of its inputs, `DM_NVN_TEXSNAP`): its scene input is neutral there and its LUT turns dark greys red. The LUT is computed every frame from the area's grading parameters (pass with `172ec899e321a2a5`, no textures), which in S'warkii hold a red-weighted gain (1.5, 1.08, 1.0), an offset (1, 0, 0) with −0.116 and a white balance of 8000 K (6500 K on the title), so the strong vignette (intensity 1.17) darkens the edges into the red shadows. The grid there is the out-of-focus foreground foliage's pixel-art blocks.
- Two compute shaders of the global shader cache did not translate (`327fce53f1fce4c1`, `6d87d48e86bd7f3c`: image atomics whose result is used, refused on MSL 2.3), and their dispatch was skipped with a red line. Both build on chain `0ce4b5e8aefb` (a build of those two keys, 2026-10-27; [Image atomics](../../../docs/reference/shaders.md#image-atomics)); not run since.
- 5,434 shaders (4,517 pixel, 672 vertex, 232 compute, 13 geometry), all but those two built for the Mac; all 5,434 built for iOS.
## Files and shaders
- `Kingship/Content/Paks/Kingship-Switch.pak` (1.97 GB, 103,018 files; the profile's `romfs_marker`) and the update's `Kingship-Switch_0_P.pak` (601 MB, 1,294 files), legacy version 4, Zlib blocks, index not encrypted; beside them 383 CRIWARE `.awb` banks, `CRIWARE/movie01_op.usm` and the vibration data. `UE4CommandLine.txt` holds the project path.
- The shaders are inline in the cooked packages and the global shader cache, as for [DRAGON QUEST XI S](../01006C300E9F0000/README.md) ([Unreal Engine 4.18 shaders inline in cooked packages](../../../docs/reference/shaders.md#unreal-engine-418-shaders-inline-in-cooked-packages)); here the paks are Zlib, so no game decoder is needed.
## Rewriter
- **Resolved: `jump table 1676f58`:** [64-bit bounds check of the index](../../../docs/reference/rewriter.md#64-bit-bounds-check-of-the-index).
## Sound
- **Resolved: `FATAL audren: missing SDK symbol _ZN2nn2os12SdkMutexType4LockEv`:** [No `SdkMutexType` in SDK 3](../../../docs/reference/audio.md#no-sdkmutextype-in-sdk-3). The renderer then runs at 32 kHz with the SDK 4 command layout (`DeviceSink` as command 0xf); not listened to yet.
## Mac routes
The work folder `<NSP folder>/010057D006492000-work/run` has `host.sh` (builds the octo1 worktree's host and copies it to `build/o1host`), `boot.sh <log> [secs] VAR=…`, `ctlrun.sh` / `ctl.sh` / `step.sh` / `see.sh` / `mash.sh` for driving a run through `DM_CTL`, `stop.sh`, `to_story.sh` (Ophilia's opening) and `ab.sh` (several variants at once on copies of the save, the same scripted route, shots at given frames).
- With system data in the save (the language notice is shown only on a fresh save): the title at about 28 s; A, A (New Game) opens the traveler selection; L picks a traveler (L once Ophilia, twice H'aanit); A opens the confirmation, whose cursor appears only after a direction press (Down, Up), then A starts the story.
- Into S'warkii: `DM_KEYS=28:A,36:A,70:L,72:L,75:A,78:Down,79:Up,80:A,95:A*480/0.6,390:B` (the village at about frame 12,000).
- In S'warkii: from the start, `LUp~1.5`, `LRight~0.1`, `A` talks to the villager on the path ("This is S'warkii, …"); the headman (green bubble, by the big house) starts the Linde scene and the Path Actions tutorial, after which the objective is to Provoke the two fledgling hunters in the plaza (Y facing one; with nobody in front Y offers only Examine, the monster roster). Characters do not block H'aanit: walking into a villager walks through it, and talking needs her to stop beside one, facing it.
- **Checked, not a bug: walking through villagers.** Probes on PhysX's query callback (`MultiQueryCallback::invoke`, the scene's pre-filter at `main+0x154d750`) show the player's movement sweep (Pawn channel, block bits `fffffef3`, touch bits `4`) only touches Pawn shapes, by the game's own collision data, while villagers' own sweeps block each other and the WorldDynamic queries that touch Pawns find the villagers' capsules.
- The game saves no story progress before its first save point; no save of the openings is kept yet.
## Found on this game
General changes first needed here; each applies to every game:
- **Rewriter:** a 64-bit bounds check of a jump table's index: [64-bit bounds check of the index](../../../docs/reference/rewriter.md#64-bit-bounds-check-of-the-index).
- **Provisional profiles:** Unreal's 64 GB alias layout only with virtual address memory (`profile_guess.py`): [Unreal's engine address region](../../../docs/reference/memory.md#unreals-engine-address-region-and-its-options).
- **NVN:** `nvnTextureBuilderGetSparseTileLayout` / `nvnTextureGetSparseTileLayout`: [The sparse tile layout of a texture](../../../docs/reference/nvn-objects.md#the-sparse-tile-layout-of-a-texture); every virtual pool with a range of its own until its first mapping decides: [Virtual pools of one texture each](../../../docs/reference/nvn-objects.md#virtual-pools-of-one-texture-each-mapped-before-and-after-the-texture); sparse textures' block height: [Sparse textures take the block height of their 64 KB tile](../../../docs/reference/rendering.md#sparse-textures-take-the-block-height-of-their-64-kb-tile).
- **Audio:** the update lock without `SdkMutexType`: [No `SdkMutexType` in SDK 3](../../../docs/reference/audio.md#no-sdkmutextype-in-sdk-3).
## Open
- A battle, the sound and the iPhone are not tested; the IPA (3.6 GB) is built but not run on a device.
- The travelers' signatures on their cards stop part-drawn ("Opli…", "Haa…"): not looked into.
- The two untranslated compute shaders (above).
- 60 fps: the host's frame-limit writes (`hle_fpslock.c`) need this build's `t.MaxFPS` and `nvn.SyncInterval` addresses.