import { GpuComputeNode, GpuFragmentParams } from '../../gpu/contract'; import { SimBindContext, SimSize, SimSlot, SimTick } from '../../gpu/scaffolds/feedbackSim'; /** The GPU device handle the build callback receives, for creating the resources your step reads. */ export type FeedbackRoot = NonNullable['root']; /** A step function run once per state cell, with the cell's column and row. */ export type FeedbackKernel = (cx: number, cy: number) => void; /** * What you get when wiring your layout: the child's picture, the display texture, any extra * textures you declared, and the buffer holding your per-frame values. */ export type FeedbackBindContext = SimBindContext & { paramsBuffer: unknown; }; /** * What a feedback simulation declares: the state's size and format, one step function, the * wiring that hands it the read and write sides, and the values it reads each frame. */ export interface FeedbackSimConfig { /** Cells per side of the square state texture. 512–768 reads sharp on most canvases. */ size: number; /** Storage precision. `rgba16float` is enough for color; `rgba32float` for exact positions. */ format: 'rgba16float' | 'rgba32float'; /** Extra textures that keep a previous and a current side, swapped together with the state. */ extraSlots?: Record; /** Extra textures with no previous side. */ textures?: Record; /** A prop that scales the clock. 0 freezes the simulation, 2 runs it twice as fast. */ speedProp?: string; /** Longest frame step accepted, in seconds. Default 0.05. Keeps a tab switch from jumping the state. */ maxDeltaTime?: number; /** The schema of the per-frame values your step reads. The engine owns the buffer. */ paramsSchema: unknown; /** * Wire your layout for one direction of the swap: `read` is last frame's state, `write` is * this frame's. Called twice, once per direction, after the child's picture exists. */ bindGroups: (ctx: FeedbackBindContext, read: SimSlot, write: SimSlot) => TGroups; /** Wire resources that do not depend on the swap direction. Called once. */ staticGroups?: (ctx: FeedbackBindContext) => unknown; /** * The rule: one step function that reads `read`, writes `write` and the display copy. * Pick the variant inside the build callback when a compile-time prop changes it. */ step: FeedbackKernel; /** Return true to skip this frame. The last state stays on screen and nothing swaps. */ skip?: (t: SimTick) => boolean; /** The values your step reads this frame, built from the clock and your props. */ values: (t: SimTick) => TParams; /** Extra outputs for the fragment beyond `childTexture` and `display`. */ outputs?: Record; } /** * A simulation that advances a picture of its own last frame by one step of your rule, every * frame. Spread it into a definition next to a `gpu.fragment` that samples `display`. * * The build callback runs once per instance of the layer, so anything you create inside it * (resources, a kernel variant picked from a compile-time prop) belongs to that instance. The * simulation needs a child. Without one, or without a GPU, nothing runs and the fragment should * fall back to the child. * * @example * ```ts * ...feedbackSim((params, root) => ({size: 768, format: 'rgba16float', speedProp: 'speed', paramsSchema: decoder.Params, bindGroups: ({childTexture, display, paramsBuffer}, read, write) => root.createBindGroup(decoder.layout, {src: childTexture, prev: read.state, next: write.state, display, params: paramsBuffer} as never), step: decoder.kernel, values: ({dt, localTime}) => ({time: localTime, dt, intensity: params.getCpuValue('intensity') as number})})) * ``` * @tip Read time from `localTime`, not the layer's clock. It stops when `speedProp` is 0 and every frame's step matches it. * @see fluidSim, gridSim, agentSim, simulate */ export declare function feedbackSim(build: (params: GpuFragmentParams, root: FeedbackRoot) => FeedbackSimConfig): { compute: GpuComputeNode; }; //# sourceMappingURL=feedback.d.ts.map