Streaming without holes 11/12: one frame budget, the partition reach follows zoom, scale and shear, rows grow in place (#404) - #773
Merged
Conversation
… root's least stretch (#404)
…ce, reopening nothing (#404)
…ueue only spends it; in-place row growth keeps headroom (#404)
…owth doubles what outgrew; the orthographic reach covers a negative near (#404)
…ows reads each ball once, the orthographic reach takes a mirrored box, the row slots take the budget per image (#404)
… reads every cell, guides spend no integration budget, the budget wiring is tested on a driven clock (#404)
… moves to the fixture; the frame's budget wiring is checked in the guides' order test (#404)
…he row claims' contract reads in one paragraph (#404)
…rowth types internal (#404)
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.
Closes #404
What changed
if (!opened) open()and itsopenedflag are gone (page/integration/arrivalQueue.ts). The queue takes the frame's budget as a required argument, and it only spends from it.world/session/prepare.ts,createFrameBudget(ARRIVAL_BUDGET_MS)), and hands it to three places:BackendContext.frameBudget;world/render/render.ts), which opens it, lets the cells and the drain spend it, then pauses it.The WebGPU row records (
webgpu/row/claims.ts) resume the budget, spend what is left, and pause it again.CLAIM's own clock (createWebgpuRowClaims().budget) is deleted. The budget counts integration time only: the engine's other work between the drain and the rows spends none of it (FrameBudget.pause/resume). With no session budget (an engine driven alone, or a barrier image), every owed row is written.cellReachreads the zoom throughperspectiveSlope(fov, zoom)(scene/partition/plan.ts).orthographicView), and a negative near plane counts too. Before, it was infinite: every cell was read.PartitionOpticsis now aPickof the engine'sCameraOptics.inCellFramedivides the reach by the root's least singular value, taken fromstretchOfinboxes.ts(now exported) rather than its shortest column. A flattened root reads every cell.growPlacements(placement/growth.ts, the one growth contract). This goes throughsizeRows(..., grown)and the newio.growinscene/partition/cells.ts, wired inworld/scene/partitionFrame.ts.createTouchedRows(rows.ts) to keepcells.tswithin 200 lines, and the test helpers moved tocells.fixture.ts.docs/FORMAT.mddescribes the new reach, budget and growth.Proof
Behaviour tests, on existing fixtures. Each one fails on develop's sources and passes on the branch; this was checked by restoring develop's
plan.ts,boxes.ts,claims.ts,arrivalQueue.ts,frameBudget.ts,cells.ts,rows.tsandpartitionFrame.tsand running the new tests:cells.test.ts: "a parent scaled down grows the rows in place, on an engine that can, and reopens nothing". The rows grow in place, all cells are placed,outgrownis 0. Without growth it still asks once.tests/integration/world-partition-parents.test.ts: "a page that shrinks the parent of placed nodes grows their rows in place: no reopen, no wait". This runs on the compiled synthetic open world (384², district shrunk 4×):renewis 0,waitingis 0, and the held cells grow.partitionFrame.test.ts: "a camera zoomed out reads the cells its wider frustum sees". At zoom 1 nothing is asked; at zoom 0.5 the cell is asked at the visible priority.plan.test.ts: "the reach follows the zoom", which covers perspective, orthographic and a negative near.claims.test.ts: "the rows a frame writes spend what its arrivals left of its one integration budget". Arrivals spend the 2 ms, so one row goes through. The engine's 5 ms between drain and rows is not counted.arrivalQueue.test.ts: "a drain spends what its frame left of the budget, and never opens it again".plan.test.ts, "a sheared root reads every cell the world reach holds, by its least singular value", including the flattened root.pnpm run check:changed: 0.pnpm run test:changed: 1418 pass, 0 fail, 1 skipped (waits for Streaming without holes 14/15: geometry written every frame is uploaded in place, never recut into pages #573), on the reviewer's head.pnpm run validate --group quick: 0.pnpm run validate --group typescript: 0.node scripts/check-pr-size.ts: 598 hand-written lines (limit 600), after the reviewer's pass and a merge of develop.Local review before push
Simplification pass: (coder) The
simplifyskill ran with 4 agents.world/session/backends.tswent past 200 lines.open/admits/spend). The session'sframeBudgetis now opened byrender.tsand passed straight to the partition frame, and the queue's hard-codedcreateFrameBudget(2)default is gone (the budget is required).serveClaimstested the budget twice.inCellFrame:stretchOfis kept for reuse, about 250 flops per frame.bindofgrowPlacements:iois already rebuilt every frame.worldPoses' range tracking increateTouchedRows: sharing it is out of scope.zoom || 1repeated fromwriteEngineCamera.Correctness review: (coder) The
code-review --fixskill found 9 issues.check:linesfailed oncells.test.ts(230),partitionFrame.test.ts(220) andbackend/types.ts(202). Helpers moved tocells.fixture.tsand a sharedasks().check:tools-typesfailed on thezoom/frameBudgetliterals and on the reference-queue cast.docs/FORMAT.mdstill described the old rules.pause/resumenow count integration time only.Simplification pass: (reviewer) The
simplifyskill ran with 4 agents (reuse, simplification, efficiency, altitude).residentRows(sizing.ts) now runs inside a frame when rows grow, and it rebuilt each cell's centre and radius for every pair. Each cell's ball is now computed once per group, with plain dx/dy/dz.pause/resumeare balanced on every path, throws included:try/finallyinserveClaimsand around the cells and drain inrender.ts.slots.apply(budget?)replaces a constructor parameter plus aboundedflag.inCellFramemoves the eye with sdk-core'stransformAffinePointinstead of a hand-written transform.world()moved tocells.fixture.ts; the other test helpers stay incells.test.ts.createTouchedRowswithworldPoses' range tracking: that code is older than this branch and out of scope.budget.stage(fn):try/finallygives the same balance.bindofgrowPlacementsand the second inversion instretchOf: each costs well under a microsecond per frame.Correctness review: (reviewer) The
code-review --fixskill found 10 issues and fixed 7.far × scale, but the reach ignored that scale.partitionFrame.tsviewOfnow multiplies the reach by the camera world matrix'smaxStretch(tested at scale 2).maxStretchthrow on every frame.stretchOfnow returns a least stretch of 0 for it, andinCellFramereads every cell (tested).guides.follow()was charged to the integration budget. It now runs beforeopen.no-useless-assignmentinrender.ts, andframeBudgetrequired inbackends.tsInputs while a fixture omits it.triangles.test.tsnow checks the order follow, open, drain, pause, draw, and thatpausestill runs when the drain throws.claims.test.tsbusy-waited on the wall clock. It now uses a clock the test moves.updatePlacements(see Not proven).residentRowsstill checks every pair of cells under a parent when rows grow, a few times per zoom or shrink. The cost is for the measurer.plan.ts,boxes.ts,cells.ts,rows.ts,sizing.ts,claims.ts,arrivalQueue.ts,frameBudget.ts,partitionFrame.tsandrender.ts.docs/FORMAT.mdfollows the change. No format version changes.#483 checklist
CLAIM's own clock is deleted in this PR. Texture tiles keep the host'smaxTextureUploadMsPerFrameupload ceiling, which is a separate setting.residentRowson the main thread, O(log) times for an ongoing change. The per-frame cost is for the measurer.growPlacements. Not held on WebGPU (see Not proven).Not proven / left out
A flattened root is the one unbounded case. A root whose world matrix is degenerate (determinant 0, or so nearly flat its inverse overflows) has no finite reach in its frame. It reads every cell and grows the rows to every placement. That is correct, since the whole flattened world may be in view, but it is world-sized.
Engine side of a grow.
cells.test.tschecks that the engine is told of the grown buffers only (from→to, and every laterupdatenames a grown buffer). The develop testbackend/autonomous/pages.test.ts("a full instance buffer grows in place…") shows the WebGL2 engine reading the grown buffer aftergrowPlacements("the kept row reads the grown buffer"). No single test drives a partition into the real engine.WebGPU rows can be starved by a burst. When cells and arrivals spend the whole 2 ms, the WebGPU row records get one row per frame until the burst passes; before, they had their own 2 ms. Nothing is lost from the image: an unranked page is covered by its resident parent. The latency is for the measurer.
Item 1 on WebGPU. The WebGPU engine implements no
growPlacements. On it, a parent scaled down or stretched unevenly, or a reach past the rows, still asks the owner to reopen, once. Growing the WebGPU tables in place is the "extendgrowPlacements" of Streaming without holes 13/15: no content change reopens a session, and a reopen never shows an empty frame #572. Estimate: more than 400 lines, withtest:gpuproof.getObjectByNamename buckets (decided in Streaming without holes 11/12: a large world loads its object table by distance, without crashing the tab #404, 25 Sept.): not delivered. It needs three parts:Estimate: 400–500 hand-written lines. It does not fit this PR (598 lines).
Other items carried in Streaming without holes 11/12: a large world loads its object table by distance, without crashing the tab #404's comments, none moved to The compiler writes the manifest in pages a reader fetches by region #750/The runtime holds only the manifest pages its view needs, and the open world loads #751 by the split (those took only the paged manifest and the open-world crash):
Engines driven outside a session. An engine driven with no session frame budget (fixtures,
tests/browser/supportmounts, bench harnesses) writes every owed WebGPU row per image, unbounded. A capture rendered outside the session frame (renderForCapture) serves owed rows from what the last frame left of the budget.Comparison mode. Row growth and row updates reach the active engine only, the same shape as
updatePlacementsandsceneApi.growPlacements.For the measurer: main-thread ms per frame and row-record latency on the open world (WebGPU rows now share the 2 ms with cells and arrivals), and zoom-out and scale-down on the open world with no reopen on WebGL2.
Lead verification
Read by the geometry lead on head
9165dd3ddagainst #404 after the CTO's two splits (what moved is listed in #404's body: #750, #751, #572, #762, #745). The measurer proved it before this pull request opened (CTO rule of 26 Sept.).packages/sdk-browser/src/page/integration/arrivalQueue.ts(if (!opened) open()and theopenedflag removed; the queue spends the session budget it is given). Proved byarrivalQueue.test.ts"a drain spends what its frame left of the budget, and never opens it again".CLAIM_BUDGET_MS(webgpu/row/claims.ts:71-91, which spends the session budget, balanced in try/finally) and by one budget per session shared by the arrival queue, the partition frame, render (world/render/render.ts:111) and the row claims. Proved byclaims.test.ts"the rows a frame writes spend what its arrivals left of its one integration budget" andtriangles.test.ts"each frame moves the followed guides once, and integrates within its budget, before it draws".cellReachand the zoom (rule 1): delivered inscene/partition/plan.ts:58(perspectiveSlope(fov, zoom), the zoomed orthographic box, absolute, with a negative near plane) andworld/scene/partitionFrame.ts:27(times the camera'smaxStretch). Proved byplan.test.ts"the reach follows the zoom…" andpartitionFrame.test.ts"a camera zoomed out, or scaled up, reads the cells its wider frustum sees".inCellFrameshear: delivered inplan.ts:79(the least singular value,stretchOf; a nearly flat root reads every cell instead of throwing). Proved byplan.test.ts"a sheared root reads every cell the world reach holds, by its least singular value".scene/partition/cells.ts:149(rows grown in place throughio.grow, only what outgrew, doubled), on engines withgrowPlacements. WebGPU moved to Streaming without holes 13/15: no content change reopens a session, and a reopen never shows an empty frame #572. Proved bycells.test.ts"a parent scaled down grows the rows in place, on an engine that can, and reopens nothing" andtests/integration/world-partition-parents.test.tson the compiled 384² district world.check:changed0,test:changed1418 pass, 0 fail;validatequick and typescript 0 (reviewer,9165dd3dd).5be70a2f7):observatory-streamedat full budget andmemory-on-a-budgetat 256 KiB;test:gpu60/66, the 6 failures develop's known ones.The zoom-out and scale-down scenes cannot be driven in the browser until The runtime holds only the manifest pages its view needs, and the open world loads #751 (the compiled district world draws no cell in the harness on either side); the integration tests carry them. Row catch-up after a burst has no metric yet (null), which is declared.
createFrameBudgetper session (withpause/resume) replaces the claims' own clock.stretchOf,maxStretch,perspectiveSlopeandtransformAffinePointare the existing ones. Rows grow through the existinggrowPlacementscontract.docs/FORMAT.mdfollows. No public member changed. The body closes Streaming without holes 11/12: a large world loads its object table by distance, without crashing the tab #404.in review,audit koandin progressare removed at hand-over;to measurestays.growPlacementsexists); 9 (the tests above, and the measurer's proof). Rules 3, 5, 6 and 8 moved (Mesh pages carry their own tables and clusters.json stays the same size whatever the world #762, The runtime holds only the manifest pages its view needs, and the open world loads #751); 11 untouched.4ecd8a1e8):scene/partition/cells.tsspellsFrameBudgetandGrowinline in the publicframesignature, so both stay internal (public-types-audit.test.ts:115). It is two type lines with no behaviour change, re-read by the lead as the short re-review;validate --group native3329 pass, 0 fail (coder).