Commit Graph
57 Commits
Author SHA1 Message Date
Fr0zkaandClaude Opus 5.5 b06397df9c streaming: speed-aware scheduler with hysteresis (L80); arrival near-first, fast travel keeps the horizon
Policy 3 (headless + Unreal): walkable ring always first; at rest/arrival LOD0-2 before far;
walking interleaves near:far ~4:1 toward heading; fast travel admits far coarse tiles right after
the walkable ring. Hysteresis: walking 0.75/0.35 m/s, fast 12/8 m/s.
Headless, far holes while moving 30 m/s: L77 9.0% / L79 77.2% / L80 8.9%. Cold LOD0-2 ready
(one clean protocol, medians): SW 1.30, FI 3.61, TN 3.07, VS 1.43 s.
Unreal 30 m/s traces end 2,233/2,237 loaded (commandlet readiness exit 1) for all policies.
Passage zero-padding cut: ~50% fewer passage iterations on active tiles, max 6.6 cm surface
displacement, no sign flips; not adopted yet (padding sweep next). World unchanged (v213).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
2026-10-27 21:39:21 +01:00
Fr0zkaandClaude Opus 5.5 47148e0a0d build: VoxelForge compiles in Unreal 5.7 on Linux (L65); Linux-only Unreal trace timing fields
Linux UE builds failed on the Core wrapper module: the Linux-only PerModuleInline.gen.cpp saw Standalone/Compat
shadow headers instead of the engine's, and WITH_EDITOR was redefined under -Werror. Compat headers now forward
to the engine headers with include_next for generated Linux module TUs (canonical Core TUs opt into the Compat
definitions via CoreNamespacePreamble.h); CoreMinimal.h undefines WITH_EDITOR first. VoxelForgeCore.Build.cs
enables that forwarding and AVX2 on Linux only; VoxelForge.Build.cs disables Unity on Linux only. Portability:
std::int64_t consistently in VoxelContentManager.cpp; grouped &&/|| in VoxelRedefinedDensityField.cpp. Linux-only
trace fields split Core generateTile / mesh copy / RMC stream build. Standalone Clang and GCC whole-view hashes
are unchanged vs 3202193. The Unreal spawn trace is blocked by the owner's new assets (SW route sweep fails).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
2026-10-26 19:19:07 +01:00
Fr0zkaandClaude Opus 5.5 fc9227f9e0 fix(unreal): U1 -- Unreal compiles again on L61; CoreGeometry asset section; versioned layout-unit upgrade
Windows MSVC catch-up after abyss L15-L61 (no Unreal there): 64-bit clipmap
shift (C4334), qualified VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel, UHT
metadata and duplicate-signature fixes. Optional CoreGeometry section on
UVoxelStrateDefinition (native caves, lattice, profiled heightfields, named
stamps, rooms/tunnels; off/empty by default), resolved after passage planning.
VoxelForgeAssetVersion 2 converts strate height/transition/gap to metres on
load (exact, 0 m change on the owner's 5 assets; packages never saved).
Gates pass (SurfaceWorld, FloatingIslands, Core-off, M2, W1/W8/W16).
KNOWN REGRESSION vs L14 (2026-10-20, same command, same PC): full set 3.9 ->
5.37 s, worker-seconds 15.8 -> 30, LOD0 1.11 -> 1.69 s, ring 0.50 -> 0.80 s;
not yet attributed (Core L15-L61 vs Unreal side).
Report: Standalone/Research/WorldAssemblerM3/Unreal1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 12:12:08 +01:00
Fr0zkaandClaude Opus 5.5 212814a75c wip(assembler): round S12 -- short ready queue keeps workers fed between ticks: full set 6.0 s -> 4.9 s
voxel.StreamingReadyQueueDepth=8 admits one extra worker-set of prioritized
tasks; voxel.StreamingBackgroundWorkerCap=1 sizes slots to the 8 TaskGraph
background workers actually running tile tasks. Full set 5.96 -> 4.91 s,
LOD0 1.40 -> 1.09 s, ring 0.49 -> 0.51 s, 40 m/s ring 75/75. Tick p95
+0.6 ms spawn / +0.26 ms walk, max flat. No geometry change.
Report: Standalone/Research/WorldAssemblerM3/Round16.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-19 14:29:38 +02:00
Fr0zkaandClaude Opus 5.5 dd9bfd3217 wip(assembler): round S11 -- bounded spawn apply budget: in-game full set 7.1 s -> 6.0 s
Startup apply mode on by default (12 meshes / 14 ms per frame, ends when the
initial set and walkable ring are ready, 30 s cap); normal play keeps the
authored budget. Backlog peak 252 -> 10, LOD8 apply wait 1.1 s -> 4 ms.
Ring 0.49 s, LOD0 1.40 s, 40 m/s ring 75/75. Remaining floor: frame-paced
admission (~8.3 submits/frame, 8 running workers max). No geometry change.
Report: Standalone/Research/WorldAssemblerM3/Round15.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-19 13:03:44 +02:00
Fr0zkaandClaude Opus 5.5 e7586a1256 wip(assembler): round S9 -- collision look-ahead: ground ring ready on arrival up to 40 m/s and 50 m/s falls
voxel.StreamingCollisionLookahead (default on) predicts the support tile 350 ms
ahead along full 3D velocity and prioritises its 9-tile collision ring. Owner
plan, BuildHost actor: 40 m/s arrival-ready 25/74 -> 74/74 (CVar-off control
15/74); 6/12/25 m/s stay 100%; -Z 50 m/s 93/93. Spawn unchanged (~7.7 s).
Critical LOD0 path at 40 m/s: generation 67 ms p50, RMC body update 38 ms p50.
LOD8 cost counted (native horizon 48 ms of 60), optimisation deferred.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-19 10:56:54 +02:00
Fr0zkaandClaude Opus 5.5 8440adc742 wip(assembler): round S8 -- region rebuilds no longer stall admission; result backlog capped
voxel.CoreRegionConcurrentAdmission (default on): tiles independent of the
entering region keep submitting while a region page compiles; pages are
immutable shared_ptr owners so running readers survive eviction. 6 m/s: 11
whole-pass skips -> 0, walkable ring ready at every centre change.
voxel.StreamingResultBacklogCap=256 bounds finished results (slow-apply worst
case 125 -> 76 MB). Spawn unchanged at ~7.7 s: LOD8 is 46% of worker time.
At 60 m/s + teleport the collision ring is outrun (102/113 centres).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-19 09:43:03 +02:00
Fr0zkaandClaude Opus 5.5 5eb9ee76ff wip(assembler): round S7 -- workers kept busy: in-game cold full view 17.5 s -> 7.8 s
Two admission blockers found by per-frame decline codes: the near reserve held
four slots idle after LOD0 was done (coarse capped at 6), and finished-but-
unapplied results counted against the task cap. Admission now counts queued +
running tasks; near reserve releases once the walkable ring is ready while
stationary (voxel.StreamingDynamicNearReserve, default on). Walkable ring
0.65 -> 0.58 s, full LOD0 2.28 -> 1.44 s, busy workers 2.2 -> 5.6 of 10.
Old behaviour behind voxel.StreamingLegacyPendingResultCap. RMC source reviewed
(read-only): batching/fast-cook ranked, not yet worth it on measured apply cost.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-19 08:10:10 +02:00
Fr0zkaandClaude Opus 5.5 b9c13c37c0 wip(assembler): round S6 -- in-game cold view counted; workers mostly idle, apply cap not the floor
Direct per-tile trace on the BuildHost actor (owner plan, 2,578 keys): full set
17.4 s legacy; gated coarse-first (after the 9-tile walkable ring) 11.7 s with
the ring still at 0.63 s, but full LOD0 slips 2.3 -> 10.2 s. Startup apply
boost changed nothing (0.4%). 37.6 worker-s over 17.4 s on 10 slots = ~3.3
busy: the floor is admission/work supply. S5's 251 ms staircase was the trace
sampler. Fixed CVar parsing in headless BeginPlay and the 60 m/s crossing
instrument. All three knobs stay opt-in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-19 06:46:54 +02:00
Fr0zkaandClaude Opus 5.5 c0596c5f24 wip(assembler): round S5 -- streaming gates + trace; coarse-first order kept opt-in (no win)
Walkable/enclosed/refined gates defined; JSONL trace joins request, worker start,
build/apply and discards. Coarse-first + dynamic near reserve behind
voxel.StreamingCoarseFirst / voxel.StreamingDynamicNearReserve (default 0 =
S4 order): matched runs showed no walkability or full-view win and slower
LOD0/1 refinement. Instruments settled: 3.1 s standalone reproduced (16.7 s was
another generation route); black early captures were clear-only frames.
Hashes equal S4; Core-off, M2, SurfaceWorld/FloatingIslands BuildHost pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-19 04:42:41 +02:00
Fr0zkaandClaude Opus 5.5 d6e69420e8 wip(assembler): round S1 -- one Core voxel-size source; per-tile feature lists (islands)
AVoxelWorld::CoreVoxelSizeCm (default 25, 32 voxels/tile) flows through plan, field,
mesher, content, water, atmosphere, edits and collision. Staged-owner BuildHost passes at
20 cm (SurfaceWorld + islands, W1/W8/W16, anchors, BP edits, collision) with a fixed-25cm
negative control; default-size hashes match round 4; Core off stays legacy 12.5 cm.
Reusable BuildWorldAssemblyFeatureTileListV1; wired into FloatingIslands only so far.
256-island stress: tile p95 112.5 -> 2.0 ms, 0 mismatches, dropped-feature control fails.
Content placement measured (no populated cells): island ground search 7.25 ms/cell vs
legacy 0.29 ms -- open. Outside-review evaluation in Round5/REPORT.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-18 18:48:38 +02:00
Fr0zkaandClaude Opus 5.5 111cf3d262 wip(assembler): M3 round 4 -- plans built inside Unreal from the owner's strates; no import needed
SurfaceWorld and FloatingIslands plans generate at startup from the resolved seed,
strate types and heights (hashed, cached). Imported JSON is an opt-in override (off by
default); an invalid override shows an on-screen notice and falls back to legacy.
BuildHost on staged owner assets: SurfaceWorld cross-system checks, islands at owner
height (route, collision, anchors, BP edits), seed 137501, W1/W8/W16, Core-off legacy.
XY-column cache verified (hashes kept); island LOD0 p95 91 ms vs 133 ms sprint deadline.
Not exercised: season-seed path (no Season in staged settings), rendered notice pixels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-10 22:48:46 +02:00
Fr0zkaandClaude Opus 5.5 4661a445be wip(assembler): M2 round 4 -- WorldAssemblerHybridV2: exact contacts, tagged tiles 1.18-1.27x MC
The round-3 'field contact error' measured hit-to-anchor distance; V2 contacts are 0 cm and on the zero
isosurface. Tagged-tile cost 5.7-6.7x -> 1.175-1.270x MC; organic 0.998x. Bound, shelf-thickness and route
audits rerun with negative controls; W1/W8/W16 stable; V1 and MarchingCubesV1 unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-10 10:41:43 +02:00
Fr0zkaandClaude Opus 5.5 22a6b30494 wip(assembler): M2 round 3 -- opt-in hybrid sharp mesher (WorldAssemblerHybridV1) in the Unreal Core route
Default MarchingCubesV1 unchanged (W1/W8/W16 = round-6 hashes). Hybrid DC only in LOD0 interior cells
with tagged features; tile-border cells and LOD1+ stay MC. BuildHost: fixtures repeat exactly, cooked
collision bounds = render bounds, same-LOD borders pass, sampled SurfaceWorld cliff bit-identical to MC.
Open: tagged tiles cost 5.7-6.7x MC; corner-top field contact error 20 cm; 5 MC doorway LOD cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-10 09:53:04 +02:00
Fr0zkaandClaude Opus 5.5 61633cb492 wip(assembler): M1 round 6 -- owner walk fixes: 25 cm geometry scale, one height datum, carve/fill sign, faster relief meshing
Owner saw content floating above native terrain, spiky triangles, reversed carve/fill and late relief.
Causes: shared mesher placed Core tiles on a 12.5 cm grid; content/atmosphere asked strate ranges in
12.5 cm voxels; Core edits add density where the legacy diff subtracts. Horizon LOD8 bridge edge
5.657 -> 2.265 cells. Relief mesh p95 60.8 -> 42.0 ms (LOD0). New BuildHost checks for scale, height,
blueprint carve/fill and per-kind streaming lag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-09 20:39:24 +02:00
Fr0zkaandClaude Opus 5.5 075e58187e wip(unreal): AVoxelWorld Core mode -- streaming, collision and carving through VoxelForgeCore
Luna, integration round 2 (BuildHost only): AVoxelWorld::bUseVoxelForgeCore (default
false) routes desired tiles, generation, upload/collision and carving through Core.
Core-only CoreClipRadius=3 / CoreMaxClipLevel=8 (owner's asset untouched); 25 cm Core
voxels in every cm<->voxel conversion; legacy brush sizes scaled to keep physical size;
UE-task JobScheduler, epoch-checked Mpsc results, existing RMC upload + collision cook;
carving via sequenced applyEditTransaction. Switch off: BuildDesiredTiles takes the old
path unchanged.
BuildHost proof (actor commandlet, staged copies of the owner's assets): all 2,226
initial tiles hash-match standalone Core; collision bodies as expected; anchor move
requests 1 / releases 1 tile; carved tile hash matches standalone; peak ~2.68 GiB.
Legacy CrossPlatformDigest automation: 115,000 samples pass (wrapper flagged an
analytics write outside isolation).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-08 20:33:46 +02:00
Fr0zkaandClaude Opus 5.5 bc0f9ee791 wip(step3): 12.5 cm voxels, fall-through fixed, brick evaluator in game, organic tunnels -- speed gate FAILS
Stopped by the owner's decision (in-game lit captures were eating the round); he judges the look in
game instead.
1. Fall-through with the research field: fixed. Deep drop, gate on: 2 holds (0.50 s), min foot
   clearance +43 cm; gate off: penetrates (-278 cm). Stress fixture offsets converted to metres.
2. VOXEL_SIZE 25 -> 12.5 cm. Physical sizes kept in metres (player, steps, gate, insets, research
   field sizes, dig radii, test thresholds); class defaults that count chunks/voxels doubled
   (ClipRadius 6, RenderDistanceChunks 1536, ViewDistance*, StrateViewMargin 6,
   PawnCollisionRadius 2, SkirtCells 4, EdgeSealThickness 128, decoration spacings, density volume
   res 256). Asset values override these defaults: the owner must double his own.
   Ring scale, six workers, idle, step-2f field before the look changes: 64 m cube (519^3) 4.55 s;
   32 m cube (259^3) 0.95 s.
3. In game: tiles go through the brick evaluator (proved by a log marker); profile switch rebuilds
   the loaded world (~5,800 tiles); full-detail fill 12.4 s after spawn in the visible-window test.
4. Look: round winding tunnels, per-column ellipsoid/lattice caches. All step-2f geometry gates pass
   at 12.5 cm (0 crumbs, 1 solid component per profile, solid share 69-87%). 259^3 ring, six workers:
   1.08-2.02 s (worst HyperComplex) -- the 600 ms gate FAILS. Lit captures failed (hidden/off-screen
   window, then blank frames); not pursued.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-01 21:21:45 +02:00
Fr0zkaandClaude Opus 5.5 41b001b652 measure(step1): cook is 1.5-2.3 ms, RMC's route is 8-11 s; CMC cannot take a field floor; cap 16 uploads
Rebuild plan step 1 (RESEARCH-SPEED-2026-09.md), all behind default-off switches.
A. Direct cook on a dedicated thread, 27-tile support cohort: core cook p50 1.50 ms, p95 2.28 ms;
   a 3,949-tri tile 1.67 ms extract+cook. PASSES the 10 ms gate. Through RMC's route the same
   support bodies became ready 8.46 s p50 / 10.80 s p95 after their meshes existed: FAILS 150 ms.
   Exact-target capsule sweep confirmed (0.75 ms after body update).
B. 100 rapid underfoot digs on CMC with a field override: 883 false-support frames, 0 false-air,
   0 invented velocity. CMC's floor comes from collision-scene traces -> step 4 = field-aware Mover.
C. 341 prequeued tiles, render on: cap 4 = 238 applies/s, cap 8 = 476, cap 16 = 639 (drain p95
   2.22 ms vs 2 ms gate); first visible 1 frame after release; no RT/RHI backlog. Tune drain,
   no grouping needed yet.
D. Same 128^3 region, 64/64 tile hashes equal in export and game. The "263 ns/sample" was export
   WALL time with 15 workers sharing a lattice (~3.4 us CPU/sample); game tile time includes
   meshing and duplicated borders (+22%); contention cap 1 vs 14: 74 vs 112 ms per tile.
   Profiling is not the cause.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-30 00:07:59 +02:00
Fr0zkaandClaude Opus 5.5 97a1874498 wip(step1): collision/upload clocks behind default-off switches -- partial, stopped on low memory
Compiled and run (cap 14 / 12 / 8), then stopped: another app held 12.5 GB, free memory fell
under the 6 GB launch floor and processes failed to start (STATUS_DLL_INIT_FAILED).

Measured so far (owner world, 120 s windows):
- 343 L0 tiles, request->collision-ready p50/p95: cap 14 = 262/440 ms, cap 8 = 196/238 ms;
  scheduler queue wait p95 141 ms -> 0.9 ms. Initial support tile at cap 14: RMC submit->body
  7.47 s, first check->ready 9.73 s; at cap 12: 0.33 s / 2.54 s; at cap 8 a 3,949-tri tile
  reached body update 2.8 ms after RMC submission. Waiting, not cooking, dominates.
- NOT confirmed: a capsule sweep hitting the exact target body (the 0.75 ms hit was another
  component); the 10 ms core-cook and 150 ms/27-tile thresholds are unscored.
- CMC cannot take a same-frame density floor: FindFloor/ComputeFloorDist trace the collision
  scene. Underfoot digs (100-dig test), upload throughput (render-enabled caps 4/8/16) and the
  8x per-sample question are not run. Per-sample profiling was fully disabled in these runs, so
  it does not explain the 8x.
Switches (all default off): voxel.Step1GenerationCap, Step1PauseGenerationAfterSeconds,
Step1MaxMeshApplies, Step1PrequeueVisibleLOD0, Step1UploadDisableCollision, Step1CollisionProbe,
Step1CoreCookBenchmark; harness -EnableRender. Also: plan step 3b, AVX2 need not match scalar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-29 17:53:57 +02:00
Fr0zkaandClaude Opus 5.5 bf22f0ffe0 perf(collision): cook stages measured, no standing on dug-out ground; review F/H/E measured
1. Collision cook breakdown (request -> ready), critical support tile:
   spawn (0,0,-1): generate 107 ms, apply < 1 ms, RMC submit->body 479 ms, total 587 ms;
   deep floor (0,0,-7): generate 82 ms, RMC 1,441 ms, total 1,548 ms.
   Almost all of the wait is inside RMC (end-of-frame update + async pool + Chaos cook + body
   install, not separable from our side). Critical priority does not shorten it. Collision is
   async, complex-as-simple, fast-cook off; skirts are ~16-18% of collision triangles.
   No cook change made. Startup outlier: first support tile waited 2.09 s for its game-thread apply.
   Underfoot dig: a carving brush that intersects the pawn's capsule and support tile now
   disables the old body and withdraws readiness on the carve frame: 0 frames standing on removed
   ground (replacement 91.8 ms later). 8 neighbouring digs: 0 gate frames. Gap audit 0.
2. Stack sampler, 49.4 s game path: top resolved leaf was the profiler's own
   VoxelDensityProfile::BucketFromName (4.86%). Linear label scan -> CRC-keyed lookup with exact
   compare: 59.26 -> 57.87 ms/tile (-2.35%), 980/980 tile hashes identical.
3. Measured only: F (classifier mode 1, no exact check) +48% worker CPU, 0/980 tiles differ ->
   not worth it. H (handoff component scans) p95 0.07 ms/frame -> not worth it.
   E (FullResClipLevels=1): worker CPU 56.7 -> 35.7 s, settle 9.6 -> 7.0 s, crossing samples
   -30%; changes the look, owner's decision.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-29 07:52:18 +02:00
Fr0zkaandClaude Opus 5.5 ce6812dbeb fix(streaming): no false gate on digs, no see-through at tile handoffs; density volume off
- Dig near the pawn: a same-tile collision recook withdrew readiness although the old body was
  still installed, so the gate held a standing pawn (957 of 1,931 frames over 8 near digs).
  A tile now stays ready during a recook while collision is still wanted and its existing body is
  valid. After: 0 gate frames, 0 falling frames over 2,604 frames. Dig directly under the pawn
  still falls (~127 cm, max ~502 cm/s). The reported "accelerating toward the ground" was not
  reproduced; the false hold is the measured part.
- See-through: the retire logic only waited for replacements that overlapped the old tile in
  volume and missed desired neighbours touching at a face/edge/corner. Handoff now includes
  contact. New surface-witness gap audit (24 surface points per tile, skirts excluded, catches an
  injected early hide). 6000 cm/s walk, 75 s: 3 gaps / 51 frames / longest 603 ms -> 0.
  Dig session: 0 -> 0. Overlap audit and edit ghost stay at 0.
- bEnableDensityVolume defaults to false; when off: no volume, no fill thread, uploads or tick
  work, no volume-dependent proof bypass; terrain MIDs get VolP5.z = 0 (enable off), other VolP*
  black, VolTex* null, so M_Test1's volume nodes return their neutral result.

Gate: deep gate-on passes (79.9 m, 3 holds, -0.101 cm), gate-off penetrates (-5.1 cm).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-29 04:53:57 +02:00
Fr0zkaandClaude Opus 5.5 0bcd67a016 perf(review): gate never deadlocks, collision radius, 20x faster digs
From the outside performance review (items 1-4; item 5 prototyped and reverted).

1. Collision gate: a support request outside the stamped desired set was cancelled by the rebuild
   it forced, and the gate held the pawn with gravity 0. Baseline deep-drop (floor at (0,0,-7),
   outside the aligned L0 ring): 1 hold of 30.0 s, never released. Fix: the target and 3 tiles
   below are retained through rebuilds at CollisionCritical; hold only when the swept capsule is
   about to enter pending support; release restores movement mode, gravity and downward speed.
   After: 3 holds, 2.21 s total, 112.8 m horizontal progress; gate off still penetrates (-7.4 cm).
2. Suspected cleanup regression (out-of-layout stacks): not real. 44 paired boundary tiles,
   30.9 ms vs 32.6 ms at b4ac996, hashes 44/44 identical. No change.
3. PawnCollisionRadiusChunks (new, default 1): L0 tiles outside the collision set stay visible
   without Chaos cooks; anchors and the gate's support column are unioned in. Cook requests
   ~217/min -> ~30/min (-86%). Critical support ready time did NOT improve (9.5 s -> 10.6 s in the
   deep-drop fixture; the wait is the cook itself, ~7.4 s cook-to-ready there).
4. Dig: exact pre-edit L0 density cached (one 35^3 grid, 171.5 KB), carve replays current mods on
   it. Sync remesh 99.4 ms -> 4.7 ms. 16/16 random carves hash-identical to full regeneration.
5. Parallel grid fill for the critical tile: 16/16 hash parity, but the waited-on tile was already
   meshed; the wait was collision cooking. Reverted.

Review G answered: M_Test1 still ray-marches VolTex0 (custom HLSL mini-sun shadow), so the density
volume is live. Known and left alone: outside the strate layout the fallback density can be solid
while the gate treats those tiles as air.
Tests: VoxelForgeCollisionDesiredTilesTest, VoxelForgePreEditDensityCacheTest; no-overlap, edit
ghost, descent corridor pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-29 02:26:44 +02:00
Fr0zkaandClaude Opus 5.5 116b57f01e wip(gate): support column kept through rebuilds, hold only on swept contact -- NEVER COMPILED OR RUN
Stopped mid-round: the PC ran out of memory (another app held 11 GB), new processes failed to start
(0xC0000142), and the round ended without verifying anything after the edit.

Confirmed in code (performance review finding 1): a support request outside the stamped desired set
forces a rebuild that cancels it, the gate asks for one unresolved tile at a time, and it holds
before the pending tile is anywhere near the pawn's next step.

Uncompiled edits: a short collision-only support column retained through desired rebuilds and
prioritised; hold only when the swept capsule is about to enter a pending tile; downward speed
restored on release; opt-in deep-drop fixture (carved shaft) in the stress harness.

Evidence so far: shallow 10-voxel fall, gate on, before the edit: 1 hold of 1.51 s, clearance
+120.6 cm. The 80/160-voxel attempts spawned OUTSIDE the strate layout (support unavailable,
zero holds) and failed the penetration check at -11.1 cm: not the reviewed loop, but worth a look.
Compile risks: GetGravityZ(), TSet pruning in the rebuild, the fixture's carve/log arguments.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-28 19:54:28 +02:00
Claude 26984d38d8 docs(comments): drop history narration, keep the why
Rewrite comments that told the story of past rounds instead of the
current code: dated "fixed on / tried on / reverted on" notes, "this used
to...", "stale paragraph removed", "Phase 1/2" progress labels, and the
"ClassifyTile still does not use ClassifyBox" claim (it does now, via
VF_ClassifyBoxWithWarpRetry). Every invariant, unit, sign convention,
trap and measurement that justified the design is kept, rephrased in the
present tense and in its original language (French or English).

Also fix comments that named symbols removed in the dead-code commit
(EvaluateSDF wrapper, FTunnelCoreWorldEvaluation support-floor fields,
BlendChunks -> TransitionBlendChunks) and the stale function name in the
VF_RollStrateParamsDetailed determinism checkf message.

Comment and message text only; no code token changed. Tests untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URDGLsu7c4nunqNFPJEFPU
2026-09-28 14:18:36 +00:00
Claude 4753416d4a refactor(sheets): remove the far sheet ring from the runtime
The owner dropped the far sheet ring. Remove it from the streaming path:
- UVoxelMarchingCubesMesher::GenerateSheetMesh (declaration + definition);
- sheet-tile branches in LoadTile / GenerateTileResult / SyncRemeshTile and
  the bSheetTile / SheetChunkZ / Hole* parameters;
- the SheetHole* XY-hole state and its per-crossing re-queue block;
- VF_MaxMarchingCubesLevel (equal to the clamped MaxClipLevel without sheets);
- the sheet-only band arming and the 'sheet' field of the desired-level
  startup-trace events (no tool reads it).

AVoxelWorld now always passes bFarSheetRing=false to the tile selector, and
the descendant index covers levels up to the MaxClipLevel clamp (8) instead
of the old sheet maximum (12); no tile above 8 can exist any more.

Kept on purpose: UVoxelSettings::bFarSheetRing / FarSheetSpanLevels
(UPROPERTYs, now marked unused), the voxel.TestFarSheetRing switch and the
far_sheet_ring / sheet_tile fields the test harness compares (sheet_tile is
always false), and the selector's sheet parameters in
VoxelClipmapDesiredTiles.h, which two automation tests still exercise.
2026-09-28 13:50:55 +00:00
Fr0zkaandClaude Opus 5.5 6ee433efa5 test(gate): stress repro judges terrain penetration -- gate on 10/10 clear, gate off 3/3 falls through
The old criteria (travel distance) could not tell a working gate from a broken one: every run,
gate on or off, ended at (455.7, 0, -58.1). That spot is measured air ~1.2 m above a real floor
(density zero-crossing at world Z ~ -275.7 cm), so it is a terrain trap, not a fall-through.

The stress test now drops the pawn fast onto an unready support tile (0,0,-1) and samples the
density at the capsule feet every frame, sweeping at <= half a voxel between positions. PASS =
no measured clearance below -5 cm.

Owner config (radius 3, max level 6, distance 768, sheets off):
- gate on: 10/10 PASS, min clearance +120.5..+120.9 cm, one hold of 0.43-0.47 s each,
  support submitted in ~0.7 ms, collision ready in 0.43-0.47 s
- gate off: 3/3 FAIL terrain_penetration, clearance -5.7..-8.1 cm
Edit-ghost, clipmap no-overlap sweep: pass. Test-harness code only; no production path changed.
This validates a49725d.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-25 06:11:08 +02:00
Fr0zkaandClaude Opus 5.5 a49725d365 wip(gate): request the support tile at once; no gate timeout -- stress criteria NOT trusted yet
- RequestCollisionGateSupportTile submits the level-0 support tile immediately, even before the
  first desired set; the desired rebuild adopts the same pending key (1 early request, 1 adoption,
  1 ready callback in all 10 runs). First check -> submit went from ~2.2 s to ~1 us.
- The 2 s gate release timeout is removed: at the owner's startup the game thread applies nothing
  for ~2.2 s, so the timeout always fired and dropped the pawn. The gate holds downward Z only;
  horizontal movement stays free.
- Controls (previous round): legacy selector+reconcile failed the stress repro 0/5, aligned 1/5,
  so 85a803b did not regress the gate.

NOT VALIDATED: the stress repro's pass criteria were rewritten (support submitted/ready, a hold,
progress during a hold). 10/10 "pass" under them, but every one of those runs, and the gate-
disabled run, ends stuck at (455.7, 0, -58.1), ~1.55 m below the Z~97-100 at which the old
passing runs travelled 113 m. Whether that spot is a real pit or the pawn below the surface is
unresolved; until it is, a pass under these criteria proves nothing about falling through.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-25 04:54:00 +02:00
Fr0zkaandClaude Opus 5.5 85a803b873 wip(streaming): LOD rings tile exactly once, cache race, reconcile 104x -- gate stress FAILS 3/4
- Clipmap: finer rings snap to child-pair boundaries, so no two desired tiles of different
  levels overlap. Owner config 635 overlapping pairs -> 0; LOD0 ring 343 -> 512 tiles.
  New test sweeps 6,174 cases; fails on 7486c29 as intended, passes here.
- Shared room-graph cache: Entry->Cache read/written only under the mutex (race fix).
- ReconcileReadyTransitionVisibility indexed: moving p95 32.0 ms -> 0.31 ms.
- Block-vs-scalar density equivalence test: 5.1M samples bit-identical; catches an injected
  optimistic EffectOverBox (5,612 mismatches).

UNVALIDATED for merge: collision-gate stress passed 1 of 4 runs on this code (fails at 4.56 m,
support collision unready at start); it passed on 7486c29. No control run yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-25 02:47:54 +02:00
Fr0zkaandClaude Opus 5 7486c299d4 fix(streaming): no stale geometry over its replacement -- edit ghost 40s -> 0.17s
The owner reported a wall staying visible after he carved through it, and overlapping LODs when crossing slopes at speed. Two causes: an edit only re-meshed the level-0 tiles of the touched chunks, so coarser tiles kept pre-edit geometry until something else re-queued them; and superseded tiles stayed drawn until the budgeted teardown reached them.

Now: edits fence late pre-edit async results and re-mesh every affected loaded/pending level; a replaced tile is hidden as soon as its replacement is fully covered, while destruction and collision removal stay budgeted (the anti-spike rule that made teardown budgeted in the first place). Load-before-unload is unchanged, so no holes.

Measured: edit ghost 40.095 s (unresolved at test exit) -> 0.172 s; 6000 cm/s slope overlap 19.952 s with 153 overlapping pairs -> 0.000 s with zero events. Gate stress PASS (113.68 m, 2 holds, 0 full stops). Apply-frame cost peaked at 0.979 ms against a 2 ms budget.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-23 21:10:20 +02:00
Fr0zkaandClaude Opus 5 f36bc1f9b2 wip(streaming): priority by class and level -- near ring 53.6s -> 1.2s, but crossing test fails
Scheduling rule: CollisionCritical -> Edited -> LOD0 -> Horizon, with coarse work capped so 4 task slots stay reserved for near work; result queues follow the same order; epoch and cancellation handling unchanged.

Measured at MaxClipLevel=5 / RenderDistanceChunks=768: spawn ring request-to-ready 53.632 s -> 1.171 s; spawn ring from trace start 68.252 -> 62.549 s; moving first complete ring 66.808 -> 63.917 s, warm ring p50 0.267 s, per-tile moving LOD0 p50 69.4 -> 62.0 ms; full settle of 10,841 tiles 110.7 -> 106.9 s. Gate stress PASS (139.99 m, 0 full stops); static perf PASS.

⚠️ NOT SAFE TO MERGE YET: the strate-crossing test fails in both patched runs with capsule_overlaps_pending_floor -- the exact guarantee the gate exists for. Codex suspects the owner's current DA_Settings (seal 50, clip level 5) rather than this change, since older passing captures used different settings. Control needed: same test, same assets, on 370c39e.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-23 19:04:13 +02:00
Fr0zkaandClaude Opus 5 370c39e473 feat(streaming): horizon distance with real tiles only -- sheets off by default
The sheet system models terrain as one ground and one cap height per column, which cannot express this world's overhangs, floating islands and open sky; three attempts to fix the MC/sheet seam produced holes, then caps over the void, then both. Measured alternative: cover the render distance with real marching-cubes tiles and switch the far-sheet ring off.

Benchmarks at RenderDistanceChunks=768, all tiles loaded at steady state: L4 57,473 tiles / 30.1M tris / 837 MiB / 2,514 s worker / 296 s to settle; L5 10,841 / 8.1M / 226 MiB / 375 s / 108 s; L6 3,281 / 2.9M / 82 MiB / 127 s / 77 s. Defaults now MaxClipLevel=5, bFarSheetRing=false. Invariants: strate crossing PASS, gate stress PASS (140.06 m, 0 full stops), determinism PASS with 10,841 tile hashes identical vs -voxel.CrtFma3=0. Peak memory ~4.2 GiB in all three.

Owner action required: his DA_Settings still serializes MaxClipLevel=3 (asset edits are out of scope for Codex), so his world needs that value raised to 5 to use the new far field. At twice the distance the estimate is ~39.5k tiles / 29-30M tris / ~0.8 GiB / ~1,500 worker-seconds, which would want L6 rather than L5.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-23 17:39:49 +02:00
Fr0zkaandClaude Opus 5 ea3f0d9a52 fix(classifier): exact lattice check before a uniform verdict; ceiling cause is NOT false certificates
ParameterRoll's 5 false box verdicts are fixed: fine room-graph intervals are now checked against exact lattice samples before a uniform verdict is accepted, while the SDF bound keeps its conservative lower bound. Lattice sweep 600 tiles / 0 violations; ParameterRoll passes with 9,577 exact lattice leaves and 774 interval disagreements caught.

The owner's ceiling holes are NOT explained by this: in his recorded session outer_classifier_mode=0, solid-proof skips were 0, and air-proof skips were outside the strate. The trace points at the far-field sheet system instead: sheet tile L5 (0,1,0) is deliberately empty inside the sheet hole while its bridge tile L4 (0,1,-1) meshed 662 ceiling triangles. Continuous ceiling is NOT verified: every render launch failed (D3D12 driver error, stalls before map load, the 6 GB cap), so the round produced no matched after-render.

Cost note: this round used 3.46M tokens on Luna 6 at max, most of it burned on failed render launches. Verification by render is unreliable in this harness; verify ceiling continuity from tile/geometry data instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-23 06:30:05 +02:00
Fr0zkaandClaude Opus 5 88f3e44402 wip(collision gate): stop the fall instead of the player -- UNVALIDATED, after-change run never happened
The round was stopped when Windows began refusing to start processes for it (0xC0000142) under memory pressure, before any after-change test run. Treat as a draft: the baseline run happened, the verification did not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-22 15:19:12 +02:00
Fr0zkaandClaude Opus 5 0dd45ca410 fix(collision gate): stop freezing the player on a stale prediction
The owner fell through a surface-only world and hovered in mid-air: log showed 'ready=1 predicted_wait=1' for 50 s. Two causes, both fixed: (1) engaging the gate zeroes velocity, so the predicted-support probe never re-ran and the gate kept waiting on the tile remembered from when the pawn was still moving -- the prediction is now re-derived every tick from the pawn's current pose; (2) a tile the streamer never requests (out of layout, or outside the desired set) could never become ready, so the wait was unbounded -- such tiles are now logged and treated as nonblocking, and any wait is bounded to 2 s with a warning.

Repro added to the harness (surface-fall scenario): before, the pawn stops at Z=1315.9 with collision_gate_prediction_deadlock; after, it reaches the ground in 2.25 s at Z=-45.6. Strate crossing still protected: 145.49 s route, max gate wait 0.109 s (limit 10 s). Perf S/M/S/M 24.265/54.262 and 23.947/53.057 s, within repeat noise. Owner and canonical geometry unchanged (44E7C813 / 94B47A1C).

Known red, pre-existing and unrelated: VoxelForge.Composer.ParameterRoll still reports false box verdicts in the owner's current single-strate surface layout (5 violations, identical in the baseline). Next round.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-22 11:17:01 +02:00
Fr0zkaandClaude Opus 5 a7b8d10de6 tooling(test): one command to test VoxelForge, with a commandlet-vs-game parity check
Tools/VoxelForgeTest.ps1 runs scenarios (canonical, owner, probe, perf, tests, parity) with the known-good flag sets, isolated user/config dirs, serialized launches, an owner-process guard, and a compact result.json + summary.txt instead of 300 MB logs that Codex has to stream. Answers the owner's question 'can the commandlet reflect in-game behaviour': -gameconfig=1 makes the commandlet use the authored settings, and the parity scenario compares per-tile geometry hashes between the commandlet export and the game's streaming path. Parity PASS: 64 commandlet tiles vs 810 game records, no differing tile.

First run of the tests scenario: 44 tests, 28 pass, 16 fail (8 composer, 6 op-stack equivalence, PassageLandsInOpenSpace, StrateParamBlendCoverage). Triage is the next round: stale expectations from the floor removal and the degrees change, versus real defects, especially the legacy-vs-op-stack equivalence ones.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-09-18 05:13:16 +02:00
Fr0zkaandClaude Opus 5 39c306b524 perf(streaming): prove sealed rock solid instead of sampling it -- crossing fix's cost recovered
4c8d06f stopped content-cutting LOD0 so the player can cross strates, at a cost of +29% static
worker CPU: LOD0 tiles in the sealed rock between strates, which the old cut skipped by accident,
were sampled in full and found solid. This proves those tiles solid instead (Codex; reviewed by me).

- UVoxelGenerator::TryProveSealedSolidTile: AllSolid only for a tile whose whole lattice lies in a
  gap/seal region. It rejects player mods (HasAnyModInChunkRange), passages plus carve pads, the
  origin spine/landing, disturbances, recipes and overrides. It is narrower than the old
  ClassifyTile, which stays off in streaming. CollisionSolidTiles / CollisionNotRequiredTiles
  bookkeeping is preserved, so the collision gate's support search still resolves.
- TryProveOutOfLayoutAirTile: a separate fail-closed constant-air proof for the zero-triangle tiles
  outside the strate layout. It never supplies collision support.
- voxel.SealedSolidProof (default 1). Mining a proven tile invalidates and re-meshes it.
- A soundness test that can fail (VoxelForgeSealedSolidProofTest): 22 candidates, 22 skips, 0
  non-solid lattice samples; a deliberately weakened diff guard produces 437 non-solid samples
  and fails.

Verified: mining tile (14,-18,-8) went from proof=1 / 0 triangles to proof=0 / 8 triangles and
re-meshed; strate-crossing test PASS (max gate 0.098 s); off/on trace 899/899 identical; export
b3e5f4c3..9b377; capability 20,830 / 10,909.

Two interleaved rounds, serialized (owner-editor count 0 before each launch):
                        baseline   crossing-fixed   + proof
  static  worker        35.5 s     41.9 s           36.0 s   (+1.4% vs baseline)
  moving  worker        78.1 s     93.2 s           78.2 s   (+0.1%)
  mesher calls static   18.078M    22.280M          18.078M
Applied triangles identical: 669,834 static / 1,331,482 moving.
Runs: one early automation probe exited 3 with no log (the corrected harness passed); one
exploratory moving run was interrupted and excluded.

Note: HasAnyModInChunkRange scans every modified chunk per candidate (cold audit #21). That is cheap
today (~20 candidates), but it grows with mining; it is queued in the correctness round.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TaFdXhTdYMkW1xrV6ApT6Y
2026-09-14 22:08:24 +02:00
Fr0zkaandClaude Opus 5 4c8d06fd8f fix(streaming): the player can walk from one strate into the next
Owner playtest: he could not walk from strate A into B. Forcing the pawn through in the editor
showed the junction tunnel (and his edits to the lower strate) were there.

Cause: UVoxelSettings::StrateContentCutMinLevel defaulted to 0, and DA_Settings does not override
it, so the strate content band (the PAWN's strate) also cut LOD0, the collision tiles. While the
player stood in A, the passage below A's seal, the gap and strate B had no LOD0 geometry, and the
collision gate correctly refused to step onto missing collision. The band only moves once the pawn
is inside B: a deadlock. bClampViewToStrate is a different setting, which is why toggling it did
not help. Some historical fall-throughs were likely this, before the gate turned "fall" into
"blocked".

Fix (Codex; reviewed by me):
- StrateContentCutMinLevel default 0 -> 1, ClampMin 1, and
  UVoxelSettings::GetEffectiveStrateContentCutMinLevel() clamps to [1,9] at every runtime
  consumer, so a legacy asset serialising 0 cannot bring the deadlock back. LOD1+ stay banded
  inside strates and go full in the gap.
- A regression test, -voxel.TestStrateCrossing=1 (off unless passed): it walks the real manager
  passage control points with AddMovementInput and normal swept character movement, with limits
  of 10 s max gate, 120 s total and a 2-voxel floor tolerance. Old code: FAIL
  collision_gate_timeout at route 5/9 (10.0 s gated). Fixed: PASS reached_lower_strate, strate
  0 -> 1, max gate 0.092 s. The trace shows band [-7..0] -> full in the gap -> [-17..-10].

Field unchanged: canonical export b3e5f4c3..9b377, capability 20,830 / 10,909. In game, the LOD0
change in the crossing scene is 11 transition-gap tiles going from 0 to 22,390 triangles; no LOD0
interior tile changed.

COST, stated plainly (two interleaved runs, baseline vs fixed DLL):
  static  generation CPU 34.1 -> 44.1 s (+29%), density calls +23%, LOD0 request p95 +8.9%
  moving  generation CPU 78.8 -> 91.1 s (+16%), density calls +26%, LOD0 request p95 +3.1%
  applied triangles identical (669,834 / 1,331,482): the extra work is LOD0 tiles in sealed rock
  that the old cut skipped. The next round proves them solid instead of sampling them.

Visual caveat: the old default of 0 was a deliberate July choice, because LOD0/1 tiles straddling
a strate boundary mixed both strates' materials. With LOD0 never cut, that mixing may reappear
near seals. The headless runs are -nullrhi, so this still needs checking in the editor.

Launches: all serialized and waited by PID. Failures: one malformed first baseline attempt, one
over-strict early harness assertion, and one run against stale staging (it logged CutMinLevel=0),
all superseded.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TaFdXhTdYMkW1xrV6ApT6Y
2026-09-14 19:46:35 +02:00
Fr0zkaandClaude Opus 5 8e40bdd92c measure(perf): in-process stack sampler; the game spends ~3/4 of worker CPU on room player-fit
A diagnostic stack-sampling profiler, off by default. A sampler thread captures the stacks of
registered generation workers with FPlatformStackWalk::CaptureThreadStackBackTrace every N us and
tags each sample with the tile's LOD. Symbolisation (inline frames included) happens after the
run, into a bounded CSV plus a summary. Registration is per tile task, never per sample; when the
sampler is off it costs one relaxed atomic load. Game: -voxel.SampleStacks=<us>. Commandlet:
-samplestacks[=us]. The streaming latency line now also prints applied tile, visible tile and
triangle counts.

Instrument checks (Codex, PERF-SAMPLED.md):
- a planted 20 ms busy loop was found at 10.5% inclusive, the expected share, then removed
- closure: 193k static / 251k moving samples, 0 capture failures, <0.75% wait frames,
  leaf resolved for 82-84%
- static game output identical with the sampler on or off: 841 tiles, 447 visible, 669,834
  triangles
- overhead at 1 ms: +4.7% static worker CPU; moving worker CPU within noise, moving timing +4-5%.
  So trust large shares, not single-digit deltas.

The finding, on the game path (the owner's instruction: profile the game, not the export):

                                all-LOD inclusive   static   moving
  BuildChunkCache                                   79.5%    80.5%
  VF_FindPlayerFitPointForRoom                      73.4%    74.0%

About three quarters of in-game worker CPU goes to player-fit searches for rooms. They are
recomputed on every per-worker room-cache rebuild, and coarse tiles span many cache windows. At
LOD0 the costs are the cave warp (3 Perlin per sample), the swept tunnel evaluated both in the
SDF and in EvaluateTunnelCoreWorld (~12% inclusive each, duplication confirmed), and the passage
structural tail (~11-14%). Candidate AABB rejection is cheap (1.5-2.2%). The 74-field params copy
does not show up as a separate cost.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VZS94UH8bSsBeg5sZaxe2Y
2026-09-13 03:32:29 +02:00
Fr0zkaandClaude Opus 5 c65b015f01 perf(streaming): outer tile classifier off by default; delete mode 2
Owner-approved, on the A/B measured in 83a0e4d: the classifier plus exact validation cost more
than it saved on the game path. With it off, ready p95 is 182 vs 190-195 ms, generation p95 is
125-127 vs 139-141 ms, and density calls drop 18%.

- GVoxelForgeOuterClassifierMode defaults to 0. Mode 1 stays reachable via
  voxel.OuterClassifierMode=1 as an A/B instrument; the clamp is now 0..1.
- Mode 2 (validation samples reused as the mesher core grid) is deleted: the reuse grid,
  FVoxelMesherCoreDensityGrid, GenerateMesh's ReusableCoreGrid parameter, bValidationCoreReused,
  TotalValidationCoreReuseTiles and its log field. It could not fire: it reused the grid only
  when validation disproved a uniform verdict, which never happened, and Mixed tiles never
  validate. The mode-1 validation loop exits early again on the first disagreeing sample.
- All three GenerateMesh call sites checked; the density-count pointer binds after BandVoxHi.

Verified by Codex, checked by me against the logs:
- build ok; the measured process loaded the staged DLL, SHA-256 1024D8CF..04D
- export OBJ SHA-256 unchanged: b3e5f4c3..9b377
- a run WITHOUT the override prints outer_classifier_mode=0 validation_density_calls=0,
  proving the new default took effect
- 841/841 tiles have identical triangle counts between mode 0 and mode 1. The 394 tiles mode 1
  skipped as uniform produce 0 triangles in mode 0, so the strate band cut (applied by the
  mesher, not by validation) was not hiding geometry.

Docs: ARCHITECTURE 8.10, CODEMAP (GenerateTileResult, ClassifyTile) and fable-idea T1.d record
that streaming no longer classifies by default. WORK-NEXT item 1 records the owner's playtest:
he can no longer fall through the world.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VZS94UH8bSsBeg5sZaxe2Y
2026-09-13 02:06:24 +02:00
Fr0zkaandClaude Opus 5 f2044135ea fix(mesher): stop forcing the nested block classifier on -- but I cannot reproduce the 3.5x
The code contradicted its own documentation. VoxelMarchingCubesMesher.h:150 says to leave
the optional 8^3-block refinement DISABLED in streaming, "repeating a proof on ..." --
and VoxelWorld.cpp forced `Mesher->bUseBlockEarlyOut = true` regardless.

It is now off by default, behind `voxel.UseBlockEarlyOut` (CVar + command line), so the
capability stays available for the focused diagnostics where it was measured to help.

⚠️ THE HEADLINE NUMBER DOES NOT REPRODUCE, AND I AM NOT CLAIMING IT
Sol's second pass read a 3.5x from the attribution table:
    classifier disabled    587.991 ms
    classifier enabled   2,056.596 ms
My own clean A/B on this flag, quiet machine, 3 runs each:
    nested OFF   0.597 / 0.613 / 0.607 s   mean 0.606
    nested ON    0.637 / 0.599 / 0.605 s   mean 0.614
Identical within noise.

So either the 2,056 ms control exercised more than this flag does, or the 3.5x lives on
a path the 128^3 export does not take. PERF-ATTRIBUTION.md describes that figure as "the
separate classifier control", which is not obviously the same lever. Until the game-path
A/B is run, treat the 3.5x as UNCONFIRMED.

What this commit is worth on its own: the code now does what its documentation says, the
behaviour is switchable rather than hardcoded, and geometry is unchanged --
SHA-256 b3e5f4c3..9b377 identical with the flag on, off, and against bb8ec51.

⚠️ An earlier measurement pass in this round was contaminated: a local llama-server was
using 5 GB and competing for cores. Those runs came out 0.67-0.78 s with the on/off
ranges overlapping. Discarded and re-measured on a quiet machine; only the clean numbers
above are reported. Worth recording as a reminder that the machine is part of the
instrument.

NOT DONE -- the round was OOM-killed partway (same llama-server):
- the three-way outer-classifier A/B (no outer / current+discarded validation / ⭐ outer
  with validation samples REUSED as the mesher core grid). That third mode is the
  interesting one: exact validation currently costs 33^3 = 35,937 density calls whose
  results are then DISCARDED, while the mesher then fills 35^3 = 42,875. A successful
  uniform verdict saves at most 16.18%, and a Mixed verdict saves nothing.
- game-path measurement of any of it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aheo2crrZmHuYtPpdY2Cb
2026-09-12 13:19:42 +02:00
Fr0zkaandClaude Opus 5 cf0319d480 perf(streaming): item 3 bought nothing, item 5 removed dead weight -- both worth knowing
Two WORK-NEXT items, one null result and one confirmed removal. Item 4 untouched.

ITEM 3 -- CANCEL OBSOLETE WORK AND PRIORITISE THE FLOOR: NULL RESULT
Clean -nullrhi, diagnostics off, 800 cm/s, 200 m of travel:

                          before            after
  request->ready p95      0.369117 s        0.367612 s
  queue wait p95          0.066949 s        0.064483 s
  generation p95          0.287926 s        0.283642 s
  p95 travel distance     2.952938 m        2.940894 m
  obsolete aborts                 0                12

Flat. Aborting obsolete tiles, sorting from the true pawn position and prioritising the
occupied/next tile changed request-to-ready by 0.4%.

⭐ AND THE NULL RESULT IS ITSELF THE FINDING. Look at the split: queue wait is 65 ms of a
368 ms p95; generation is 284 ms. **The queue was never the problem, so reordering it
cannot help.** Only 12 tiles in a 200 m run were obsolete enough to abort. The floor is
late because generation is slow, not because work is badly ordered -- item 3 establishes
that by exclusion, which is worth more than the 0.4%.

The machinery is kept: it is correct, it costs nothing, and it will matter once
generation is fast enough for ordering to be the binding constraint.

ITEM 5 -- VERDICT CACHE REMOVED. It was pure overhead.
Clean moving-session audit:

  outer  calls/probes/hits    2,109 / 2,109 / 0
  nested calls/probes/hits   19,904 / 19,904 / 0
  enclosing hits                              0
  stores 15,877, evictions 15,365

⇒ It stored 15,877 proofs, evicted 15,365 of them, and hit ZERO times. A moving player
visits unique regions; the cache was filling and thrashing a 512-entry table under a
global lock for no benefit. Same-binary A/B: request p95 0.367645 vs 0.365978,
generation p95 0.285559 vs 0.285992 -- neutral within noise, so its cost was the lock and
the linear scans rather than time we can see.

Removed: the global cache, its lock, the entry table, the invalidation path, the
cache-only profiler fields and the synthetic probe. The proof classifier stays.

⚠️ I asked for this cache two days ago on the strength of a commandlet measurement that
re-visited the same region repeatedly. That access pattern is nothing like a moving
player, and I should have said so when specifying it.

VERIFIED BY ME: build 0 errors; export 0.563 / 0.565 / 0.566 s (35de59b was 0.545-0.563,
flat as expected); geometry SHA-256 b3e5f4c3..9b377 UNCHANGED; capability 20,830 /
10,909, connected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aheo2crrZmHuYtPpdY2Cb
2026-09-12 06:49:20 +02:00
Fr0zkaandClaude Opus 5 081cc5e459 fix(streaming): collision-ready gate -- the pawn waits for a COOKED floor, not a submitted one
WORK-NEXT item 1, plus all three of item 6. Item 2 (the fused evaluator) untouched as
instructed.

⭐ RMC DOES EXPOSE WHAT WE NEEDED, so the gate rests on a real signal:
  OnCollisionBodyUpdated                              (delegate -- diagnostic)
  SetCollisionConfig() -> TFuture<ERealtimeMeshCollisionUpdateResult>  (AUTHORITATIVE)
RealtimeMeshComponent was READ ONLY, per the owner's grant. Nothing there was modified.

THE INVARIANT
Until now nothing guaranteed a tile the player occupies has collision. apply_s ended at
mesh SUBMISSION; RMC cooks asynchronously and we recorded no completion, so no
measurement this project has ever taken said when physics could actually collide.

  - per-tile CollisionReadyTiles state, with stale-callback protection
  - HandleTileCollisionCookComplete driven by RMC's cook future
  - pawn gated at BeginPlay and pre-physics on the FEET-SUPPORT tile -- deliberately not
    the actor-centre tile, because the floor that catches a falling pawn is what must be
    cooked -- plus the predicted movement path and solid/air traversal
  - movement-component state saved/restored, so a temporary stream wait cannot
    permanently change a character's movement mode
  - new trace fields: mesh_submit_to_cook_s, collision_request_to_cook_s,
    body_event_to_future_s -- three boundaries where we had one blurred number

ITEM 6, all three:
  - LoadedTiles is now recorded only after SUCCESSFUL mesh submission, and a failed
    mesh init retries. Previously a failed init left a tile marked loaded forever -- a
    permanent hole.
  - origin spawning fixed (the zero-position guard made a pawn at exactly
    FVector::ZeroVector indistinguishable from "no pawn" -- and the owner spawns at 0,0)
  - exact core-lattice validation for uniform whole-tile verdicts, which previously had
    no safety net while block verdicts did

⚠️ I FIXED TWO COMPILE ERRORS MYSELF rather than spend another Codex round on them:
`GetMovementMode()` / `GetCustomMovementMode()` do not exist on
UCharacterMovementComponent -- MovementMode and CustomMovementMode are public fields.
Two lines. Codex did not build this round, so it could not have caught them.

VERIFIED BY ME: build 0 errors after the patch; export 1.022 / 1.055 s (aa62971 was
1.034 / 1.089); geometry SHA-256 b3e5f4c3..9b377 UNCHANGED -- generation is untouched,
as it should be for a streaming-lifecycle change.

⚠️ UNVERIFIED AND IMPORTANT: the submission-to-cook gap is still unmeasured -- Codex had
no live process to measure it in. The instrumentation exists and will report it on the
next play session. And the GATE ITSELF IS UNTESTED IN MOTION: if a support tile never
becomes collision-ready, the pawn stays frozen rather than falling. That trade is
deliberate, but the owner should watch for it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aheo2crrZmHuYtPpdY2Cb
2026-09-12 03:38:58 +02:00
Fr0zkaandClaude Opus 5 aa62971047 tools(trace): Play-to-steady-state timeline -- startup is 83ms, the other 8.2s is tiles
Measurement only, no optimisation, per the instruction. First time anyone has looked at
the path BEFORE LoadTile, and first time any measurement has used the owner's REAL
assets instead of the commandlet's synthetic strates.

Trace: Saved/StartupTraceRealFinalTrace.startup.jsonl -- 873 JSONL records, 268 KB,
841 tile records, bounded at 4096 with zero suppressed. Structured, own file,
stream-parseable; the 13 GB log pile taught that lesson twice.

TIMELINE
  BeginPlay                    45.585 ms
  StrateManager.Initialize     29.018 ms
  GeneratePassages              6.628 ms   (5 passages)
  first BuildDesiredTiles       2.710 ms
  ------------------------------------------
  all startup stages           ~83 ms
  BeginPlay -> steady state     8.216 s
  process launch -> stop       20.164 s

⚠️ My hypothesis was wrong: I expected layout resolution or GeneratePassages to be a
hidden one-off cost. They are 83 ms combined. **The 8.2 seconds is tiles, all of it.**
A further ~2.05 s sits between BeginPlay finishing and the first desired-set build, and
the trace attributes that to engine/map/player startup, not to us.

REAL ASSET RESOLUTION -- two things worth the owner's attention
  settings                /Game/VoxelForge/DA_Settings, seed 0
  ⭐ five slots, ALL resolving to /Game/VoxelForge/DA_Strate3
  InterStrateGapChunks    2
  MaxClipLevel 4, ClipRadius 3, RenderDistanceChunks 0
  bFarSheetRing true, but NO sheet tiles exist because render distance is 0
  ⭐ bEnableDensityVolume FALSE -- already off, no volume object, no GPU upload

DA_Strate1 (the 10.7 KB surface strate) and DA_Strate2 (rooms + water) are not in play
at all. Every strate in the world is Strate3.

THE DESIRED SET -- 841 tiles, and the shape is the story
  LOD0  343 tiles    8 m each     56 x 56 x 56 m
  LOD1  218         16 m         112 x 112 x 80 m
  LOD2  120         32 m         224 x 224 x 96 m
  LOD3   80         64 m         448 x 448 x 128 m
  LOD4   80        128 m         896 x 896 x 256 m

⭐ LOD4 covers 896 x 896 x 256 m -- about 205 MILLION cubic metres of cave -- and its
verdicts are 0 AllAir / 0 AllSolid / 80 Mixed. Not one LOD4 tile can be skipped, and
each costs 127.5 ms p50 / 291.6 ms max to generate. That is roughly 12 s of CPU spent on
geometry nobody can see from inside a cave.

TILE DISTRIBUTIONS (p50 / p95 / max ms)
  LOD  tiles  verdicts A/S/M   req->ready        generation        mesh
   0    343   98/123/122    44.9/420.8/819.5    9.6/358.2/767.6   0/341.6/726.1
   1    218   49/83/86      31.1/148.9/188.6    8.7/134.3/164.2   0/117.8/151.1
   2    120   0/39/81       34.0/61.6/76.9     10.9/43.6/63.7   9.9/40.4/60.5
   3     80   0/2/78        46.4/85.9/96.3     38.5/80.7/87.8  31.0/67.5/70.7
   4     80   0/0/80       148.2/272.8/304.2  127.5/253.8/291.6 127.4/253.8/291.5
  totals: 147 AllAir, 247 AllSolid, 447 Mixed, 669,834 triangles
  apply never exceeds 0.43 ms at any level
  zero classifier-cache and region-cache hits on a fresh process, as expected

⇒ The two costs are LOD0's 122 meshed tiles (~360 ms p95 each) and LOD4's 80
unskippable tiles (~150 ms each). Apply, queue and startup are all noise beside them.

VERIFIED BY ME: build 0 errors; export 1.034 / 1.089 s (5ddd966 was 1.013-1.075, so the
instrumentation does not distort); geometry SHA-256 b3e5f4c3..9b377 UNCHANGED. Game peak
RSS 2.07 GB.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aheo2crrZmHuYtPpdY2Cb
2026-09-11 20:03:24 +02:00
Fr0zkaandClaude Opus 5 5156084cac audit(gen): which post-hoc ops are tunnel-floors in disguise -- and the X+2A budget is unsound
The owner's framing for the night: "if it sees performance opportunity by unifying
some process that should be, or like the tunnels, have it be native more than patching
it after generation". This is the audit. No generator behaviour changed; the only code
here is request/generation timing instrumentation in VoxelWorld.

STRUCTURAL-POST AUDIT -- verdict per operator
  FBoundarySealOp    0.000011 us/sample  KEEP. A boundary condition, not a workaround.
  FXYEdgeSealOp      0.000054 us/sample  KEEP. Domain constraint; identity today with
                                         WorldRadiusVoxels=0.
  FOriginSpineOp     0.000588 us/sample  PARTLY family, but origin anchoring and
                                         ordering are special.
  FPassageCarveOp    0.019977 us/sample  Could share the volume/floor representation,
                                         but its boundary-crossing order and
                                         current-density carve are load-bearing.
  floor protection   0.061298 us/sample  ⭐ THIS IS STILL THE REAL PATCH.

  TunnelAuthoredFloorProtectionClamps fired 29,634 / 29,634 -- every authored floor
  sample -- while the named support/room fallbacks fired ZERO. So the backstop is not
  a rare safety net; it is doing the work, and the tunnel floor is not yet native.

⭐ AND IT NAMED THE PATH OUT, which I had not seen: the floor is authored at build time
  but LATER WRITERS can still refill or erase the clearance, so a clamp re-asserts it.
  Native means either composing the floor AFTER all writers, or making every later
  writer floor-aware with a proof it cannot violate the invariant. Removing the clamp
  today would be unsafe. It did not remove it.

PRIMITIVE FAMILY -- valid, but my proposal was too crude
  Algebraically sound: solid + floor via SmoothMax + bounded relief + smooth union.
  Rooms, swept tunnels, landing rooms and some passages can share a cached
  walkable-volume DESCRIPTOR (shape/profile, baked floor, relief params, mouth/room
  ownership, capability metadata).
  ⛔ But they should NOT become one undifferentiated operator: tunnels are swept
  multi-segment chains, passages cross structural boundaries, origin landings have
  special finite geometry, seals are domain conditions. Flattening those would WEAKEN
  interval proofs and operation ordering -- the opposite of what I hoped unification
  would buy. Renaming the passage carve would not help either; it Lerps against
  incoming density, so its global bound stays unbounded regardless.

X + 2A CLEARANCE BUDGET -- SETTLED, AND IT IS UNSOUND AS SPECIFIED
  A surface-displacement budget needs BOTH an upper bound on field perturbation AND a
  lower bound on the gradient near the zero surface. The second fails at flat or
  degenerate crossings, and the current-density Lerp/Min operators make a global bound
  unavailable. Perlin derivative bounds alone do not fix it.
  ⇒ Usable only inside separately proven primitive regions; elsewhere the validator and
  backstop must stay. The owner and I both assumed interval propagation already gave
  us this. It does not, and now we know why rather than just that.

ITEM ZERO -- the suspected in-game regression: NARROWED, NOT CONFIRMED
  Queue wait is real, but meshed latency is primarily GENERATION time, not queue. One
  EMPTY LOD0 generation took 551.7 ms, so it is not pure queue saturation either.
  RoomGraphSource measures 0.278939 us/sample, which cannot explain a 400-1000 ms mesh.
  Codex declined to call it a confirmed code regression on this data. Still open, and
  it now points at the known-unsolved LOD0 mixed-classifier fallback rather than at the
  floor work.

REFERENCE ARCHETYPE -- costs, and a correction to my arithmetic
  CorridorRadius 12.5 voxels is a 6.25 m DIAMETER (I said 3.1 m), and validation caps
  it at 16 voxels. The 15-20 m reference hall needs a separate gallery archetype or a
  larger cell-size budget; raising the value violates current lattice/wall assumptions.
  FCaveTerraceMod is a true no-op disabled (0.000531 us/sample); enabled it adds two
  cached SDF probes, estimated lower bound ~0.56 us for a qualifying sample -- estimated,
  not measured. Columns belong to slab generation; TunnelNetwork needs a baked column
  op. Water is presentation, not rock density, and null-RHI cannot price it.

Build 0 errors. No RMC files, links, historical checkouts or logs touched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aheo2crrZmHuYtPpdY2Cb
2026-09-11 05:05:47 +02:00
Fr0zkaandClaude Opus 5 383ec86c37 perf(apply): stutter is NOT the game thread -- and the flat floor cannot be relaxed
Two things the owner reported from playing 412a7ad. One is answered, one is a null
result that Codex flagged itself rather than dressing up.

THE FLATTENED TUNNEL FLOORS -- capability-safe, and worth almost nothing
He has now flagged these twice: every tunnel carries a smooth flat strip running in
and out through otherwise craggy rock. The idea was to make the floor CONDITIONAL --
assert it only where the natural rock fails walkability, leave the rock alone
elsewhere. Cheaper and better looking, in theory.

The floor is now skipped only when the exact no-floor field passes the same
player-fit validator at foot offsets 0, -1, +1. Unknown or failed keeps the slab.

  natural-floor skips accepted   1,539 of 1,529,241 tests   = 0.10%
  player-fit cells        20,153 -> 20,153
  reachable cells         10,483 -> 10,483
  arrival -> departure    connected, both
  dihedral p50/p90        2.5429 / 33.8499 deg, IDENTICAL
  mesh time (their batch) 1.757135 -> 1.757140 s

Capability preserved exactly. Visual complaint NOT solved, and no speed gained.

⭐ The useful part is what it PROVED by trying to go further. Every broader policy
lost walkable space:
    analytic-only        fit -217, reachable -195
    tightened analytic   fit  -76, reachable  -76
    exact +/-4 foot      fit  -16, reachable  -16
⇒ The floor cannot be relaxed without changing the walkability contract or adding a
route-aware global proof. That is a real constraint, now measured rather than assumed.

Codex's own words, and it was right to lead with them: "the conservative floor change
is capability-safe, but it does not visibly solve the flattened-strip complaint."

THE STUTTER -- measured, and it is not where we thought
Game-thread apply, meshed tiles, under -nullrhi:

  total apply            p50 0.107 ms   p95 0.153 ms   max 0.273 ms
    stream upload/RMC    p50 0.040      p95 0.088      max 0.124
    component creation   p50 0.019      p95 0.021      max 0.035
    registration         p50 0.010      p95 0.011      max 0.020
    collision config     p50 0.005      p95 0.007      max 0.019

A quarter of a millisecond at worst. The game-thread drain is NOT the hitch.
⇒ Remaining visible stutter is most likely render-thread/RHI work or asynchronous
collision cooking behind RealtimeMeshComponent, which is off-limits for edits.

Added anyway as a cheap safety valve: MaxMeshApplyMilliseconds, default 2 ms of drain
budget per frame. Over 729 apply frames: drain p50/p95/max 0.111/0.182/0.313 ms,
budget hits 0, at most 2 visible applies in a frame. It costs nothing today and caps
a future burst.

Request-to-ready, now timed after game-thread application:
  all     p50 57.1 ms   p95 432.9 ms   max 919.4 ms   -> 2.60 m at 6 m/s
  meshed  p50 134.1 ms  p95 457.4 ms   max 919.4 ms

⚠️ My own verification: 1.197 / 1.131 / 1.118 s vs 1.088-1.102 s on 412a7ad. Mean
+4.7% -- below the 17% noise floor, but slower in all three runs, and the validator
runs 1.5M times to accept 1,539 skips. Geometry moved by 4 triangles (153,740 ->
153,744), which is allowed. If the next round cannot make that validator earn its
place, it should come out.

Build 0 errors. Peak RSS 1.67 GB export / 1.90 GB game. Nothing outside
Plugins/VoxelForge; RealtimeMeshComponent untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aheo2crrZmHuYtPpdY2Cb
2026-09-10 14:46:29 +02:00
Fr0zkaandClaude Opus 5 d38d4cabef perf(gen): interval propagation -- blocks can finally be PROVEN empty. 1.55s -> 1.21s
Task 4 ran 11 hours and was killed by system memory pressure before it could file a
report. The work survived, builds clean (0 errors), and is verified faster with
byte-identical output. This commit is my verification, not its summary -- it never
got to write one.

THE STRUCTURAL FIX
0e9df9e established why no block could ever be skipped: OriginSpineOp,
PassageCarveOp and the active seals Lerp/Min/Max against the CURRENT density, so
with an unknown incoming density their supremum is genuinely unbounded. Not a
missing bound -- the operator semantics.

The answer is to propagate a density INTERVAL through the stack, and that is what
landed: FVoxelBoxSdfInterval + PropagateSdfOverBox on the operator interface, an
interval fold through every converter and modifier, interval arithmetic down into
the noise primitives themselves (gradient corners, warp, worm frequencies), and a
BlockSkip lattice the mesher consults per 8^3 block.

Its own guiding comment, which is the right default:
  "no finite interval is a proof. Unknown costs the skip and protects the geometry."

  128^3 TunnelNetwork seed 1337 slot 1, freshly built binary, 3 runs each:
    before  1.515 / 1.554 / 1.569 s
    after   1.213 / 1.258 / 1.171 s     -22.5%, 1.28x -- clears the 17% floor

  Geometry SHA-256 98eaad61..d3c4f IDENTICAL to 0e9df9e. 153,740 triangles either
  way. It skips only provably-empty work; nothing about the world changed.

It also fixed one cause of the 5.6 s empty tiles found in 0e9df9e: an LOD4 tile
legitimately spans 19^3 = 6,859 chunk keys and was rebuilding per-chunk state
thousands of times within a single tile. A shared cache removed that failure mode;
remaining slow empties were LOD0 and are a separate fast-classifier bail, still open.

Partially landed, unfinished when killed: RequestStartCycles on FChunkResult, to
report request-to-ready latency rather than worker generation time. Harmless as is.

⚠️ WHAT KILLED IT, AND IT IS OURS
A single UnrealEditor-Cmd commandlet grew to 19.5 GB, leaving 1.3 GB free on a
32 GB machine, and the OS killed the agent. Freeing it restored 21.6 GB. The owner
said twice that his machine should not be under memory pressure and he was right
both times: first the 411 MiB worker caches, now a 19.5 GB commandlet. A generation
run must not need 19.5 GB. Next task.

Cumulative on this region: 342.2 -> 16.2 -> 2.62 -> 1.49 -> 1.21 s  (~283x).
3,374 insertions / 342 deletions across 12 files. Build 0 errors. Nothing outside
Plugins/VoxelForge; RealtimeMeshComponent untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aheo2crrZmHuYtPpdY2Cb
2026-09-09 21:11:13 +02:00
Fr0zkaandClaude Opus 5 0576e800c4 perf(gen): the landing work was per-voxel -- 342s to 2.6s, same world
The owner played 64f5d61 and reported the truth: "generation still takes ages...
TunnelNetwork doesn't even finish generating, as big as the surface world" while
SurfaceWorld completed in under 10s, and stopping PIE hung for 30+ seconds.

e08b49a's 21x was real and irrelevant. It fixed per-CHUNK state rebuilds, which
dominate the commandlet export path. The game is bound by per-VOXEL evaluation.
Same symptom, different bottleneck, and nobody had measured the second one.

WHY PIE HUNG
bShuttingDown was never checked inside VoxelMarchingCubesMesher.cpp -- zero
occurrences. A worker inside GenerateMesh could not abort, so 64f5d61's correct
unconditional EndPlay wait was waiting on tiles physically unable to stop. The
hang was not a teardown bug; it was evidence that one tile took tens of seconds.
Added shutdown polling per slab/row in the mesher and through GenerateTileResult;
aborted results carry Epoch + bAborted and ProcessQueue drops them. EndPlay still
waits unconditionally -- the 3s mark only logs now.

  PIE stop   3.409s / 44.759s  ->  4 ms teardown, 38 us worker drain

WHERE THE PER-VOXEL COST WAS
The owner's own comparison was the sharpest instrument available. Profiled both:

  GetDensityAt          TunnelNetwork 12.64 us/sample   SurfaceWorld 1.32
  tunnel candidates evaluated      7.088 per sample                 0
  tunnel-core candidates evaluated 7.201 per sample                 0
  room candidates evaluated        1.000 per sample                 0

  largest buckets: RoomGraphSource 2.57, TunnelCorePosts 0.76,
                   PassageCarveOp 0.22, PassageStructuralPosts 0.20

The three regressing commits map cleanly, and one is innocent:
  7a20eee drainage tree  -- cache-build work, NOT per-voxel
  413c15d landings       -- repeated full passage modification per sample
  55bf807 landing rooms  -- room SDF + tunnel/core evaluation per sample

Fixes: FPassageCarveOp carves only, landing/support moved to the structural MC
post; tunnel core support and SDF fused into one pass; room graph caches shared
per worker on complete deterministic keys; tunnel density cache uses open
addressing with 4096 slots. Room cache rebuilds fell to 9 per 42,875 samples, so
what remains is genuine O(local cached room/tunnel count) work.

  TunnelNetwork 128^3, seed 1337 slot 1, same region:
    342.2 s / 163.161 us/vox   ->   2.623 s / 1.251 us/vox     (130x)

  Geometry SHA-256 aaf95559..066b4, IDENTICAL to the pre-optimisation run at the
  same roughness. 153,692 triangles either way. Verified independently of the
  agent's own report.

NOT FIXED, and the first one is the next real target:
- L4 tiles still cost 16.93 s. Hit rate falls 99.9% (L0) -> 23.7% (L4), but
  1024 -> 4096 slots only moved 18.39s -> 16.93s and the 1,927 misses ARE the
  distinct key count. That is low reuse, not eviction. A bigger cache will not
  save it.
- TunnelNetwork is still ~10x SurfaceWorld (0.354s vs 0.028s on a matched 32^3).
- The live PIE tile table is SurfaceWorld: this checkout's PIE map is configured
  that way. TunnelNetwork went through the same mesher and density path via the
  commandlet, but no TunnelNetwork tile has been timed in an actual game session.
  Export numbers are not presented as game numbers.

Still outstanding, untouched here: the inter-strate passage has intersections it
should not have, routes through open passage when it should not, and should
descend from a low point of the current strate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aheo2crrZmHuYtPpdY2Cb
2026-09-08 23:47:53 +02:00
Fr0zkaandClaude Opus 5 faa62d419e feat(runtime): the composed world can BE the game world
Jahni: "can you not implement those settings? as in, have them be live in
game?" This is that. The composer stops being a lab tool.

THE ARCHITECTURAL LINE, which is the whole design (COMPOSER-NOTES §3.1):
COMPOSITION stays editor-only, EVALUATION becomes runtime. Rolling,
measuring, preview, selection, promotion, the corpus -- none of it reaches
a player's machine. What crosses into the runtime is exactly two things:
reading a season manifest, and materialising a recipe into an op stack.

  UVoxelSeasonAsset      a cookable UPrimaryDataAsset holding the manifest,
                         so a season ships in a packaged game. The JSON
                         stays the reviewable, git-diffable artefact.
  UVoxelSettings::Season  optional. Unset => behaviour is exactly as today.
  GetEffective*()         season seed / spine radius / world radius / season
                         number override the authored values when assigned.
                         The test deliberately sets DIFFERENT authored
                         values, so "the season is authoritative" is proved
                         rather than coincidental.

⭐ MULTIPLAYER DETERMINISM, PROVED: two independent runtime managers built
the same season and compared 393,216 density samples BIT-IDENTICALLY. This
is the property the whole multiplayer model rests on -- replicate seed +
layout + diff, never geometry.

⭐ AND A STALE SEASON IS DETECTABLE, NOT SILENT. Initialize() now returns
bool and an assigned-but-invalid season NEVER falls back to the authored
pool. Falling back would hand a client a world that looks fine and is not
the season -- silent divergence, the worst failure mode in a shared world.
GetCurrentSeasonContentHash() is exposed BlueprintPure to compare at join.

HasComposerRecipeOverride() is gone. It force-classified every custom recipe
as Mixed, which was correct while box verdicts were only proved for
hand-written stacks -- but 8e346a3 made EffectOverBox correct in ISOLATION,
proved on a stack no builder produces. Keeping the force would have cost
tile skipping on every composed world forever.

VF_RollStrateRegionCount moved behind WITH_EDITOR while the partition-seed
helpers stayed runtime: rolling out, evaluation in. Lateral regions remain
gated off.

⚠️ Sol hit its usage limit mid-task, so the edits arrived unvalidated. I
built and ran them myself rather than assume -- and they did not compile:
VoxelForgeComposerSeasonTest.cpp was missing UObject/Package.h, which every
other test picks up transitively through the shared fixture it does not use.
One include; every NewObject/GetTransientPackage in the file was failing.

Editor build clean. SHIPPING BUILD SUCCEEDS -- the invariant that proves no
composer roller is linked into a packaged game. 30 succeeded / 0 failed.

⚠️ UNVERIFIED: whether it looks right on screen. Nothing headless can tell
us that; it needs Jahni to assign a season asset and press play.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJiTcTSDNkKBtnNdiBtdaU
2026-09-05 08:37:01 +02:00
Fr0zkaandClaude Opus 5 d717bc74ff feat(editor): walk a composed candidate in PIE + why rooms read angular
PART B -- the thing Jahni asked for: "any way to test those in game?"
A candidate existed only as measurements and PNGs. Now he can stand in one.

  Select the VoxelWorld actor in PIE -> Live Edit|Composer ->
  ComposerSeed / ComposerCandidateIndex / ComposerTargetStrateIndex /
  bComposerRollStructure -> click Apply Composer Candidate.

The button and the automation tests call the SAME roll wrapper, which was
the load-bearing requirement: a second roll path could drift, and then he
would be judging a different world than the contact sheet describes. A
determinism sample is logged on apply (seed, index, recipe, archetype,
metrics, epoch, eight-point density verdict) so drift is caught the day it
happens rather than in a month.

Hazards handled, since this is the first composer code to touch the live
runtime path: preflight materialisation before any live mutation, generation
and decoration tasks drained, PassagesVersion bumped to invalidate
thread_local caches, reuse of the existing RegenerateAllChunks / Epoch
invalidation rather than a new path, no Initialize() or GeneratePassages()
during apply (the re-entrancy trap), DiffLayer locks untouched, and custom
recipes conservatively force ClassifyTile to Mixed.

Scope is deliberate: a DENSITY-ONLY overlay. Layout height, authored
passages, content, water and biome metadata stay from the existing slot;
RebuildStrates() clears it. A full composed layout replacement needs a safe
rebuild path that does not exist yet.

Editor-only, verified: a Shipping build succeeds with the bridge excluded
(96/96 actions), so §3.1's "offline only, never on a player's machine"
still holds.

⚠️ UNVERIFIED BY DESIGN: a button cannot be tested headlessly and Codex was
told not to claim it works. Jahni presses it and confirms the visual plus
the "determinism=PASS" log line.

PART A -- why rooms read angular, measured not guessed. One of three room
shapes is VoxelSDF::RoundedBox:
  ShapeA = (.8 Rxy, .8 Rxy, .8 Rz)   ShapeR = .25 Rxy
so rounding is 31% of the horizontal half-extent but 78% of the vertical.
Corners are rounded; the broad planar FACES survive. SmoothMin (blend 4)
softens room/tunnel junctions not isolated corners; the cave warp bends
geometry but preserves flat regions; roughness is surface-scale only.
The name is accurate -- "rounded box" is not "blob".

NOT ACTED ON. Jahni corrected his own read: the angular archetype is Maze
(lattice source, rectilinear BY DESIGN and correct), and TunnelNetwork is
"alright-ish". Codex's recommendation is recorded for if he ever wants it:
~5-10% boxes, 10-20% capsules, rest ellipsoids, or a larger ShapeR.

Also closed REVIEW_FINDINGS EVoxelPassageType/Style as WONTFIX, enacting a
2026-08-16 judgement that recommended closure and was left open.

Build clean (Editor + Shipping), 25 succeeded / 0 failed, run by me.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJiTcTSDNkKBtnNdiBtdaU
2026-09-04 22:03:01 +02:00
Fr0zkaandClaude Opus 5 302cf16681 fix(world): the voxel field is in ACTOR space -- make every entry point agree
The city's hole is not at world (0,0), so the AVoxelWorld actor has to move.
It couldn't: the plugin was only HALF actor-relative, and had been for a long
time without symptoms because the actor always sat at the origin.

Already correct: GetBiomeAtWorldLocation, GetVoxelSurfaceHeightAt,
UpdateDecorations, UpdateLandmarks, UpdateWater, UVoxelDensityVolume::Update,
the passage debug draw, the clipmap material.

Not correct: the streaming centre, the streaming anchors, all six carve/fill
entry points, GetStrateAtPosition, and UVoxelAtmosphereManager::UpdateForPlayer
-- whose own comment documented the assumption out loud ("actor at
origin/identity"). Carving would have landed offset; the strate lookup would
have been quietly wrong by the actor's Z.

The trap worth recording: because the content managers already convert
internally, converting PlayerLastPos once at the source would have DOUBLE-
converted four call sites. The fix has to be per-boundary. That was only
visible after reading each consumer -- the estimate before that read was wrong
in both directions.

WorldToLocalCm / WorldToLocalVoxel / LocalVoxelToWorld are now declared as the
only sanctioned boundary between Unreal coordinates and voxel coordinates, and
the real deliverable is the invariant next to them: no `/ VOXEL_SIZE` applied
to a parameter named `World*` outside those three functions. A grep can check
that. The risk was never a missed site among the ones found -- it is the next
one nobody notices.

Also adds a translation-only guard in BeginPlay. Tiles, ClassifyBox verdicts,
the clipmap window, culling and streaming distance all assume world-axis-
aligned boxes; a rotation or non-unit scale breaks that structurally rather
than gracefully, so it logs an Error naming what was set.

With the actor at the origin every conversion is identity, so this is a no-op
until the actor actually moves. Built and confirmed working.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DPUuCtn5zTLui6WybLbkNb
2026-08-23 17:47:23 +02:00