import { TgpuRoot, TgpuBindGroupLayout, TgpuBindGroup } from 'typegpu'; import { AnyWgslData, WgslStruct, WgslArray } from 'typegpu/data'; import * as d from 'typegpu/data'; /** The bind-group-layout entry key the combined uniform struct is bound under. */ export declare const UNIFORM_ENTRY_KEY: "uniforms"; /** * Name sanitization — uniform names become WGSL struct member identifiers verbatim, so strip * anything WGSL-invalid (dots from `Math.random` ids, hyphens in user ids, …). Node keys are * additionally prefixed `n_` so a numeric-leading id can't produce an invalid identifier. */ export declare function sanitizeId(id: string): string; /** * Sanitize a STRUCT-MEMBER name: strip WGSL-invalid chars, then suffix `_` if the result is a * reserved word. Node keys already get an `n_` prefix (never reserved), so only per-field names * (props + synthetics) route through here — the accessor path derives from the same handle * `gpuPath`, so struct field + accessor stay consistent. */ export declare function sanitizeFieldId(id: string): string; /** The runtime struct key for a node, e.g. `n_abc123`. */ export declare function nodeKey(rawId: string): string; type VecKind = 'vec2' | 'vec3' | 'vec4'; type FieldKind = 'scalar' | VecKind; /** * Infer the packed-struct field schema from a JS value (post-transform): * number → f32, boolean → f32 (transforms already encode booleans as ±1), * {x,y}/vec2f → vec2f, {x,y,z}/vec3f → vec3f, color/{x,y,z,w}/vec4f → vec4f, * number[] → arrayOf(f32, length). * Strings (urls / shape JSON / origin / compile-time selects) are CPU-only — they never * enter the struct; callers mark those `cpu: true` and get a mirror-only FieldHandle. * * CAUTION (strict uniform layout): `arrayOf(f32, n)` has a 4-byte stride, which is invalid in * the uniform address space on browsers without `uniform_buffer_standard_layout` (arrays there * need 16-byte strides). No shader hits this inference today — array props (colorStops) pass * explicit vec4f/vec3f schemas, whose 16-byte strides are valid everywhere. A new plain * `number[]` prop must pack into vec4s (`d.arrayOf(d.vec4f, ceil(n/4))`), not rely on this. */ export declare function inferFieldSchema(value: unknown): AnyWgslData; /** Minimal surface the store exposes to its handles for coalesced dirty-tracking. */ interface DirtySink { markScalar(handle: FieldHandle): void; markArray(handle: ArrayFieldHandle, index: number | 'all'): void; } /** * A live view over a vector field's CPU mirror, returned by `FieldHandle.value` for vector * fields. Its component accessors and `.copy(...)` / `.set(...)` methods route back into the * store's coalesced patch — supporting in-place idioms like `uniform.value.copy(data)` and * `smoothedUniform.value.set(x, y)`. */ export declare class VectorFieldView { private readonly handle; constructor(handle: FieldHandle); private get n(); private component; private write; get x(): number; set x(v: number); get y(): number; set y(v: number); get z(): number; set z(v: number); get w(): number; set w(v: number); get r(): number; set r(v: number); get g(): number; set g(v: number); get b(): number; set b(v: number); get a(): number; set a(v: number); /** `Vector.copy(src)` — copy another vector-like's components in-place. */ copy(src: unknown): this; /** `Vector.set(x, y[, z[, w]])`. */ set(...values: number[]): this; toArray(): number[]; } /** * Backed by one field in the packed struct; keeps a CPU mirror for `.value` reads and routes * `.value` writes into the store's coalesced patch. */ export declare class FieldHandle { /** Struct-relative path, e.g. `['n_x3', 'speed']`. See the GPU reference contract. */ readonly gpuPath: readonly [string, string]; /** Dotted struct-relative accessor, e.g. `'n_x3.speed'`. */ readonly accessorPath: string; /** The prop transform (if any). */ readonly transform?: (value: unknown) => unknown; /** CPU-only props (strings/JSON) have no struct field — mirror-only, never patched. */ readonly cpu: boolean; /** The field's WGSL schema (undefined for cpu-only handles). */ readonly schema?: AnyWgslData; /** @internal component count (1 for scalar, 2/3/4 for vectors). */ readonly _componentCount: number; /** @internal CPU mirror: index 0 for scalar; [x,y,(z),(w)] for vectors; raw value for cpu-only. */ _mirror: number[]; private _raw; private readonly _kind; private readonly _view?; private readonly _sink; constructor(opts: { sink: DirtySink; gpuPath: [string, string]; schema?: AnyWgslData; initial: unknown; transform?: (value: unknown) => unknown; cpu?: boolean; }); /** @internal enqueue this field into the store's coalesced patch. */ _enqueue(): void; /** @internal the value written into a `buffer.patch` for this field. */ _patchValue(): number | number[]; /** * CPU mirror read. Scalars return a number; vectors return a live view whose * `.x/.y/.z/.w`, `.copy()`, and `.set()` all patch; cpu-only returns the raw stored value. */ get value(): unknown; /** * CPU mirror write of a FINAL (post-transform) value. Accepts a number, a Vector-like * `{x,y,z,w}`, a `d.vecXf` instance, a `VectorFieldView`, a plain array, or (for cpu-only) * any raw value. Routes into the coalesced patch. Assigning the same view back * (`u.value = u.value`) is a no-op copy that still marks the field dirty, matching the * "re-assign to flush" idiom. */ set value(next: unknown); /** * Set from a RAW prop value, applying the prop transform first (the `updateUniformValue` * path). CPU-only handles store the raw value untouched. */ setFromRaw(raw: unknown): void; /** Direct component write for vectors (`u.setComponents(x, y)`), avoiding the view. */ setComponents(...values: number[]): void; } /** * Wraps one fixed-size `d.arrayOf(...)` struct field. Whole-array writes (`.array = [...]`) * mark the field for a full patch; per-element writes (`.setElement(i, v)`) coalesce into a * sparse `patch({ n_x: { colors: { 3: v } } })`. * * Supports the colorStops flow: `const a = def.colorsArray.array; a[i] = …; def.colorsArray.array = a` * (read the live mirror, mutate it, re-assign to flush). The `.array` getter returns the live * mirror, and the setter always marks dirty even when the same array reference is re-assigned. */ export declare class ArrayFieldHandle { readonly gpuPath: readonly [string, string]; readonly accessorPath: string; readonly transform?: (value: unknown) => unknown; readonly schema: WgslArray; readonly length: number; /** Element kind — 'scalar' for `arrayOf(f32,…)` (flat float arrays), else a vec kind. */ readonly elementKind: FieldKind; private _mirror; private readonly _sink; constructor(opts: { sink: DirtySink; gpuPath: [string, string]; schema: WgslArray; initial?: number[]; transform?: (value: unknown) => unknown; }); /** Flat mirror length: `elementCount * componentsPerElement`. */ private get flatLength(); /** The live CPU mirror array. Mutate + re-assign via the setter to flush. */ get array(): number[]; set array(next: number[]); /** Coalesced single-element write → sparse patch. */ setElement(index: number, value: number): void; /** Components per element (1 for scalar element arrays). */ get componentsPerElement(): number; /** * @internal whole-array patch payload. typegpu's dataIO serializes vec-element * arrays from PER-ELEMENT tuples, not a flat component array — a flat 32-float * array for `arrayOf(vec4f, 8)` writes NaN garbage. The flat number[] mirror is the * CPU-side surface; chunk it here. */ _wholeArray(): number[] | number[][]; /** @internal single-element patch payload (scalar element arrays; flat index). */ _elementAt(index: number): number; /** @internal element tuple at ELEMENT index (vec element arrays). */ _elementTuple(elemIndex: number): number[]; } /** * The `_sys` struct default. `kit/coords.ts` owns the canonical shape and can inject its own * via `createUniformStore(root, { systemSchema, systemInitial })`. These fields cover the * built-ins the renderer feeds every frame (time, viewport, pointer). */ export declare const SystemUniforms: d.WgslStruct<{ time: d.F32; deltaTime: d.F32; frame: d.F32; viewportSize: d.Vec2f; logicalViewportSize: d.Vec2f; aspect: d.F32; pointer: d.Vec2f; pointerActive: d.F32; }>; /** One field to register on a node (or the system struct). */ export interface FieldInit { /** WGSL-safe field name (prop name). */ name: string; /** * The field's WGSL schema (`d.f32`, `d.vec2f`, `d.vec4f`, `d.arrayOf(...)`, …). Omit to * infer from `initial` (see `inferFieldSchema`); pass `cpu: true` for string/JSON props * that must not enter the struct. */ schema?: AnyWgslData; /** Initial FINAL value (already transformed) OR raw value if `transform` is given. */ initial: unknown; /** Optional prop transform, retained on the handle for `updateUniformValue`-style writes. */ transform?: (value: unknown) => unknown; /** Marks a CPU-only prop (string/JSON/origin) — mirror-only, no struct field. */ cpu?: boolean; } export interface FinalizeResult { schema: WgslStruct; layout: TgpuBindGroupLayout; buffer: { patch: (partial: unknown) => void; write: (data: unknown) => void; destroy?: () => void; }; bindGroup: TgpuBindGroup; uniformEntryKey: string; } export interface UniformStoreOptions { /** Override the `_sys` struct schema (kit/coords supplies the canonical one). */ systemSchema?: WgslStruct; /** Initial values for the system fields (defaults cover the built-in shape). */ systemInitial?: Record; } /** Handles for a single node's fields, keyed by prop name. */ export type NodeHandles = Record; export interface UniformStore { /** Register a node's GPU-relevant props; returns its FieldHandles keyed by prop name. */ defineNode(rawId: string, fields: FieldInit[]): NodeHandles; /** Register the `_sys` system fields; returns their handles keyed by field name. */ defineSystem(fields?: FieldInit[]): NodeHandles; /** * Assemble the combined struct, create the uniform buffer + bind group, and write initial * values. Idempotent — returns the cached result on subsequent calls. */ finalize(): FinalizeResult; /** Coalesced flush — one `buffer.patch(...)` for every field touched since the last flush. */ flush(): void; /** Whole-buffer write fallback (`buffer.write(...)`). */ writeAll(): void; /** Build the GPU accessor string for glue WGSL, e.g. `layout.$.uniforms.n_x3.speed`. */ gpuAccessor(handle: FieldHandle | ArrayFieldHandle, layoutVar?: string): string; /** The combined struct (available after `finalize`). */ readonly schema?: WgslStruct; readonly layout?: TgpuBindGroupLayout; readonly buffer?: FinalizeResult['buffer']; readonly bindGroup?: TgpuBindGroup; /** The `_sys` handles (after `defineSystem`). */ readonly systemHandles: NodeHandles; destroy(): void; } /** * Create a per-composition uniform store around a TgpuRoot. Register nodes + system fields, * then `finalize()` to build the buffer/layout/bind group. Drives the coalesced per-frame * patch (`flush()`). */ export declare function createUniformStore(root: TgpuRoot, options?: UniformStoreOptions): UniformStore; /** * Applies the prop transform and writes in-place (in-place semantics live inside the store). * Array (colorStops) handles are updated through their own path (their transform reshapes the * arrays), so callers route those separately; here we set the whole array when a plain array is * given. */ export declare function updateFieldValue(handle: FieldHandle | ArrayFieldHandle, rawValue: unknown): void; export {}; //# sourceMappingURL=uniformStore.d.ts.map