/** * The batched property-driver tick: which entities have drivers in flight, and * advancing them for one frame. * * Extraction 6 of the `Scene.ts` decomposition * (`forge/decisions/file-decomposition-2026-08.md` §2), shipped at **heavily * reduced scope** — see `DEC-0025` for the per-member measurement. The decided * `RenderScheduler` scope named nine members and 989 lines; `loop` and `render` * are not movable at all, so what ships is the two separable sub-clusters this * file and {@link DirtyTracker} own. * * ## What this owns * * The candidate registry (`_activeDriverEntities`), the six reused scratch * arrays, and the batch pass itself. The registry is the reason the pass is * O(active drivers) rather than O(tree size), so registration and ticking are one * domain: every write to the set exists to serve the walk that reads it. * * ## What is held, and what is passed in * * {@link WasmBackendFacade} is held: it is assigned once in `Scene`'s constructor * and the anim backend is reached only through its public surface (`anim`, * `animReason`, `animBatchedLastFrame`), exactly as `HitTester` reaches the hit * backend. * * `dt`, the per-kind `gate` and `currentFrame` are per-call arguments * (`DEC-0019` rule 5): * * - `currentFrame` is written by `render` and belongs to the frame loop, which * did not move. * - `gate` is `Scene.animGate`, a **public mutable field** that tests and * benchmarks assign directly (`scene.animGate = { … }` at 8 sites, plus the * `animDriverGateCount` alias setter). Holding it would both go stale and force * the public field to become an accessor pair, which this sequence exists to * avoid. * * ## What deliberately did not move * * `_tickBatchedDrivers`'s caller. `render` (575 lines) calls twelve `Scene` * methods spanning six domains, and `loop` calls `render`, `syncA11y` and * `enforceA11yDomOrder`. Both would need a `Scene` back-edge, which `DEC-0019` * rule 1 forbids and which `DEC-0020` and `DEC-0021` already refused in the * bound-callback form. */ import { Entity } from '../Entity'; import type { WasmBackendFacade } from './WasmBackendFacade'; /** Per-kind driver gates, in active batchable drivers. See `Scene.animGate`. */ export interface AnimDriverGate { spring: number; tween: number; mixed: number; } export declare class DriverTicker { private readonly backends; private readonly activeEntities; private springEntities; private springProps; private springDrivers; private tweenEntities; private tweenProps; private tweenDrivers; constructor(backends: WasmBackendFacade); /** The candidate set, for the delegating accessor `Scene` keeps for its tests. */ get active(): Set; /** Register a single entity whose driver has just started. */ register(entity: Entity): void; /** * Drop `entity` and its whole subtree from the batched-driver candidate set. * Called by `Scene.remove`/`Scene.hideOverlay` on detach: without this a * removed-but-still-animating entity stays pinned in the Set (a leak) and its * drivers keep ticking every frame even though it is off-tree. If it is later * re-added, {@link registerSubtree} re-registers any node that still has live * drivers, so the motion resumes. */ unregisterSubtree(entity: Entity): void; /** * Re-register every node in `entity`'s subtree that still has live property * drivers. Called by `Scene.add`/`Scene.showOverlay` so re-attaching a subtree * that was removed mid-animation resumes its batched drivers (they were * dropped from the candidate set on removal, but the driver state still lives * on each entity). */ registerSubtree(entity: Entity): void; /** * Advance every registered entity's active drivers for this frame, batching * whichever are batchable (`SpringDriver`; `TweenDriver` with a named * easing) through one WASM call each when the driver-count gate is open, and * ticking the rest (a `TweenDriver` using a custom `EasingFn`) directly in * JS regardless of the gate. A "claimed" entity must have ALL its drivers * advanced here so it can be safely stamped `_driversTickedFrame` — leaving * one unclaimed would silently stall it, since `tickDrivers()` skips the * whole entity once stamped. * * Must run before ANY entity's `update()`/`tickDrivers()` this frame (see * the call site in `Scene.render`) — the same ordering constraint G1 Stage 4 * discovered: a value this pass writes must be final before anything reads * it, including the JS-mode interleaved walk and the WASM-mode transform * pre-pass. */ tick(dt: number, gate: AnimDriverGate, currentFrame: number): void; }