Skip to content

Commit 68e5f20

Browse files
feat: Support structs and arrays in console.log (#1732)
1 parent d9a4b3f commit 68e5f20

File tree

13 files changed

+1671
-692
lines changed

13 files changed

+1671
-692
lines changed

apps/typegpu-docs/src/content/docs/fundamentals/utils.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ compute.dispatch();
143143
// "[GPU] Call number 2"
144144
```
145145

146+
Currently supported data types for logging include scalars, vectors, matrices, structs, and fixed-size arrays.
147+
146148
Under the hood, TypeGPU translates `console.log` to a series of serializing functions that write the logged arguments to a buffer that is read and deserialized after every draw/dispatch call.
147149

148150
The buffer is of fixed size, which may limit the total amount of information that can be logged; if the buffer overflows, additional logs are dropped.
@@ -193,8 +195,6 @@ There are some limitations (some of which we intend to alleviate in the future):
193195
- `console.log` only works when used in TGSL, when calling or resolving a TypeGPU pipeline.
194196
Otherwise, for example when using `tgpu.resolve` on a WGSL template, logs are ignored.
195197
- `console.log` only works in fragment and compute shaders.
196-
This is due to [WebGPU limitation](https://www.w3.org/TR/WGSL/#address-space) that does not allow modifying buffers during the vertex shader stage.
197-
- TypeGPU needs to handle every logged data type individually.
198-
Currently, we only support scalar, vector and matrix types.
198+
This is due to a [WebGPU limitation](https://www.w3.org/TR/WGSL/#address-space) that does not allow modifying buffers during the vertex shader stage.
199199
- `console.log` currently does not support template literals and string substitutions.
200200
- Other `console` methods like `clear` or `warn` are not yet supported.

apps/typegpu-docs/src/examples/tests/log-test/index.ts

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ export const controls = {
2626
onButtonClick: () =>
2727
prepareDispatch(root, () => {
2828
'kernel';
29-
console.log(d.u32(1), d.vec3u(2, 3, 4), d.u32(5), d.u32(6));
29+
console.log(1, d.vec3u(2, 3, 4), 5, 6);
3030
}).dispatch(),
3131
},
3232
'String literals': {
3333
onButtonClick: () =>
3434
prepareDispatch(root, () => {
3535
'kernel';
36-
console.log(d.u32(2), 'plus', d.u32(3), 'equals', d.u32(5));
36+
console.log(2, 'plus', 3, 'equals', 5);
3737
}).dispatch(),
3838
},
3939
'Two logs': {
@@ -88,6 +88,32 @@ export const controls = {
8888
}
8989
}).dispatch(),
9090
},
91+
'Compound types': {
92+
onButtonClick: () => {
93+
const SimpleStruct = d.struct({ vec: d.vec3u, num: d.u32 });
94+
const ComplexStruct = d.struct({ nested: SimpleStruct, bool: d.bool });
95+
const SimpleArray = d.arrayOf(d.u32, 2);
96+
const ComplexArray = d.arrayOf(SimpleArray, 3);
97+
98+
prepareDispatch(root, () => {
99+
'kernel';
100+
const simpleStruct = SimpleStruct({ vec: d.vec3u(1, 2, 3), num: 4 });
101+
console.log(simpleStruct);
102+
103+
const complexStruct = ComplexStruct({
104+
nested: simpleStruct,
105+
bool: true,
106+
});
107+
console.log(complexStruct);
108+
109+
const simpleArray = SimpleArray([1, 2]);
110+
console.log(simpleArray);
111+
112+
const complexArray = ComplexArray([[3, 4], [5, 6], [7, 8]]);
113+
console.log(complexArray);
114+
}).dispatch();
115+
},
116+
},
91117
'Two threads': {
92118
onButtonClick: () =>
93119
prepareDispatch(root, (x) => {
@@ -115,7 +141,7 @@ export const controls = {
115141
const test = prepareDispatch(root, () => {
116142
'kernel';
117143
for (let i = d.u32(); i < logCountUniform.$; i++) {
118-
console.log('Log index', d.u32(i) + 1, 'out of', logCountUniform.$);
144+
console.log('Log index', i + 1, 'out of', logCountUniform.$);
119145
}
120146
});
121147
logCountUniform.write(3);
@@ -143,7 +169,7 @@ export const controls = {
143169
in: { pos: d.builtin.position },
144170
out: d.vec4f,
145171
})(({ pos }) => {
146-
console.log('X:', d.u32(pos.x), 'Y:', d.u32(pos.y));
172+
console.log('X:', pos.x, 'Y:', pos.y);
147173
return d.vec4f(0.769, 0.392, 1.0, 1);
148174
});
149175

@@ -186,7 +212,7 @@ export const controls = {
186212
try {
187213
prepareDispatch(root, () => {
188214
'kernel';
189-
console.log(d.vec3u(), d.vec3u(), d.vec3u());
215+
console.log(d.mat4x4f(), d.mat4x4f(), 1);
190216
}).dispatch();
191217
} catch (err) {
192218
console.log(err);

packages/typegpu/src/data/alignmentOf.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { safeStringify } from '../shared/stringify.ts';
12
import {
23
type AnyData,
34
getCustomAlignment,
@@ -12,26 +13,29 @@ import {
1213
isWgslArray,
1314
isWgslStruct,
1415
} from './wgslTypes.ts';
15-
import { safeStringify } from '../shared/safeStringify.ts';
1616

1717
const knownAlignmentMap: Record<string, number> = {
1818
f32: 4,
1919
f16: 2,
2020
i32: 4,
2121
u32: 4,
22+
bool: 4,
2223
u16: 2,
2324
vec2f: 8,
2425
vec2h: 4,
2526
vec2i: 8,
2627
vec2u: 8,
28+
vec2b: 8,
2729
vec3f: 16,
2830
vec3h: 8,
2931
vec3i: 16,
3032
vec3u: 16,
33+
vec3b: 16,
3134
vec4f: 16,
3235
vec4h: 8,
3336
vec4i: 16,
3437
vec4u: 16,
38+
vec4b: 16,
3539
mat2x2f: 8,
3640
mat3x3f: 16,
3741
mat4x4f: 16,

packages/typegpu/src/resolutionCtx.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import {
3434
import { provideCtx, topLevelState } from './execMode.ts';
3535
import { naturalsExcept } from './shared/generators.ts';
3636
import type { Infer } from './shared/repr.ts';
37-
import { safeStringify } from './shared/safeStringify.ts';
37+
import { safeStringify } from './shared/stringify.ts';
3838
import { $internal, $providing, $resolve } from './shared/symbols.ts';
3939
import {
4040
bindGroupLayout,

packages/typegpu/src/shared/safeStringify.ts

Lines changed: 0 additions & 13 deletions
This file was deleted.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { isMatInstance, isVecInstance } from '../data/wgslTypes.ts';
2+
3+
export function safeStringify(item: unknown): string {
4+
const asString = String(item);
5+
if (asString !== '[object Object]') {
6+
return asString;
7+
}
8+
9+
try {
10+
return JSON.stringify(item);
11+
} catch (error) {
12+
console.error('Error parsing JSON:', error);
13+
return '<invalid json>';
14+
}
15+
}
16+
17+
export function niceStringify(item: unknown): string {
18+
if (isVecInstance(item) || isMatInstance(item)) {
19+
return item.toString();
20+
}
21+
22+
if (Array.isArray(item)) {
23+
return `[${item.map(niceStringify).join(', ')}]`;
24+
}
25+
26+
if (item && typeof item === 'object') {
27+
return `{ ${
28+
Object.entries(item).map(([key, value]) =>
29+
`${key}: ${niceStringify(value)}`
30+
).join(', ')
31+
} }`;
32+
}
33+
34+
return String(item);
35+
}

packages/typegpu/src/std/atomic.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1+
import { createDualImpl } from '../core/function/dualImpl.ts';
12
import { stitch } from '../core/resolve/stitch.ts';
2-
import { snip, type Snippet } from '../data/snippet.ts';
33
import { i32, u32 } from '../data/numeric.ts';
4+
import { snip, type Snippet } from '../data/snippet.ts';
45
import {
56
type AnyWgslData,
67
type atomicI32,
78
type atomicU32,
89
isWgslData,
910
Void,
1011
} from '../data/wgslTypes.ts';
11-
import { createDualImpl } from '../core/function/dualImpl.ts';
12-
import { safeStringify } from '../shared/safeStringify.ts';
12+
import { safeStringify } from '../shared/stringify.ts';
1313
type AnyAtomic = atomicI32 | atomicU32;
1414

1515
export const workgroupBarrier = createDualImpl(

packages/typegpu/src/tgpuBindGroupLayout.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ import {
6161
import type { TgpuNamable } from './shared/meta.ts';
6262
import { getName, setName } from './shared/meta.ts';
6363
import type { Infer, MemIdentity } from './shared/repr.ts';
64-
import { safeStringify } from './shared/safeStringify.ts';
64+
import { safeStringify } from './shared/stringify.ts';
6565
import { $gpuValueOf, $internal } from './shared/symbols.ts';
6666
import type {
6767
Default,

0 commit comments

Comments
 (0)