@aardworx/wombat.rendering 0.6.0 → 0.7.0
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/dist/core/bufferView.d.ts +73 -12
- package/dist/core/bufferView.d.ts.map +1 -1
- package/dist/core/bufferView.js +225 -9
- package/dist/core/bufferView.js.map +1 -1
- package/dist/core/elementType.d.ts +16 -0
- package/dist/core/elementType.d.ts.map +1 -0
- package/dist/core/elementType.js +80 -0
- package/dist/core/elementType.js.map +1 -0
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/renderObject.d.ts +3 -3
- package/dist/core/renderObject.d.ts.map +1 -1
- package/dist/resources/preparedRenderObject.d.ts +2 -2
- package/dist/resources/preparedRenderObject.d.ts.map +1 -1
- package/dist/resources/preparedRenderObject.js +22 -72
- package/dist/resources/preparedRenderObject.js.map +1 -1
- package/package.json +1 -1
- package/src/core/bufferView.ts +271 -20
- package/src/core/elementType.ts +84 -0
- package/src/core/index.ts +5 -1
- package/src/core/renderObject.ts +3 -3
- package/src/resources/preparedRenderObject.ts +42 -78
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// ElementType — the data type of a single element in a BufferView.
|
|
2
|
+
//
|
|
3
|
+
// Mirrors Aardvark.Rendering's `BufferView.ElementType : System.Type`
|
|
4
|
+
// pattern: structural information about the element kept separate
|
|
5
|
+
// from the per-frame buffer content. The runtime maps `ElementType`
|
|
6
|
+
// to a `GPUVertexFormat`/`GPUIndexFormat` and a byte size at
|
|
7
|
+
// pipeline-layout time.
|
|
8
|
+
|
|
9
|
+
export type ElementType =
|
|
10
|
+
// scalar
|
|
11
|
+
| "f32" | "u32" | "i32"
|
|
12
|
+
| "u16" | "i16"
|
|
13
|
+
| "u8" | "i8"
|
|
14
|
+
// vec — float
|
|
15
|
+
| "v2f" | "v3f" | "v4f"
|
|
16
|
+
// vec — int
|
|
17
|
+
| "v2i" | "v3i" | "v4i"
|
|
18
|
+
// vec — uint
|
|
19
|
+
| "v2u" | "v3u" | "v4u"
|
|
20
|
+
// matrix (float, square)
|
|
21
|
+
| "m22f" | "m33f" | "m44f"
|
|
22
|
+
// packed colour
|
|
23
|
+
| "c4b" | "c3b";
|
|
24
|
+
|
|
25
|
+
export const ElementType = {
|
|
26
|
+
/** Size of one element in bytes. */
|
|
27
|
+
byteSize(t: ElementType): number {
|
|
28
|
+
switch (t) {
|
|
29
|
+
case "f32": case "u32": case "i32": return 4;
|
|
30
|
+
case "u16": case "i16": return 2;
|
|
31
|
+
case "u8": case "i8": return 1;
|
|
32
|
+
case "v2f": case "v2i": case "v2u": return 8;
|
|
33
|
+
case "v3f": case "v3i": case "v3u": return 12;
|
|
34
|
+
case "v4f": case "v4i": case "v4u": return 16;
|
|
35
|
+
case "m22f": return 16;
|
|
36
|
+
case "m33f": return 36;
|
|
37
|
+
case "m44f": return 64;
|
|
38
|
+
case "c4b": return 4;
|
|
39
|
+
case "c3b": return 3;
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* GPUVertexFormat for use as a vertex / instance attribute.
|
|
45
|
+
* `normalized=true` only affects integer types — picks the `unorm`
|
|
46
|
+
* / `snorm` form. Matrix types are not directly supported as
|
|
47
|
+
* vertex formats; the auto-instancing pass splits them into
|
|
48
|
+
* column attributes upstream.
|
|
49
|
+
*/
|
|
50
|
+
toVertexFormat(t: ElementType, normalized = false): GPUVertexFormat {
|
|
51
|
+
switch (t) {
|
|
52
|
+
case "f32": return "float32";
|
|
53
|
+
case "u32": return "uint32";
|
|
54
|
+
case "i32": return "sint32";
|
|
55
|
+
case "v2f": return "float32x2";
|
|
56
|
+
case "v3f": return "float32x3";
|
|
57
|
+
case "v4f": return "float32x4";
|
|
58
|
+
case "v2i": return "sint32x2";
|
|
59
|
+
case "v3i": return "sint32x3";
|
|
60
|
+
case "v4i": return "sint32x4";
|
|
61
|
+
case "v2u": return "uint32x2";
|
|
62
|
+
case "v3u": return "uint32x3";
|
|
63
|
+
case "v4u": return "uint32x4";
|
|
64
|
+
case "u16": return normalized ? "unorm16x2" as GPUVertexFormat : "uint16";
|
|
65
|
+
case "i16": return normalized ? "snorm16x2" as GPUVertexFormat : "sint16";
|
|
66
|
+
case "u8": return normalized ? "unorm8x4" as GPUVertexFormat : "uint8";
|
|
67
|
+
case "i8": return normalized ? "snorm8x4" as GPUVertexFormat : "sint8";
|
|
68
|
+
case "c4b": return normalized ? "unorm8x4" : "uint8x4";
|
|
69
|
+
case "c3b":
|
|
70
|
+
// No 3-component byte format in WebGPU; promoted to x4 with
|
|
71
|
+
// a dummy fourth byte by the upload path.
|
|
72
|
+
throw new Error(`ElementType.toVertexFormat: c3b cannot be a vertex format directly; pad to c4b`);
|
|
73
|
+
case "m22f": case "m33f": case "m44f":
|
|
74
|
+
throw new Error(`ElementType.toVertexFormat: ${t} is a matrix; split into column attributes first`);
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
/** Index format. Throws unless the type is `u16` or `u32`. */
|
|
79
|
+
toIndexFormat(t: ElementType): GPUIndexFormat {
|
|
80
|
+
if (t === "u16") return "uint16";
|
|
81
|
+
if (t === "u32") return "uint32";
|
|
82
|
+
throw new Error(`ElementType.toIndexFormat: ${t} is not a valid index type (use u16 or u32)`);
|
|
83
|
+
},
|
|
84
|
+
} as const;
|
package/src/core/index.ts
CHANGED
|
@@ -11,10 +11,14 @@ export type {
|
|
|
11
11
|
HostBufferSource,
|
|
12
12
|
} from "./buffer.js";
|
|
13
13
|
|
|
14
|
-
export
|
|
14
|
+
export {
|
|
15
15
|
BufferView,
|
|
16
16
|
} from "./bufferView.js";
|
|
17
17
|
|
|
18
|
+
export {
|
|
19
|
+
ElementType,
|
|
20
|
+
} from "./elementType.js";
|
|
21
|
+
|
|
18
22
|
export type {
|
|
19
23
|
ClearColor,
|
|
20
24
|
ClearValues,
|
package/src/core/renderObject.ts
CHANGED
|
@@ -45,9 +45,9 @@ export interface RenderObject {
|
|
|
45
45
|
* individual buffer values are reactive. The shader's required-input
|
|
46
46
|
* set is fixed at compile; the map must contain at least those names.
|
|
47
47
|
*/
|
|
48
|
-
readonly vertexAttributes: HashMap<string,
|
|
48
|
+
readonly vertexAttributes: HashMap<string, BufferView>;
|
|
49
49
|
/** name → instance buffer view; e.g. "modelMatrix", "instanceColor". */
|
|
50
|
-
readonly instanceAttributes?: HashMap<string,
|
|
50
|
+
readonly instanceAttributes?: HashMap<string, BufferView>;
|
|
51
51
|
/** name → uniform value; runtime packs into UBO based on shader layout. */
|
|
52
52
|
readonly uniforms: HashMap<string, aval<unknown>>;
|
|
53
53
|
/** name → texture source (CPU image or pre-built GPUTexture). */
|
|
@@ -58,6 +58,6 @@ export interface RenderObject {
|
|
|
58
58
|
readonly storageBuffers?: HashMap<string, aval<IBuffer>>;
|
|
59
59
|
|
|
60
60
|
/** Index buffer for indexed draws. */
|
|
61
|
-
readonly indices?:
|
|
61
|
+
readonly indices?: BufferView;
|
|
62
62
|
readonly drawCall: aval<DrawCall>;
|
|
63
63
|
}
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
16
|
AdaptiveResource,
|
|
17
|
+
BufferView,
|
|
18
|
+
ElementType,
|
|
17
19
|
type CompiledEffect,
|
|
18
20
|
type FramebufferSignature,
|
|
19
21
|
type ProgramInterface,
|
|
@@ -25,7 +27,6 @@ import {
|
|
|
25
27
|
HashMap,
|
|
26
28
|
cval,
|
|
27
29
|
} from "@aardworx/wombat.adaptive";
|
|
28
|
-
import type { BufferView } from "../core/bufferView.js";
|
|
29
30
|
import type { Type } from "@aardworx/wombat.shader/ir";
|
|
30
31
|
import { prepareAdaptiveBuffer } from "./adaptiveBuffer.js";
|
|
31
32
|
import { prepareAdaptiveTexture } from "./adaptiveTexture.js";
|
|
@@ -34,32 +35,6 @@ import { prepareUniformBuffer } from "./uniformBuffer.js";
|
|
|
34
35
|
import { compileRenderPipeline, type CompileRenderPipelineDescription } from "./renderPipeline.js";
|
|
35
36
|
import { BufferUsage, ShaderStage } from "./webgpuFlags.js";
|
|
36
37
|
|
|
37
|
-
// ---------------------------------------------------------------------------
|
|
38
|
-
// Helpers
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
|
-
|
|
41
|
-
function isIndexFormat(fmt: string): boolean {
|
|
42
|
-
return fmt === "uint16" || fmt === "uint32";
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function vertexFormatStride(fmt: GPUVertexFormat): number {
|
|
46
|
-
switch (fmt) {
|
|
47
|
-
case "float32": return 4;
|
|
48
|
-
case "float32x2": return 8;
|
|
49
|
-
case "float32x3": return 12;
|
|
50
|
-
case "float32x4": return 16;
|
|
51
|
-
case "uint32": case "sint32": return 4;
|
|
52
|
-
case "uint32x2": case "sint32x2": return 8;
|
|
53
|
-
case "uint32x3": case "sint32x3": return 12;
|
|
54
|
-
case "uint32x4": case "sint32x4": return 16;
|
|
55
|
-
case "uint16x2": case "sint16x2": case "float16x2": return 4;
|
|
56
|
-
case "uint16x4": case "sint16x4": case "float16x4": return 8;
|
|
57
|
-
case "uint8x2": case "sint8x2": case "unorm8x2": case "snorm8x2": return 2;
|
|
58
|
-
case "uint8x4": case "sint8x4": case "unorm8x4": case "snorm8x4": return 4;
|
|
59
|
-
default: throw new Error(`vertexFormatStride: unsupported format ${fmt}`);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
38
|
interface VertexBindingInfo {
|
|
64
39
|
readonly name: string;
|
|
65
40
|
readonly slot: number;
|
|
@@ -371,10 +346,10 @@ export class PreparedRenderObject {
|
|
|
371
346
|
private readonly device: GPUDevice,
|
|
372
347
|
private readonly vertexBindings: readonly VertexBindingInfo[],
|
|
373
348
|
private readonly vertexBuffers: ReadonlyMap<string, AdaptiveResource<GPUBuffer>>,
|
|
374
|
-
private readonly vertexViews: ReadonlyMap<string,
|
|
349
|
+
private readonly vertexViews: ReadonlyMap<string, BufferView>,
|
|
375
350
|
private readonly indexBuffer: AdaptiveResource<GPUBuffer> | undefined,
|
|
376
351
|
private readonly indexFormat: GPUIndexFormat | undefined,
|
|
377
|
-
private readonly indices:
|
|
352
|
+
private readonly indices: BufferView | undefined,
|
|
378
353
|
groups: readonly GroupDesc[],
|
|
379
354
|
private readonly drawCall: aval<import("../core/index.js").DrawCall>,
|
|
380
355
|
pipelineCtx: PipelineBuildContext,
|
|
@@ -479,15 +454,12 @@ export class PreparedRenderObject {
|
|
|
479
454
|
for (const vb of this.vertexBindings) {
|
|
480
455
|
const res = this.vertexBuffers.get(vb.name);
|
|
481
456
|
if (res === undefined) throw new Error(`PreparedRenderObject.record: missing vertex buffer for "${vb.name}"`);
|
|
482
|
-
const
|
|
483
|
-
const view = viewAval !== undefined ? viewAval.getValue(token) : undefined;
|
|
457
|
+
const view = this.vertexViews.get(vb.name);
|
|
484
458
|
const buf = res.getValue(token);
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
//
|
|
488
|
-
|
|
489
|
-
// buffer for any non-zero offset; let WebGPU default.
|
|
490
|
-
pass.setVertexBuffer(vb.slot, buf, view.offset);
|
|
459
|
+
const offset = view !== undefined ? BufferView.offsetOf(view) : 0;
|
|
460
|
+
if (offset > 0) {
|
|
461
|
+
// size omitted → WebGPU uses (buffer.size − offset).
|
|
462
|
+
pass.setVertexBuffer(vb.slot, buf, offset);
|
|
491
463
|
} else {
|
|
492
464
|
pass.setVertexBuffer(vb.slot, buf);
|
|
493
465
|
}
|
|
@@ -495,8 +467,8 @@ export class PreparedRenderObject {
|
|
|
495
467
|
|
|
496
468
|
if (this.indexBuffer !== undefined && this.indices !== undefined && this.indexFormat !== undefined) {
|
|
497
469
|
const buf = this.indexBuffer.getValue(token);
|
|
498
|
-
|
|
499
|
-
pass.setIndexBuffer(buf, this.indexFormat,
|
|
470
|
+
// size omitted → WebGPU uses (buffer.size − offset).
|
|
471
|
+
pass.setIndexBuffer(buf, this.indexFormat, BufferView.offsetOf(this.indices));
|
|
500
472
|
}
|
|
501
473
|
|
|
502
474
|
const dc = this.drawCall.getValue(token);
|
|
@@ -541,12 +513,16 @@ export function prepareRenderObject(
|
|
|
541
513
|
|
|
542
514
|
// Per-attribute resolution + grouping. Multiple attributes can
|
|
543
515
|
// share one underlying GPUBuffer slot when they all point at the
|
|
544
|
-
// same `IBuffer
|
|
545
|
-
// auto-instancing rewrite emits 4 vec4
|
|
546
|
-
// M44 instance trafo — without packing
|
|
547
|
-
// `maxVertexBuffers` (8) for any moderately rich
|
|
548
|
-
// scope). Each attribute's `view.offset` becomes its
|
|
516
|
+
// same `aval<IBuffer>` (by reference identity) with the same
|
|
517
|
+
// stride + stepMode (the auto-instancing rewrite emits 4 vec4
|
|
518
|
+
// column attributes per M44 instance trafo — without packing
|
|
519
|
+
// we'd hit `maxVertexBuffers` (8) for any moderately rich
|
|
520
|
+
// instancing scope). Each attribute's `view.offset` becomes its
|
|
549
521
|
// shader-location offset within the shared layout.
|
|
522
|
+
//
|
|
523
|
+
// BufferView is now structural-eager (elementType/offset/stride),
|
|
524
|
+
// so no `force()` is needed to discover layout — the attribute
|
|
525
|
+
// grouping reads the plain fields directly.
|
|
550
526
|
interface ResolvedBinding {
|
|
551
527
|
readonly name: string;
|
|
552
528
|
readonly shaderLocation: number;
|
|
@@ -554,8 +530,7 @@ export function prepareRenderObject(
|
|
|
554
530
|
readonly stride: number;
|
|
555
531
|
readonly offset: number;
|
|
556
532
|
readonly format: GPUVertexFormat;
|
|
557
|
-
readonly
|
|
558
|
-
readonly buffer: import("../core/index.js").IBuffer;
|
|
533
|
+
readonly view: BufferView;
|
|
559
534
|
}
|
|
560
535
|
const resolved: ResolvedBinding[] = [];
|
|
561
536
|
for (const vb of vertexBindings) {
|
|
@@ -567,33 +542,28 @@ export function prepareRenderObject(
|
|
|
567
542
|
const isInstance = fromVertex === undefined && fromInstance !== undefined;
|
|
568
543
|
const stepMode: GPUVertexStepMode = isInstance ? "instance" : "vertex";
|
|
569
544
|
|
|
570
|
-
const
|
|
571
|
-
const
|
|
572
|
-
const
|
|
573
|
-
const
|
|
574
|
-
? viewFormat : vb.format;
|
|
575
|
-
const stride = initialView.stride === 0
|
|
576
|
-
? 0
|
|
577
|
-
: (initialView.stride > 0 ? initialView.stride : vertexFormatStride(format));
|
|
545
|
+
const view = (fromVertex ?? fromInstance)!;
|
|
546
|
+
const format = ElementType.toVertexFormat(view.elementType, view.normalized ?? false);
|
|
547
|
+
const stride = BufferView.strideOf(view);
|
|
548
|
+
const offset = BufferView.offsetOf(view);
|
|
578
549
|
resolved.push({
|
|
579
550
|
name: vb.name,
|
|
580
551
|
shaderLocation: vb.slot,
|
|
581
|
-
stepMode, stride, offset
|
|
582
|
-
format,
|
|
552
|
+
stepMode, stride, offset,
|
|
553
|
+
format, view,
|
|
583
554
|
});
|
|
584
555
|
}
|
|
585
556
|
|
|
586
|
-
// Group resolved bindings by (IBuffer, stride, stepMode).
|
|
587
|
-
//
|
|
588
|
-
//
|
|
589
|
-
// single setVertexBuffer call at draw time.
|
|
557
|
+
// Group resolved bindings by (IBuffer-aval identity, stride, stepMode).
|
|
558
|
+
// The aval reference is the natural identity now that BufferView
|
|
559
|
+
// owns its `aval<IBuffer>` directly.
|
|
590
560
|
type GroupKey = string;
|
|
591
561
|
interface BindingGroup {
|
|
592
562
|
readonly key: GroupKey;
|
|
593
563
|
readonly slot: number; // GPU vertex-buffer slot index
|
|
594
564
|
readonly stride: number;
|
|
595
565
|
readonly stepMode: GPUVertexStepMode;
|
|
596
|
-
readonly
|
|
566
|
+
readonly view: BufferView;
|
|
597
567
|
readonly bufferRes: AdaptiveResource<GPUBuffer>;
|
|
598
568
|
readonly members: ResolvedBinding[];
|
|
599
569
|
}
|
|
@@ -607,26 +577,25 @@ export function prepareRenderObject(
|
|
|
607
577
|
const vbGroups = new Map<GroupKey, BindingGroup>();
|
|
608
578
|
let nextSlot = 0;
|
|
609
579
|
const vertexBuffers = new Map<string, AdaptiveResource<GPUBuffer>>();
|
|
610
|
-
const vertexViews = new Map<string,
|
|
580
|
+
const vertexViews = new Map<string, BufferView>();
|
|
611
581
|
for (const r of resolved) {
|
|
612
|
-
const bufId = idOf(r.buffer as unknown as object);
|
|
582
|
+
const bufId = idOf(r.view.buffer as unknown as object);
|
|
613
583
|
const key = `${bufId}|${r.stride}|${r.stepMode}`;
|
|
614
584
|
let group = vbGroups.get(key);
|
|
615
585
|
if (!group) {
|
|
616
|
-
const
|
|
617
|
-
const bufferRes = prepareAdaptiveBuffer(device, bufAval, {
|
|
586
|
+
const bufferRes = prepareAdaptiveBuffer(device, r.view.buffer, {
|
|
618
587
|
usage: BufferUsage.VERTEX,
|
|
619
588
|
...(opts.label !== undefined ? { label: `${opts.label}.${r.name}` } : {}),
|
|
620
589
|
});
|
|
621
590
|
group = {
|
|
622
591
|
key, slot: nextSlot++, stride: r.stride, stepMode: r.stepMode,
|
|
623
|
-
|
|
592
|
+
view: r.view, bufferRes, members: [],
|
|
624
593
|
};
|
|
625
594
|
vbGroups.set(key, group);
|
|
626
595
|
}
|
|
627
596
|
group.members.push(r);
|
|
628
597
|
vertexBuffers.set(r.name, group.bufferRes);
|
|
629
|
-
vertexViews.set(r.name, r.
|
|
598
|
+
vertexViews.set(r.name, r.view);
|
|
630
599
|
}
|
|
631
600
|
const vertexLayouts: GPUVertexBufferLayout[] = [];
|
|
632
601
|
for (const g of vbGroups.values()) {
|
|
@@ -663,21 +632,16 @@ export function prepareRenderObject(
|
|
|
663
632
|
}
|
|
664
633
|
vertexBindings = drawTimeBindings;
|
|
665
634
|
|
|
666
|
-
// Index buffer — `
|
|
635
|
+
// Index buffer — `elementType` (u16 / u32) determines `GPUIndexFormat`.
|
|
636
|
+
// Structural fields are eager on BufferView, so no force needed.
|
|
667
637
|
let indexBuffer: AdaptiveResource<GPUBuffer> | undefined;
|
|
668
638
|
let indexFormat: GPUIndexFormat | undefined;
|
|
669
639
|
if (obj.indices !== undefined) {
|
|
670
|
-
|
|
671
|
-
indexBuffer = prepareAdaptiveBuffer(device, bufAval, {
|
|
640
|
+
indexBuffer = prepareAdaptiveBuffer(device, obj.indices.buffer, {
|
|
672
641
|
usage: BufferUsage.INDEX,
|
|
673
642
|
...(opts.label !== undefined ? { label: `${opts.label}.indices` } : {}),
|
|
674
643
|
});
|
|
675
|
-
|
|
676
|
-
// BufferView aval is expected to settle on a stable format —
|
|
677
|
-
// changing index format would require a fresh PreparedRO.
|
|
678
|
-
const initial = obj.indices.force();
|
|
679
|
-
indexFormat = initial.indexFormat
|
|
680
|
-
?? (initial.format === "uint16" ? "uint16" : "uint32");
|
|
644
|
+
indexFormat = ElementType.toIndexFormat(obj.indices.elementType);
|
|
681
645
|
}
|
|
682
646
|
|
|
683
647
|
const slotOf = (e: { group: number; slot: number }) => e.slot;
|
|
@@ -767,8 +731,8 @@ export function prepareRenderObject(
|
|
|
767
731
|
);
|
|
768
732
|
}
|
|
769
733
|
|
|
770
|
-
function emptyAttrMap(): HashMap<string,
|
|
771
|
-
return HashMap.empty<string,
|
|
734
|
+
function emptyAttrMap(): HashMap<string, BufferView> {
|
|
735
|
+
return HashMap.empty<string, BufferView>();
|
|
772
736
|
}
|
|
773
737
|
|
|
774
738
|
/**
|