typegpu 0.12.6 → 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.
Files changed (87) hide show
  1. package/common/writeSoA.js +16 -0
  2. package/core/buffer/buffer.js +47 -11
  3. package/core/commandEncoder/attachments.js +5 -2
  4. package/core/function/autoIO.js +5 -3
  5. package/core/function/comptime.js +7 -5
  6. package/core/function/createCallableSchema.js +4 -3
  7. package/core/function/dualImpl.js +20 -19
  8. package/core/function/ioSchema.d.ts +17 -2
  9. package/core/function/ioSchema.js +74 -27
  10. package/core/function/tgpuFn.d.ts +11 -1
  11. package/core/function/tgpuFn.js +9 -2
  12. package/core/function/tgpuFragmentFn.d.ts +1 -1
  13. package/core/function/tgpuFragmentFn.js +2 -1
  14. package/core/function/tgpuVertexFn.js +2 -1
  15. package/core/pipeline/connectAttachmentToShader.d.ts +2 -2
  16. package/core/pipeline/connectAttachmentToShader.js +16 -21
  17. package/core/pipeline/connectTargetsToShader.d.ts +5 -1
  18. package/core/pipeline/connectTargetsToShader.js +23 -18
  19. package/core/pipeline/renderPipeline.d.ts +9 -6
  20. package/core/pipeline/renderPipeline.js +22 -22
  21. package/core/pipeline/timeable.js +15 -2
  22. package/core/resolve/namespace.js +2 -2
  23. package/core/root/configurableImpl.js +1 -2
  24. package/core/root/rootTypes.d.ts +6 -2
  25. package/core/slot/accessor.d.ts +1 -1
  26. package/core/slot/accessor.js +72 -5
  27. package/core/texture/externalTexture.d.ts +1 -1
  28. package/core/texture/externalTexture.js +3 -1
  29. package/core/texture/texture.d.ts +1 -1
  30. package/core/texture/texture.js +12 -5
  31. package/core/unroll/tgpuUnroll.d.ts +4 -3
  32. package/core/unroll/tgpuUnroll.js +7 -1
  33. package/core/valueProxyUtils.d.ts +4 -2
  34. package/core/valueProxyUtils.js +4 -2
  35. package/data/autoStruct.d.ts +1 -1
  36. package/data/autoStruct.js +8 -4
  37. package/data/compiledIO.js +12 -9
  38. package/data/dataIO.d.ts +10 -6
  39. package/data/dataIO.js +19 -4
  40. package/data/dataTypes.d.ts +0 -5
  41. package/data/dataTypes.js +0 -6
  42. package/data/deepEqual.js +19 -9
  43. package/data/matrix.js +10 -3
  44. package/data/matrixColumnsAccess.d.ts +8 -0
  45. package/data/matrixColumnsAccess.js +15 -0
  46. package/data/offsetUtils.js +181 -102
  47. package/data/partialIO.d.ts +8 -1
  48. package/data/partialIO.js +36 -19
  49. package/data/ref.d.ts +12 -5
  50. package/data/ref.js +51 -24
  51. package/data/snippet.d.ts +13 -4
  52. package/data/snippet.js +17 -4
  53. package/data/struct.js +3 -0
  54. package/data/wgslTypes.d.ts +6 -3
  55. package/internal.d.ts +1 -0
  56. package/nameUtils.d.ts +7 -0
  57. package/nameUtils.js +60 -0
  58. package/package.json +1 -1
  59. package/resolutionCtx.js +1 -1
  60. package/shared/meta.js +1 -1
  61. package/shared/symbols.d.ts +5 -0
  62. package/shared/symbols.js +6 -1
  63. package/shared/tseynit.js +54 -50
  64. package/std/boolean.js +9 -10
  65. package/std/numeric.js +1 -4
  66. package/std/operators.js +9 -4
  67. package/std/packing.js +10 -10
  68. package/std/texture.d.ts +2 -2
  69. package/tgpuLogger.d.ts +1 -1
  70. package/tgpuLogger.js +0 -2
  71. package/tgsl/accessIndex.js +37 -38
  72. package/tgsl/accessProp.js +10 -7
  73. package/tgsl/conversion.js +3 -2
  74. package/tgsl/forOfUtils.js +8 -8
  75. package/tgsl/generationHelpers.d.ts +4 -3
  76. package/tgsl/generationHelpers.js +18 -5
  77. package/tgsl/infixDispatch.d.ts +4 -1
  78. package/tgsl/infixDispatch.js +13 -1
  79. package/tgsl/makeDereferenceable.d.ts +1 -1
  80. package/tgsl/makeDereferenceable.js +3 -1
  81. package/tgsl/shellless.js +5 -9
  82. package/tgsl/stringifySnippet.d.ts +9 -0
  83. package/tgsl/stringifySnippet.js +24 -0
  84. package/tgsl/wgslGenerator.d.ts +1 -0
  85. package/tgsl/wgslGenerator.js +180 -86
  86. package/types.d.ts +23 -2
  87. package/types.js +30 -5
