@aardworx/wombat.rendering 0.6.0 → 0.7.1
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 +74 -12
- package/dist/core/bufferView.d.ts.map +1 -1
- package/dist/core/bufferView.js +187 -9
- package/dist/core/bufferView.js.map +1 -1
- package/dist/core/elementType.d.ts +81 -0
- package/dist/core/elementType.d.ts.map +1 -0
- package/dist/core/elementType.js +151 -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 +230 -20
- package/src/core/elementType.ts +178 -0
- package/src/core/index.ts +6 -1
- package/src/core/renderObject.ts +3 -3
- package/src/resources/preparedRenderObject.ts +42 -78
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// ElementType — runtime token + structural metadata for the data
|
|
2
|
+
// type of a single element in a `BufferView`. The TypeScript-side
|
|
3
|
+
// equivalent of F#'s `typeof<T>` / `System.Type`: a value (not a
|
|
4
|
+
// string tag) carrying byte size, vertex / index format, and a
|
|
5
|
+
// phantom type parameter that flows through to `BufferView<T>`-
|
|
6
|
+
// generic call sites.
|
|
7
|
+
//
|
|
8
|
+
// All entries are exposed as singleton properties on `ElementType`:
|
|
9
|
+
//
|
|
10
|
+
// ElementType.V3f // ElementKind<V3f>
|
|
11
|
+
// ElementType.U16 // ElementKind<number>
|
|
12
|
+
// ElementType.M44f // ElementKind<M44f>
|
|
13
|
+
//
|
|
14
|
+
// `BufferView.ofArray` infers the kind from the source array's
|
|
15
|
+
// constructor identity (V3fArray → ElementType.V3f, Float32Array →
|
|
16
|
+
// ElementType.F32, etc.) so the explicit second argument is rarely
|
|
17
|
+
// needed.
|
|
18
|
+
|
|
19
|
+
import type {
|
|
20
|
+
V2f, V3f, V4f, M22f, M33f, M44f, V2i, V3i, V4i,
|
|
21
|
+
} from "@aardworx/wombat.base";
|
|
22
|
+
import {
|
|
23
|
+
V2fArray, V3fArray, V4fArray,
|
|
24
|
+
V2iArray, V3iArray, V4iArray,
|
|
25
|
+
V2uiArray, V3uiArray, V4uiArray,
|
|
26
|
+
} from "@aardworx/wombat.base";
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// ElementKind<T>
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Phantom-typed token describing a single element's data type. The
|
|
34
|
+
* `T` parameter is a brand only — it carries the source-side type
|
|
35
|
+
* (V3f, M44f, number, …) into the type system at zero runtime cost.
|
|
36
|
+
*
|
|
37
|
+
* Identity equality is the equality used everywhere in the renderer
|
|
38
|
+
* (group keys, bind plans). All instances are singletons under the
|
|
39
|
+
* `ElementType` namespace below.
|
|
40
|
+
*/
|
|
41
|
+
export class ElementKind<T> {
|
|
42
|
+
constructor(
|
|
43
|
+
/** Short human-readable tag (e.g. `"v3f"`, `"u16"`). */
|
|
44
|
+
readonly name: string,
|
|
45
|
+
/** Size of one element in bytes. */
|
|
46
|
+
readonly byteSize: number,
|
|
47
|
+
/** GPUVertexFormat for use as a vertex attribute. `undefined` for matrix and non-vertex types. */
|
|
48
|
+
readonly vertexFormat: GPUVertexFormat | undefined,
|
|
49
|
+
/** GPUIndexFormat when used as an index buffer. `undefined` unless the type is `u16` / `u32`. */
|
|
50
|
+
readonly indexFormat: GPUIndexFormat | undefined,
|
|
51
|
+
) {}
|
|
52
|
+
|
|
53
|
+
/** Phantom — never read at runtime. Forces TS to track T. */
|
|
54
|
+
declare readonly _brand: T;
|
|
55
|
+
|
|
56
|
+
toString(): string { return this.name; }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* `ElementType` doubles as a type alias (the structural shape) and
|
|
61
|
+
* a value namespace (the singleton tokens). `ElementType` in type
|
|
62
|
+
* position is `ElementKind<unknown>`; in value position it's the
|
|
63
|
+
* collection of singletons below.
|
|
64
|
+
*/
|
|
65
|
+
export type ElementType = ElementKind<unknown>;
|
|
66
|
+
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Singleton tokens
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
const _V2f = new ElementKind<V2f>("v2f", 8, "float32x2", undefined);
|
|
72
|
+
const _V3f = new ElementKind<V3f>("v3f", 12, "float32x3", undefined);
|
|
73
|
+
const _V4f = new ElementKind<V4f>("v4f", 16, "float32x4", undefined);
|
|
74
|
+
const _M22f = new ElementKind<M22f>("m22f", 16, undefined, undefined);
|
|
75
|
+
const _M33f = new ElementKind<M33f>("m33f", 36, undefined, undefined);
|
|
76
|
+
const _M44f = new ElementKind<M44f>("m44f", 64, undefined, undefined);
|
|
77
|
+
const _V2i = new ElementKind<V2i>("v2i", 8, "sint32x2", undefined);
|
|
78
|
+
const _V3i = new ElementKind<V3i>("v3i", 12, "sint32x3", undefined);
|
|
79
|
+
const _V4i = new ElementKind<V4i>("v4i", 16, "sint32x4", undefined);
|
|
80
|
+
const _V2u = new ElementKind<V2i>("v2u", 8, "uint32x2", undefined);
|
|
81
|
+
const _V3u = new ElementKind<V3i>("v3u", 12, "uint32x3", undefined);
|
|
82
|
+
const _V4u = new ElementKind<V4i>("v4u", 16, "uint32x4", undefined);
|
|
83
|
+
const _F32 = new ElementKind<number>("f32", 4, "float32", undefined);
|
|
84
|
+
const _U32 = new ElementKind<number>("u32", 4, "uint32", "uint32");
|
|
85
|
+
const _I32 = new ElementKind<number>("i32", 4, "sint32", undefined);
|
|
86
|
+
const _U16 = new ElementKind<number>("u16", 2, undefined, "uint16");
|
|
87
|
+
const _I16 = new ElementKind<number>("i16", 2, undefined, undefined);
|
|
88
|
+
const _U8 = new ElementKind<number>("u8", 1, undefined, undefined);
|
|
89
|
+
const _I8 = new ElementKind<number>("i8", 1, undefined, undefined);
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Construct a vertex view; throws if the kind has no vertex format
|
|
93
|
+
* (matrix / unsupported type).
|
|
94
|
+
*/
|
|
95
|
+
function asVertexFormat(t: ElementType): GPUVertexFormat {
|
|
96
|
+
if (t.vertexFormat === undefined) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`ElementType.${t.name}: no GPUVertexFormat (matrices must be split into column attributes; ` +
|
|
99
|
+
`unsupported types must be repacked first)`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
return t.vertexFormat;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function asIndexFormat(t: ElementType): GPUIndexFormat {
|
|
106
|
+
if (t.indexFormat === undefined) {
|
|
107
|
+
throw new Error(`ElementType.${t.name}: not a valid index type (use ElementType.U16 or ElementType.U32)`);
|
|
108
|
+
}
|
|
109
|
+
return t.indexFormat;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// Constructor → ElementType registry — drives `BufferView.ofArray`
|
|
114
|
+
// auto-inference. Keyed on class identity (V3fArray, Float32Array,
|
|
115
|
+
// …) so it survives minification.
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
|
|
118
|
+
const FROM_CTOR: ReadonlyMap<unknown, ElementType> = new Map<unknown, ElementType>([
|
|
119
|
+
// wombat.base packed arrays
|
|
120
|
+
[V2fArray, _V2f], [V3fArray, _V3f], [V4fArray, _V4f],
|
|
121
|
+
[V2iArray, _V2i], [V3iArray, _V3i], [V4iArray, _V4i],
|
|
122
|
+
[V2uiArray, _V2u], [V3uiArray, _V3u], [V4uiArray, _V4u],
|
|
123
|
+
// native typed arrays (interpreted as scalars)
|
|
124
|
+
[Float32Array, _F32],
|
|
125
|
+
[Uint32Array, _U32],
|
|
126
|
+
[Int32Array, _I32],
|
|
127
|
+
[Uint16Array, _U16],
|
|
128
|
+
[Int16Array, _I16],
|
|
129
|
+
[Uint8Array, _U8],
|
|
130
|
+
[Int8Array, _I8],
|
|
131
|
+
]);
|
|
132
|
+
|
|
133
|
+
function fromArray(arr: unknown): ElementType {
|
|
134
|
+
const ctor = (arr as { constructor?: unknown }).constructor;
|
|
135
|
+
if (ctor === undefined) {
|
|
136
|
+
throw new Error("ElementType.fromArray: input has no constructor");
|
|
137
|
+
}
|
|
138
|
+
const et = FROM_CTOR.get(ctor);
|
|
139
|
+
if (et === undefined) {
|
|
140
|
+
const name = (ctor as { name?: string }).name ?? "<anonymous>";
|
|
141
|
+
throw new Error(
|
|
142
|
+
`ElementType.fromArray: cannot infer ElementType from ${name}; ` +
|
|
143
|
+
`pass the kind explicitly (e.g. ElementType.V3f)`,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
return et;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
// Public namespace
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Singleton tokens + helpers. Most call sites use the singleton
|
|
155
|
+
* directly (`ElementType.V3f`); the helpers are kept on the
|
|
156
|
+
* namespace so `import { ElementType }` is the only import you
|
|
157
|
+
* need.
|
|
158
|
+
*/
|
|
159
|
+
export const ElementType = {
|
|
160
|
+
// Float vectors
|
|
161
|
+
V2f: _V2f, V3f: _V3f, V4f: _V4f,
|
|
162
|
+
// Float matrices (square)
|
|
163
|
+
M22f: _M22f, M33f: _M33f, M44f: _M44f,
|
|
164
|
+
// Signed-int vectors
|
|
165
|
+
V2i: _V2i, V3i: _V3i, V4i: _V4i,
|
|
166
|
+
// Unsigned-int vectors
|
|
167
|
+
V2u: _V2u, V3u: _V3u, V4u: _V4u,
|
|
168
|
+
// Scalars
|
|
169
|
+
F32: _F32, U32: _U32, I32: _I32,
|
|
170
|
+
U16: _U16, I16: _I16, U8: _U8, I8: _I8,
|
|
171
|
+
|
|
172
|
+
/** Infer from a wombat.base packed array or a native TypedArray. */
|
|
173
|
+
fromArray,
|
|
174
|
+
/** GPUVertexFormat for `t`; throws if `t` has no vertex form. */
|
|
175
|
+
toVertexFormat: asVertexFormat,
|
|
176
|
+
/** GPUIndexFormat for `t`; throws if `t` is not `u16` / `u32`. */
|
|
177
|
+
toIndexFormat: asIndexFormat,
|
|
178
|
+
} as const;
|
package/src/core/index.ts
CHANGED
|
@@ -11,10 +11,15 @@ 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
|
+
ElementKind,
|
|
21
|
+
} from "./elementType.js";
|
|
22
|
+
|
|
18
23
|
export type {
|
|
19
24
|
ClearColor,
|
|
20
25
|
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);
|
|
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
|
/**
|