@solidrt/flux-types 0.0.44 → 0.0.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/gui/gpu.d.ts CHANGED
@@ -7,11 +7,14 @@
7
7
  // ids (-> destroyRenderPipeline).
8
8
  // Layering: compileShader/linkProgram are the raw GL primitives (complete
9
9
  // sources, explicit header opt-in); createRenderPipeline pairs a program with
10
- // draw state (topology, blend, depth, vertex layout - how it draws);
10
+ // draw state (topology, blend, cull, depth, vertex layout - how it draws);
11
11
  // createShaderTarget builds a texture-backed target over a pipeline (size,
12
- // buffer, uniforms, clear - where it draws). createShaderTexture/
13
- // createPipelineTexture are fused conveniences (compile + link + pipeline +
14
- // target in one call, curated preamble) - named for what they return.
12
+ // buffer, uniforms, clear - where it draws); createDrawTarget holds an
13
+ // ordered, mutable LIST of such draws in one target (addDraw/removeDraw +
14
+ // per-entry setters, sharing one depth buffer) - the multi-pass render pass.
15
+ // createShaderTexture/createPipelineTexture are fused conveniences (compile +
16
+ // link + pipeline + target in one call, curated preamble) - named for what
17
+ // they return.
15
18
  //
16
19
  // Sampling is a per-texture property declared at creation: every create path
17
20
  // accepts `{ filter?, wrap? }` ("linear"/"nearest", "clamp"/"repeat";
