@solidrt/flux-types 0.0.44 → 0.0.45

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.
Files changed (2) hide show
  1. package/gui/gpu.d.ts +231 -34
  2. package/package.json +1 -1
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
@@ -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,
@@ -305,13 +332,15 @@ declare module "flux:gpu" {
305
332
  * {@link setShaderSize}, 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
@@ -398,24 +428,75 @@ declare module "flux:gpu" {
398
428
  */
399
429
  export type BlendMode = "none" | "add"
400
430
  /**
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.
431
+ * Face culling for a pipeline's draws. "none" (default) rasters both faces
432
+ * - the two-sided fallback open surfaces need. "back" discards faces wound
433
+ * away from the camera, halving a closed mesh's fragment work; "front"
434
+ * discards the other set (shadow and inside-out tricks). The winding rule
435
+ * is WebGPU's, fixed: counter-clockwise AS DISPLAYED (screen coordinates,
436
+ * y down) = front. Measured after every flip, so it just works: a mesh
437
+ * exported counter-clockwise-front for a y-up world, drawn through a
438
+ * standard right-handed camera (looking down -z) with the usual y
439
+ * negation for display, culls correctly with "back". If "back" shows you
440
+ * the mesh's inside anyway, the winding reaching the screen is mirrored -
441
+ * either the exporter winds clockwise, or the hand-rolled projection is
442
+ * left-handed (the classic: camera looking toward +z without mirroring
443
+ * x). Fix the rig, flip the exporter, or use "front".
444
+ */
445
+ export type CullMode = "none" | "back" | "front"
446
+ /**
447
+ * One float attribute of an interleaved record - a vertex of `attributes`
448
+ * or an instance record of `instanceAttributes`. The list's order defines
449
+ * the byte layout; locations are resolved by name against the vertex
450
+ * shader's `in` declarations.
404
451
  */
405
452
  export type VertexAttribute = { name: string; format: "f32" | "vec2" | "vec3" | "vec4" }
406
453
  /**
407
454
  * A pipeline target's draw as data, WebGPU-style: `firstVertex` +
408
455
  * `vertexCount` pick the vertex range `[firstVertex, firstVertex +
409
456
  * 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.
457
+ * instances (`glDrawArraysInstanced`) told apart by `gl_InstanceID` (and
458
+ * by their `instanceAttributes` records, when the pipeline declares any).
459
+ * All keys optional: at create, `firstVertex` defaults to 0, `vertexCount`
460
+ * to the rest of the buffer, and `instanceCount` to one instance per
461
+ * record of the entry's `instanceBuffer` - 1 without one, the plain draw;
462
+ * in {@link setDraw}, absent keys keep their current value.
463
+ * `instanceCount: 0` draws nothing - a cheap off switch. With an instance
464
+ * buffer bound, `instanceCount` is bounds-checked against it like every
465
+ * fetch (instances 0..N-1 each read one record). Two GL facts worth
466
+ * knowing: `gl_VertexID` includes `firstVertex` (as in WebGPU), and
467
+ * `gl_InstanceID` always counts from 0 - ES 3.0 has no base instance.
417
468
  */
418
469
  export type DrawRange = { firstVertex?: number; vertexCount?: number; instanceCount?: number }
470
+ /**
471
+ * The element type of an index buffer: "uint16" halves index bandwidth and
472
+ * addresses meshes up to 65535 vertices, "uint32" covers the rest -
473
+ * WebGPU's two formats exactly.
474
+ */
475
+ export type IndexFormat = "uint16" | "uint32"
476
+ /**
477
+ * An entry's index binding: any {@link createBuffer} buffer plus its
478
+ * element type (the buffer is typeless bytes, so the format must be
479
+ * declared - as WebGPU does at setIndexBuffer). One buffer kind serves
480
+ * both roles; there is no separate index-buffer create. With a binding
481
+ * present the draw is `glDrawElements`: vertices are fetched through the
482
+ * index VALUES, so shared vertices are stored (and shaded) once, and the
483
+ * range speaks {@link IndexRange} instead of {@link DrawRange}. The
484
+ * index-buffer fetch is bounds-checked like every range; the index values
485
+ * themselves are not checked against the vertex buffer (that would mean
486
+ * reading them back) - an out-of-range index is the same undefined fetch
487
+ * raw GL gives you.
488
+ */
489
+ export type IndexBinding = { indexBuffer: BufferId; indexFormat: IndexFormat }
490
+ /**
491
+ * The index-counted spelling of a draw range, for indexed entries
492
+ * (WebGPU's drawIndexed vocabulary): `firstIndex` + `indexCount` pick the
493
+ * range of the INDEX buffer, `instanceCount` as in {@link DrawRange}.
494
+ * Same defaults and merge rules; the vertex-named keys throw on an
495
+ * indexed entry (and these throw on a plain one), so a range never
496
+ * silently counts the wrong thing. `gl_VertexID` reads the index value;
497
+ * there is no base vertex (ES 3.0, like ES 3.0's missing base instance).
498
+ */
499
+ export type IndexRange = { firstIndex?: number; indexCount?: number; instanceCount?: number }
419
500
 
420
501
  /**
421
502
  * Compile a GLSL ES vertex+fragment pipeline into an offscreen texture of
@@ -426,11 +507,14 @@ declare module "flux:gpu" {
426
507
  * row of the target and +1 the bottom, so camera-up geometry must negate y
427
508
  * (or fold the flip into its projection) to display up. `attributes`
428
509
  * 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
510
+ * omit both for attributeless rendering via gl_VertexID.
511
+ * `instanceAttributes` describes one per-instance record in
512
+ * `instanceBuffer` (see {@link createRenderPipeline}; declare both or
513
+ * neither). The {@link DrawRange} keys pick what is drawn: `vertexCount`
514
+ * defaults to the rest of the buffer from `firstVertex` on,
515
+ * `instanceCount` draws the range as N instances told apart by
516
+ * `gl_InstanceID` and defaults to one per instance-buffer record; a fetch
517
+ * past either buffer's end throws. With
434
518
  * `depth: true` the pipeline gets a private depth buffer, cleared and tested
435
519
  * on every render; `depthWrite: false` (requires `depth: true`) keeps the
436
520
  * test but stops the draw from writing depth. `blend` sets the draw's own blending (see
@@ -455,14 +539,18 @@ declare module "flux:gpu" {
455
539
  textures?: Record<string, TextureId>
456
540
  attributes?: VertexAttribute[]
457
541
  buffer?: BufferId
542
+ /** See {@link createRenderPipeline}'s `instanceAttributes`. */
543
+ instanceAttributes?: VertexAttribute[]
544
+ instanceBuffer?: BufferId
458
545
  topology?: Topology
459
546
  depth?: boolean
460
547
  depthWrite?: boolean
461
548
  blend?: BlendMode
549
+ cull?: CullMode
462
550
  clearColor?: [number, number, number, number]
463
551
  render?: "auto" | "manual"
464
552
  loadOp?: "clear" | "load"
465
- } & DrawRange &
553
+ } & (DrawRange | (IndexBinding & IndexRange)) &
466
554
  SamplerOptions &
