Skip to content

Commit 86ba56d

Browse files
fix: Throw error when accessing matrix elements directly (#1625)
1 parent 104a289 commit 86ba56d

File tree

4 files changed

+94
-23
lines changed

4 files changed

+94
-23
lines changed

packages/typegpu/src/data/dataTypes.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,9 @@ export class InfixDispatch {
236236
readonly operator: (lhs: Snippet, rhs: Snippet) => Snippet,
237237
) {}
238238
}
239+
240+
export class MatrixColumnsAccess {
241+
constructor(
242+
readonly matrix: Snippet,
243+
) {}
244+
}

packages/typegpu/src/tgsl/wgslGenerator.ts

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
import * as tinyest from 'tinyest';
2+
import { stitch, stitchWithExactTypes } from '../core/resolve/stitch.ts';
23
import { arrayOf } from '../data/array.ts';
34
import {
45
type AnyData,
56
InfixDispatch,
67
isData,
78
isLooseData,
9+
MatrixColumnsAccess,
810
UnknownData,
911
} from '../data/dataTypes.ts';
10-
import { isSnippet, snip, type Snippet } from '../data/snippet.ts';
1112
import { abstractInt, bool, u32 } from '../data/numeric.ts';
13+
import { isSnippet, snip, type Snippet } from '../data/snippet.ts';
1214
import * as wgsl from '../data/wgslTypes.ts';
1315
import { ResolutionError, WgslTypeError } from '../errors.ts';
1416
import { getName } from '../shared/meta.ts';
1517
import { $internal } from '../shared/symbols.ts';
18+
import { add, div, mul, sub } from '../std/operators.ts';
1619
import { type FnArgsConversionHint, isMarkedInternal } from '../types.ts';
20+
import {
21+
convertStructValues,
22+
convertToCommonType,
23+
tryConvertSnippet,
24+
} from './conversion.ts';
1725
import {
1826
coerceToSnippet,
1927
concretize,
@@ -22,13 +30,6 @@ import {
2230
getTypeForPropAccess,
2331
numericLiteralToSnippet,
2432
} from './generationHelpers.ts';
25-
import {
26-
convertStructValues,
27-
convertToCommonType,
28-
tryConvertSnippet,
29-
} from './conversion.ts';
30-
import { add, div, mul, sub } from '../std/operators.ts';
31-
import { stitch, stitchWithExactTypes } from '../core/resolve/stitch.ts';
3233

3334
const { NodeTypeCatalog: NODE } = tinyest;
3435

@@ -309,7 +310,7 @@ export function generateExpression(
309310
}
310311

311312
if (wgsl.isMat(target.dataType) && property === 'columns') {
312-
return snip(target.value, target.dataType);
313+
return snip(new MatrixColumnsAccess(target), UnknownData);
313314
}
314315

315316
if (
@@ -331,9 +332,16 @@ export function generateExpression(
331332
const [_, targetNode, propertyNode] = expression;
332333
const target = generateExpression(ctx, targetNode);
333334
const property = generateExpression(ctx, propertyNode);
334-
const targetStr = ctx.resolve(target.value, target.dataType);
335335
const propertyStr = ctx.resolve(property.value, property.dataType);
336336

337+
if (target.value instanceof MatrixColumnsAccess) {
338+
return snip(
339+
stitch`${target.value.matrix}[${propertyStr}]`,
340+
getTypeForIndexAccess(target.value.matrix.dataType as AnyData),
341+
);
342+
}
343+
const targetStr = ctx.resolve(target.value, target.dataType);
344+
337345
if (target.dataType.type === 'unknown') {
338346
// No idea what the type is, so we act on the snippet's value and try to guess
339347

@@ -351,6 +359,12 @@ export function generateExpression(
351359
);
352360
}
353361

362+
if (wgsl.isMat(target.dataType)) {
363+
throw new Error(
364+
"The only way of accessing matrix elements in TGSL is through the 'columns' property.",
365+
);
366+
}
367+
354368
if (wgsl.isPtr(target.dataType)) {
355369
return snip(
356370
`(*${targetStr})[${propertyStr}]`,

packages/typegpu/tests/examples/individual/fluid-with-atomics.test.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,24 @@ describe('fluid with atomics example', () => {
2121
@builtin(global_invocation_id) gid: vec3u,
2222
}
2323
24-
@group(0) @binding(0) var<storage, read_write> nextState_5: array<atomic<u32>, 1048576>;
24+
@group(0) @binding(0) var<uniform> size_6: vec2u;
2525
26-
@group(0) @binding(1) var<uniform> size_7: vec2u;
27-
28-
fn getIndex_6(x: u32, y: u32) -> u32 {
29-
var h = size_7.y;
30-
var w = size_7.x;
26+
fn getIndex_5(x: u32, y: u32) -> u32 {
27+
var h = size_6.y;
28+
var w = size_6.x;
3129
return (((y % h) * w) + (x % w));
3230
}
3331
32+
@group(0) @binding(1) var<storage, read_write> nextState_7: array<atomic<u32>, 1048576>;
33+
3434
fn updateCell_4(x: u32, y: u32, value: u32) {
35-
atomicStore(&nextState_5[getIndex_6(x, y)], value);
35+
atomicStore(&nextState_7[getIndex_5(x, y)], value);
3636
}
3737
3838
@group(0) @binding(2) var<storage, read> currentStateBuffer_10: array<u32, 1048576>;
3939
4040
fn getCell_9(x: u32, y: u32) -> u32 {
41-
return currentStateBuffer_10[getIndex_6(x, y)];
41+
return currentStateBuffer_10[getIndex_5(x, y)];
4242
}
4343
4444
fn isClearCell_8(x: u32, y: u32) -> bool {
@@ -59,14 +59,14 @@ describe('fluid with atomics example', () => {
5959
}
6060
6161
fn getCellNext_15(x: u32, y: u32) -> u32 {
62-
return atomicLoad(&nextState_5[getIndex_6(x, y)]);
62+
return atomicLoad(&nextState_7[getIndex_5(x, y)]);
6363
}
6464
6565
fn addToCell_14(x: u32, y: u32, value: u32) {
6666
var cell = getCellNext_15(x, y);
6767
var waterLevel = (cell & MAX_WATER_LEVEL_12);
6868
var newWaterLevel = min((waterLevel + value), MAX_WATER_LEVEL_12);
69-
atomicAdd(&nextState_5[getIndex_6(x, y)], (newWaterLevel - waterLevel));
69+
atomicAdd(&nextState_7[getIndex_5(x, y)], (newWaterLevel - waterLevel));
7070
}
7171
7272
fn isWaterSource_16(x: u32, y: u32) -> bool {
@@ -81,7 +81,7 @@ describe('fluid with atomics example', () => {
8181
var cell = getCellNext_15(x, y);
8282
var waterLevel = (cell & MAX_WATER_LEVEL_12);
8383
var newWaterLevel = max((waterLevel - min(value, waterLevel)), 0);
84-
atomicSub(&nextState_5[getIndex_6(x, y)], (waterLevel - newWaterLevel));
84+
atomicSub(&nextState_7[getIndex_5(x, y)], (waterLevel - newWaterLevel));
8585
}
8686
8787
fn getWaterLevel_19(x: u32, y: u32) -> u32 {
@@ -107,7 +107,7 @@ describe('fluid with atomics example', () => {
107107
updateCell_4(x, y, (3 << 24));
108108
return true;
109109
}
110-
if (((((y == 0) || (y == (size_7.y - 1))) || (x == 0)) || (x == (size_7.x - 1)))) {
110+
if (((((y == 0) || (y == (size_6.y - 1))) || (x == 0)) || (x == (size_6.x - 1)))) {
111111
subtractFromCell_18(x, y, getWaterLevel_19(x, y));
112112
return true;
113113
}

packages/typegpu/tests/tgsl/wgslGenerator.test.ts

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import * as tinyest from 'tinyest';
22
import { beforeEach, describe, expect } from 'vitest';
3-
import { snip } from '../../src/data/snippet.ts';
43
import * as d from '../../src/data/index.ts';
54
import { abstractFloat, abstractInt } from '../../src/data/numeric.ts';
5+
import { snip } from '../../src/data/snippet.ts';
66
import { Void, type WgslArray } from '../../src/data/wgslTypes.ts';
77
import { provideCtx } from '../../src/execMode.ts';
88
import tgpu from '../../src/index.ts';
@@ -932,4 +932,55 @@ describe('wgslGenerator', () => {
932932
}`),
933933
);
934934
});
935+
936+
it('throws error when accessing matrix elements directly', () => {
937+
const testFn = tgpu.fn([])(() => {
938+
const matrix = d.mat4x4f();
939+
const element = matrix[4];
940+
});
941+
942+
expect(() => asWgsl(testFn))
943+
.toThrowErrorMatchingInlineSnapshot(`
944+
[Error: Resolution of the following tree failed:
945+
- <root>
946+
- fn:testFn: The only way of accessing matrix elements in TGSL is through the 'columns' property.]
947+
`);
948+
});
949+
950+
it('generates correct code when accessing matrix elements through .columns', () => {
951+
const testFn = tgpu.fn([])(() => {
952+
const matrix = d.mat4x4f();
953+
const column = matrix.columns[1];
954+
const element = column[0];
955+
const directElement = matrix.columns[1][0];
956+
});
957+
958+
expect(asWgsl(testFn)).toMatchInlineSnapshot(`
959+
"fn testFn() {
960+
var matrix = mat4x4f();
961+
var column = matrix[1];
962+
var element = column[0];
963+
var directElement = matrix[1][0];
964+
}"
965+
`);
966+
});
967+
968+
it('resolves when accessing matrix elements through .columns', () => {
969+
const matrix = tgpu['~unstable'].workgroupVar(d.mat4x4f);
970+
const index = tgpu['~unstable'].workgroupVar(d.u32);
971+
972+
const testFn = tgpu.fn([])(() => {
973+
const element = matrix.$.columns[index.$];
974+
});
975+
976+
expect(asWgsl(testFn)).toMatchInlineSnapshot(`
977+
"var<workgroup> index: u32;
978+
979+
var<workgroup> matrix: mat4x4f;
980+
981+
fn testFn() {
982+
var element = matrix[index];
983+
}"
984+
`);
985+
});
935986
});

0 commit comments

Comments
 (0)