typegpu 0.12.6 → 0.12.7
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/common/writeSoA.js +16 -0
- package/core/buffer/buffer.js +47 -11
- package/core/commandEncoder/attachments.js +5 -2
- package/core/function/autoIO.js +4 -2
- package/core/function/ioSchema.d.ts +7 -2
- package/core/function/ioSchema.js +41 -15
- package/core/function/tgpuFragmentFn.d.ts +1 -1
- package/core/function/tgpuFragmentFn.js +2 -1
- package/core/function/tgpuVertexFn.js +2 -1
- package/core/pipeline/connectAttachmentToShader.d.ts +2 -2
- package/core/pipeline/connectAttachmentToShader.js +16 -21
- package/core/pipeline/connectTargetsToShader.d.ts +5 -1
- package/core/pipeline/connectTargetsToShader.js +23 -18
- package/core/pipeline/renderPipeline.d.ts +5 -4
- package/core/pipeline/renderPipeline.js +10 -12
- package/core/resolve/namespace.js +2 -2
- package/core/root/rootTypes.d.ts +6 -2
- package/core/slot/accessor.d.ts +1 -1
- package/core/slot/accessor.js +72 -5
- package/core/texture/texture.js +8 -4
- package/data/autoStruct.d.ts +1 -1
- package/data/autoStruct.js +8 -4
- package/data/deepEqual.js +19 -9
- package/data/matrix.js +10 -3
- package/data/partialIO.d.ts +8 -1
- package/data/partialIO.js +36 -19
- package/data/ref.js +2 -2
- package/data/snippet.d.ts +6 -4
- package/data/snippet.js +8 -4
- package/data/wgslTypes.d.ts +3 -0
- package/internal.d.ts +1 -0
- package/nameUtils.d.ts +7 -0
- package/nameUtils.js +60 -0
- package/package.json +1 -1
- package/shared/meta.js +1 -1
- package/shared/symbols.js +1 -1
- package/shared/tseynit.js +54 -50
- package/std/boolean.js +9 -10
- package/std/operators.js +9 -4
- package/std/texture.d.ts +2 -2
- package/tgsl/accessIndex.js +19 -23
- package/tgsl/forOfUtils.js +6 -6
- package/tgsl/generationHelpers.js +2 -2
- package/tgsl/wgslGenerator.js +85 -23
package/core/slot/accessor.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { isData } from "../../data/dataTypes.js";
|
|
1
|
+
import { isData, undecorate } from "../../data/dataTypes.js";
|
|
2
2
|
import { schemaCallWrapper } from "../../data/schemaCallWrapper.js";
|
|
3
3
|
import { isSnippet, snip } from "../../data/snippet.js";
|
|
4
|
+
import { deepEqual } from "../../data/deepEqual.js";
|
|
5
|
+
import { arrayOf } from "../../data/array.js";
|
|
6
|
+
import { UnknownData } from "../../data/dataTypes.js";
|
|
7
|
+
import { isAbstract, isWgslArray } from "../../data/wgslTypes.js";
|
|
4
8
|
import { getResolutionCtx } from "../../execMode.js";
|
|
5
9
|
import { makeDereferenceable } from "../../tgsl/makeDereferenceable.js";
|
|
6
10
|
import { makeResolvable } from "../../tgsl/makeResolvable.js";
|
|
@@ -19,10 +23,65 @@ export function mutableAccessor(schemaOrConstructor, defaultValue) {
|
|
|
19
23
|
// --------------
|
|
20
24
|
// Implementation
|
|
21
25
|
// --------------
|
|
26
|
+
/**
|
|
27
|
+
* Accessors typed with a runtime-sized array schema (e.g. `d.arrayOf(d.f32)`) accept
|
|
28
|
+
* any array of that element type. If the provided JS value has a concrete length, we
|
|
29
|
+
* use a statically-sized schema so that the generated code can take advantage of it.
|
|
30
|
+
*/
|
|
31
|
+
function concretizeSchema(accessorSchema, value) {
|
|
32
|
+
if (isWgslArray(accessorSchema) && accessorSchema.elementCount === 0 && Array.isArray(value)) {
|
|
33
|
+
if (value.length === 0) {
|
|
34
|
+
throw new Error(`Cannot use empty array as an accessor value. Empty arrays aren't representable in shader code.`);
|
|
35
|
+
}
|
|
36
|
+
return arrayOf(accessorSchema.elementType, value.length);
|
|
37
|
+
}
|
|
38
|
+
return accessorSchema;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Origins of values that can be assigned to (or are handles, like storage textures).
|
|
42
|
+
* Accessors can only refer to module-scope resources, so function-scope origins
|
|
43
|
+
* ('function', 'local-def') are never provided to them.
|
|
44
|
+
*/
|
|
45
|
+
const mutableOrigins = new Set(['mutable', 'workgroup', 'private', 'handle']);
|
|
46
|
+
/**
|
|
47
|
+
* Whether a value of type `providedType` can be used in place of `accessorSchema` without
|
|
48
|
+
* any conversion. Runtime-sized array schemas accept arrays of any length with a matching element type.
|
|
49
|
+
*/
|
|
50
|
+
function matchesSchema(accessorSchema, providedType) {
|
|
51
|
+
if (providedType === UnknownData) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const expected = undecorate(accessorSchema);
|
|
55
|
+
const provided = undecorate(providedType);
|
|
56
|
+
if (isWgslArray(expected) && expected.elementCount === 0 && isWgslArray(provided)) {
|
|
57
|
+
return deepEqual(expected.elementType, provided.elementType);
|
|
58
|
+
}
|
|
59
|
+
return deepEqual(expected, provided);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Values with a concrete WGSL type (buffers, variables, GPU functions, ...) are used as-is,
|
|
63
|
+
* so they have to match the accessor's schema exactly. Mutable accessors are also assigned to,
|
|
64
|
+
* so the provided value has to be mutable.
|
|
65
|
+
*/
|
|
66
|
+
function validateAccessorSnippet(accessor, snippet) {
|
|
67
|
+
const isMutable = accessor.resourceType === 'mutable-accessor';
|
|
68
|
+
const description = `${isMutable ? 'mutable accessor' : 'accessor'} '${getName(accessor) ?? '<unnamed>'}'`;
|
|
69
|
+
if (isMutable && !mutableOrigins.has(snippet.origin)) {
|
|
70
|
+
throw new Error(`Values provided to ${description} must be mutable (e.g. mutable buffers, private or workgroup variables), got a value with origin '${snippet.origin}'. Use tgpu.accessor for read-only access.`);
|
|
71
|
+
}
|
|
72
|
+
if (!matchesSchema(accessor.schema, snippet.dataType)) {
|
|
73
|
+
throw new Error(`Value of type '${String(snippet.dataType)}' does not match the schema of ${description}: '${String(accessor.schema)}'.`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
22
76
|
/**
|
|
23
77
|
* @returns A snippet representing the accessor.
|
|
24
78
|
*/
|
|
25
79
|
function createAccessorSnippet(accessor) {
|
|
80
|
+
const snippet = createUnvalidatedAccessorSnippet(accessor);
|
|
81
|
+
validateAccessorSnippet(accessor, snippet);
|
|
82
|
+
return snippet;
|
|
83
|
+
}
|
|
84
|
+
function createUnvalidatedAccessorSnippet(accessor) {
|
|
26
85
|
// oxlint-disable-next-line typescript/no-non-null-assertion -- it's there
|
|
27
86
|
const ctx = getResolutionCtx();
|
|
28
87
|
let value = getGpuValueRecursively(ctx.unwrap(accessor.slot));
|
|
@@ -35,23 +94,31 @@ function createAccessorSnippet(accessor) {
|
|
|
35
94
|
if (isSnippet(value)) {
|
|
36
95
|
value = value.value;
|
|
37
96
|
}
|
|
97
|
+
value = getGpuValueRecursively(value);
|
|
38
98
|
}
|
|
39
99
|
const ownSnippet = getOwnSnippet(value);
|
|
40
100
|
if (ownSnippet) {
|
|
41
101
|
return ownSnippet;
|
|
42
102
|
}
|
|
43
103
|
if (isGPUCallable(value)) {
|
|
44
|
-
|
|
104
|
+
const result = value[$gpuCallable].call(ctx, []);
|
|
105
|
+
if (result.dataType !== UnknownData && !isAbstract(result.dataType)) {
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
// The result is a plain JS value (e.g. an array literal or a number returned from
|
|
109
|
+
// `tgpu.comptime`), so we treat it the same way as a directly provided JS value.
|
|
110
|
+
value = result.value;
|
|
45
111
|
}
|
|
46
112
|
if (isTgpuFn(value) || hasTinyestMetadata(value)) {
|
|
47
113
|
const fn = ctx.resolve(value);
|
|
48
|
-
return ctx.withResetIndentLevel(() => snip(`${fn.value}()`,
|
|
114
|
+
return ctx.withResetIndentLevel(() => snip(`${fn.value}()`, fn.dataType, /* origin */ 'runtime', fn.possibleSideEffects));
|
|
49
115
|
}
|
|
50
116
|
ctx.pushMode(new NormalState());
|
|
51
117
|
try {
|
|
118
|
+
const schema = concretizeSchema(accessor.schema, value);
|
|
52
119
|
// Doing a deep copy each time so that we don't have to deal with refs
|
|
53
|
-
const cloned = schemaCallWrapper(
|
|
54
|
-
return snip(cloned,
|
|
120
|
+
const cloned = schemaCallWrapper(schema, value);
|
|
121
|
+
return snip(cloned, schema, 'constant', /* possibleSideEffects */ false);
|
|
55
122
|
}
|
|
56
123
|
finally {
|
|
57
124
|
ctx.popMode('normal');
|
package/core/texture/texture.js
CHANGED
|
@@ -405,10 +405,14 @@ export class TgpuTextureRenderViewImpl {
|
|
|
405
405
|
};
|
|
406
406
|
this[$internal] = {
|
|
407
407
|
unwrap: () => {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
408
|
+
const soul = this[$soul];
|
|
409
|
+
if (!soul.raw) {
|
|
410
|
+
soul.raw = soul.texture[$internal].materialize().createView({
|
|
411
|
+
label: getName(this) ?? '<unnamed>',
|
|
412
|
+
...soul.descriptor,
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
return soul.raw;
|
|
412
416
|
},
|
|
413
417
|
format: descriptor.format ?? baseTexture.props.format,
|
|
414
418
|
aspect: descriptor.aspect,
|
package/data/autoStruct.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export declare class AutoStruct implements BaseData, SelfResolvable {
|
|
|
12
12
|
[$internal]: Record<string, never>;
|
|
13
13
|
type: 'auto-struct';
|
|
14
14
|
readonly [$repr]: Record<string, unknown>;
|
|
15
|
-
constructor(validProps: Record<string, BaseData>, typeForExtraProps: BaseData | undefined, locations?: Record<string, number
|
|
15
|
+
constructor(validProps: Record<string, BaseData>, typeForExtraProps: BaseData | undefined, locations?: Record<string, number>, autoInterpolateIntegers?: boolean);
|
|
16
16
|
/**
|
|
17
17
|
* Used for accessing builtins, varying and attributes in code.
|
|
18
18
|
*/
|
package/data/autoStruct.js
CHANGED
|
@@ -22,11 +22,17 @@ export class AutoStruct {
|
|
|
22
22
|
#locations;
|
|
23
23
|
#cachedStruct;
|
|
24
24
|
#typeForExtraProps;
|
|
25
|
-
|
|
25
|
+
#autoInterpolateIntegers;
|
|
26
|
+
static {
|
|
27
|
+
AutoStruct.prototype[$internal] = {};
|
|
28
|
+
AutoStruct.prototype.type = 'auto-struct';
|
|
29
|
+
}
|
|
30
|
+
constructor(validProps, typeForExtraProps, locations, autoInterpolateIntegers = false) {
|
|
26
31
|
this.#validProps = validProps;
|
|
27
32
|
this.#typeForExtraProps = typeForExtraProps;
|
|
28
33
|
this.#allocated = {};
|
|
29
34
|
this.#locations = locations;
|
|
35
|
+
this.#autoInterpolateIntegers = autoInterpolateIntegers;
|
|
30
36
|
this.#usedWgslKeys = new Set();
|
|
31
37
|
}
|
|
32
38
|
/**
|
|
@@ -67,7 +73,7 @@ export class AutoStruct {
|
|
|
67
73
|
if (!this.#cachedStruct) {
|
|
68
74
|
this.#cachedStruct = createIoSchema(Object.fromEntries(Object.values(this.#allocated).map((alloc) => {
|
|
69
75
|
return [alloc.prop, alloc.type];
|
|
70
|
-
})), this.#locations);
|
|
76
|
+
})), this.#locations, this.#autoInterpolateIntegers);
|
|
71
77
|
const ownName = getName(this);
|
|
72
78
|
// Passing the given name forward
|
|
73
79
|
if (ownName) {
|
|
@@ -83,5 +89,3 @@ export class AutoStruct {
|
|
|
83
89
|
return `auto-struct:${getName(this) ?? '<unnamed>'}`;
|
|
84
90
|
}
|
|
85
91
|
}
|
|
86
|
-
AutoStruct.prototype[$internal] = {};
|
|
87
|
-
AutoStruct.prototype.type = 'auto-struct';
|
package/data/deepEqual.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isDisarray, isLooseDecorated, isUnstruct } from "./dataTypes.js";
|
|
2
|
+
import { isWgslStorageTexture, isWgslTexture } from "./texture.js";
|
|
2
3
|
import { isAtomic, isDecorated, isPtr, isWgslArray, isWgslStruct } from "./wgslTypes.js";
|
|
3
4
|
/**
|
|
4
5
|
* Performs a deep comparison of two TypeGPU data schemas.
|
|
@@ -53,11 +54,20 @@ export function deepEqual(a, b) {
|
|
|
53
54
|
if (isPtr(a) && isPtr(b)) {
|
|
54
55
|
return (a.addressSpace === b.addressSpace &&
|
|
55
56
|
a.access === b.access &&
|
|
57
|
+
a.implicit === b.implicit &&
|
|
56
58
|
deepEqual(a.inner, b.inner));
|
|
57
59
|
}
|
|
58
60
|
if (isAtomic(a) && isAtomic(b)) {
|
|
59
61
|
return deepEqual(a.inner, b.inner);
|
|
60
62
|
}
|
|
63
|
+
if (isWgslTexture(a) && isWgslTexture(b)) {
|
|
64
|
+
// Dimension and multisampling are already encoded in the type
|
|
65
|
+
return deepEqual(a.sampleType, b.sampleType);
|
|
66
|
+
}
|
|
67
|
+
if (isWgslStorageTexture(a) && isWgslStorageTexture(b)) {
|
|
68
|
+
// Dimension is already encoded in the type
|
|
69
|
+
return a.format === b.format && a.access === b.access;
|
|
70
|
+
}
|
|
61
71
|
if ((isDecorated(a) && isDecorated(b)) || (isLooseDecorated(a) && isLooseDecorated(b))) {
|
|
62
72
|
if (!deepEqual(a.inner, b.inner)) {
|
|
63
73
|
return false;
|
|
@@ -65,18 +75,18 @@ export function deepEqual(a, b) {
|
|
|
65
75
|
if (a.attribs.length !== b.attribs.length) {
|
|
66
76
|
return false;
|
|
67
77
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (attrsA[i] !== attrsB[i]) {
|
|
78
|
+
for (let i = 0; i < a.attribs.length; i++) {
|
|
79
|
+
const attrA = a.attribs[i];
|
|
80
|
+
const attrB = b.attribs[i];
|
|
81
|
+
const paramsA = attrA.params ?? [];
|
|
82
|
+
const paramsB = attrB.params ?? [];
|
|
83
|
+
if (attrA.type !== attrB.type ||
|
|
84
|
+
paramsA.length !== paramsB.length ||
|
|
85
|
+
paramsA.some((param, idx) => param !== paramsB[idx])) {
|
|
77
86
|
return false;
|
|
78
87
|
}
|
|
79
88
|
}
|
|
89
|
+
return true;
|
|
80
90
|
}
|
|
81
91
|
// All other types have been checked for equality at the start
|
|
82
92
|
return true;
|
package/data/matrix.js
CHANGED
|
@@ -8,7 +8,7 @@ import { numericLiteralToSnippet } from "../tgsl/generationHelpers.js";
|
|
|
8
8
|
import { f32 } from "./numeric.js";
|
|
9
9
|
import {} from "./snippet.js";
|
|
10
10
|
import { vec2f, vec3f, vec4f } from "./vector.js";
|
|
11
|
-
import { isVec, } from "./wgslTypes.js";
|
|
11
|
+
import { isMat, isMatInstance, isVec, } from "./wgslTypes.js";
|
|
12
12
|
export class MatBase {
|
|
13
13
|
}
|
|
14
14
|
function createMatSchema(options) {
|
|
@@ -18,7 +18,14 @@ function createMatSchema(options) {
|
|
|
18
18
|
normalImpl: (...args) => {
|
|
19
19
|
const elements = [];
|
|
20
20
|
for (const arg of args) {
|
|
21
|
-
if (
|
|
21
|
+
if (isMatInstance(arg)) {
|
|
22
|
+
for (const column of arg.columns) {
|
|
23
|
+
for (const elem of column) {
|
|
24
|
+
elements.push(elem);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
else if (typeof arg === 'number') {
|
|
22
29
|
elements.push(arg);
|
|
23
30
|
}
|
|
24
31
|
else {
|
|
@@ -35,7 +42,7 @@ function createMatSchema(options) {
|
|
|
35
42
|
}
|
|
36
43
|
return new options.MatImpl(...elements);
|
|
37
44
|
},
|
|
38
|
-
argTypes: (...args) => args.map((arg) => (isVec(arg) ? arg : f32)),
|
|
45
|
+
argTypes: (...args) => args.map((arg) => (isVec(arg) || isMat(arg) ? arg : f32)),
|
|
39
46
|
codegenImpl: (ctx, args) => ctx.gen.typeInstantiation(schema, args),
|
|
40
47
|
});
|
|
41
48
|
const schema = Object.assign(construct, {
|
package/data/partialIO.d.ts
CHANGED
|
@@ -4,10 +4,17 @@ import type { InferPatch } from '../shared/repr.ts';
|
|
|
4
4
|
export interface WriteInstruction {
|
|
5
5
|
data: Uint8Array<ArrayBuffer>;
|
|
6
6
|
gpuOffset: number;
|
|
7
|
+
/** How many bytes to upload. Can be larger than `data`, e.g. to cover trailing padding. */
|
|
8
|
+
uploadSize: number;
|
|
7
9
|
}
|
|
8
10
|
/**
|
|
9
11
|
* Converts `{idx, value}[]` sparse arrays into `Record<number, T>` format.
|
|
10
12
|
*/
|
|
11
13
|
export declare function convertPartialToPatch(schema: wgsl.BaseData, data: unknown): unknown;
|
|
12
|
-
|
|
14
|
+
/**
|
|
15
|
+
* @param validateRange Called for every byte range that is about to be written, before any data
|
|
16
|
+
* is written into `targetBuffer`. Returns how many bytes to upload for that range.
|
|
17
|
+
* Throwing from it leaves `targetBuffer` untouched.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getPatchInstructions<TData extends wgsl.BaseData>(schema: TData, data: unknown, targetBuffer?: ArrayBuffer, validateRange?: (start: number, end: number) => number): WriteInstruction[];
|
|
13
20
|
export declare function patchArrayBuffer<T extends BaseData>(buffer: ArrayBuffer, schema: T, data: InferPatch<T>): void;
|
package/data/partialIO.js
CHANGED
|
@@ -37,7 +37,12 @@ export function convertPartialToPatch(schema, data) {
|
|
|
37
37
|
return data;
|
|
38
38
|
}
|
|
39
39
|
const isLittleEndian = getSystemEndianness() === 'little';
|
|
40
|
-
|
|
40
|
+
/**
|
|
41
|
+
* @param validateRange Called for every byte range that is about to be written, before any data
|
|
42
|
+
* is written into `targetBuffer`. Returns how many bytes to upload for that range.
|
|
43
|
+
* Throwing from it leaves `targetBuffer` untouched.
|
|
44
|
+
*/
|
|
45
|
+
export function getPatchInstructions(schema, data, targetBuffer, validateRange) {
|
|
41
46
|
const totalSize = sizeOf(schema);
|
|
42
47
|
if (totalSize === 0 || data === undefined || data === null) {
|
|
43
48
|
return [];
|
|
@@ -46,6 +51,8 @@ export function getPatchInstructions(schema, data, targetBuffer) {
|
|
|
46
51
|
const writer = new BufferWriter(buf);
|
|
47
52
|
const compiledView = new DataView(buf);
|
|
48
53
|
const segments = [];
|
|
54
|
+
// Deferred until every range is validated
|
|
55
|
+
const pendingWrites = [];
|
|
49
56
|
function collect(node, value, offset, padding) {
|
|
50
57
|
if (value === undefined || value === null) {
|
|
51
58
|
return;
|
|
@@ -67,7 +74,9 @@ export function getPatchInstructions(schema, data, targetBuffer) {
|
|
|
67
74
|
const elementPadding = elementSize - sizeOf(arrSchema.elementType);
|
|
68
75
|
if (ArrayBuffer.isView(value)) {
|
|
69
76
|
const copyLen = Math.min(value.byteLength, arrSchema.elementCount * elementSize);
|
|
70
|
-
|
|
77
|
+
pendingWrites.push(() => {
|
|
78
|
+
new Uint8Array(buf, offset, copyLen).set(new Uint8Array(value.buffer, value.byteOffset, copyLen));
|
|
79
|
+
});
|
|
71
80
|
segments.push({ start: offset, end: offset + copyLen, padding });
|
|
72
81
|
return;
|
|
73
82
|
}
|
|
@@ -88,17 +97,19 @@ export function getPatchInstructions(schema, data, targetBuffer) {
|
|
|
88
97
|
}
|
|
89
98
|
const leafSize = sizeOf(node);
|
|
90
99
|
const compiledWriter = getCompiledWriter(node);
|
|
91
|
-
|
|
92
|
-
compiledWriter
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
100
|
+
pendingWrites.push(() => {
|
|
101
|
+
if (compiledWriter) {
|
|
102
|
+
compiledWriter(compiledView, offset, value, isLittleEndian, offset + leafSize);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
writer.seekTo(offset);
|
|
106
|
+
writeData(writer, node, value);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
98
109
|
segments.push({ start: offset, end: offset + leafSize, padding });
|
|
99
110
|
}
|
|
100
111
|
collect(schema, data, 0);
|
|
101
|
-
const
|
|
112
|
+
const runs = [];
|
|
102
113
|
let run = null;
|
|
103
114
|
for (const seg of segments) {
|
|
104
115
|
if (run && seg.start === run.end + (run.padding ?? 0)) {
|
|
@@ -106,21 +117,27 @@ export function getPatchInstructions(schema, data, targetBuffer) {
|
|
|
106
117
|
}
|
|
107
118
|
else {
|
|
108
119
|
if (run) {
|
|
109
|
-
|
|
110
|
-
gpuOffset: run.start,
|
|
111
|
-
data: new Uint8Array(buf, run.start, run.end - run.start).slice(),
|
|
112
|
-
});
|
|
120
|
+
runs.push(run);
|
|
113
121
|
}
|
|
114
122
|
run = seg;
|
|
115
123
|
}
|
|
116
124
|
}
|
|
117
125
|
if (run) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
126
|
+
runs.push(run);
|
|
127
|
+
}
|
|
128
|
+
const uploads = runs.map(({ start, end }) => ({
|
|
129
|
+
start,
|
|
130
|
+
end,
|
|
131
|
+
uploadSize: validateRange?.(start, end) ?? end - start,
|
|
132
|
+
}));
|
|
133
|
+
for (const write of pendingWrites) {
|
|
134
|
+
write();
|
|
122
135
|
}
|
|
123
|
-
return
|
|
136
|
+
return uploads.map(({ start, end, uploadSize }) => ({
|
|
137
|
+
gpuOffset: start,
|
|
138
|
+
data: new Uint8Array(buf, start, end - start).slice(),
|
|
139
|
+
uploadSize,
|
|
140
|
+
}));
|
|
124
141
|
}
|
|
125
142
|
export function patchArrayBuffer(buffer, schema, data) {
|
|
126
143
|
const instructions = getPatchInstructions(schema, data, buffer);
|
package/data/ref.js
CHANGED
|
@@ -4,7 +4,7 @@ import { setName } from "../shared/meta.js";
|
|
|
4
4
|
import { $gpuCallable, $internal, $ownSnippet, $resolve } from "../shared/symbols.js";
|
|
5
5
|
import { UnknownData } from "./dataTypes.js";
|
|
6
6
|
import { createPtrFromOrigin, explicitFrom } from "./ptr.js";
|
|
7
|
-
import {
|
|
7
|
+
import { isStoredInMemory, snip, withDataType, } from "./snippet.js";
|
|
8
8
|
import { isNaturallyEphemeral, isPtr } from "./wgslTypes.js";
|
|
9
9
|
export const _ref = (() => {
|
|
10
10
|
const impl = ((value) => INTERNAL_createRef(value));
|
|
@@ -23,7 +23,7 @@ export const _ref = (() => {
|
|
|
23
23
|
- Try 'd.ref(${typeStr}(${value}));' instead to create a new referencable value.
|
|
24
24
|
-----`);
|
|
25
25
|
}
|
|
26
|
-
if (
|
|
26
|
+
if (isStoredInMemory(value) && isNaturallyEphemeral(value.dataType)) {
|
|
27
27
|
const typeStr = ctx.resolve(value.dataType).value;
|
|
28
28
|
throw new WgslTypeError(stitch `d.ref(${value}) is illegal, cannot take a reference to a scalar value.
|
|
29
29
|
-----
|
package/data/snippet.d.ts
CHANGED
|
@@ -7,25 +7,26 @@ export type Origin = 'uniform' | 'readonly' | 'mutable' | 'workgroup' | 'private
|
|
|
7
7
|
*/
|
|
8
8
|
export declare function fallthroughCopyOrigin(origin: Origin): Origin;
|
|
9
9
|
/**
|
|
10
|
-
* Whether a snippet
|
|
10
|
+
* Whether a snippet refers to a value already stored in memory.
|
|
11
11
|
*
|
|
12
12
|
* @example
|
|
13
13
|
* ```ts
|
|
14
14
|
* function foo(a: number) {
|
|
15
|
+
* 'use gpu';
|
|
15
16
|
* const color = d.vec3f(1, 2, 3);
|
|
16
17
|
* return color * a;
|
|
17
18
|
* }
|
|
18
19
|
*
|
|
19
|
-
* //
|
|
20
|
+
* // Stored in memory:
|
|
20
21
|
* // - color
|
|
21
22
|
* // - a
|
|
22
23
|
* //
|
|
23
|
-
* //
|
|
24
|
+
* // Ephemeral values:
|
|
24
25
|
* // - d.vec3f(1, 2, 3)
|
|
25
26
|
* // - color * a
|
|
26
27
|
* ```
|
|
27
28
|
*/
|
|
28
|
-
export declare function
|
|
29
|
+
export declare function isStoredInMemory(snippet: Snippet): boolean;
|
|
29
30
|
export declare const originToPtrParams: {
|
|
30
31
|
readonly uniform: {
|
|
31
32
|
readonly space: "uniform";
|
|
@@ -61,6 +62,7 @@ export declare const originToPtrParams: {
|
|
|
61
62
|
};
|
|
62
63
|
};
|
|
63
64
|
export type OriginToPtrParams = typeof originToPtrParams;
|
|
65
|
+
export declare function isAddressableOrigin(origin: Origin): origin is keyof OriginToPtrParams;
|
|
64
66
|
export interface Snippet {
|
|
65
67
|
readonly value: unknown;
|
|
66
68
|
/**
|
package/data/snippet.js
CHANGED
|
@@ -16,25 +16,26 @@ export function fallthroughCopyOrigin(origin) {
|
|
|
16
16
|
return 'runtime';
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
|
-
* Whether a snippet
|
|
19
|
+
* Whether a snippet refers to a value already stored in memory.
|
|
20
20
|
*
|
|
21
21
|
* @example
|
|
22
22
|
* ```ts
|
|
23
23
|
* function foo(a: number) {
|
|
24
|
+
* 'use gpu';
|
|
24
25
|
* const color = d.vec3f(1, 2, 3);
|
|
25
26
|
* return color * a;
|
|
26
27
|
* }
|
|
27
28
|
*
|
|
28
|
-
* //
|
|
29
|
+
* // Stored in memory:
|
|
29
30
|
* // - color
|
|
30
31
|
* // - a
|
|
31
32
|
* //
|
|
32
|
-
* //
|
|
33
|
+
* // Ephemeral values:
|
|
33
34
|
* // - d.vec3f(1, 2, 3)
|
|
34
35
|
* // - color * a
|
|
35
36
|
* ```
|
|
36
37
|
*/
|
|
37
|
-
export function
|
|
38
|
+
export function isStoredInMemory(snippet) {
|
|
38
39
|
return !(snippet.origin === 'runtime' || snippet.origin === 'constant');
|
|
39
40
|
}
|
|
40
41
|
export const originToPtrParams = {
|
|
@@ -48,6 +49,9 @@ export const originToPtrParams = {
|
|
|
48
49
|
// Local declarations are also in the `function` address space
|
|
49
50
|
'local-def': { space: 'function', access: 'read-write' },
|
|
50
51
|
};
|
|
52
|
+
export function isAddressableOrigin(origin) {
|
|
53
|
+
return origin in originToPtrParams;
|
|
54
|
+
}
|
|
51
55
|
class SnippetImpl {
|
|
52
56
|
value;
|
|
53
57
|
dataType;
|
package/data/wgslTypes.d.ts
CHANGED
|
@@ -709,6 +709,7 @@ export interface Mat2x2f extends BaseData {
|
|
|
709
709
|
readonly [$validUniformSchema]: true;
|
|
710
710
|
(...elements: [number, number, number, number]): m2x2f;
|
|
711
711
|
(...columns: [v2f, v2f]): m2x2f;
|
|
712
|
+
(other: m2x2f): m2x2f;
|
|
712
713
|
(): m2x2f;
|
|
713
714
|
identity(): m2x2f;
|
|
714
715
|
}
|
|
@@ -724,6 +725,7 @@ export interface Mat3x3f extends BaseData {
|
|
|
724
725
|
readonly [$validUniformSchema]: true;
|
|
725
726
|
(...elements: [number, number, number, number, number, number, number, number, number]): m3x3f;
|
|
726
727
|
(...columns: [v3f, v3f, v3f]): m3x3f;
|
|
728
|
+
(other: m3x3f): m3x3f;
|
|
727
729
|
(): m3x3f;
|
|
728
730
|
identity(): m3x3f;
|
|
729
731
|
}
|
|
@@ -739,6 +741,7 @@ export interface Mat4x4f extends BaseData {
|
|
|
739
741
|
readonly [$validUniformSchema]: true;
|
|
740
742
|
(...elements: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number]): m4x4f;
|
|
741
743
|
(...columns: [v4f, v4f, v4f, v4f]): m4x4f;
|
|
744
|
+
(other: m4x4f): m4x4f;
|
|
742
745
|
(): m4x4f;
|
|
743
746
|
identity(): m4x4f;
|
|
744
747
|
translation(vec: v3f): m4x4f;
|
package/internal.d.ts
CHANGED
|
@@ -13,4 +13,5 @@ export type { RestoreContext } from './serial/types.ts';
|
|
|
13
13
|
export { deserializeDataSchema, serializeDataSchema, type SerializedDataSchema, } from './serial/schema.ts';
|
|
14
14
|
export type { ResolutionCtx, FunctionArgument } from './types.ts';
|
|
15
15
|
export type { Snippet, ResolvedSnippet, Origin } from './data/snippet.ts';
|
|
16
|
+
export type { AutoStruct } from './data/autoStruct.ts';
|
|
16
17
|
export type { ShaderGenerator, ResolvedStatement, BinaryOperator, ShaderGeneratorClass, FunctionDefinitionOptions, ConstantDefinitionOptions, VariableDefinitionOptions, } from './tgsl/shaderGenerator.ts';
|
package/nameUtils.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
export declare const bannedTokens: Set<string>;
|
|
2
|
+
/**
|
|
3
|
+
* Predeclared enumerants that appear as template parameters, e.g. `var<storage, read_write>` or
|
|
4
|
+
* `texture_storage_2d<rgba8unorm, write>`. A module-scope or local declaration with one of these names
|
|
5
|
+
* would shadow the enumerant, so a template using it would refer to the declaration instead.
|
|
6
|
+
* Unlike `bannedTokens`, these remain valid as struct member names, which never shadow anything.
|
|
7
|
+
*/
|
|
8
|
+
export declare const templateEnumerants: Set<string>;
|
|
2
9
|
export declare const builtins: Set<string>;
|
|
3
10
|
/**
|
|
4
11
|
* Sanitizes the primer so that it is compliant with WGSL guidelines.
|
package/nameUtils.js
CHANGED
|
@@ -178,6 +178,66 @@ export const bannedTokens = new Set([
|
|
|
178
178
|
'uniform',
|
|
179
179
|
'storage',
|
|
180
180
|
]);
|
|
181
|
+
/**
|
|
182
|
+
* Predeclared enumerants that appear as template parameters, e.g. `var<storage, read_write>` or
|
|
183
|
+
* `texture_storage_2d<rgba8unorm, write>`. A module-scope or local declaration with one of these names
|
|
184
|
+
* would shadow the enumerant, so a template using it would refer to the declaration instead.
|
|
185
|
+
* Unlike `bannedTokens`, these remain valid as struct member names, which never shadow anything.
|
|
186
|
+
*/
|
|
187
|
+
export const templateEnumerants = new Set([
|
|
188
|
+
// address spaces
|
|
189
|
+
'function',
|
|
190
|
+
'private',
|
|
191
|
+
'workgroup',
|
|
192
|
+
'uniform',
|
|
193
|
+
'storage',
|
|
194
|
+
'immediate',
|
|
195
|
+
// access modes
|
|
196
|
+
'read',
|
|
197
|
+
'write',
|
|
198
|
+
'read_write',
|
|
199
|
+
// texel formats
|
|
200
|
+
'rgba8unorm',
|
|
201
|
+
'rgba8snorm',
|
|
202
|
+
'rgba8uint',
|
|
203
|
+
'rgba8sint',
|
|
204
|
+
'rgba16unorm',
|
|
205
|
+
'rgba16snorm',
|
|
206
|
+
'rgba16uint',
|
|
207
|
+
'rgba16sint',
|
|
208
|
+
'rgba16float',
|
|
209
|
+
'rg8unorm',
|
|
210
|
+
'rg8snorm',
|
|
211
|
+
'rg8uint',
|
|
212
|
+
'rg8sint',
|
|
213
|
+
'rg16unorm',
|
|
214
|
+
'rg16snorm',
|
|
215
|
+
'rg16uint',
|
|
216
|
+
'rg16sint',
|
|
217
|
+
'rg16float',
|
|
218
|
+
'r8unorm',
|
|
219
|
+
'r8snorm',
|
|
220
|
+
'r8uint',
|
|
221
|
+
'r8sint',
|
|
222
|
+
'r16unorm',
|
|
223
|
+
'r16snorm',
|
|
224
|
+
'r16uint',
|
|
225
|
+
'r16sint',
|
|
226
|
+
'r16float',
|
|
227
|
+
'r32uint',
|
|
228
|
+
'r32sint',
|
|
229
|
+
'r32float',
|
|
230
|
+
'rg32uint',
|
|
231
|
+
'rg32sint',
|
|
232
|
+
'rg32float',
|
|
233
|
+
'rgba32uint',
|
|
234
|
+
'rgba32sint',
|
|
235
|
+
'rgba32float',
|
|
236
|
+
'bgra8unorm',
|
|
237
|
+
'rgb10a2unorm',
|
|
238
|
+
'rgb10a2uint',
|
|
239
|
+
'rg11b10ufloat',
|
|
240
|
+
]);
|
|
181
241
|
export const builtins = new Set([
|
|
182
242
|
// constructors
|
|
183
243
|
'array',
|
package/package.json
CHANGED
package/shared/meta.js
CHANGED
package/shared/symbols.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const version = "0.12.
|
|
1
|
+
const version = "0.12.7";
|
|
2
2
|
export const $internal = Symbol(`typegpu:${version}:$internal`);
|
|
3
3
|
/** A plain record of all definitional state of a resource, surviving transfer between runtimes */
|
|
4
4
|
export const $soul = Symbol(`typegpu:${version}:$soul`);
|