import { CompiledSystem } from '@pylinka/compiler'; import { System, ParamDef, PylinkaProject } from '@pylinka/graph'; import { b as CompiledStats, S as SystemClock, C as CompiledParticlesOptions, a as CompiledParticlesHandle } from '../emitter-DCgFspU4.js'; import { S as SpriteSource, A as AtlasAnim, M as MaskTable } from '../mask-DvK7Ulmr.js'; export { C as CompiledAtlasOptions } from '../mask-DvK7Ulmr.js'; import { K as KnobStore } from '../knobs-CVFP2Gxg.js'; import '../atlas-D83bp0cn.js'; /** * Compiled WebGL2 transform-feedback backend (REQUIREMENTS.md §13.12). Runs the * compiler's 'webgl2' output: the fused step vertex shader (emitSrc) with the * discard fragment stage (updateSrc), over ping-pong interleaved 56-byte state * buffers (WEBGL2_LAYOUT). Spawning is the cursor-window scheme — the runtime * advances `u_spawnCursor` by spawnCount each frame. Rendering mirrors §13.8. * * This is the compiled counterpart of the interpreted `@pylinka/core/webgl` * engine: same driving surface, but the whole graph runs as generated code. */ interface WebGL2SimOptions { sprite?: SpriteSource; /** atlas animation config (column advance + row pick); defaults to static */ anim?: AtlasAnim; /** emission-mask point table (overrides the analytic spawn shape) */ mask?: MaskTable; /** sub-emitter parent sim: this child spawns on the parent's particle deaths */ subParent?: WebGL2CompiledSim; /** share a project-wide knob store (KnobBus fan-out); defaults to its own */ knobs?: KnobStore; seed?: number; startX?: number; startY?: number; onRecompile?: (info: { ms: number; reason: 'structural' | 'blend'; }) => void; } /** Per-system compiled TF simulation + §13.8-style draw. */ declare class WebGL2CompiledSim { readonly stats: CompiledStats; readonly knobs: KnobStore; readonly clock: SystemClock; compiled: CompiledSystem; system: System; private readonly gl; private readonly onRecompile; private valueTable; private spawnCursor; private cur; private readonly _capacity; private readonly subParent; /** parent-slot count (death detection loops these); child pool = parentCap × burstMax. */ private parentCap; /** death-burst copies: `max` k-region passes per frame (1 = classic sub-emitter). */ private burstMax; private subProg; /** [childCur][parentCur][burstK] — one VAO per ping-pong × burst copy. */ private subVAOs; private uSub; private stepProg; private readonly renderProg; private readonly bufs; private stepVAOs; private readonly renderVAOs; private readonly tf; private readonly cornerBuf; private uStep; private readonly uRender; private readonly tex; private maskTex; private maskCount; private maskW; private spriteCols; private spriteRows; private spriteFrameW; private spriteFrameH; private spriteAtlasW; private spriteAtlasH; private spritePad; private anim; /** scratch for the flags readback (aliveCount) */ private readbackWords; get capacity(): number; /** Live ping-pong index + state buffers, so a sub-emitter child can bind them. */ get curIndex(): number; bufferAt(i: number): WebGLBuffer; constructor(gl: WebGL2RenderingContext, system: System, params: ParamDef[], opts: WebGL2SimOptions); /** Sub-emitter program + VAOs: child state[childCur] + parent cur/prev state * ([childCur][parentCur]), so death is read from the parent's ping-pong. */ private buildSubStep; /** VAO for the sub-step. Own state is read from the child region for this * burst copy (`childOff` = k · parentCap · stride), so `max` passes cover the * whole child pool; parent cur/prev are always read from region 0 (parent * slot = vertex id). i_pVel feeds velocity inheritance under a burst. */ private makeSubVAO; /** RG32F row-major point table (2048 wide), sampled with texelFetch. */ private uploadMask; private cacheStepUniforms; private uploadSprite; private makeBuffer; private makeStepVAO; private makeRenderVAO; /** Zero both state buffers (flags 0 = dead) and rewind the cursor. */ resetPool(): void; /** One TF step: schedule spawns, flush uniforms, run the fused kernel. */ step(dt: number): void; /** Sub-emitter TF step: spawn on parent deaths (read from the parent's * current + previous state), then run the child's own update. The parent * is stepped earlier this frame, so its `curIndex` is the fresh buffer. */ private subStep; /** Instanced draw of the current state into the bound framebuffer. */ draw(sx: number, sy: number, ox: number, oy: number, sizeScale: number): void; /** Synchronous flags readback (debug tier — mirrors the interpreted engine). */ aliveCount(): number; /** Same semantics as the WebGPU sim: zero-recompile for value edits. */ applyProject(next: PylinkaProject, systemName?: string): boolean; destroy(): void; } /** * Standalone compiled WebGL2 runtime — same handle surface as the webgpu * backend, synchronous creation (no adapter/device round-trip). */ declare function createParticles(target: HTMLCanvasElement | WebGL2RenderingContext, project: PylinkaProject, opts?: CompiledParticlesOptions): CompiledParticlesHandle; /** * Render program for the compiled WebGL2 backend — the GLSL mirror of the * §13.8 WebGPU render pipeline, reading the fused 56-byte interleaved state * records the compiler's TF step shader writes (WEBGL2_LAYOUT). u_atlas = * (cols, rows, sizeScale, unused), matching the WebGPU R.atlas convention. */ declare const COMPILED_RENDER_VS = "#version 300 es\nprecision highp float;\nprecision highp int;\n\nlayout(location = 0) in vec2 a_corner;\nlayout(location = 1) in vec2 a_pos; // instance \u2014 state offset 0\nlayout(location = 2) in vec4 a_color; // instance \u2014 state offset 32\nlayout(location = 3) in float a_size; // instance \u2014 state offset 48\nlayout(location = 4) in float a_rot; // instance \u2014 state offset 52\nlayout(location = 5) in uint a_flags; // instance \u2014 state offset 28\nlayout(location = 6) in float a_age; // instance \u2014 state offset 16\nlayout(location = 7) in float a_life; // instance \u2014 state offset 20\nlayout(location = 8) in uint a_seed; // instance \u2014 state offset 24\n\nuniform vec4 u_scaleOffset; // clip = world.xy * xy + zw (\u00A713.8)\nuniform vec4 u_grid; // cols, rows, sizeScale, pad(px)\nuniform vec4 u_anim; // fps, play(0 stretch / 1 loop / 2 hold), pick(0 per-particle / 1 fixed), fixedRow\nuniform vec4 u_frame; // frameW, frameH, atlasW, atlasH (px)\n\nout vec2 v_uv;\nout vec4 v_tint;\n\nvoid main() {\n float s = a_size * float(a_flags & 1u) * u_grid.z;\n float c = cos(a_rot);\n float sn = sin(a_rot);\n vec2 local = vec2(a_corner.x * c - a_corner.y * sn, a_corner.x * sn + a_corner.y * c) * s;\n vec2 world = a_pos + local;\n\n // atlas cell: column advances with life, row is per-particle (or fixed).\n float cols = max(u_grid.x, 1.0);\n float rows = max(u_grid.y, 1.0);\n float tN = clamp(a_age / max(a_life, 1e-4), 0.0, 1.0);\n float seedN = float(a_seed & 0xffffu) / 65536.0;\n float row = clamp((u_anim.z > 0.5) ? u_anim.w : floor(seedN * rows), 0.0, rows - 1.0);\n float col = (u_anim.y > 1.5)\n ? clamp(floor(a_age * u_anim.x), 0.0, cols - 1.0)\n : (u_anim.y > 0.5)\n ? mod(floor(a_age * u_anim.x), cols)\n : clamp(floor(tN * cols), 0.0, cols - 1.0);\n vec2 cellPx = vec2(col, row) * (u_frame.xy + u_grid.w);\n\n gl_Position = vec4(world * u_scaleOffset.xy + u_scaleOffset.zw, 0.0, 1.0);\n v_uv = (cellPx + (a_corner + 0.5) * u_frame.xy) / u_frame.zw;\n v_tint = a_color;\n}"; declare const COMPILED_RENDER_FS = "#version 300 es\nprecision highp float;\nin vec2 v_uv;\nin vec4 v_tint;\nout vec4 frag;\nuniform sampler2D u_tex;\nvoid main() {\n vec4 t = texture(u_tex, v_uv); // premultiplied (\u00A713.1)\n frag = vec4(t.rgb * v_tint.rgb * v_tint.a, t.a * v_tint.a);\n}"; export { COMPILED_RENDER_FS, COMPILED_RENDER_VS, CompiledParticlesHandle, CompiledParticlesOptions, CompiledStats, WebGL2CompiledSim, type WebGL2SimOptions, createParticles };