typegpu 0.12.7 → 0.12.8
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/core/function/autoIO.js +2 -2
- package/core/function/comptime.js +7 -5
- package/core/function/createCallableSchema.js +4 -3
- package/core/function/dualImpl.js +20 -19
- package/core/function/ioSchema.d.ts +10 -0
- package/core/function/ioSchema.js +37 -16
- package/core/function/tgpuFn.d.ts +11 -1
- package/core/function/tgpuFn.js +9 -2
- package/core/pipeline/renderPipeline.d.ts +4 -2
- package/core/pipeline/renderPipeline.js +12 -10
- package/core/pipeline/timeable.js +15 -2
- package/core/root/configurableImpl.js +1 -2
- package/core/texture/externalTexture.d.ts +1 -1
- package/core/texture/externalTexture.js +3 -1
- package/core/texture/texture.d.ts +1 -1
- package/core/texture/texture.js +4 -1
- package/core/unroll/tgpuUnroll.d.ts +4 -3
- package/core/unroll/tgpuUnroll.js +7 -1
- package/core/valueProxyUtils.d.ts +4 -2
- package/core/valueProxyUtils.js +4 -2
- package/data/compiledIO.js +12 -9
- package/data/dataIO.d.ts +10 -6
- package/data/dataIO.js +19 -4
- package/data/dataTypes.d.ts +0 -5
- package/data/dataTypes.js +0 -6
- package/data/matrixColumnsAccess.d.ts +8 -0
- package/data/matrixColumnsAccess.js +15 -0
- package/data/offsetUtils.js +181 -102
- package/data/ref.d.ts +12 -5
- package/data/ref.js +50 -23
- package/data/snippet.d.ts +7 -0
- package/data/snippet.js +9 -0
- package/data/struct.js +3 -0
- package/data/wgslTypes.d.ts +3 -3
- package/package.json +1 -1
- package/resolutionCtx.js +1 -1
- package/shared/meta.js +1 -1
- package/shared/symbols.d.ts +5 -0
- package/shared/symbols.js +6 -1
- package/std/numeric.js +1 -4
- package/std/packing.js +10 -10
- package/tgpuLogger.d.ts +1 -1
- package/tgpuLogger.js +0 -2
- package/tgsl/accessIndex.js +18 -15
- package/tgsl/accessProp.js +10 -7
- package/tgsl/conversion.js +3 -2
- package/tgsl/forOfUtils.js +2 -2
- package/tgsl/generationHelpers.d.ts +4 -3
- package/tgsl/generationHelpers.js +16 -3
- package/tgsl/infixDispatch.d.ts +4 -1
- package/tgsl/infixDispatch.js +13 -1
- package/tgsl/makeDereferenceable.d.ts +1 -1
- package/tgsl/makeDereferenceable.js +3 -1
- package/tgsl/shellless.js +5 -9
- package/tgsl/stringifySnippet.d.ts +9 -0
- package/tgsl/stringifySnippet.js +24 -0
- package/tgsl/wgslGenerator.d.ts +1 -0
- package/tgsl/wgslGenerator.js +98 -66
- package/types.d.ts +23 -2
- package/types.js +30 -5
package/core/function/autoIO.js
CHANGED
|
@@ -67,9 +67,9 @@ export class AutoVertexFn {
|
|
|
67
67
|
setName(impl, 'vertexFn');
|
|
68
68
|
}
|
|
69
69
|
this.#core = createFnCore(impl, 'vertex');
|
|
70
|
-
this.autoIn = new AutoStruct({ ...builtinVertexIn, ...attribs }, undefined
|
|
70
|
+
this.autoIn = new AutoStruct({ ...builtinVertexIn, ...attribs }, undefined);
|
|
71
71
|
setName(this.autoIn, 'VertexIn');
|
|
72
|
-
this.autoOut = new AutoStruct(builtinVertexOut, undefined,
|
|
72
|
+
this.autoOut = new AutoStruct(builtinVertexOut, undefined, locations,
|
|
73
73
|
/* autoInterpolateIntegers */ true);
|
|
74
74
|
setName(this.autoOut, 'VertexOut');
|
|
75
75
|
}
|
|
@@ -2,7 +2,8 @@ import { WgslTypeError } from "../../errors.js";
|
|
|
2
2
|
import { setName } from "../../shared/meta.js";
|
|
3
3
|
import { $getNameForward, $gpuCallable, $internal } from "../../shared/symbols.js";
|
|
4
4
|
import { coerceToSnippet } from "../../tgsl/generationHelpers.js";
|
|
5
|
-
import {
|
|
5
|
+
import { stringifySnippet } from "../../tgsl/stringifySnippet.js";
|
|
6
|
+
import { asComptime, NormalState } from "../../types.js";
|
|
6
7
|
export function isComptimeFn(value) {
|
|
7
8
|
return !!value?.[$internal]?.isComptime;
|
|
8
9
|
}
|
|
@@ -38,15 +39,16 @@ export function comptime(func) {
|
|
|
38
39
|
impl[$getNameForward] = func;
|
|
39
40
|
impl[$gpuCallable] = {
|
|
40
41
|
call(ctx, args) {
|
|
41
|
-
|
|
42
|
+
const comptimeArgs = args.map((s) => asComptime(s));
|
|
43
|
+
if (!comptimeArgs.every((s) => s !== undefined)) {
|
|
42
44
|
throw new WgslTypeError(`Called comptime function with runtime-known values: ${args
|
|
43
|
-
.filter((
|
|
44
|
-
.map((s) => `'${s
|
|
45
|
+
.filter((_, idx) => comptimeArgs[idx] === undefined)
|
|
46
|
+
.map((s) => `'${stringifySnippet(s)}'`)
|
|
45
47
|
.join(', ')}`);
|
|
46
48
|
}
|
|
47
49
|
ctx.pushMode(new NormalState());
|
|
48
50
|
try {
|
|
49
|
-
return coerceToSnippet(func(...
|
|
51
|
+
return coerceToSnippet(func(...comptimeArgs.map((s) => s.value)));
|
|
50
52
|
}
|
|
51
53
|
finally {
|
|
52
54
|
ctx.popMode();
|
|
@@ -3,7 +3,7 @@ import { isPtr } from "../../data/wgslTypes.js";
|
|
|
3
3
|
import { setName } from "../../shared/meta.js";
|
|
4
4
|
import { $gpuCallable } from "../../shared/symbols.js";
|
|
5
5
|
import { tryConvertSnippet } from "../../tgsl/conversion.js";
|
|
6
|
-
import {
|
|
6
|
+
import { asComptime, NormalState } from "../../types.js";
|
|
7
7
|
export function callableSchema(options) {
|
|
8
8
|
const impl = ((...args) => {
|
|
9
9
|
return options.normalImpl(...args);
|
|
@@ -30,10 +30,11 @@ export function callableSchema(options) {
|
|
|
30
30
|
return tryConvertSnippet(ctx, s, argType, false);
|
|
31
31
|
});
|
|
32
32
|
let result;
|
|
33
|
-
|
|
33
|
+
const comptimeArgs = converted.map((s) => asComptime(s));
|
|
34
|
+
if (comptimeArgs.every((s) => s !== undefined)) {
|
|
34
35
|
ctx.pushMode(new NormalState());
|
|
35
36
|
try {
|
|
36
|
-
result = snip(options.normalImpl(...
|
|
37
|
+
result = snip(options.normalImpl(...comptimeArgs.map((s) => s.value)), options.schema(),
|
|
37
38
|
// Functions give up ownership of their return value
|
|
38
39
|
/* origin */ 'constant');
|
|
39
40
|
}
|
|
@@ -3,7 +3,7 @@ import { setName } from "../../shared/meta.js";
|
|
|
3
3
|
import { $gpuCallable } from "../../shared/symbols.js";
|
|
4
4
|
import { tryConvertSnippet } from "../../tgsl/conversion.js";
|
|
5
5
|
import { concretize } from "../../tgsl/generationHelpers.js";
|
|
6
|
-
import {
|
|
6
|
+
import { asComptime, NormalState } from "../../types.js";
|
|
7
7
|
import { isPtr } from "../../data/wgslTypes.js";
|
|
8
8
|
export class MissingCpuImplError extends Error {
|
|
9
9
|
constructor(message) {
|
|
@@ -43,25 +43,26 @@ export function dualImpl(options) {
|
|
|
43
43
|
}
|
|
44
44
|
return tryConvertSnippet(ctx, s, argType, !options.ignoreImplicitCastWarning);
|
|
45
45
|
});
|
|
46
|
-
if (!options.noComptime &&
|
|
47
|
-
converted.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
46
|
+
if (!options.noComptime && typeof options.normalImpl === 'function') {
|
|
47
|
+
const comptimeArgs = converted.map((s) => asComptime(s));
|
|
48
|
+
if (comptimeArgs.every((s) => s !== undefined)) {
|
|
49
|
+
ctx.pushMode(new NormalState());
|
|
50
|
+
try {
|
|
51
|
+
return snip(options.normalImpl(...comptimeArgs.map((s) => s.value)), returnType,
|
|
52
|
+
// Functions give up ownership of their return value
|
|
53
|
+
/* origin */ 'constant', options.sideEffects);
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
// cpuImpl may in some cases be present but implemented only partially.
|
|
57
|
+
// In that case, if the MissingCpuImplError is thrown, we fallback to codegenImpl.
|
|
58
|
+
// If it is any other error, we just rethrow.
|
|
59
|
+
if (!(e instanceof MissingCpuImplError)) {
|
|
60
|
+
throw e;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
ctx.popMode('normal');
|
|
61
65
|
}
|
|
62
|
-
}
|
|
63
|
-
finally {
|
|
64
|
-
ctx.popMode('normal');
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
68
|
const possibleSideEffects = options.sideEffects || args.some((a) => a.possibleSideEffects);
|
|
@@ -7,6 +7,16 @@ export type WithLocations<T extends Record<string, BaseData>> = {
|
|
|
7
7
|
export type IOLayoutToSchema<T> = T extends BaseData ? HasCustomLocation<T> extends true ? T : Decorate<T, Location<0>> : T extends Record<string, BaseData> ? WgslStruct<WithLocations<T>> : T extends {
|
|
8
8
|
type: 'void';
|
|
9
9
|
} ? void : never;
|
|
10
|
+
/**
|
|
11
|
+
* Assigns locations to members.
|
|
12
|
+
* The priority of assigned location is as follows:
|
|
13
|
+
* - location already present in member,
|
|
14
|
+
* - location provided in `locations`,
|
|
15
|
+
* - a free number.
|
|
16
|
+
*
|
|
17
|
+
* Assumes `locations` are consistent with already decorated `members`,
|
|
18
|
+
* throws if a location is repeated.
|
|
19
|
+
*/
|
|
10
20
|
export declare function withLocations<T extends BaseData>(members: Record<string, T> | undefined, locations?: Record<string, number>, autoInterpolateIntegers?: boolean): Record<string, BaseData>;
|
|
11
21
|
export declare function separateBuiltins(schema: Record<string, BaseData>, locations?: Record<string, number>): SeparatedEntryArgs;
|
|
12
22
|
export declare function separateAllAsPositional(schema: Record<string, BaseData>): SeparatedEntryArgs;
|
|
@@ -3,33 +3,42 @@ import { isBuiltin } from "../../data/attributes.js";
|
|
|
3
3
|
import { getCustomLocation, isData } from "../../data/dataTypes.js";
|
|
4
4
|
import { INTERNAL_createStruct } from "../../data/struct.js";
|
|
5
5
|
import { isDecorated, isInteger, isIntegerVec, isInterpolateAttrib, isVoid, } from "../../data/wgslTypes.js";
|
|
6
|
+
/**
|
|
7
|
+
* Assigns locations to members.
|
|
8
|
+
* The priority of assigned location is as follows:
|
|
9
|
+
* - location already present in member,
|
|
10
|
+
* - location provided in `locations`,
|
|
11
|
+
* - a free number.
|
|
12
|
+
*
|
|
13
|
+
* Assumes `locations` are consistent with already decorated `members`,
|
|
14
|
+
* throws if a location is repeated.
|
|
15
|
+
*/
|
|
6
16
|
export function withLocations(members, locations = {}, autoInterpolateIntegers = false) {
|
|
7
17
|
let nextLocation = 0;
|
|
8
|
-
const usedCustomLocations = new Set(
|
|
9
|
-
|
|
18
|
+
const usedCustomLocations = new Set([
|
|
19
|
+
...Object.values(locations),
|
|
20
|
+
...Object.values(members ?? {})
|
|
21
|
+
.map(getCustomLocation)
|
|
22
|
+
.filter((v) => v !== undefined),
|
|
23
|
+
]);
|
|
24
|
+
const entries = Object.entries(members ?? {})
|
|
10
25
|
.map(([key, member]) => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
usedCustomLocations.add(customLocation);
|
|
17
|
-
}
|
|
18
|
-
return [
|
|
19
|
-
key,
|
|
20
|
-
autoInterpolateIntegers ? withFlatInterpolationForInteger(member) : member,
|
|
21
|
-
];
|
|
26
|
+
// Interpolate
|
|
27
|
+
const interpolated = autoInterpolateIntegers
|
|
28
|
+
? withFlatInterpolationForInteger(member)
|
|
29
|
+
: member;
|
|
30
|
+
return [key, interpolated];
|
|
22
31
|
})
|
|
23
32
|
.map(([key, member]) => {
|
|
33
|
+
// Assign locations
|
|
24
34
|
if (isBuiltin(member)) {
|
|
25
|
-
// skipping builtins
|
|
26
35
|
return [key, member];
|
|
27
36
|
}
|
|
28
37
|
if (getCustomLocation(member) !== undefined) {
|
|
29
38
|
// this member is already marked
|
|
30
39
|
return [key, member];
|
|
31
40
|
}
|
|
32
|
-
if (locations[key]) {
|
|
41
|
+
if (locations[key] !== undefined) {
|
|
33
42
|
// location has been determined by a previous procedure
|
|
34
43
|
return [key, location(locations[key], member)];
|
|
35
44
|
}
|
|
@@ -37,7 +46,19 @@ export function withLocations(members, locations = {}, autoInterpolateIntegers =
|
|
|
37
46
|
nextLocation++;
|
|
38
47
|
}
|
|
39
48
|
return [key, location(nextLocation++, member)];
|
|
40
|
-
})
|
|
49
|
+
});
|
|
50
|
+
// Check for duplicate locations
|
|
51
|
+
const used = new Map();
|
|
52
|
+
entries.forEach(([key, member]) => {
|
|
53
|
+
const location = getCustomLocation(member);
|
|
54
|
+
if (location !== undefined) {
|
|
55
|
+
if (used.has(location)) {
|
|
56
|
+
throw new Error(`Location '${location}' is used by multiple keys: '${used.get(location)}', '${key}'.`);
|
|
57
|
+
}
|
|
58
|
+
used.set(location, key);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
return Object.fromEntries(entries);
|
|
41
62
|
}
|
|
42
63
|
export function separateBuiltins(schema, locations = {}) {
|
|
43
64
|
const positionalArgs = [];
|
|
@@ -6,7 +6,7 @@ import type { Prettify } from '../../shared/utilityTypes.ts';
|
|
|
6
6
|
import type { DualFn } from '../../types.ts';
|
|
7
7
|
import { type Providing } from '../slot/slotTypes.ts';
|
|
8
8
|
import type { AnyFn, Implementation, InferArgs, InferImplSchema, InheritArgNames } from './fnTypes.ts';
|
|
9
|
-
import type { Withable } from '../root/rootTypes.ts';
|
|
9
|
+
import type { Configurable, Withable } from '../root/rootTypes.ts';
|
|
10
10
|
/**
|
|
11
11
|
* Describes a function signature (its arguments and return type)
|
|
12
12
|
*/
|
|
@@ -29,6 +29,11 @@ interface TgpuFnBase<ImplSchema extends AnyFn> extends TgpuNamable, Withable<Tgp
|
|
|
29
29
|
readonly shell: TgpuFnShellHeader<Parameters<ImplSchema>, Extract<ReturnType<ImplSchema>, BaseData>>;
|
|
30
30
|
readonly [$providing]?: Providing | undefined;
|
|
31
31
|
$uses(dependencyMap: Record<string, unknown>): this;
|
|
32
|
+
/**
|
|
33
|
+
* Applies a configuration transform to the slots and accessors this function reads,
|
|
34
|
+
* as `root.pipe` does to a pipeline's.
|
|
35
|
+
*/
|
|
36
|
+
pipe(transform: (cfg: Configurable) => Configurable): TgpuFn<ImplSchema>;
|
|
32
37
|
}
|
|
33
38
|
export type TgpuFn<ImplSchema extends AnyFn = (...args: any[]) => any> = DualFn<InferImplSchema<ImplSchema>> & TgpuFnBase<ImplSchema>;
|
|
34
39
|
/**
|
|
@@ -40,6 +45,11 @@ export interface TgpuGenericFn<T extends AnyFn> extends TgpuNamable, Withable<Tg
|
|
|
40
45
|
};
|
|
41
46
|
readonly [$providing]?: Providing | undefined;
|
|
42
47
|
readonly resourceType: 'generic-function';
|
|
48
|
+
/**
|
|
49
|
+
* Applies a configuration transform to the slots and accessors this function reads,
|
|
50
|
+
* as `root.pipe` does to a pipeline's.
|
|
51
|
+
*/
|
|
52
|
+
pipe(transform: (cfg: Configurable) => Configurable): TgpuGenericFn<T>;
|
|
43
53
|
(...args: Parameters<T>): ReturnType<T>;
|
|
44
54
|
}
|
|
45
55
|
export declare function fn<Args extends BaseData[] | []>(argTypes: Args, returnType?: undefined): TgpuFnShell<Args, Void>;
|
package/core/function/tgpuFn.js
CHANGED
|
@@ -12,6 +12,7 @@ import { dualImpl } from "./dualImpl.js";
|
|
|
12
12
|
import { createFnCore } from "./fnCore.js";
|
|
13
13
|
import { stripTemplate } from "./templateUtils.js";
|
|
14
14
|
import { comptime } from "./comptime.js";
|
|
15
|
+
import { ConfigurableImpl } from "../root/configurableImpl.js";
|
|
15
16
|
export function fn(argTypesOrCallback, returnType) {
|
|
16
17
|
if (typeof argTypesOrCallback === 'function') {
|
|
17
18
|
return createGenericFn(argTypesOrCallback, []);
|
|
@@ -39,6 +40,9 @@ export function isGenericFn(value) {
|
|
|
39
40
|
function stringifyPair([slot, value]) {
|
|
40
41
|
return `${getName(slot) ?? '<unnamed>'}=${value}`;
|
|
41
42
|
}
|
|
43
|
+
function bindingsOf(transform) {
|
|
44
|
+
return transform(new ConfigurableImpl([])).bindings;
|
|
45
|
+
}
|
|
42
46
|
function createFn(shell, _implementation) {
|
|
43
47
|
let pairs = [];
|
|
44
48
|
// Unwrapping generic functions
|
|
@@ -68,6 +72,7 @@ function createFn(shell, _implementation) {
|
|
|
68
72
|
const s = isAccessor(slot) || isMutableAccessor(slot) ? slot.slot : slot;
|
|
69
73
|
return createBoundFunction(fn, [[s, value]]);
|
|
70
74
|
}),
|
|
75
|
+
pipe: comptime((transform) => createBoundFunction(fn, bindingsOf(transform))),
|
|
71
76
|
[$resolve](ctx) {
|
|
72
77
|
if (typeof implementation === 'string') {
|
|
73
78
|
addArgTypesToExternals(implementation, shell.argTypes, core);
|
|
@@ -129,6 +134,7 @@ function createBoundFunction(innerFn, pairs) {
|
|
|
129
134
|
const s = isAccessor(slot) || isMutableAccessor(slot) ? slot.slot : slot;
|
|
130
135
|
return createBoundFunction(innerFn, [...pairs, [s, value]]);
|
|
131
136
|
}),
|
|
137
|
+
pipe: comptime((transform) => createBoundFunction(innerFn, [...pairs, ...bindingsOf(transform)])),
|
|
132
138
|
};
|
|
133
139
|
const call = dualImpl({
|
|
134
140
|
name: undefined, // setting name here would override autonaming
|
|
@@ -170,10 +176,11 @@ function createGenericFn(inner, pairs) {
|
|
|
170
176
|
[$resolve](ctx) {
|
|
171
177
|
return ctx.resolve(inner);
|
|
172
178
|
},
|
|
173
|
-
with(slot, value) {
|
|
179
|
+
with: comptime((slot, value) => {
|
|
174
180
|
const s = isAccessor(slot) || isMutableAccessor(slot) ? slot.slot : slot;
|
|
175
181
|
return createGenericFn(inner, [...pairs, [s, value]]);
|
|
176
|
-
},
|
|
182
|
+
}),
|
|
183
|
+
pipe: comptime((transform) => createGenericFn(inner, [...pairs, ...bindingsOf(transform)])),
|
|
177
184
|
};
|
|
178
185
|
const call = (...args) => {
|
|
179
186
|
return inner(...args);
|
|
@@ -286,8 +286,10 @@ declare class RenderPipelineCore implements SelfResolvable {
|
|
|
286
286
|
};
|
|
287
287
|
}
|
|
288
288
|
/**
|
|
289
|
-
*
|
|
290
|
-
*
|
|
289
|
+
* Throws, when vertexOut and fragmentIn don't match in terms of custom locations.
|
|
290
|
+
*
|
|
291
|
+
* @privateRemarks
|
|
292
|
+
* Note that vertexOut is undefined in shellless functions.
|
|
291
293
|
*/
|
|
292
294
|
export declare function matchUpVaryingLocations(vertexOut: TgpuVertexFn.Out | undefined, fragmentIn: TgpuFragmentFn.In | undefined, vertexFnName: string, fragmentFnName: string): Record<string, number>;
|
|
293
295
|
export {};
|
|
@@ -181,7 +181,7 @@ class TgpuRenderPipelineImpl {
|
|
|
181
181
|
withStencilReference(reference) {
|
|
182
182
|
return this.#withPriors({ stencilReference: reference });
|
|
183
183
|
}
|
|
184
|
-
withIndexBuffer(buffer, indexFormatOrOffset,
|
|
184
|
+
withIndexBuffer(buffer, indexFormatOrOffset, offsetBytesOrSizeElements, sizeBytesOrUndefined) {
|
|
185
185
|
if (isGPUBuffer(buffer)) {
|
|
186
186
|
if (typeof indexFormatOrOffset !== 'string') {
|
|
187
187
|
throw new Error('If a GPUBuffer is passed, indexFormat must be provided.');
|
|
@@ -190,8 +190,8 @@ class TgpuRenderPipelineImpl {
|
|
|
190
190
|
indexBuffer: {
|
|
191
191
|
buffer,
|
|
192
192
|
indexFormat: indexFormatOrOffset,
|
|
193
|
-
offsetBytes:
|
|
194
|
-
sizeBytes:
|
|
193
|
+
offsetBytes: offsetBytesOrSizeElements,
|
|
194
|
+
sizeBytes: sizeBytesOrUndefined,
|
|
195
195
|
},
|
|
196
196
|
});
|
|
197
197
|
}
|
|
@@ -207,8 +207,8 @@ class TgpuRenderPipelineImpl {
|
|
|
207
207
|
offsetBytes: indexFormatOrOffset !== undefined
|
|
208
208
|
? indexFormatOrOffset * sizeOf(elementType)
|
|
209
209
|
: undefined,
|
|
210
|
-
sizeBytes:
|
|
211
|
-
?
|
|
210
|
+
sizeBytes: offsetBytesOrSizeElements !== undefined
|
|
211
|
+
? offsetBytesOrSizeElements * sizeOf(elementType)
|
|
212
212
|
: undefined,
|
|
213
213
|
},
|
|
214
214
|
});
|
|
@@ -469,8 +469,10 @@ class RenderPipelineCore {
|
|
|
469
469
|
}
|
|
470
470
|
}
|
|
471
471
|
/**
|
|
472
|
-
*
|
|
473
|
-
*
|
|
472
|
+
* Throws, when vertexOut and fragmentIn don't match in terms of custom locations.
|
|
473
|
+
*
|
|
474
|
+
* @privateRemarks
|
|
475
|
+
* Note that vertexOut is undefined in shellless functions.
|
|
474
476
|
*/
|
|
475
477
|
export function matchUpVaryingLocations(vertexOut = {}, fragmentIn = {}, vertexFnName, fragmentFnName) {
|
|
476
478
|
const locations = {};
|
|
@@ -495,13 +497,13 @@ export function matchUpVaryingLocations(vertexOut = {}, fragmentIn = {}, vertexF
|
|
|
495
497
|
saveLocation(key, customLocation);
|
|
496
498
|
}
|
|
497
499
|
else if (locations[key] !== customLocation) {
|
|
498
|
-
|
|
500
|
+
throw new Error(`Mismatched location between vertexFn (${vertexFnName}) output (${locations[key]}) and fragmentFn (${fragmentFnName}) input (${customLocation}) for the key "${key}".`);
|
|
499
501
|
}
|
|
500
502
|
}
|
|
501
503
|
// automatically assign remaining locations to the rest
|
|
502
504
|
let nextLocation = 0;
|
|
503
|
-
for (const key of Object.
|
|
504
|
-
if (isBuiltin(
|
|
505
|
+
for (const [key, value] of [...Object.entries(vertexOut), ...Object.entries(fragmentIn)]) {
|
|
506
|
+
if (isBuiltin(value) || locations[key] !== undefined) {
|
|
505
507
|
continue;
|
|
506
508
|
}
|
|
507
509
|
while (usedLocations.has(nextLocation)) {
|
|
@@ -41,14 +41,27 @@ async function readTimestamps(querySet, registrations) {
|
|
|
41
41
|
if (!querySet.available) {
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
let result;
|
|
45
|
+
try {
|
|
46
|
+
result = await querySet.read();
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// The read was cancelled or failed (e.g. the device was destroyed or lost mid-read).
|
|
50
|
+
// Genuine validation errors are already reported through the device's error handling.
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
45
53
|
for (const { priors, callback } of registrations) {
|
|
46
54
|
const start = result[priors.timestampWrites?.beginningOfPassWriteIndex ?? 0];
|
|
47
55
|
const end = result[priors.timestampWrites?.endOfPassWriteIndex ?? 1];
|
|
48
56
|
if (start === undefined || end === undefined) {
|
|
49
57
|
throw new Error('QuerySet did not return valid timestamps.');
|
|
50
58
|
}
|
|
51
|
-
|
|
59
|
+
try {
|
|
60
|
+
await callback(start, end);
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
console.error('An error was thrown from a performance callback (withPerformanceCallback). Errors thrown here cannot be caught by the caller, so handle them within the callback itself.', err);
|
|
64
|
+
}
|
|
52
65
|
}
|
|
53
66
|
}
|
|
54
67
|
/** Returns false when the encoder is one we cannot defer work to, meaning the callback never fires */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { $gpuValueOf, $internal, $resolve } from '../../shared/symbols.ts';
|
|
2
2
|
import type { LayoutMembership } from '../../tgpuBindGroupLayout.ts';
|
|
3
3
|
import { type WgslExternalTexture } from '../../data/texture.ts';
|
|
4
|
-
import type
|
|
4
|
+
import { type ResolutionCtx, type SelfResolvable } from '../../types.ts';
|
|
5
5
|
import { type ResolvedSnippet } from '../../data/snippet.ts';
|
|
6
6
|
import type { Infer } from '../../shared/repr.ts';
|
|
7
7
|
export interface TgpuExternalTexture {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { $gpuValueOf, $internal, $ownSnippet, $resolve } from "../../shared/symbols.js";
|
|
1
|
+
import { $comptimeValueOf, $gpuValueOf, $internal, $ownSnippet, $resolve, } from "../../shared/symbols.js";
|
|
2
2
|
import { getName, setName } from "../../shared/meta.js";
|
|
3
3
|
import { textureExternal } from "../../data/texture.js";
|
|
4
4
|
import { valueProxyHandler } from "../valueProxyUtils.js";
|
|
5
5
|
import { inCodegenMode } from "../../execMode.js";
|
|
6
|
+
import { runtimeOnly } from "../../types.js";
|
|
6
7
|
import { snip } from "../../data/snippet.js";
|
|
7
8
|
export function isExternalTexture(value) {
|
|
8
9
|
return value?.resourceType === 'external-texture';
|
|
@@ -33,6 +34,7 @@ export class TgpuExternalTextureImpl {
|
|
|
33
34
|
get [$ownSnippet]() {
|
|
34
35
|
return snip(this, schema, 'handle', false);
|
|
35
36
|
},
|
|
37
|
+
[$comptimeValueOf]: runtimeOnly,
|
|
36
38
|
[$resolve]: (ctx) => ctx.resolve(this),
|
|
37
39
|
toString: () => `textureExternal:${getName(this) ?? '<unnamed>'}.$`,
|
|
38
40
|
}, valueProxyHandler);
|
|
@@ -9,7 +9,7 @@ import type { TgpuDeviceOwningSoul, TgpuSoul } from '../../shared/soul.ts';
|
|
|
9
9
|
import { $gpuValueOf, $internal, $repr, $resolve, $soul } from '../../shared/symbols.ts';
|
|
10
10
|
import type { Default, TypedArray, UnionToIntersection } from '../../shared/utilityTypes.ts';
|
|
11
11
|
import type { LayoutMembership } from '../../tgpuBindGroupLayout.ts';
|
|
12
|
-
import type
|
|
12
|
+
import { type ResolutionCtx, type SelfResolvable } from '../../types.ts';
|
|
13
13
|
import type { ExperimentalTgpuRoot } from '../root/rootTypes.ts';
|
|
14
14
|
import type { TextureProps } from './textureProps.ts';
|
|
15
15
|
import type { AllowedUsages, LiteralToExtensionMap, RenderFlag, SampledFlag } from './usageExtension.ts';
|
package/core/texture/texture.js
CHANGED
|
@@ -3,7 +3,8 @@ import { inCodegenMode } from "../../execMode.js";
|
|
|
3
3
|
import { snip } from "../../data/snippet.js";
|
|
4
4
|
import { getName, setName } from "../../shared/meta.js";
|
|
5
5
|
import { getTextureFormatInfo, } from "./textureFormats.js";
|
|
6
|
-
import { $gpuValueOf, $internal, $ownSnippet, $repr, $resolve, $soul, } from "../../shared/symbols.js";
|
|
6
|
+
import { $gpuValueOf, $internal, $ownSnippet, $comptimeValueOf, $repr, $resolve, $soul, } from "../../shared/symbols.js";
|
|
7
|
+
import { runtimeOnly } from "../../types.js";
|
|
7
8
|
import { valueProxyHandler } from "../valueProxyUtils.js";
|
|
8
9
|
import { clearTextureWithColor, generateTextureMipmaps, writeImages } from "./textureUtils.js";
|
|
9
10
|
import { mipLevelSize, origin3d, resolveImageWrite, writeRegionSize, } from "./textureWrite.js";
|
|
@@ -313,6 +314,7 @@ class TgpuFixedTextureViewImpl {
|
|
|
313
314
|
get [$ownSnippet]() {
|
|
314
315
|
return snip(this, schema, /* origin */ 'handle', false);
|
|
315
316
|
},
|
|
317
|
+
[$comptimeValueOf]: runtimeOnly,
|
|
316
318
|
[$resolve]: (ctx) => ctx.resolve(this),
|
|
317
319
|
toString: () => `${this.toString()}.$`,
|
|
318
320
|
}, valueProxyHandler);
|
|
@@ -375,6 +377,7 @@ export class TgpuLaidOutTextureViewImpl {
|
|
|
375
377
|
get [$ownSnippet]() {
|
|
376
378
|
return snip(this, schema, /* origin */ 'handle', false);
|
|
377
379
|
},
|
|
380
|
+
[$comptimeValueOf]: runtimeOnly,
|
|
378
381
|
[$resolve]: (ctx) => ctx.resolve(this),
|
|
379
382
|
toString: () => `${this.toString()}.$`,
|
|
380
383
|
}, valueProxyHandler);
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { $internal, $resolve } from '../../shared/symbols.ts';
|
|
1
|
+
import { $comptimeValueOf, $internal, $resolve } from '../../shared/symbols.ts';
|
|
2
|
+
import { type ComptimeValue, type ResolutionCtx, type SelfResolvable, type WithComptimeValue } from '../../types.ts';
|
|
2
3
|
import { type ResolvedSnippet, type Snippet } from '../../data/snippet.ts';
|
|
3
|
-
import type { ResolutionCtx, SelfResolvable } from '../../types.ts';
|
|
4
4
|
/**
|
|
5
5
|
* The result of calling `tgpu.unroll(...)`. The code responsible for
|
|
6
6
|
* generating shader code can check if the value of a snippet is
|
|
7
7
|
* an instance of `UnrollableIterable`, and act accordingly.
|
|
8
8
|
*/
|
|
9
|
-
export declare class UnrollableIterable implements SelfResolvable {
|
|
9
|
+
export declare class UnrollableIterable implements SelfResolvable, WithComptimeValue {
|
|
10
10
|
readonly [$internal] = true;
|
|
11
11
|
readonly snippet: Snippet;
|
|
12
12
|
constructor(snippet: Snippet);
|
|
13
|
+
[$comptimeValueOf](): ComptimeValue;
|
|
13
14
|
[$resolve](ctx: ResolutionCtx): ResolvedSnippet;
|
|
14
15
|
}
|
|
15
16
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { $gpuCallable, $internal, $resolve } from "../../shared/symbols.js";
|
|
1
|
+
import { $comptimeValueOf, $gpuCallable, $internal, $resolve } from "../../shared/symbols.js";
|
|
2
2
|
import { setName } from "../../shared/meta.js";
|
|
3
|
+
import { asComptime, } from "../../types.js";
|
|
3
4
|
import { withValue } from "../../data/snippet.js";
|
|
4
5
|
/**
|
|
5
6
|
* The result of calling `tgpu.unroll(...)`. The code responsible for
|
|
@@ -12,6 +13,11 @@ export class UnrollableIterable {
|
|
|
12
13
|
constructor(snippet) {
|
|
13
14
|
this.snippet = snippet;
|
|
14
15
|
}
|
|
16
|
+
[$comptimeValueOf]() {
|
|
17
|
+
// Outside of for-of loops, `tgpu.unroll(...)` is transparent, just like in JS
|
|
18
|
+
const known = asComptime(this.snippet);
|
|
19
|
+
return known ? { comptime: true, value: known.value } : { comptime: false };
|
|
20
|
+
}
|
|
15
21
|
[$resolve](ctx) {
|
|
16
22
|
return ctx.resolveSnippet(this.snippet);
|
|
17
23
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
import { type SelfResolvable, type WithOwnSnippet } from '../types.ts';
|
|
2
|
-
|
|
1
|
+
import { type SelfResolvable, type WithComptimeValue, type WithOwnSnippet } from '../types.ts';
|
|
2
|
+
type ValueProxyTarget = SelfResolvable & WithOwnSnippet & WithComptimeValue;
|
|
3
|
+
export declare const valueProxyHandler: ProxyHandler<ValueProxyTarget>;
|
|
3
4
|
export declare function getGpuValueRecursively<T>(value: unknown): T;
|
|
5
|
+
export {};
|
package/core/valueProxyUtils.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { getGPUValue } from "../getGPUValue.js";
|
|
2
|
-
import { $internal, $ownSnippet, $resolve } from "../shared/symbols.js";
|
|
2
|
+
import { $comptimeValueOf, $internal, $ownSnippet, $resolve } from "../shared/symbols.js";
|
|
3
3
|
import { accessIndex } from "../tgsl/accessIndex.js";
|
|
4
4
|
import { accessProp } from "../tgsl/accessProp.js";
|
|
5
|
-
import { getOwnSnippet } from "../types.js";
|
|
5
|
+
import { getOwnSnippet, runtimeOnly, } from "../types.js";
|
|
6
6
|
export const valueProxyHandler = {
|
|
7
7
|
get(target, prop) {
|
|
8
8
|
if (prop in target) {
|
|
@@ -26,6 +26,7 @@ export const valueProxyHandler = {
|
|
|
26
26
|
[$internal]: true,
|
|
27
27
|
[$resolve]: (ctx) => ctx.resolveSnippet(accessed),
|
|
28
28
|
[$ownSnippet]: accessed,
|
|
29
|
+
[$comptimeValueOf]: runtimeOnly,
|
|
29
30
|
toString: () => `${String(target)}[${prop}]`,
|
|
30
31
|
}, valueProxyHandler);
|
|
31
32
|
}
|
|
@@ -38,6 +39,7 @@ export const valueProxyHandler = {
|
|
|
38
39
|
[$internal]: true,
|
|
39
40
|
[$resolve]: (ctx) => ctx.resolveSnippet(accessed),
|
|
40
41
|
[$ownSnippet]: accessed,
|
|
42
|
+
[$comptimeValueOf]: runtimeOnly,
|
|
41
43
|
toString: () => `${String(target)}.${prop}`,
|
|
42
44
|
}, valueProxyHandler);
|
|
43
45
|
},
|
package/data/compiledIO.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { roundUp } from "../mathUtils.js";
|
|
2
2
|
import { logger } from "../tgpuLogger.js";
|
|
3
3
|
import { alignmentOf } from "./alignmentOf.js";
|
|
4
|
-
import { isDisarray, isUnstruct } from "./dataTypes.js";
|
|
4
|
+
import { isDisarray, isLooseDecorated, isUnstruct } from "./dataTypes.js";
|
|
5
5
|
import { offsetsForProps } from "./offsets.js";
|
|
6
6
|
import { sizeOf } from "./sizeOf.js";
|
|
7
7
|
import { formatToWGSLType, isPackedData } from "./vertexFormatData.js";
|
|
@@ -131,7 +131,7 @@ export function buildWriter(node, offsetExpr, valueExpr, depth = 0, partial = fa
|
|
|
131
131
|
const emitBlock = (addr, block) => partial ? `if (${addr} < endOffset) {\n${block}}\n` : block;
|
|
132
132
|
function go(node, offsetExpr, valueExpr, depth) {
|
|
133
133
|
const loopVar = ['i', 'j', 'k'][depth] || `i${depth}`;
|
|
134
|
-
if (wgsl.isAtomic(node) || wgsl.isDecorated(node)) {
|
|
134
|
+
if (wgsl.isAtomic(node) || wgsl.isDecorated(node) || isLooseDecorated(node)) {
|
|
135
135
|
return go(node.inner, offsetExpr, valueExpr, depth);
|
|
136
136
|
}
|
|
137
137
|
if (wgsl.isWgslStruct(node) || isUnstruct(node)) {
|
|
@@ -178,15 +178,18 @@ export function buildWriter(node, offsetExpr, valueExpr, depth = 0, partial = fa
|
|
|
178
178
|
const primitive = typeToPrimitive[node.type];
|
|
179
179
|
const writeFunc = primitiveToWriteFunction[primitive];
|
|
180
180
|
const matSize = wgsl.isMat2x2f(node) ? 2 : wgsl.isMat3x3f(node) ? 3 : 4;
|
|
181
|
-
const rowStride = roundUp(matSize * 4, 8);
|
|
182
181
|
const components = ['x', 'y', 'z', 'w'];
|
|
183
|
-
const wgslElementStride =
|
|
184
|
-
return Array.from({ length: matSize *
|
|
185
|
-
const col = Math.floor(idx /
|
|
186
|
-
const row = idx %
|
|
182
|
+
const wgslElementStride = roundUp(matSize, 2);
|
|
183
|
+
return Array.from({ length: matSize * wgslElementStride }, (_, idx) => {
|
|
184
|
+
const col = Math.floor(idx / wgslElementStride);
|
|
185
|
+
const row = idx % wgslElementStride;
|
|
186
|
+
const addr = `(${offsetExpr} + ${idx * 4})`;
|
|
187
|
+
if (row >= matSize) {
|
|
188
|
+
// mat3x3f padding
|
|
189
|
+
return emitWrite(writeFunc, addr, '0');
|
|
190
|
+
}
|
|
187
191
|
const packedIndex = col * matSize + row;
|
|
188
|
-
|
|
189
|
-
return emitWrite(writeFunc, `(${offsetExpr} + ${col * rowStride + row * 4})`, `ArrayBuffer.isView(${valueExpr}) ? ${valueExpr}[${wgslIndex}] : Array.isArray(${valueExpr}) ? ${valueExpr}[${packedIndex}] : ${valueExpr}.columns[${col}].${components[row]}`);
|
|
192
|
+
return emitWrite(writeFunc, addr, `ArrayBuffer.isView(${valueExpr}) ? ${valueExpr}[${idx}] : Array.isArray(${valueExpr}) ? ${valueExpr}[${packedIndex}] : ${valueExpr}.columns[${col}].${components[row]}`);
|
|
190
193
|
}).join('');
|
|
191
194
|
}
|
|
192
195
|
if (isPackedData(node)) {
|
package/data/dataIO.d.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { type ISerialInput, type ISerialOutput } from 'typed-binary';
|
|
2
2
|
import type { Infer, InferInput } from '../shared/repr.ts';
|
|
3
|
-
import
|
|
4
|
-
import { type BaseData } from './wgslTypes.ts';
|
|
3
|
+
import * as wgsl from './wgslTypes.ts';
|
|
5
4
|
import type { BufferWriteOptions } from '../core/buffer/buffer.ts';
|
|
6
|
-
export declare function writeData<TData extends wgsl.BaseData>(output: ISerialOutput, schema: TData, value:
|
|
5
|
+
export declare function writeData<TData extends wgsl.BaseData>(output: ISerialOutput, schema: TData, value: InferInput<TData>): void;
|
|
7
6
|
export declare function readData<TData extends wgsl.BaseData>(input: ISerialInput, schema: TData): Infer<TData>;
|
|
8
|
-
export declare function calculateOffsets<T extends BaseData>(options: BufferWriteOptions | undefined, schema: T, data: InferInput<T> | ArrayBuffer | ArrayBufferView): {
|
|
7
|
+
export declare function calculateOffsets<T extends wgsl.BaseData>(options: BufferWriteOptions | undefined, schema: T, data: InferInput<T> | ArrayBuffer | ArrayBufferView): {
|
|
9
8
|
startOffset: number;
|
|
10
9
|
endOffset: number;
|
|
11
10
|
};
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
/**
|
|
12
|
+
* A function for filling in buffers with data based on TypeGPU schemas.
|
|
13
|
+
* When schema instances or JS arrays are passed, the padding is applied automatically.
|
|
14
|
+
* ArrayBuffers are expected to be already padded.
|
|
15
|
+
*/
|
|
16
|
+
export declare function writeToArrayBuffer<T extends wgsl.BaseData>(buffer: ArrayBuffer, schema: T, data: InferInput<T> | ArrayBuffer | ArrayBufferView, options?: BufferWriteOptions): void;
|
|
17
|
+
export declare function readFromArrayBuffer<T extends wgsl.BaseData>(buffer: ArrayBuffer, schema: T): Infer<T>;
|