@@ -81,6 +84,13 @@ declare module "flux:gpu" {
81
84
  export type ProgramId = number & { readonly __program: unique symbol }
82
85
  /** The render-pipeline id space ({@link createRenderPipeline}); see {@link TextureId} for the brand model. */
83
86
  export type RenderPipelineId = number & { readonly __renderPipeline: unique symbol }
87
+ /**
88
+ * A draw-entry handle on a draw target ({@link addDraw}); see
89
+ * {@link TextureId} for the brand model. Target-scoped and stable: an id
90
+ * keeps naming its entry across other adds and removes (never an index),
91
+ * and a removed entry's id errors from then on rather than aliasing.
92
+ */
93
+ export type DrawId = number & { readonly __draw: unique symbol }
84
94
  /**
85
95
  * Shader uniform values by name. A number drives a scalar uniform (`float`,
86
96
  * or `int`/`bool`, truncated); a flat number array drives a typed uniform
@@ -113,7 +123,7 @@ declare module "flux:gpu" {
113
123
  * messages, so a chain of targets reads as "bloom-h samples particle-verts"
114
124
  * instead of anonymous ids. Not unique, never interpreted; set at create,
115
125
  * kept across id-stable resizes ({@link resizeTexture},
116
- * {@link setShaderSize}).
126
+ * {@link setTargetSize}).
117
127
  */
118
128
  export type LabelOption = { label?: string }
119
129
  /**
@@ -176,8 +186,8 @@ declare module "flux:gpu" {
176
186
  * working, and shaders sampling the texture re-render. `data` seeds the new
177
187
  * contents and, like {@link createMutableTexture}, must hold at least one
178
188
  * frame at the id's format (which survives the resize, like the sampler
179
- * state). Shader/pipeline target ids are rejected - resize those with
180
- * {@link setShaderSize}.
189
+ * state). Render target ids are rejected - resize those with
190
+ * {@link setTargetSize}.
181
191
  */
182
192
  export function resizeTexture(id: TextureId, data: Uint8Array, width: number, height: number): void
183
193
  /**
@@ -195,7 +205,7 @@ declare module "flux:gpu" {
195
205
  * for the value shapes and the validation contract - a typo'd name throws
196
206
  * here, at the create). It is its own argument, not an option, because it
197
207
  * is the initial value of a live channel - the same values the `<texture
198
- * params>` prop and {@link setShaderParams} drive later; pass `null` (or
208
+ * params>` prop and {@link setTargetParams} drive later; pass `null` (or
199
209
  * omit it) for a shader with none. `opts.textures` binds sampler2D
200
210
  * uniforms to texture ids - any texture id, including another
201
211
  * shader/pipeline target's output, under a name that must be an active
@@ -273,19 +283,36 @@ declare module "flux:gpu" {
273
283
  * (its own id space, like programs and buffers - not a texture id): the
274
284
  * pipeline state object of every modern GPU API. The pipeline owns HOW its
275
285
  * targets draw - `attributes` (the interleaved vertex layout; omit for
276
- * attributeless rendering via gl_VertexID), `topology`, `blend`, `depth`,
277
- * `depthWrite` (`false` requires `depth: true`) - while each target brings
278
- * its own size, buffer, uniforms, and clear. Creating a pipeline compiles
279
- * nothing, and many pipelines may share one program. The vocabulary is
280
- * validated here, so a bad word throws at this call site. Free with
286
+ * attributeless rendering via gl_VertexID), `instanceAttributes` (the
287
+ * per-instance layout, fetched from each entry's `instanceBuffer` - see
288
+ * the option's doc), `topology`, `blend`, `cull`, `depth`, `depthWrite`
289
+ * (`false` requires `depth: true`) - while each target brings its own
290
+ * size, buffers, uniforms, and clear. Both layouts share one attribute
291
+ * namespace (each name is one `in` of the vertex stage), so a name in
292
+ * both lists throws. Creating a pipeline compiles nothing, and many
293
+ * pipelines may share one program. The vocabulary is validated here, so a
294
+ * bad word throws at this call site. Free with
281
295
  * {@link destroyRenderPipeline}; the program is yours and outlives it.
282
296
  */
283
297
  export function createRenderPipeline(
284
298
  program: ProgramId,
285
299
  opts?: {
286
300
  attributes?: VertexAttribute[]
301
+ /**
302
+ * One interleaved record per INSTANCE (WebGPU's `stepMode:
303
+ * "instance"`): these attributes read from the entry's
304
+ * `instanceBuffer` and advance per instance instead of per vertex, so
305
+ * every vertex of instance N sees record N - real per-instance state
306
+ * (offsets, colors, a packed transform) with no `gl_InstanceID`
307
+ * arithmetic. Declaring any makes `instanceBuffer` required on every
308
+ * entry drawn with this pipeline. A mat4 per instance is its four
309
+ * vec4 columns, reassembled in the shader (attributes have no matrix
310
+ * formats, as in WebGPU).
311
+ */
312
+ instanceAttributes?: VertexAttribute[]
287
313
  topology?: Topology
288
314
  blend?: BlendMode
315
+ cull?: CullMode
289
316
  depth?: boolean
290
317
  depthWrite?: boolean
291
318
  } & LabelOption,
@@ -301,17 +328,19 @@ declare module "flux:gpu" {
301
328
  * Create a render target over a {@link createRenderPipeline} pipeline and
302
329
  * render it once: the target half of {@link createPipelineTexture}. Returns
303
330
  * a texture id exactly like the fused creates do (drive uniforms
304
- * via the `params` prop or {@link setShaderParams}, resize with
305
- * {@link setShaderSize}, destroy with {@link destroyTexture}). Many targets
331
+ * via the `params` prop or {@link setTargetParams}, resize with
332
+ * {@link setTargetSize}, destroy with {@link destroyTexture}). Many targets
306
333
  * may share one pipeline, and creating a target compiles nothing. `buffer`
307
334
  * supplies the concrete vertex buffer the pipeline's attribute layout
308
- * describes (required when the pipeline declares attributes); the
309
- * {@link DrawRange} keys pick what is drawn from it - `vertexCount`
335
+ * describes (required when the pipeline declares attributes), and
336
+ * `instanceBuffer` the per-instance records its `instanceAttributes`
337
+ * describe (required exactly when it declares any); the
338
+ * {@link DrawRange} keys pick what is drawn from them - `vertexCount`
310
339
  * defaults to the rest of the buffer from `firstVertex` on,
311
- * `instanceCount` repeats the range - and a vertex fetch past the
312
- * buffer's end throws here. A fullscreen pass over an attributeless
313
- * pipeline is `vertexCount: 3` with a covering-triangle vertex stage.
314
- * Draw-state keys
340
+ * `instanceCount` to one instance per instance-buffer record (1 without
341
+ * one) - and a fetch past either buffer's end throws here. A fullscreen
342
+ * pass over an attributeless pipeline is `vertexCount: 3` with a
343
+ * covering-triangle vertex stage. Draw-state keys
315
344
  * (`attributes`, `topology`, `blend`, `depth`, `depthWrite`) belong to the
316
345
  * pipeline and throw here. `params` and `textures` are validated against
317
346
  * the pipeline's program (see {@link ShaderParams}).
@@ -341,10 +370,11 @@ declare module "flux:gpu" {
341
370
  opts?: {
342
371
  textures?: Record<string, TextureId>
343
372
  buffer?: BufferId
373
+ instanceBuffer?: BufferId
344
374
  clearColor?: [number, number, number, number]
345
375
  render?: "auto" | "manual"
346
376
  loadOp?: "clear" | "load"
347
- } & DrawRange &
377
+ } & (DrawRange | (IndexBinding & IndexRange)) &
348
378
  SamplerOptions &
349
379
  LabelOption,
350
380
  ): TextureId
@@ -355,36 +385,6 @@ declare module "flux:gpu" {
355
385
  * immediately.
356
386
  */
357
387
  export function destroyProgram(id: ProgramId): void
358
- /**
359
- * Update a shader texture's uniforms by name and re-render it (see
360
- * {@link ShaderParams} for the value shapes and the validation contract -
361
- * an unknown name or a mismatched length throws here, on the line that
362
- * wrote it). On a manual target nothing renders here; the values apply at
363
- * its next {@link renderTarget}.
364
- */
365
- export function setShaderParams(id: TextureId, params: ShaderParams): void
366
- /**
367
- * Rebind a shader texture's sampler2D inputs by uniform name and re-render
368
- * it with its last-applied params - the sampler analog of
369
- * {@link setShaderParams}. Bindings not named keep their current source, so
370
- * a single input can be retargeted (post-process source swap, ping-pong
371
- * between two data textures) without recompiling the shader. Throws if the
372
- * shader or a source texture id is unknown, if a binding names anything
373
- * but an active `sampler2D` uniform, if it names the shader's own target
374
- * (same-pass feedback), or if it would close a sampling cycle among
375
- * runtime-rendered targets. A cycle through a
376
- * `render: "manual"` target is legal - the runtime never renders one, so
377
- * the loop only steps when the app calls {@link renderTarget}.
378
- */
379
- export function setShaderTextures(id: TextureId, textures: Record<string, TextureId>): void
380
- /**
381
- * Resize a shader or pipeline target texture in place and re-render it: the
382
- * id, compiled program, last-applied params, and sampler bindings all carry
383
- * over; only the output size changes. The setDraw analog for output
384
- * size.
385
- */
386
- export function setShaderSize(id: TextureId, width: number, height: number): void
387
-
388
388
  export type Topology = "points" | "lines" | "line-strip" | "triangles" | "triangle-strip"
389
389
  /**
390
390
  * Blending for a pipeline's own draw. "none" (default) overwrites:
@@ -398,24 +398,75 @@ declare module "flux:gpu" {
398
398
  */
399
399
  export type BlendMode = "none" | "add"
400
400
  /**
401
- * One float attribute of an interleaved vertex. The attribute list's order
402
- * defines the byte layout; locations are resolved by name against the
403
- * vertex shader's `in` declarations.
401
+ * Face culling for a pipeline's draws. "none" (default) rasters both faces
402
+ * - the two-sided fallback open surfaces need. "back" discards faces wound
403
+ * away from the camera, halving a closed mesh's fragment work; "front"
404
+ * discards the other set (shadow and inside-out tricks). The winding rule
405
+ * is WebGPU's, fixed: counter-clockwise AS DISPLAYED (screen coordinates,
406
+ * y down) = front. Measured after every flip, so it just works: a mesh
407
+ * exported counter-clockwise-front for a y-up world, drawn through a
408
+ * standard right-handed camera (looking down -z) with the usual y
409
+ * negation for display, culls correctly with "back". If "back" shows you
410
+ * the mesh's inside anyway, the winding reaching the screen is mirrored -
411
+ * either the exporter winds clockwise, or the hand-rolled projection is
412
+ * left-handed (the classic: camera looking toward +z without mirroring
413
+ * x). Fix the rig, flip the exporter, or use "front".
414
+ */
415
+ export type CullMode = "none" | "back" | "front"
416
+ /**
417
+ * One float attribute of an interleaved record - a vertex of `attributes`
418
+ * or an instance record of `instanceAttributes`. The list's order defines
419
+ * the byte layout; locations are resolved by name against the vertex
420
+ * shader's `in` declarations.
404
421
  */
405
422
  export type VertexAttribute = { name: string; format: "f32" | "vec2" | "vec3" | "vec4" }
406
423
  /**
407
424
  * A pipeline target's draw as data, WebGPU-style: `firstVertex` +
408
425
  * `vertexCount` pick the vertex range `[firstVertex, firstVertex +
409
426
  * vertexCount)` of the buffer, `instanceCount` draws that range as N
410
- * instances (`glDrawArraysInstanced`) told apart by `gl_InstanceID`. All
411
- * keys optional: at create, `firstVertex` defaults to 0, `vertexCount` to
412
- * the rest of the buffer and `instanceCount` to 1 (the plain draw); in
413
- * {@link setDraw}, absent keys keep their current value. `instanceCount: 0`
414
- * draws nothing - a cheap off switch. Two GL facts worth knowing:
415
- * `gl_VertexID` includes `firstVertex` (as in WebGPU), and `gl_InstanceID`
416
- * always counts from 0 - ES 3.0 has no base instance.
427
+ * instances (`glDrawArraysInstanced`) told apart by `gl_InstanceID` (and
428
+ * by their `instanceAttributes` records, when the pipeline declares any).
429
+ * All keys optional: at create, `firstVertex` defaults to 0, `vertexCount`
430
+ * to the rest of the buffer, and `instanceCount` to one instance per
431
+ * record of the entry's `instanceBuffer` - 1 without one, the plain draw;
432
+ * in {@link setDraw}, absent keys keep their current value.
433
+ * `instanceCount: 0` draws nothing - a cheap off switch. With an instance
434
+ * buffer bound, `instanceCount` is bounds-checked against it like every
435
+ * fetch (instances 0..N-1 each read one record). Two GL facts worth
436
+ * knowing: `gl_VertexID` includes `firstVertex` (as in WebGPU), and
437
+ * `gl_InstanceID` always counts from 0 - ES 3.0 has no base instance.
417
438
  */
418
439
  export type DrawRange = { firstVertex?: number; vertexCount?: number; instanceCount?: number }
440
+ /**
441
+ * The element type of an index buffer: "uint16" halves index bandwidth and
442
+ * addresses meshes up to 65535 vertices, "uint32" covers the rest -
443
+ * WebGPU's two formats exactly.
444
+ */
445
+ export type IndexFormat = "uint16" | "uint32"
446
+ /**
447
+ * An entry's index binding: any {@link createBuffer} buffer plus its
448
+ * element type (the buffer is typeless bytes, so the format must be
449
+ * declared - as WebGPU does at setIndexBuffer). One buffer kind serves
450
+ * both roles; there is no separate index-buffer create. With a binding
451
+ * present the draw is `glDrawElements`: vertices are fetched through the
452
+ * index VALUES, so shared vertices are stored (and shaded) once, and the
453
+ * range speaks {@link IndexRange} instead of {@link DrawRange}. The
454
+ * index-buffer fetch is bounds-checked like every range; the index values
455
+ * themselves are not checked against the vertex buffer (that would mean
456
+ * reading them back) - an out-of-range index is the same undefined fetch
457
+ * raw GL gives you.
458
+ */
459
+ export type IndexBinding = { indexBuffer: BufferId; indexFormat: IndexFormat }
460
+ /**
461
+ * The index-counted spelling of a draw range, for indexed entries
462
+ * (WebGPU's drawIndexed vocabulary): `firstIndex` + `indexCount` pick the
463
+ * range of the INDEX buffer, `instanceCount` as in {@link DrawRange}.
464
+ * Same defaults and merge rules; the vertex-named keys throw on an
465
+ * indexed entry (and these throw on a plain one), so a range never
466
+ * silently counts the wrong thing. `gl_VertexID` reads the index value;
467
+ * there is no base vertex (ES 3.0, like ES 3.0's missing base instance).
468
+ */
469
+ export type IndexRange = { firstIndex?: number; indexCount?: number; instanceCount?: number }
419
470
 
420
471
  /**
421
472
  * Compile a GLSL ES vertex+fragment pipeline into an offscreen texture of
@@ -426,11 +477,14 @@ declare module "flux:gpu" {
426
477
  * row of the target and +1 the bottom, so camera-up geometry must negate y
427
478
  * (or fold the flip into its projection) to display up. `attributes`
428
479
  * describes one interleaved vertex in `buffer` (a {@link createBuffer} id);
429
- * omit both for attributeless rendering via gl_VertexID. The
430
- * {@link DrawRange} keys pick what is drawn: `vertexCount` defaults to the
431
- * rest of the buffer from `firstVertex` on, `instanceCount` draws the
432
- * range as N instances told apart by `gl_InstanceID`; a vertex fetch past
433
- * the buffer's end throws. With
480
+ * omit both for attributeless rendering via gl_VertexID.
481
+ * `instanceAttributes` describes one per-instance record in
482
+ * `instanceBuffer` (see {@link createRenderPipeline}; declare both or
483
+ * neither). The {@link DrawRange} keys pick what is drawn: `vertexCount`
484
+ * defaults to the rest of the buffer from `firstVertex` on,
485
+ * `instanceCount` draws the range as N instances told apart by
486
+ * `gl_InstanceID` and defaults to one per instance-buffer record; a fetch
487
+ * past either buffer's end throws. With
434
488
  * `depth: true` the pipeline gets a private depth buffer, cleared and tested
435
489
  * on every render; `depthWrite: false` (requires `depth: true`) keeps the
436
490
  * test but stops the draw from writing depth. `blend` sets the draw's own blending (see
@@ -442,7 +496,7 @@ declare module "flux:gpu" {
442
496
  * {@link renderTarget}, and `loadOp: "load"` (manual-only) keeps the
443
497
  * previous contents under each draw.
444
498
  * Returns a texture id: display it with `<texture src>`, drive uniforms via
445
- * the `params` prop or {@link setShaderParams}, destroy with
499
+ * the `params` prop or {@link setTargetParams}, destroy with
446
500
  * {@link destroyTexture}.
447
501
  */
448
502
  export function createPipelineTexture(
@@ -455,14 +509,18 @@ declare module "flux:gpu" {
455
509
  textures?: Record<string, TextureId>
456
510
  attributes?: VertexAttribute[]
457
511
  buffer?: BufferId
512
+ /** See {@link createRenderPipeline}'s `instanceAttributes`. */
513
+ instanceAttributes?: VertexAttribute[]
514
+ instanceBuffer?: BufferId
458
515
  topology?: Topology
459
516
  depth?: boolean
460
517
  depthWrite?: boolean
461
518
  blend?: BlendMode
519
+ cull?: CullMode
462
520
  clearColor?: [number, number, number, number]
463
521
  render?: "auto" | "manual"
464
522
  loadOp?: "clear" | "load"
465
- } & DrawRange &
523
+ } & (DrawRange | (IndexBinding & IndexRange)) &
466
524
  SamplerOptions &
467
525
  LabelOption,
468
526
  ): TextureId
@@ -496,12 +554,198 @@ declare module "flux:gpu" {
496
554
  * buffer size) - the out-of-bounds draw GL itself never checks; a target
497
555
  * without vertex fetch (attributeless) accepts any non-negative range.
498
556
  * (On a manual target nothing renders here; the range applies at its next
499
- * {@link renderTarget}.)
500
- */
501
- export function setDraw(id: TextureId, draw: DrawRange): void
557
+ * {@link renderTarget}.) An indexed target takes the {@link IndexRange}
558
+ * spelling instead, bounds-checked against its index buffer; the pair
559
+ * that does not match the target's mode throws.
560
+ */
561
+ export function setDraw(id: TextureId, draw: DrawRange | IndexRange): void
562
+ /**
563
+ * Create a draw target: a render target whose contents are an ordered,
564
+ * mutable LIST of draws - one render clears once, then executes every
565
+ * entry in list order into the same storage. The multi-pass shape of every
566
+ * 3D API (N meshes, N pipelines, one shared depth buffer), retained: where
567
+ * WebGPU re-encodes a render pass every frame, this target holds the pass
568
+ * as state and re-renders on demand. Entries are added and removed at any
569
+ * time ({@link addDraw}/{@link removeDraw}) and updated per entry
570
+ * ({@link setDrawParams}, {@link setDrawTextures}, {@link setDrawRange}).
571
+ *
572
+ * `depth: true` gives the target its own depth storage, shared by every
573
+ * entry and cleared once per render - this is what makes cross-entry
574
+ * occlusion work. It is the storage half of the depth story; whether an
575
+ * entry tests/writes depth is its pipeline's `depth`/`depthWrite` state,
576
+ * and adding a depth-testing pipeline to a target without storage throws.
577
+ *
578
+ * `params` seeds the target's SHARED params - the target-level values
579
+ * every entry reads, the same live channel {@link setTargetParams} drives
580
+ * later (positional like every create's params; see there for the
581
+ * precedence and validation contract). `opts.textures` seeds the shared
582
+ * sampler bindings the same way, the channel {@link setTargetTextures}
583
+ * drives (in opts like every create's textures). At creation there are no
584
+ * entries to validate against, so names are accepted as-is and simply
585
+ * apply to whichever later entries' programs declare them.
586
+ *
587
+ * The render contract is unchanged: the list is input data like params, so
588
+ * "render twice = render once" still holds and the default `render:
589
+ * "auto"` target re-renders exactly when its entries or their inputs
590
+ * change - a static scene costs zero passes, however many entries it
591
+ * holds, and one render is ONE pass however many entries it draws.
592
+ * `render: "manual"` and `loadOp: "load"` compose exactly as on
593
+ * {@link createShaderTarget}. With no entries a render is the clear alone.
594
+ * Returns a texture id (display, resize, destroy like any target; entries
595
+ * die with it).
596
+ */
597
+ export function createDrawTarget(
598
+ width: number,
599
+ height: number,
600
+ params?: ShaderParams | null,
601
+ opts?: {
602
+ depth?: boolean
603
+ textures?: Record<string, TextureId>
604
+ clearColor?: [number, number, number, number]
605
+ render?: "auto" | "manual"
606
+ loadOp?: "clear" | "load"
607
+ } & SamplerOptions &
608
+ LabelOption,
609
+ ): TextureId
610
+ /**
611
+ * Append a draw entry to a draw target: `pipeline` draws `opts.buffer`
612
+ * (required when the pipeline declares attributes) with its own `params`
613
+ * and `textures`, last in list order - the same per-entry shape
614
+ * {@link createShaderTarget} takes, addressed to one entry of the list.
615
+ * Returns the entry's {@link DrawId}, the handle every per-entry update
616
+ * takes. Everything validates here at the call site: unknown ids, depth
617
+ * compatibility (see {@link createDrawTarget}), uniform names and arities,
618
+ * the vertex-fetch bound, per-entry texture-unit count, and sampling
619
+ * cycles. List order is draw order - later entries land over earlier ones
620
+ * where depth does not decide - so painter-style layering is append order,
621
+ * and per-entry `params` is where per-object state (a model matrix) lives.
622
+ * `before` inserts the entry immediately before an existing one instead
623
+ * of appending (it must name a live entry); for wholesale reordering use
624
+ * {@link setDrawOrder}. An {@link IndexBinding} makes the entry draw
625
+ * indexed - real meshes share most vertices, and indexing stores and
626
+ * shades each one once - with the range in {@link IndexRange} spelling.
627
+ * `instanceBuffer` supplies the per-instance records the pipeline's
628
+ * `instanceAttributes` describe (required exactly when it declares any);
629
+ * `instanceCount` then defaults to one instance per record.
630
+ *
631
+ * Seed every uniform the entry's program declares - here, via the
632
+ * target's shared params, or with a later write. GL uniform state lives
633
+ * on the program object, so a declared name nothing writes holds
634
+ * whatever the last draw through that program applied, from any entry
635
+ * or target sharing it - not zero (only a freshly linked program reads
636
+ * the link-time zero). Coverage is deliberately not validated here:
637
+ * adding entries first and setting shared values after is legal.
638
+ */
639
+ export function addDraw(
640
+ target: TextureId,
641
+ pipeline: RenderPipelineId,
642
+ params?: ShaderParams | null,
643
+ opts?: {
644
+ textures?: Record<string, TextureId>
645
+ buffer?: BufferId
646
+ instanceBuffer?: BufferId
647
+ before?: DrawId
648
+ } & (DrawRange | (IndexBinding & IndexRange)),
649
+ ): DrawId
650
+ /**
651
+ * Remove a draw entry from a draw target. Remaining entries keep their
652
+ * order and ids; the removed id errors from then on (ids are never
653
+ * reused). The entry's pipeline and buffer are yours and unaffected.
654
+ */
655
+ export function removeDraw(target: TextureId, draw: DrawId): void
656
+ /**
657
+ * Update one draw entry's uniforms by name: {@link setTargetParams}
658
+ * addressed to a single entry, same merge and validation contract. The
659
+ * per-object hot path - a moved mesh is one setDrawParams with its new
660
+ * model matrix.
661
+ */
662
+ export function setDrawParams(target: TextureId, draw: DrawId, params: ShaderParams): void
663
+ /**
664
+ * Update a target's target-level uniforms by name, on any target kind,
665
+ * with the usual merge-by-name (see {@link ShaderParams} for value shapes;
666
+ * a bad name or a mismatched length throws here, on the line that wrote
667
+ * it). On a single-program target (a fragment texture or a pipeline
668
+ * target) the target level IS its one pass: every name validates against
669
+ * that program and the target re-renders. On a manual target nothing
670
+ * renders here; the values apply at its next {@link renderTarget}.
671
+ *
672
+ * On a draw target these are the SHARED params: values every entry reads
673
+ * - a camera's view-projection above all - written once per target
674
+ * instead of once per entry. Shared values apply at render before each
675
+ * entry's own params, so an entry naming the same uniform overrides the
676
+ * shared value (specific beats general), and they are target state: entry
677
+ * add/remove/rebuild cannot lose them. A draw target legitimately mixes
678
+ * material classes, so coverage may be partial: a name only some entries'
679
+ * programs declare is applied where declared and skipped elsewhere.
680
+ * Validation follows: each name must be an active settable uniform of at
681
+ * least ONE current entry's program (with the matching arity everywhere it
682
+ * is declared) - a name no entry declares throws. With no entries yet,
683
+ * names are accepted as-is; an entry added later whose program lacks an
684
+ * already-set name is never a retroactive error, the value just skips it.
685
+ */
686
+ export function setTargetParams(target: TextureId, params: ShaderParams): void
687
+ /**
688
+ * Rebind a target's target-level sampler2D inputs by uniform name, on any
689
+ * target kind - {@link setTargetParams}'s sampler analog. Bindings not
690
+ * named keep their current source, so a single input can be retargeted
691
+ * (post-process source swap, ping-pong between two data textures) without
692
+ * recompiling anything. Bound sources are live dependencies: the target
693
+ * re-renders when one changes. Every path throws if the target or a
694
+ * source texture id is unknown, if a binding names the target's own
695
+ * texture (same-pass feedback), or if it would close a sampling cycle
696
+ * among runtime-rendered targets; a cycle through a `render: "manual"`
697
+ * target is legal - the runtime never renders one, so the loop only steps
698
+ * when the app calls {@link renderTarget}. On a single-program target each
699
+ * name must be an active `sampler2D` of its one program.
700
+ *
701
+ * On a draw target these are the SHARED bindings: sources every entry
702
+ * reads - an environment map, a shadow map, a LUT - bound once per
703
+ * target, with the shared-params rules throughout: an entry's own binding
704
+ * for the same name wins; a name only some entries' programs declare
705
+ * binds where declared and is skipped elsewhere; shared bindings are
706
+ * target state that entry add/remove/rebuild cannot lose. Each name must
707
+ * be an active sampler2D of at least ONE current entry's program (with no
708
+ * entries yet names are accepted as-is, and a later entry never
709
+ * retroactively errors), and each entry's effective inputs (its own plus
710
+ * the applicable shared ones) must fit the device's texture units.
711
+ */
712
+ export function setTargetTextures(target: TextureId, textures: Record<string, TextureId>): void
713
+ /**
714
+ * Resize a render target of any kind in place and re-render it: the id,
715
+ * compiled programs, last-applied params, sampler bindings, and draw
716
+ * state all carry over; only the output size changes. The setDraw analog
717
+ * for output size. (Pixel textures resize with {@link resizeTexture},
718
+ * which carries seed pixels instead.)
719
+ */
720
+ export function setTargetSize(id: TextureId, width: number, height: number): void
721
+ /**
722
+ * Rebind one draw entry's sampler2D inputs by uniform name:
723
+ * {@link setTargetTextures} addressed to a single entry, same merge,
724
+ * validation, and cycle rules. Entries bind independently - two entries
725
+ * may bind the same uniform name to different sources.
726
+ */
727
+ export function setDrawTextures(target: TextureId, draw: DrawId, textures: Record<string, TextureId>): void
728
+ /**
729
+ * Update one draw entry's draw range: {@link setDraw} addressed to a
730
+ * single entry, same partial merge, bounds validation, and vocabulary
731
+ * rule (an indexed entry speaks {@link IndexRange}).
732
+ */
733
+ export function setDrawRange(target: TextureId, draw: DrawId, update: DrawRange | IndexRange): void
734
+ /**
735
+ * Reorder a draw target's list. `order` must name every current entry
736
+ * exactly once - a full permutation of the live {@link DrawId}s; a
737
+ * missing, duplicate, or unknown id throws, naming the problem. List
738
+ * order is draw order, which makes this the sorting verb: sort opaque
739
+ * entries front-to-back (early depth rejection) and transparent ones
740
+ * back-to-front, and re-issue the order when the camera moves. Entry
741
+ * state (params, textures, ranges) rides along untouched; ids are
742
+ * unaffected. Like every draw-list write it re-renders an auto target
743
+ * once at the next flush, and folds silently on a manual one.
744
+ */
745
+ export function setDrawOrder(target: TextureId, order: DrawId[]): void
502
746
  /**
503
747
  * Render a `render: "manual"` target once, now. Renders land in call order
504
- * relative to every other GPU call: a `setShaderParams`/`writeBuffer`
748
+ * relative to every other GPU call: a `setTargetParams`/`writeBuffer`
505
749
  * issued before is visible to the pass, a {@link readTexture} issued after
506
750
  * observes it, and two renders run the pass twice in order. Inputs are
507
751
  * fresh: pending runtime-driven renders of sampled targets resolve first.
package/index.d.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  /// <reference path="./modules/sqlite.d.ts" />
6
6
  /// <reference path="./modules/subprocess.d.ts" />
7
7
  /// <reference path="./modules/svg.d.ts" />
8
+ /// <reference path="./modules/image.d.ts" />
8
9
  /// <reference path="./modules/p2p.d.ts" />
9
10
  /// <reference path="./modules/net.d.ts" />
10
11
  /// <reference path="./modules/mdns.d.ts" />
@@ -0,0 +1,24 @@
1
+ declare module "flux:image" {
2
+ /** Decoded pixels: tightly-packed RGBA8 plus the pixel dimensions. */
3
+ export type DecodedImage = {
4
+ data: Uint8Array
5
+ width: number
6
+ height: number
7
+ }
8
+
9
+ /**
10
+ * Decodes encoded image bytes (png, jpeg, webp, gif, bmp, ico) into raw
11
+ * RGBA8 pixels plus the decoded dimensions. Synchronous, pure CPU. Throws
12
+ * when the bytes are not a decodable image.
13
+ */
14
+ export function decodeImage(bytes: Uint8Array): DecodedImage
15
+
16
+ /**
17
+ * Encodes raw RGBA8 pixels into an image file, the reverse of `decodeImage`
18
+ * (`encodeImage(decodeImage(bytes))` round-trips). `format` defaults to
19
+ * `"png"` (lossless, keeps alpha); `"jpeg"` drops the alpha channel and
20
+ * takes `quality` in 0..1 (default 0.9, ignored for png). Throws when
21
+ * `data.length` does not match `width * height * 4`.
22
+ */
23
+ export function encodeImage(img: DecodedImage, options?: { format?: "png" | "jpeg"; quality?: number }): Uint8Array
24
+ }
@@ -1,7 +1,9 @@
1
1
  declare module "flux:process" {
2
2
  /**
3
- * The program's command-line arguments. `argv[0]` is the script path;
4
- * `argv[1]` onward are the user-supplied arguments.
3
+ * The arguments the app was started with; empty when there are none.
4
+ * App arguments only: no executable path, no script path (deliberately
5
+ * simpler than Node/Bun's two leading entries), so `argv[0]` is the first
6
+ * argument.
5
7
  */
6
8
  export let argv: string[]
7
9
  /** The host OS: "darwin", "win32", "linux", "android", ... */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidrt/flux-types",
3
- "version": "0.0.44",
3
+ "version": "0.0.46",
4
4
  "license": "MIT",
5
5
  "author": "Antoine van Wel",
6
6
  "types": "index.d.ts",