467
555
  LabelOption,
468
556
  ): TextureId
@@ -496,9 +584,118 @@ declare module "flux:gpu" {
496
584
  * buffer size) - the out-of-bounds draw GL itself never checks; a target
497
585
  * without vertex fetch (attributeless) accepts any non-negative range.
498
586
  * (On a manual target nothing renders here; the range applies at its next
499
- * {@link renderTarget}.)
587
+ * {@link renderTarget}.) An indexed target takes the {@link IndexRange}
588
+ * spelling instead, bounds-checked against its index buffer; the pair
589
+ * that does not match the target's mode throws.
590
+ */
591
+ export function setDraw(id: TextureId, draw: DrawRange | IndexRange): void
592
+ /**
593
+ * Create a draw target: a render target whose contents are an ordered,
594
+ * mutable LIST of draws - one render clears once, then executes every
595
+ * entry in list order into the same storage. The multi-pass shape of every
596
+ * 3D API (N meshes, N pipelines, one shared depth buffer), retained: where
597
+ * WebGPU re-encodes a render pass every frame, this target holds the pass
598
+ * as state and re-renders on demand. Entries are added and removed at any
599
+ * time ({@link addDraw}/{@link removeDraw}) and updated per entry
600
+ * ({@link setDrawParams}, {@link setDrawTextures}, {@link setDrawRange}).
601
+ *
602
+ * `depth: true` gives the target its own depth storage, shared by every
603
+ * entry and cleared once per render - this is what makes cross-entry
604
+ * occlusion work. It is the storage half of the depth story; whether an
605
+ * entry tests/writes depth is its pipeline's `depth`/`depthWrite` state,
606
+ * and adding a depth-testing pipeline to a target without storage throws.
607
+ *
608
+ * The render contract is unchanged: the list is input data like params, so
609
+ * "render twice = render once" still holds and the default `render:
610
+ * "auto"` target re-renders exactly when its entries or their inputs
611
+ * change - a static scene costs zero passes, however many entries it
612
+ * holds, and one render is ONE pass however many entries it draws.
613
+ * `render: "manual"` and `loadOp: "load"` compose exactly as on
614
+ * {@link createShaderTarget}. With no entries a render is the clear alone.
615
+ * Returns a texture id (display, resize, destroy like any target; entries
616
+ * die with it).
617
+ */
618
+ export function createDrawTarget(
619
+ width: number,
620
+ height: number,
621
+ opts?: {
622
+ depth?: boolean
623
+ clearColor?: [number, number, number, number]
624
+ render?: "auto" | "manual"
625
+ loadOp?: "clear" | "load"
626
+ } & SamplerOptions &
627
+ LabelOption,
628
+ ): TextureId
629
+ /**
630
+ * Append a draw entry to a draw target: `pipeline` draws `opts.buffer`
631
+ * (required when the pipeline declares attributes) with its own `params`
632
+ * and `textures`, last in list order - the same per-entry shape
633
+ * {@link createShaderTarget} takes, addressed to one entry of the list.
634
+ * Returns the entry's {@link DrawId}, the handle every per-entry update
635
+ * takes. Everything validates here at the call site: unknown ids, depth
636
+ * compatibility (see {@link createDrawTarget}), uniform names and arities,
637
+ * the vertex-fetch bound, per-entry texture-unit count, and sampling
638
+ * cycles. List order is draw order - later entries land over earlier ones
639
+ * where depth does not decide - so painter-style layering is append order,
640
+ * and per-entry `params` is where per-object state (a model matrix) lives.
641
+ * `before` inserts the entry immediately before an existing one instead
642
+ * of appending (it must name a live entry); for wholesale reordering use
643
+ * {@link setDrawOrder}. An {@link IndexBinding} makes the entry draw
644
+ * indexed - real meshes share most vertices, and indexing stores and
645
+ * shades each one once - with the range in {@link IndexRange} spelling.
646
+ * `instanceBuffer` supplies the per-instance records the pipeline's
647
+ * `instanceAttributes` describe (required exactly when it declares any);
648
+ * `instanceCount` then defaults to one instance per record.
649
+ */
650
+ export function addDraw(
651
+ target: TextureId,
652
+ pipeline: RenderPipelineId,
653
+ params?: ShaderParams | null,
654
+ opts?: {
655
+ textures?: Record<string, TextureId>
656
+ buffer?: BufferId
657
+ instanceBuffer?: BufferId
658
+ before?: DrawId
659
+ } & (DrawRange | (IndexBinding & IndexRange)),
660
+ ): DrawId
661
+ /**
662
+ * Remove a draw entry from a draw target. Remaining entries keep their
663
+ * order and ids; the removed id errors from then on (ids are never
664
+ * reused). The entry's pipeline and buffer are yours and unaffected.
665
+ */
666
+ export function removeDraw(target: TextureId, draw: DrawId): void
667
+ /**
668
+ * Update one draw entry's uniforms by name: {@link setShaderParams}
669
+ * addressed to a single entry, same merge and validation contract. The
670
+ * per-object hot path - a moved mesh is one setDrawParams with its new
671
+ * model matrix.
672
+ */
673
+ export function setDrawParams(target: TextureId, draw: DrawId, params: ShaderParams): void
674
+ /**
675
+ * Rebind one draw entry's sampler2D inputs by uniform name:
676
+ * {@link setShaderTextures} addressed to a single entry, same merge,
677
+ * validation, and cycle rules. Entries bind independently - two entries
678
+ * may bind the same uniform name to different sources.
679
+ */
680
+ export function setDrawTextures(target: TextureId, draw: DrawId, textures: Record<string, TextureId>): void
681
+ /**
682
+ * Update one draw entry's draw range: {@link setDraw} addressed to a
683
+ * single entry, same partial merge, bounds validation, and vocabulary
684
+ * rule (an indexed entry speaks {@link IndexRange}).
685
+ */
686
+ export function setDrawRange(target: TextureId, draw: DrawId, update: DrawRange | IndexRange): void
687
+ /**
688
+ * Reorder a draw target's list. `order` must name every current entry
689
+ * exactly once - a full permutation of the live {@link DrawId}s; a
690
+ * missing, duplicate, or unknown id throws, naming the problem. List
691
+ * order is draw order, which makes this the sorting verb: sort opaque
692
+ * entries front-to-back (early depth rejection) and transparent ones
693
+ * back-to-front, and re-issue the order when the camera moves. Entry
694
+ * state (params, textures, ranges) rides along untouched; ids are
695
+ * unaffected. Like every draw-list write it re-renders an auto target
696
+ * once at the next flush, and folds silently on a manual one.
500
697
  */
501
- export function setDraw(id: TextureId, draw: DrawRange): void
698
+ export function setDrawOrder(target: TextureId, order: DrawId[]): void
502
699
  /**
503
700
  * Render a `render: "manual"` target once, now. Renders land in call order
504
701
  * relative to every other GPU call: a `setShaderParams`/`writeBuffer`
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.45",
4
4
  "license": "MIT",
5
5
  "author": "Antoine van Wel",
6
6
  "types": "index.d.ts",