@@ -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
  },
@@ -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
  */
@@ -22,11 +22,17 @@ export class AutoStruct {
22
22
  #locations;
23
23
  #cachedStruct;
24
24
  #typeForExtraProps;
25
- constructor(validProps, typeForExtraProps, locations) {
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';
@@ -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 = rowStride / 4;
184
- return Array.from({ length: matSize * matSize }, (_, idx) => {
185
- const col = Math.floor(idx / matSize);
186
- const row = idx % matSize;
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
- const wgslIndex = col * wgslElementStride + row;
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 type * as wgsl from './wgslTypes.ts';
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: Infer<TData>): void;
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
- export declare function writeToArrayBuffer<T extends BaseData>(buffer: ArrayBuffer, schema: T, data: InferInput<T> | ArrayBuffer | ArrayBufferView, options?: BufferWriteOptions): void;
13
- export declare function readFromArrayBuffer<T extends BaseData>(buffer: ArrayBuffer, schema: T): Infer<T>;
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>;
package/data/dataIO.js CHANGED
@@ -4,7 +4,7 @@ import { alignmentOf, customAlignmentOf } from "./alignmentOf.js";
4
4
  import { mat2x2f, mat3x3f, mat4x4f } from "./matrix.js";
5
5
  import { sizeOf } from "./sizeOf.js";
6
6
  import { vec2f, vec2h, vec2i, vec2u, vec3f, vec3h, vec3i, vec3u, vec4f, vec4h, vec4i, vec4u, } from "./vector.js";
7
- import { isWgslArray } from "./wgslTypes.js";
7
+ import * as wgsl from "./wgslTypes.js";
8
8
  import { getCompiledWriter } from "./compiledIO.js";
9
9
  import { getName } from "../shared/meta.js";
10
10
  import { roundUp } from "../mathUtils.js";
@@ -104,8 +104,18 @@ const dataWriters = {
104
104
  }
105
105
  },
106
106
  mat3x3f(output, _, value) {
107
- for (let i = 0; i < value.length; ++i) {
108
- output.writeFloat32(value[i]);
107
+ if (value instanceof Float32Array || wgsl.isMatInstance(value)) {
108
+ for (let i = 0; i < value.length; ++i) {
109
+ output.writeFloat32(value[i]);
110
+ }
111
+ }
112
+ else {
113
+ for (let i = 0; i < 3; ++i) {
114
+ for (let j = 0; j < 3; ++j) {
115
+ output.writeFloat32(value[3 * i + j]);
116
+ }
117
+ output.writeFloat32(0);
118
+ }
109
119
  }
110
120
  },
111
121
  mat4x4f(output, _, value) {
@@ -607,7 +617,7 @@ export function calculateOffsets(options, schema, data) {
607
617
  const bufferSize = sizeOf(schema);
608
618
  const startOffset = options?.startOffset ?? 0;
609
619
  let naturalSize = undefined;
610
- if (isWgslArray(schema) && Array.isArray(data)) {
620
+ if (wgsl.isWgslArray(schema) && Array.isArray(data)) {
611
621
  const arrayData = data;
612
622
  naturalSize =
613
623
  arrayData.length * roundUp(sizeOf(schema.elementType), alignmentOf(schema.elementType));
@@ -619,6 +629,11 @@ export function calculateOffsets(options, schema, data) {
619
629
  const endOffset = options?.endOffset ?? naturalEndOffset ?? bufferSize;
620
630
  return { startOffset, endOffset };
621
631
  }
632
+ /**
633
+ * A function for filling in buffers with data based on TypeGPU schemas.
634
+ * When schema instances or JS arrays are passed, the padding is applied automatically.
635
+ * ArrayBuffers are expected to be already padded.
636
+ */
622
637
  export function writeToArrayBuffer(buffer, schema, data, options) {
623
638
  const { startOffset, endOffset } = calculateOffsets(options, schema, data);
624
639
  // Fast path: raw byte copy, user guarantees the padded layout
@@ -3,7 +3,6 @@ import type { Infer, InferGPURecord, InferInput, InferInputRecord, InferPatch, I
3
3
  import type { $gpuRepr, $inRepr, $invalidSchemaReason, $memIdent, $repr, $reprPartial, $reprPatch, $validVertexSchema } from '../shared/symbols.ts';
4
4
  import type { Prettify } from '../shared/utilityTypes.ts';
5
5
  import type { WgslExternalTexture, WgslStorageTexture, WgslTexture } from './texture.ts';
6
- import type { Snippet } from './snippet.ts';
7
6
  import type { PackedData } from './vertexFormatData.ts';
8
7
  import * as wgsl from './wgslTypes.ts';
9
8
  import type { WgslComparisonSampler, WgslSampler } from './sampler.ts';
@@ -130,8 +129,4 @@ export type AnyData = wgsl.AnyWgslData | AnyLooseData;
130
129
  export type AnyConcreteData = Exclude<AnyData, wgsl.AbstractInt | wgsl.AbstractFloat | wgsl.Void | WgslTexture | WgslStorageTexture | WgslExternalTexture | WgslSampler | WgslComparisonSampler>;
131
130
  export declare const UnknownData: unique symbol;
132
131
  export type UnknownData = typeof UnknownData;
133
- export declare class MatrixColumnsAccess {
134
- readonly matrix: Snippet;
135
- constructor(matrix: Snippet);
136
- }
137
132
  export {};
package/data/dataTypes.js CHANGED
@@ -73,9 +73,3 @@ export function isData(value) {
73
73
  return wgsl.isWgslData(value) || isLooseData(value);
74
74
  }
75
75
  export const UnknownData = Symbol('UNKNOWN');
76
- export class MatrixColumnsAccess {
77
- matrix;
78
- constructor(matrix) {
79
- this.matrix = matrix;
80
- }
81
- }
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
- // Create comparable string representations for each attribute
69
- const getAttrKey = (attr) => {
70
- const anyAttr = attr;
71
- return `${anyAttr.type}(${(anyAttr.params ?? []).join(',')})`;
72
- };
73
- const attrsA = a.attribs.map(getAttrKey);
74
- const attrsB = b.attribs.map(getAttrKey);
75
- for (let i = 0; i < attrsA.length; i++) {
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 (typeof arg === 'number') {
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, {
@@ -0,0 +1,8 @@
1
+ import { $comptimeValueOf } from '../shared/symbols.ts';
2
+ import { type ComptimeValue, type WithComptimeValue } from '../types.ts';
3
+ import type { Snippet } from './snippet.ts';
4
+ export declare class MatrixColumnsAccess implements WithComptimeValue {
5
+ readonly matrix: Snippet;
6
+ constructor(matrix: Snippet);
7
+ [$comptimeValueOf](): ComptimeValue;
8
+ }
@@ -0,0 +1,15 @@
1
+ import { $comptimeValueOf } from "../shared/symbols.js";
2
+ import { asComptime } from "../types.js";
3
+ export class MatrixColumnsAccess {
4
+ matrix;
5
+ constructor(matrix) {
6
+ this.matrix = matrix;
7
+ }
8
+ [$comptimeValueOf]() {
9
+ // Same as accessing `.columns` on a matrix in JS
10
+ const known = asComptime(this.matrix);
11
+ return known
12
+ ? { comptime: true, value: known.value.columns }
13
+ : { comptime: false };
14
+ }
15
+ }