import { TgpuRoot } from 'typegpu'; /** * Spring-damper step for smooth mouse tracking. Returns `[newPosition, newVelocity]`. * When smoothing and momentum are both 0 the target is returned immediately (zero cost). * Large deltas are integrated in ≤ 1/60s sub-steps (see {@link SPRING_MAX_SUBSTEP}). */ export declare function applySpring(current: number, velocity: number, target: number, smoothing: number, momentum: number, dt: number): [number, number]; /** The easing names the auto-animate driver accepts. */ export type EasingName = 'linear' | 'quad' | 'expo' | 'bounce' | 'sine'; /** Uncharted-style multi-segment bounce ease. */ export declare function applyBounceEase(t: number): number; /** Maps a 0..1 phase through the named easing. Default is `sine`. */ export declare function applyEasing(t: number, easing: string): number; export interface ClampEnv { /** Device `maxTextureDimension2D` (or the 8192 spec-minimum fallback pre-init). */ maxTextureDim: number; /** The DPR factor we back the canvas at. */ pixelRatio: number; /** Live viewport width in CSS px (window.innerWidth). */ viewportWidth: number; /** Live viewport height in CSS px (window.innerHeight). */ viewportHeight: number; } /** * Clamps requested CSS-pixel dimensions to a safe GPU buffer size while preserving aspect * ratio. The buffer (cssSize × pixelRatio) never exceeds the viewport or * `maxTextureDimension2D`. Pure (env injected) so it is golden-testable; `index.ts` wraps it * with the live window + DPR. The `-1` slack guards a borderline value from rounding over the * cap. */ export declare function clampToTextureCap(w: number, h: number, env: ClampEnv): { width: number; height: number; }; /** * The elapsed-seconds clock the shader `time` uniform reads. With a shared wall-clock origin * (synced multi-tile animation), time is `(now - origin)/1000` so every renderer sharing the * origin lands on the same value and self-heals across pauses. Without one, it accumulates * the (already clamped) per-frame delta — "seconds since this renderer started". */ export declare function deriveElapsedTime(sharedTimeOrigin: number | null, prevElapsed: number, deltaTime: number, now: number): number; /** Off-screen render interval (1 FPS) when not forced to full frame rate. */ export declare const OFF_SCREEN_FPS_INTERVAL = 1000; /** * On-screen minimum frame interval — a 60 FPS cap with 1ms jitter tolerance so a 60Hz * display hits a true 60fps without the aliasing that a cap exactly equal to the refresh * interval produces. */ export declare const MIN_FRAME_INTERVAL: number; export interface FrameGateState { /** Timestamp (performance.now ms) of the last rendered frame, or 0 if none yet. */ lastRenderTime: number; /** IntersectionObserver visibility — false throttles to 1 FPS. */ isVisible: boolean; /** When true, off-screen throttling is bypassed (hidden-canvas partner integrations). */ forceFullFrameRate: boolean; /** * Optional host-imposed minimum frame interval (ms) while ON-screen — the * per-renderer frame-rate cap (`setFrameRateCap`). A multi-tile canvas uses it * to demote tiny tiles to e.g. 15 FPS. 0/undefined = the default 60 FPS cap. * Never loosens the cap below MIN_FRAME_INTERVAL, and never affects the * off-screen 1 FPS throttle. */ minInterval?: number; } export interface FrameGateResult { /** Whether this frame should render (false = skip, throttled). */ render: boolean; /** Delta since the last frame, in seconds, clamped to 0.1. */ deltaTime: number; } /** * Decides whether the current tick should render and, if so, the clamped delta. Variable * frame rate: 1 FPS off-screen, up to 60 FPS on-screen (with the jitter-tolerant cap), delta * clamped to 0.1s. The synthetic-frame path bypasses this entirely (see {@link syntheticDelta}); * it is only used by the RAF-driven live loop. */ export declare function frameGate(now: number, state: FrameGateState): FrameGateResult; /** * Resolves when the device has finished all previously submitted work, via * `device.queue.onSubmittedWorkDone()`. Falls back to a one-frame timeout if the queue does * not expose the API (never expected on a real device; keeps the screenshot/export path from * hanging on a mock). */ export declare function awaitGpuIdle(root: TgpuRoot): Promise; /** * The ordered per-frame steps. Kept as an interface + a pure runner so the canonical order is * declared once and unit-testable with mocks that record call order (no device needed). */ export interface FrameSequence { /** CPU driver springs / easing + animated-time advance (before anything is flushed). */ updateDrivers(): void; /** Ensure the desired composition is built + bound. Return false to skip (nothing to draw). */ ensureComposition(): boolean; /** onBeforeRender shader callbacks — they write uniforms, so they run before flush. */ beforeRender(): void; /** Coalesced uniform buffer patch for the bound composition. */ flush(): void; /** Draw the composition currently selected by the pipeline cache (swap-when-ready). */ render(): void; /** Promote the pending composition once it has drawn a frame + fire onReady. */ markReady(): void; /** onAfterRender shader callbacks. */ afterRender(): void; } /** * Runs one frame's steps in the canonical order: drivers/time → [ensure composition] → * onBeforeRender → flush → render → markReady → onAfterRender. `ensureComposition` returning * false short-circuits the rest (no root composed yet). onBeforeRender precedes flush because * those callbacks write uniforms; in steady state `ensureComposition` is a cache-hit no-op. */ export declare function runFrameSequence(seq: FrameSequence): void; export interface FrameLoop { /** Start the loop (idempotent). No-op if already running. */ start(): void; /** Stop the loop and cancel any pending frame. */ stop(): void; /** Whether the loop is currently running. */ readonly running: boolean; } /** * A minimal `requestAnimationFrame` loop that invokes `tick` once per frame. The throttle * decision lives in `tick` itself (via {@link frameGate}); this factory only owns the RAF * scheduling + teardown. SSR-safe: `requestAnimationFrame` is resolved lazily at `start()`, * never at import. */ export declare function createFrameLoop(tick: () => void): FrameLoop; //# sourceMappingURL=frame.d.ts.map