Status
Blocked by the Phase 2 MVP in #SUB4. This issue freezes the future compilation/orchestration contract; implementation must not invent per-node policies.
Reserved API
type PendingPipelines = "skip" | "sync";
interface SceneView {
// constructor default, render override; precedence render > view > "skip"
render(camera: SceneCamera, o?: { frame?: Frame; pendingPipelines?: PendingPipelines }): void;
compile(node?: SceneNode): Promise<void>; // missing fingerprints + geometry uploads
readonly stats: { pendingPipelines:number; syncCompiles:number; /* MVP stats */ };
}skip uses createRenderPipelineAsync; a node draws only when its draw/pipeline/uploads are ready. sync blocks deterministically (tests/mocks). compile(node?) is incremental: no-op for known fingerprints, and with a node compiles only that subtree including uploads.
Atomic draw semantics
Skip is atomic per draw, never per subtree. Parent ready + child pending still draws parent; child ready + parent pending still draws child because transforms are CPU. visible=false remains the separate subtree-pruning mechanism. There is no per-node pending policy.
// streaming while loop continues: pending children are skipped individually
const root = scene(); const ship = mesh(box(), mat); root.add(ship);
const view = createSceneView(gpu, root, { target, pendingPipelines: "skip" });
frameLoop(gpu, f => view.render(camera, { frame: f }));
// later: root.add(mesh(importedMesh, newMaterial)); // ready siblings keep drawing// child introduces a new material under ready parent
parent.add(mesh(box(), brandNewMaterial));
view.render(camera, { pendingPipelines: "skip" }); // parent draw is present; only child may skip// reveal a composed model atomically
node.visible = false; root.add(node);
await view.compile(node); // shaders + geometry uploads for subtree
node.visible = true; // first visible frame has all draws ready// transition between views
const next = createSceneView(gpu, nextRoot, { target, pendingPipelines: "sync" });
await next.compile();
active = next; // swap only after complete compile// post-processing shader swap
const nextFx = effect(gpu, nextShader);
await nextFx.compile(target);
frame(gpu, f => f.pass(target, nextFx));Stats and errors
stats.pendingPipelines reports currently skipped draws; stats.syncCompiles counts blocking compiles for jank diagnostics. Compile deduplicates shared fingerprints: two material objects with same shader/layout/state compile once, while bindings remain distinct. Opacity crossing 1 re-keys and may enqueue a new pipeline. Dispose/cancel during compile or frame must settle safely; post-dispose calls throw VGPU-SCENE-VIEW-DISPOSED.
const a = shaderMaterial({ source, opacity: 1 });
const b = shaderMaterial({ source, opacity: 1 }); // same fingerprint
await view.compile(); // one pipeline compile, two binding records
b.set({ opacity: .5 }); // transparent re-key; compile only new fingerprintAcceptance
Add deterministic mock tests for skip/sync precedence (render > view > skip), per-draw readiness, incremental compile/uploads, stats, shared fingerprints, reveal atomicity, streaming, transition swap, and cancellation. Keep view.reveal(node) sugar explicitly Phase 3.
Note: this issue's target surface has moved to the new
renderTree/sceneViewvocabulary (no morecreateSceneView/SceneView) following the Scene API redesign in #219. The reserved orchestration contract (pendingPipelines,view.compile(),view.stats, atomic-per-draw skip) still applies, now layered on top ofrenderTree()'s kernel-owned retained state instead of a constructed view object. See #219 (parent) and #215 (renderTree sub-issue) for full context.