Repository navigation
Conversation
…-494) samplesFramebuffer walked Object.values of everything a material reaches, so every texture's mip data was enumerated one byte at a time. On Machinefall's map-walk that held the main thread for 22.9 s during load, and the playtest bridge timed out before the world appeared. An ArrayBuffer view holds numbers, never a node, so the walk now skips it. Red: world-bundles.spec.ts "asks a textured surface whether it can be recorded without reading its pixels" read the pixel array 11 times (expected 11 to be +0). Green after: 15 of 15 in the file.
A BundleGroup re-record walks every draw it holds. With one main group, each key a streaming walk minted made three walk the whole main pass in that frame: Machinefall's map-walk read traversed-draw p95 257-291 in 30-frame windows for ~90 frames after the overlay dropped, and the first 300-frame window read 157 against 31-78 settled. The main record is now eight groups keyed by an FNV-1a hash of the key name, so a mint re-records its own shard and the others replay what they hold. Red: world-bundles.spec.ts "re-records a mint's share of the main pass, not the whole of it" found one main group (expected 1 to be greater than 1). Green after. The three cases that claimed one group now claim that every dressed main mesh sits in a shard and the shards hold nothing else. world-bundles + world-gpu-scene: 86 passed.
The native GPURenderBundleEncoder had draw and drawIndexed but no drawIndirect or drawIndexedIndirect. A GPU-scene main batch is an indirect draw, and bundles are on by default, so recording the world on the desktop host threw "s.drawIndexedIndirect is not a function" every frame and cascaded into null bind groups: 24,804 console errors on the streamed-world scenario with the published prebuilt host. The two handlers mirror the render-pass ones. Red: the public binding surface check in webgpu_bindings_reentrancy_test.cpp failed with "GPURenderBundleEncoder.drawIndirect binding missing". Green after. The desktop streamed-world scenario now runs on the checkout host (THREENATIVE_RUNTIME_BINARY=build/tn-linux/mystral, nativeEntry src/world-native.ts, ui.renderer native): exit 0, 440 native frames, 0 console errors. WorldProbe publishes stats().bundle as bundleOn and bundleChildren, and the scenario asserts both. Its diagnostics block is web-only (the desktop runner produces no runtimeDiagnostics), and its residentCells row no longer asks the start and end counts to differ: a full ring holds 3 cells at both ends of a straight flight, and residency change is what evictions (0 -> 9) and residenceChanges assert.
Eight shards held the mid-walk record spikes to 145 traversed draws in a 30-frame window, but one asset mints several keys (levels, parts) in one frame and they land in two or three shards. Over three Machinefall map-walk runs the first 300-frame window after startup read p95 141 / 105 / 105, so one run still missed the 120 budget. A replay costs one executeBundles per group, so thirty-two groups cost little next to the draws they stop re-walking. world-bundles + world-gpu-scene: 86 passed.
…draws (PRD-494) Generated by pnpm --filter @threenative/runtime-native native:coverage after the render-bundle indirect-draw handlers landed; pnpm budgets reported the source digest stale.
…changed (PRD-494) Guards the sharding invariant: with one group a join without a re-record was masked by any other key minted in the same load; with a group per shard the repair needs a bump in the same shard. Over a settle, a camera turn and a streaming walk on a CPU-path world, every shard whose member meshes changed in an update has a higher BundleGroup.version after it. Mutation-checked: with #bumpBundle a no-op for main shards the spec reads "walk-2: 1 meshes" and fails; green on the real code. It found no stale join in this fixture.
…during has settled (PRD-494) three encodes a draw into a bundle only when its pipeline is ready, then marks the bundle current; the replay never re-checks. A shard recorded while a compileAsync overlapped therefore left the draws whose pipelines were still compiling out of its record until something bumped that same group. With one main group any mint bumped it within a frame, which hid this. With 32 shards the repair needs a bump in the same shard, so Machinefall's overlay-drop frame lost its mid-field forest (shadows drawn, trees missing) on 2 of 3 candidate captures and 0 of 2 reference captures. WorldCells now remembers the groups bumped while the renderer reports a compile in flight (compiling, or compileCount moved since the last update) and bumps each once in the first update that sees it settled. Only those shards pay, so the traversed draw count AC-1 measures does not grow. Red: world-bundles.spec.ts "re-records a shard recorded during a compile once the compile has settled" failed with "expected 7 to be greater than 7". Green after; world-bundles + world-gpu-scene: 88 passed.
AC-1: traversed main draws p95 48 in the load window and 85 at worst over 3 Machinefall map-walk runs on the final core. The world gate shows 0 LOSS and 0 disallowed popping but exits 1 on its absolute 4/5 floor, which the reference arm misses too; the PRD records that, the capture race on pose-start, and the adaptive LOD bias as the likely owner of the mid-field frame. Native leg: the streamed-world desktop scenario passes on the checkout host with bundles on, after the native render bundle encoder gained indirect draws. AC-2 sign re-confirmed under load; the quiet-host tick stands. The runbook's link to the PRD follows it to done/.
Contributor
Author
jonit-dev
added a commit
that referenced
this pull request
Oct 9, 2026
The owner wants one PR for PRD-494. #475 carried the sharded main record, native bundle indirect draws and the PRD close; it lands here instead. Conflicts: world-cells.ts (kept gpuPlaced and the shard wording) and the native census (regenerated with pnpm census).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TL;DR
Finishes PRD-494 (the main pass fits the draw budget). PR #423 merged the PRD's engine code. This PR fixes a load stall that code caused, brings the walking main-pass draw count to p95 48 in the load window (85 at worst) over 3 Machinefall runs, makes native render bundles work with it, and moves the PRD to
done/.One thing to decide (João): this is a second PR for PRD-494. #473 was my first PR for it, but its branch
feat/prd-494-finishwas overwritten by a fast-forward from therunbook-120worktree (feat/machinefall-120fps-runbook, headd2bb5f649), which merged PRD-478 runbook work and PRD-538/541/489 docs into it (218 files). I did not force-push or merge that PRD-478 work. This PR carries only PRD-494, on its own branch fromdevelop. Squash-merging #473 would land the PRD-478 work too, which is not mine to merge. #473 includes my first commit (2002139c9).Why
On
developf7dd08621, Machinefall?scene=map-walknever finishes loading under the playtest harness. A Chrome trace found one 22.9 s main-thread task. 18 s of it wasbundleSafe's material walk (samplesFramebuffer) enumerating every texture's mip bytes throughObject.values.What changed
ArrayBufferviewsworld-cells.tsexpected 11 to be +0, greenBundleGroupshards (FNV-1a of the key name), so a mint re-records its shard and not every main drawworld-cells.tsworld-cells.tsexpected 7 to be greater than 7GPURenderBundleEncodergetsdrawIndirect/drawIndexedIndirectbindings_commands.cppdrawIndirect binding missing, greenWorldProbepublishesbundleOn/bundleChildren; desktop scenario asserts thempnpm budgetsexit 0Evidence (Machinefall
?scene=map-walk, RTX 2080 WebGPU, final cored8e6543fe)pnpm visuals:world, 3 fresh blind critics, settled capture): 0 LOSS,pose-start2 → 3,walk-08/16/24equal at 4, 0 disallowed popping of 61 reported events. The gate exits 1 on its absolute 4/5 floor alone, which the reference misses too (2 onpose-start). It is not green by exit code. The frame shows the mid-field forest under adaptive LOD bias (TN_LOD_BIASabout 2.3 at 15–40 ms GPU against an 8.33 ms budget), which this PR does not touch.pose-startrow is a race, not a loss. The first pair gave 1 LOSS there. The same frame flips within one arm: reference lacked the mid-field trees in 1 of 5 captures, candidate in 2 of 6, final core in 1 of 5.--target desktopon the checkout host: streamed-world scenario passes, 440 frames, 0 console errors;TN_WORLD_BUNDLE on reason=default children=7 records=18in a long-settle log. With the published prebuilt host it threwdrawIndexedIndirect is not a functionevery frame (24,804 errors).world-bundles+world-gpu-scene88 passed after; prose lane 244 passed;pnpm typecheck,pnpm budgetsexit 0.Before / after frames: reference, settled · candidate, settled · race frames: reference, candidate, full forest.
Not run or not claimed
world-capture-settledscenario and the desktop run recipe (nativeEntry: src/world-native.ts,ui.renderer: "native") are local files, not in this PR.