Skip to content

Commit 459ee43

Browse files
authored
feat: array.length in TGSL (#1097)
1 parent 634d1b1 commit 459ee43

File tree

3 files changed

+115
-33
lines changed

3 files changed

+115
-33
lines changed

apps/typegpu-docs/src/content/examples/simulation/boids-next/index.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,10 @@ const TriangleData = d.struct({
3030
});
3131

3232
const renderBindGroupLayout = tgpu.bindGroupLayout({
33-
trianglePos: { uniform: d.arrayOf(TriangleData, triangleAmount) },
3433
colorPalette: { uniform: d.vec3f },
3534
});
3635

37-
const { trianglePos, colorPalette } = renderBindGroupLayout.bound;
36+
const { colorPalette } = renderBindGroupLayout.bound;
3837

3938
const VertexOutput = {
4039
position: d.builtin.position,
@@ -61,7 +60,6 @@ const mainVert = tgpu['~unstable']
6160
return VertexOutput(pos, color);
6261
}`)
6362
.$uses({
64-
trianglePos,
6563
colorPalette,
6664
getRotationFromVelocity,
6765
rotate,
@@ -223,7 +221,7 @@ const mainCompute = tgpu['~unstable']
223221
let alignmentCount = 0;
224222
let cohesionCount = 0;
225223

226-
for (let i = d.u32(0); i < std.arrayLength(currentTrianglePos.value); i++) {
224+
for (let i = d.u32(0); i < currentTrianglePos.value.length; i++) {
227225
if (i === index) {
228226
continue;
229227
}
@@ -295,7 +293,6 @@ const computePipeline = root['~unstable']
295293

296294
const renderBindGroups = [0, 1].map((idx) =>
297295
root.createBindGroup(renderBindGroupLayout, {
298-
trianglePos: trianglePosBuffers[idx],
299296
colorPalette: colorPaletteBuffer,
300297
}),
301298
);

packages/typegpu/src/smol/wgslGenerator.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
import type * as smol from 'tinyest';
22
import * as d from '../data';
3-
import * as wgsl from '../data/wgslTypes';
3+
import { abstractInt } from '../data/numeric.js';
4+
import * as wgsl from '../data/wgslTypes.js';
45
import {
56
type ResolutionCtx,
67
type Resource,
78
UnknownData,
89
type Wgsl,
910
isWgsl,
10-
} from '../types';
11+
} from '../types.js';
1112
import {
1213
getTypeForIndexAccess,
1314
getTypeForPropAccess,
1415
getTypeFromWgsl,
1516
numericLiteralToResource,
16-
} from './generationHelpers';
17+
} from './generationHelpers.js';
1718

1819
const parenthesizedOps = [
1920
'==',
@@ -192,6 +193,24 @@ export function generateExpression(
192193
dataType: getTypeForPropAccess(target.dataType as Wgsl, property),
193194
};
194195
}
196+
197+
if (wgsl.isWgslArray(target.dataType)) {
198+
if (property === 'length') {
199+
if (target.dataType.elementCount === 0) {
200+
// Dynamically-sized array
201+
return {
202+
value: `arrayLength(&${ctx.resolve(target.value)})`,
203+
dataType: d.u32,
204+
};
205+
}
206+
207+
return {
208+
value: String(target.dataType.elementCount),
209+
dataType: abstractInt,
210+
};
211+
}
212+
}
213+
195214
// biome-ignore lint/suspicious/noExplicitAny: <sorry TypeScript>
196215
const propValue = (target.value as any)[property];
197216

packages/typegpu/tests/array.test.ts

Lines changed: 91 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,74 @@
11
import { BufferReader, BufferWriter } from 'typed-binary';
22
import { describe, expect, it } from 'vitest';
3-
import { arrayOf, sizeOf, vec3f, vec3u } from '../src/data';
3+
import tgpu from '../src';
4+
import * as d from '../src/data';
45
import { readData, writeData } from '../src/data/dataIO';
56
import { StrictNameRegistry } from '../src/nameRegistry';
67
import { resolve } from '../src/resolutionCtx';
78
import type { Infer } from '../src/shared/repr';
9+
import { parse, parseResolved } from './utils/parseResolved';
810

911
describe('array', () => {
1012
it('takes element alignment into account when measuring', () => {
11-
const TestArray = arrayOf(vec3u, 3);
12-
expect(sizeOf(TestArray)).toEqual(48);
13+
const TestArray = d.arrayOf(d.vec3u, 3);
14+
expect(d.sizeOf(TestArray)).toEqual(48);
1315
});
1416

1517
it('aligns array elements when writing', () => {
16-
const TestArray = arrayOf(vec3u, 3);
17-
const buffer = new ArrayBuffer(sizeOf(TestArray));
18+
const TestArray = d.arrayOf(d.vec3u, 3);
19+
const buffer = new ArrayBuffer(d.sizeOf(TestArray));
1820
const writer = new BufferWriter(buffer);
1921

2022
writeData(writer, TestArray, [
21-
vec3u(1, 2, 3),
22-
vec3u(4, 5, 6),
23-
vec3u(7, 8, 9),
23+
d.vec3u(1, 2, 3),
24+
d.vec3u(4, 5, 6),
25+
d.vec3u(7, 8, 9),
2426
]);
2527
expect([...new Uint32Array(buffer)]).toEqual([
2628
1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9, 0,
2729
]);
2830
});
2931

3032
it('aligns array elements when reading', () => {
31-
const TestArray = arrayOf(vec3u, 3);
32-
const buffer = new ArrayBuffer(sizeOf(TestArray));
33+
const TestArray = d.arrayOf(d.vec3u, 3);
34+
const buffer = new ArrayBuffer(d.sizeOf(TestArray));
3335
const reader = new BufferReader(buffer);
3436

3537
new Uint32Array(buffer).set([1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9, 0]);
3638

3739
expect(readData(reader, TestArray)).toEqual([
38-
vec3u(1, 2, 3),
39-
vec3u(4, 5, 6),
40-
vec3u(7, 8, 9),
40+
d.vec3u(1, 2, 3),
41+
d.vec3u(4, 5, 6),
42+
d.vec3u(7, 8, 9),
4143
]);
4244
});
4345

4446
it('encodes and decodes arrays properly', () => {
45-
const TestArray = arrayOf(vec3f, 5);
47+
const TestArray = d.arrayOf(d.vec3f, 5);
4648

47-
const buffer = new ArrayBuffer(sizeOf(TestArray));
49+
const buffer = new ArrayBuffer(d.sizeOf(TestArray));
4850

4951
const value: Infer<typeof TestArray> = [
50-
vec3f(1.5, 2, 3.5),
51-
vec3f(),
52-
vec3f(-1.5, 2, 3.5),
53-
vec3f(1.5, -2, 3.5),
54-
vec3f(1.5, 2, 15),
52+
d.vec3f(1.5, 2, 3.5),
53+
d.vec3f(),
54+
d.vec3f(-1.5, 2, 3.5),
55+
d.vec3f(1.5, -2, 3.5),
56+
d.vec3f(1.5, 2, 15),
5557
];
5658

5759
writeData(new BufferWriter(buffer), TestArray, value);
5860
expect(readData(new BufferReader(buffer), TestArray)).toEqual(value);
5961
});
6062

6163
it('throws when trying to read/write a runtime-sized array', () => {
62-
const TestArray = arrayOf(vec3f, 0);
64+
const TestArray = d.arrayOf(d.vec3f, 0);
6365

64-
expect(sizeOf(TestArray)).toBeNaN();
66+
expect(d.sizeOf(TestArray)).toBeNaN();
6567

6668
expect(() =>
6769
writeData(new BufferWriter(new ArrayBuffer(0)), TestArray, [
68-
vec3f(),
69-
vec3f(),
70+
d.vec3f(),
71+
d.vec3f(),
7072
]),
7173
).toThrow();
7274

@@ -80,6 +82,70 @@ describe('array', () => {
8082
});
8183

8284
it('throws when trying to nest runtime sized arrays', () => {
83-
expect(() => arrayOf(arrayOf(vec3f, 0), 0)).toThrow();
85+
expect(() => d.arrayOf(d.arrayOf(d.vec3f, 0), 0)).toThrow();
86+
});
87+
});
88+
89+
describe('array.length', () => {
90+
it('works for dynamically-sized arrays in TGSL', () => {
91+
const layout = tgpu.bindGroupLayout({
92+
values: {
93+
storage: (n: number) => d.arrayOf(d.f32, n),
94+
access: 'mutable',
95+
},
96+
});
97+
98+
const foo = tgpu['~unstable'].fn([]).does(() => {
99+
let acc = d.f32(1);
100+
for (let i = 0; i < layout.bound.values.value.length; i++) {
101+
layout.bound.values.value[i] = acc;
102+
acc *= 2;
103+
}
104+
});
105+
106+
expect(parseResolved({ foo })).toEqual(
107+
parse(/* wgsl */ `
108+
@group(0) @binding(0) var <storage, read_write> values: array<f32>;
109+
110+
fn foo() {
111+
var acc = f32(1);
112+
for (var i = 0; (i < arrayLength(&values)); i++) {
113+
values[i] = acc;
114+
acc *= 2;
115+
}
116+
}
117+
`),
118+
);
119+
});
120+
121+
it('works for statically-sized arrays in TGSL', () => {
122+
const layout = tgpu.bindGroupLayout({
123+
values: {
124+
storage: d.arrayOf(d.f32, 128),
125+
access: 'mutable',
126+
},
127+
});
128+
129+
const foo = tgpu['~unstable'].fn([]).does(() => {
130+
let acc = d.f32(1);
131+
for (let i = 0; i < layout.bound.values.value.length; i++) {
132+
layout.bound.values.value[i] = acc;
133+
acc *= 2;
134+
}
135+
});
136+
137+
expect(parseResolved({ foo })).toEqual(
138+
parse(/* wgsl */ `
139+
@group(0) @binding(0) var <storage, read_write> values: array<f32, 128>;
140+
141+
fn foo() {
142+
var acc = f32(1);
143+
for (var i = 0; (i < 128); i++) {
144+
values[i] = acc;
145+
acc *= 2;
146+
}
147+
}
148+
`),
149+
);
84150
});
85151
});

0 commit comments

Comments
 (0)