import { GpuComputeNode, GpuFragmentParams } from '../../gpu/contract'; import { ComputeStep } from '../../gpu/compute'; /** The TypeGPU root, via the contract (direct `typegpu` imports are restricted to src/gpu/). */ export type GridRoot = NonNullable['root']; /** The renderer's per-frame params, as the grid consumers read them. */ export interface GridFrameParams { pointer?: { x: number; y: number; }; deltaTime?: number; dimensions?: { width: number; height: number; }; } /** One frame of the simulation, handed to every stage. */ export interface GridFrame { /** `fp.deltaTime ?? 0`, clamped to `clampDt`. */ dt: number; /** `Date.now()` this frame — the settle gates' clock. */ now: number; num(key: string, fallback: number): number; getCpuValue(key: string): unknown; frameParams: GridFrameParams; } /** One stage of the frame program. Phases run in order: ready → tick → skip → emit. */ export interface GridStage { /** Frame gate: false → the whole frame returns null (a late-bound input is still pending). */ ready?(): boolean; /** Pre-gate bookkeeping (pointer smoothing, activity flags). */ tick?(f: GridFrame): void; /** Settle gate: true → skip the frame entirely (the last published texture persists). */ skip?(f: GridFrame): boolean; /** Push this stage's dispatches / uniform writes. */ emit?(f: GridFrame, nodes: ComputeStep[]): void; } export interface GridSimConfig { /** Compute outputs the fragment samples (state/display textures, child RTT handles). */ outputs: Record; /** Late input binding (child RTT → luma prepass, child-modulated kernels). Spread only if set. */ bindInputs?: (resolve: (key: string) => { texture: unknown; } | undefined) => void; /** Frame-delta clamp (seconds). */ clampDt: number; /** Frames with `dt <= 0`: run anyway (default) or skip (Liquify's guard). */ zeroDt?: 'run' | 'skip'; stages: GridStage[]; } /** * The grid-simulation noun. Spread it into a definition: `...gridSim((params, root) => ({...}))`. * The build callback runs at compute-node creation (per instance, after the no-device bail); it may * return null to opt out entirely (a required child is missing). */ export declare function gridSim(build: (params: GpuFragmentParams, root: GridRoot) => GridSimConfig | null): { compute: GpuComputeNode; }; /** A parity-alternating sort chain's handle: which ping-pong side is current after this frame. */ export interface SortPassStage extends GridStage { side(): 'A' | 'B'; } export declare const op: { /** Named pre-gate host step (pointer smoothing, activity flags). Runs in the tick phase. */ readonly host: (name: string, run: (f: GridFrame) => void) => GridStage; /** Named emit-phase host step (per-frame uniform writes / prop derivation). */ readonly values: (name: string, run: (f: GridFrame) => void) => GridStage; /** * Settle gate: once nothing has driven the sim for `settleMs` of SIMULATED time, skip frames * entirely — the last published texture persists on screen, so a settled sim costs zero GPU time. * * Simulated, not wall-clock: a hidden tab stops rAF, so no frames step the field while the user * is away. Measured on the wall clock the gate would freeze the sim the moment the tab came back * with the field still fully visible (it never decayed). Only frames that actually dispatch * count toward the settle budget. */ readonly settle: (opts: { activeWhen: (f: GridFrame) => boolean; settleMs: (f: GridFrame) => number; }) => GridStage; /** Frame gate on a late-bound input (child RTT arriving via `bindInputs`). */ readonly readyWhen: (fn: () => boolean) => GridStage; /** One dispatch of a fixed pass. */ readonly pass: (step: ComputeStep) => GridStage; /** A per-frame cache prepass, resolved per frame (PixelSort's late-rebound child-luma snapshot). */ readonly cache: (step: (f: GridFrame) => ComputeStep) => GridStage; /** The publish tail: write the sim state into the texture the fragment samples. Resolved per * frame so it can follow a ping-pong side (`() => output.with(side() === 'A' ? bgA : bgB)`). */ readonly publish: (step: (f: GridFrame) => ComputeStep) => GridStage; /** One-shot (re)initialisation ahead of the step loop, latched on `staleWhen`. */ readonly seedOnce: (opts: { pass: ComputeStep; staleWhen: (f: GridFrame) => boolean; onSeed?: () => void; }) => GridStage; /** N ordered iterations of a step (the caller's `step` handles its own ping-pong swap). */ readonly iterate: (opts: { count: (f: GridFrame) => number; step: (i: number, nodes: ComputeStep[], f: GridFrame) => void; }) => GridStage; /** * Odd-even transposition sort chain: `passes` compare-swap dispatches, alternating parity and * ping-pong side per pass. The parity tick PERSISTS across frames (harness policy — a new frame * continues the transposition sequence where the last one stopped, which is what makes the sort * converge "bit by bit"). `side()` reports the current buffer for the publish pass. */ readonly sortPass: (opts: { passes: (f: GridFrame) => number; pass: (parity: 0 | 1, side: "A" | "B") => ComputeStep; }) => SortPassStage; }; /** * Viewport dimensions with the standard resize-tracked fallback: `tracked()` is the last * `onResize` value (what a texture-space prepass wants), `safe(fp)` prefers this frame's reported * dimensions and floors both at 1 (what an aspect computation wants). */ export declare function trackedViewport(params: GpuFragmentParams): { tracked(): { width: number; height: number; }; safe(fp: GridFrameParams): { width: number; height: number; }; }; /** One frame of a CPU grid program. */ export interface HostFrame { /** Wall-clock delta since the last frame, clamped (these sims ignore the renderer's deltaTime). */ dt: number; now: number; pointer: { x: number; y: number; }; frameParams: unknown; } /** A named CPU step. Return `'skip'` to end the frame (idle gate). */ export interface HostStep { name: string; run(f: HostFrame): void | 'skip'; } export declare const hostStep: (name: string, run: (f: HostFrame) => void | "skip") => HostStep; /** * An ordered CPU frame program for `onBeforeRender`: wall-clock dt (clamped), then each named step * in order until one skips. */ export declare function hostGridProgram(opts: { clampDt: number; steps: HostStep[]; }): (fp: unknown) => void; /** * The CPU field's GPU face: an rgba16float data texture (filterable — a filtering sampler rejects * r32float, so cells are half-encoded before upload) registered as a media texture the fragment * samples. `texData` is the upload staging buffer the publish step packs into. */ export declare function hostFieldTexture(params: GpuFragmentParams, opts: { size: number; label: string; }): { texData: Uint16Array; texture: ReturnType; upload(): void; }; //# sourceMappingURL=grids.d.ts.map