- Export commandlet writes the live Core adapter's world_assembly + resolved layout pair; the headless owner path imports that pair (manifest/layout equal, layout_chunk_width_q validated). owner_world.json re-exported from the owner's staged assets on the Linux host. - Adapter SW route: Core clearance/radius floors, 0.5 m support search, outward-facing tie-break; passage mouth/mid radius floors match Core (36/24 owner voxels). - FI in Unreal now uses the legacy (compiled analytic) generator unless a plan is imported: the auto native-FI plan failed the passage audit on the owner plan (1843 blocked samples, 945 open-air violations). Owner decides whether native FI returns. - In-engine spawn traces per strate (3 each): SW GenerateTile 42.7 worker-s vs 20.1 headless for equal tile counts; LOD0 p95 45-169 ms in UE. - Fast tier green; all three passages pass on the shared plan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
126 lines
8.1 KiB
Markdown
126 lines
8.1 KiB
Markdown
# Linux 70: Unreal and headless plan parity
|
||
|
||
## Result
|
||
|
||
The strict Unreal export and the headless import now resolve the same Core manifest and layout:
|
||
`manifest_equal=True`, `layout_equal=True`, manifest hash `bcc0d0d33ea39435`, layout identity
|
||
`resolved-layout-v3-fnv64:0589300eb07dfe06`, and `layout_chunk_width_q=8192`. The headless log reports
|
||
`source=owner_export/unreal_core_adapter`. Slot bounds are `0: top 0 / bottom -63`,
|
||
`1: -64 / -93`, `2: -94 / -109`, and `3: -110 / -125`.
|
||
|
||
The exact-plan passage audit passes all three interfaces, with `natural=3/3` and zero route-binding
|
||
failures. The fast tier also passes (268.4 s), including peer determinism and the new four-strate
|
||
plan stage. The new floating-islands style-2 output is pinned in
|
||
`Standalone/Tools/verify/hash-baselines/owner-hash-floating-islands-style2-v214.log`; the historical
|
||
L68 baseline was left intact.
|
||
|
||
## Divergences and fixes
|
||
|
||
- **Export and manifest.** The pre-L70 owner JSON had no root resolved Core manifest/layout pair.
|
||
Its inactive `settings_asset.WorldAssemblyManifestJson` also lacked `layout_chunk_width_q` and
|
||
carried an FI plan with top/bottom `-16/-79`, while the live FI slot is `-64/-93`.
|
||
`bUseWorldAssemblyManifestOverride` is false, so Unreal did not consume that stale string; the
|
||
headless fallback instead made its own plan. The export commandlet now writes
|
||
`world_assembly`, `core_world_assembly_json`, and `resolved_layout_json` from the live Unreal
|
||
Core adapter. The headless owner path imports that pair and validates width and live slot bounds.
|
||
The embedded legacy string remains inactive; use the root pair.
|
||
|
||
- **Slots, passage inputs, and route.** The four live slot bounds and authored passage configs
|
||
agree after export. Before unification, the independently resolved SurfaceWorld upper mouth
|
||
differed: headless fallback `[0,0,-383938]` versus Unreal `[8192,16384,-381197]` in Q units.
|
||
Unreal's candidate search had weaker clearance and support constraints and a different tie
|
||
choice. The adapter now uses the Core clearance/radius floors, checks route support, and prefers
|
||
the outward-facing candidate; the Core radial phase override was removed. The composed route
|
||
support deck and root taper were corrected, and the imported-plan passage audit now samples the
|
||
serialized Core route. The final canonical routes and passage configs compare equal.
|
||
|
||
- **Generators and FI dispatch.** Recipes in the resolved layout are `SurfaceWorld.Heightfield2D`
|
||
v2, `FloatingIslands.ClusterField` v0, `legacy.TunnelNetwork`, and `legacy.VerticalShafts`.
|
||
The owner currently has no native FI plan: `bUseWorldAssemblyManifestOverride=false` leaves
|
||
`bImportedOverride=false`, so the adapter's `Type == FloatingIslands && bImportedOverride`
|
||
branch does not build one. `NativeFloatingIslandDensityAt` then has no bound `IslandPlan` and the
|
||
FI slot uses `compiled-analytic/FloatingIslands`. This is the same default FI dispatch on both
|
||
sides of the final plan.
|
||
|
||
- **The L69 solid sample.** R37's notes place `(-43.0,-34.5,-448.5) m` on the lower inclusive
|
||
boundary of the wider compiled region. L68's smaller compile queried `EvaluateFast`; L69's wider
|
||
compile read the packed rock bit, and the same point was solid in both L69 headless and Unreal
|
||
runs. The source of that historical packed bit is still unidentified. L70's exact-plan composed
|
||
density audit passes all three interfaces; that does not identify the old bit's density
|
||
contributor. The route-planner difference was real, but R37 did not find it sufficient to
|
||
explain that sample.
|
||
|
||
## Re-export on Windows
|
||
|
||
Set the three paths for the owner's machine, then run this PowerShell command. It overwrites
|
||
`Standalone\data\owner_world.json` with the live four-strate export and its canonical plan pair.
|
||
|
||
```powershell
|
||
$UE = 'C:\Path\To\UnrealEngine'
|
||
$VFHost = 'C:\Path\To\VFHost'
|
||
$VoxelForge = Join-Path $VFHost 'Plugins\VoxelForge'
|
||
& "$UE\Engine\Binaries\Win64\UnrealEditor-Cmd.exe" "$VFHost\VFHost.uproject" `
|
||
-run=VoxelForgeExplore -nullrhi -unattended -nop4 -nosplash -NoSound `
|
||
-NoZenAutoLaunch -notraceserver -nocrashreports -DDC-ForceMemoryCache `
|
||
-settingsref=/Game/VoxelForge/DA_Settings.DA_Settings -modes=export `
|
||
-gameconfig=1 -standaloneworldexport=1 -ownerfourstrateplan=1 `
|
||
"-out=$VoxelForge\Standalone\data" `
|
||
"-abslog=$VoxelForge\Saved\Linux\l70\owner-export.log"
|
||
```
|
||
|
||
## Spawn traces
|
||
|
||
Three Unreal traces per strate completed. The table uses Unreal medians; `walkable` is the 9/9
|
||
walkable-gate time, `LOD0` is the 512-tile ring time, `full` is spawn to `initial_set_ready`, and
|
||
`worker` is the sum of tile `worker_task_s`. Headless values are one run at the same arrival;
|
||
`worker` sums `WorldAssemblerLODWorker.worker_sum_s`.
|
||
|
||
| Strate | Walkable UE | LOD0 UE | Headless first / neighborhood | Full UE / headless (tiles) | Worker UE / headless (s) | UE LOD0 p50 / p95 (ms) | UE GenerateTile / mesh copy / RMC (s) |
|
||
|---|---:|---:|---:|---:|---:|---:|---:|
|
||
| 0 SurfaceWorld | 0.833 s | 2.063 s | 0.406 / 0.556 s | 7.039 / 2.057 s (2850 / 2850) | 42.912 / 20.064 | 0.924 / 125.243 | 42.688 / 0.035 / 0.190 |
|
||
| 1 FloatingIslands | 1.813 s | 3.200 s | 0.377 / 0.660 s | 6.642 / 5.493 s (2249 / 2249) | 48.219 / 61.516 | 16.819 / 168.878 | 47.968 / 0.037 / 0.214 |
|
||
| 2 TunnelNetwork | 0.361 s | 2.684 s | 0.393 / 0.547 s | 5.626 / 4.634 s (2233 / 2233) | 37.894 / 50.868 | 8.188 / 166.935 | 37.655 / 0.035 / 0.203 |
|
||
| 3 VerticalShafts | 0.379 s | 1.191 s | 0.400 / 0.413 s | 3.651 / 2.258 s (2329 / 2329) | 16.425 / 22.227 | 2.323 / 44.996 | 16.217 / 0.033 / 0.178 |
|
||
|
||
The full-set wall gaps exceed 30% at strates 0 (+242%) and 3 (+62%); the worker-sum gap exceeds
|
||
30% only at strate 0 (+114%). Unreal ran with `streaming_background_worker_cap=1`, an eight-slot
|
||
effective queue, startup apply boost, and the live asset clip settings (radius 6, max clip 6;
|
||
observed levels 0–8). The headless table used 12 workers, radius 3, max clip 8, max level 8. Its
|
||
tile counts match Unreal at every LOD for these arrivals, but these are not identical runtime
|
||
settings. The explicit headless radius-6/max-clip-6 run hit the 160-million-node shape safety
|
||
bound at 17,689 desired tiles before generation. Unreal's full-set milestone also includes its
|
||
tile scheduling and delivery path, while headless `whole_view_ready` is the direct pipeline
|
||
milestone. Strate 3's higher wall time with 26% fewer worker-seconds suggests readiness/scheduling
|
||
overhead. Strate 0's worker excess is in `GenerateTile` itself (42.688 s versus 20.064 s for the
|
||
headless worker sum); plan and per-LOD tile counts do not explain it, and this trace does not
|
||
isolate the remaining runtime cost.
|
||
|
||
## Native FI experiment
|
||
|
||
I temporarily removed the `&& bImportedOverride` gate, rebuilt, and exported the auto-native FI
|
||
plan (hash `303f3d266dc40a7c`). Dispatch changed to `FloatingIslands-native`; the 2249-tile
|
||
whole-view density/mesh/payload/topology hash changed from off
|
||
`c1e5e72f879b0327/2c3994301a77a14e/1e3c8f74f439e4ff/ba850f95f0b889c8` to
|
||
on `11042f19685926d1/56c6d3d46d8ed3b1/d81e70785c858145/7e608c8cb9027a70` (one run each).
|
||
The on-plan audit failed: 2/3 interfaces
|
||
passed, the FI route had 1843 blocked samples, and open-air had 945 violations. Leave native FI
|
||
off for the owner.
|
||
|
||
## Build and failed runs
|
||
|
||
Linux UBT succeeded with the final native-FI-off code. `Source/` files for the Windows build are
|
||
`Source/VoxelForge/Private/VoxelForgeCoreAdapter.cpp` and
|
||
`Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp`. The build completed before the
|
||
timing series; no Unreal build ran during timing. Every process was taskset-limited and capped at
|
||
6 GiB; UE used RSS and headless used address space. No run exceeded 30 minutes; each valid Unreal
|
||
trace took about 53 s.
|
||
|
||
- Initial UE trace attempts inherited an address-space cap and failed during UE startup/mapping;
|
||
switching the trace driver to the RSS cap produced all 12 valid traces.
|
||
- The radius-6 headless comparison stopped at the 160-million-node bound noted above.
|
||
- A diagnostic passage-audit run with extra max-clip/output flags threw `std::bad_alloc`; the
|
||
bounded rerun without those diagnostic flags passed all three interfaces.
|
||
- The one-line native-FI experiment intentionally failed the passage audit as described above.
|
||
|
||
No commit, push, or stash was made.
|