Skip to content

Commit 12d31e3

Browse files
feat: Allow passing raw wgsl implementation to shell à la template literal (#1128)
1 parent ea1079a commit 12d31e3

File tree

12 files changed

+363
-147
lines changed

12 files changed

+363
-147
lines changed

apps/typegpu-docs/src/content/docs/fundamentals/functions/index.mdx

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,27 @@ These functions can reference outside resources, like other user-defined or help
1919
## Creating a function
2020

2121
Functions are constructed by first defining their shells, which specify their inputs and outputs.
22-
Then the actual WGSL implementation is passed in as an argument to a shell invocation.
22+
Then the actual WGSL implementation is passed in as an argument to a shell invocation. If the code string is a template literal, you can omit the parentheses, which may result in a more compact Biome/Prettier formatting.
2323

2424
The following code defines a function that accepts one argument and returns one value.
2525

2626
```ts
2727
const getGradientColor = tgpu['~unstable']
28-
.fn({ ratio: d.f32 }, d.vec4f)(`{
29-
let color = mix(vec4f(0.769, 0.392, 1.0, 1), d.vec4f(0.114, 0.447, 0.941, 1), ratio);
28+
.fn(
29+
{ ratio: d.f32 },
30+
d.vec4f,
31+
)(`{
32+
let color = mix(vec4f(0.769, 0.392, 1.0, 1), vec4f(0.114, 0.447, 0.941, 1), ratio);
3033
return color;
3134
}`)
3235
.$name('getGradientColor');
36+
37+
// or
38+
39+
const getGradientColor = tgpu['~unstable'].fn({ ratio: d.f32 }, d.vec4f)`{
40+
let color = mix(vec4f(0.769, 0.392, 1.0, 1), vec4f(0.114, 0.447, 0.941, 1), ratio);
41+
return color;
42+
}`.$name('getGradientColor');
3343
```
3444

3545
If you're using Visual Studio Code, you can use an [extension](https://marketplace.visualstudio.com/items?itemName=ggsimm.wgsl-literal) that brings syntax highlighting to the code fragments marked with `/* wgsl */` comments.
@@ -40,18 +50,16 @@ Functions can use external resources passed inside a record via the `$uses` meth
4050
Externals can be any value or TypeGPU resource that can be resolved to WGSL (functions, buffer usages, slots, accessors, constants, variables, declarations, vectors, matrices, textures, samplers etc.).
4151

4252
```ts
43-
const getBlue = tgpu['~unstable']
44-
.fn({}, d.vec4f)(`{
45-
return vec4f(0.114, 0.447, 0.941, 1);
46-
}`);
53+
const getBlue = tgpu['~unstable'].fn({}, d.vec4f)`{
54+
return vec4f(0.114, 0.447, 0.941, 1);
55+
}`;
4756

4857
const purple = d.vec4f(0.769, 0.392, 1.0, 1);
4958

50-
const getGradientColor = tgpu['~unstable']
51-
.fn({ ratio: d.f32 }, d.vec4f)(`{
52-
let color = mix(purple, getBlue(), ratio);
53-
return color;
54-
}`)
59+
const getGradientColor = tgpu['~unstable'].fn({ ratio: d.f32 }, d.vec4f)`{
60+
let color = mix(purple, getBlue(), ratio);
61+
return color;
62+
}`
5563
.$uses({ purple, getBlue })
5664
.$name('getGradientColor');
5765
```
@@ -79,11 +87,10 @@ Defining entry functions is similar to regular ones, but is done through dedicat
7987
They can be passed to root-defined pipelines and they accept special arguments like builtins (`d.builtin`) and decorated data (`d.location`).
8088

8189
```ts
82-
const mainVertex = tgpu['~unstable']
83-
.vertexFn({
84-
in: { vertexIndex: d.builtin.vertexIndex },
85-
out: { outPos: d.builtin.position, uv: d.vec2f },
86-
})(/* wgsl */ `{
90+
const mainVertex = tgpu['~unstable'].vertexFn({
91+
in: { vertexIndex: d.builtin.vertexIndex },
92+
out: { outPos: d.builtin.position, uv: d.vec2f },
93+
}) /* wgsl */`{
8794
var pos = array<vec2f, 3>(
8895
vec2(0.0, 0.5),
8996
vec2(-0.5, -0.5),
@@ -97,13 +104,14 @@ const mainVertex = tgpu['~unstable']
97104
);
98105
99106
return Out(vec4f(pos[in.vertexIndex], 0.0, 1.0), uv[in.vertexIndex]);
100-
}`);
107+
}`;
101108

102-
const mainFragment = tgpu['~unstable']
103-
.fragmentFn({ in: { uv: d.vec2f }, out: d.vec4f })(/* wgsl */ `{
109+
const mainFragment = tgpu['~unstable'].fragmentFn({
110+
in: { uv: d.vec2f },
111+
out: d.vec4f,
112+
}) /* wgsl */`{
104113
return getGradientColor((in.uv[0] + in.uv[1]) / 2);
105-
}`)
106-
.$uses({ getGradientColor });
114+
}`.$uses({ getGradientColor });
107115
```
108116

109117
When entry function inputs or outputs are specified as objects containing builtins and inter-stage variables, the WGSL implementations need to access these arguments as passed in via structs.

apps/typegpu-docs/src/content/examples/rendering/3d-fish/render.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,12 @@ export const vertexShader = tgpu['~unstable']
8686
})
8787
.$name('vertexShader');
8888

89-
const sampleTexture = tgpu['~unstable']
90-
.fn(
91-
{ uv: d.vec2f },
92-
d.vec4f,
93-
)(/* wgsl */ `{
94-
return textureSample(layout.$.modelTexture, layout.$.sampler, uv);
95-
}`)
89+
const sampleTexture = tgpu['~unstable'].fn(
90+
{ uv: d.vec2f },
91+
d.vec4f,
92+
) /* wgsl */`{
93+
return textureSample(layout.$.modelTexture, layout.$.sampler, uv);
94+
}`
9695
.$uses({ layout })
9796
.$name('sampleTexture');
9897

apps/typegpu-docs/src/content/examples/rendering/box-raytracing/index.ts

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,10 @@ const renderBindGroup = root.createBindGroup(renderLayout, {
106106

107107
// functions
108108

109-
const getBoxIntersection = tgpu['~unstable']
110-
.fn(
111-
{ boundMin: d.vec3f, boundMax: d.vec3f, ray: RayStruct },
112-
IntersectionStruct,
113-
)(/* wgsl */ `{
109+
const getBoxIntersection = tgpu['~unstable'].fn(
110+
{ boundMin: d.vec3f, boundMax: d.vec3f, ray: RayStruct },
111+
IntersectionStruct,
112+
) /* wgsl */`{
114113
var tMin: f32;
115114
var tMax: f32;
116115
var tMinY: f32;
@@ -167,15 +166,14 @@ const getBoxIntersection = tgpu['~unstable']
167166
}
168167
169168
return IntersectionStruct(tMin > 0 && tMax > 0, tMin, tMax);
170-
}`)
169+
}`
171170
.$uses({ IntersectionStruct })
172171
.$name('box_intersection');
173172

174-
const vertexFunction = tgpu['~unstable']
175-
.vertexFn({
176-
in: { vertexIndex: d.builtin.vertexIndex },
177-
out: { outPos: d.builtin.position },
178-
})(/* wgsl */ `{
173+
const vertexFunction = tgpu['~unstable'].vertexFn({
174+
in: { vertexIndex: d.builtin.vertexIndex },
175+
out: { outPos: d.builtin.position },
176+
}) /* wgsl */`{
179177
var pos = array<vec2f, 6>(
180178
vec2<f32>( 1, 1),
181179
vec2<f32>( 1, -1),
@@ -186,17 +184,15 @@ const vertexFunction = tgpu['~unstable']
186184
);
187185
188186
return Out(vec4f(pos[in.vertexIndex], 0, 1));
189-
}`)
190-
.$name('vertex_main');
187+
}`.$name('vertex_main');
191188

192189
const boxSizeAccessor = tgpu['~unstable'].accessor(d.u32);
193190
const canvasDimsAccessor = tgpu['~unstable'].accessor(CanvasDimsStruct);
194191

195-
const fragmentFunction = tgpu['~unstable']
196-
.fragmentFn({
197-
in: { position: d.builtin.position },
198-
out: d.vec4f,
199-
})(/* wgsl */ `{
192+
const fragmentFunction = tgpu['~unstable'].fragmentFn({
193+
in: { position: d.builtin.position },
194+
out: d.vec4f,
195+
}) /* wgsl */`{
200196
let minDim = f32(min(canvasDims.width, canvasDims.height));
201197
202198
var ray: RayStruct;
@@ -246,7 +242,7 @@ const fragmentFunction = tgpu['~unstable']
246242
}
247243
248244
return color;
249-
}`)
245+
}`
250246
.$uses({
251247
...renderLayout.bound,
252248
RayStruct,
@@ -266,12 +262,9 @@ const fragmentFunction = tgpu['~unstable']
266262
const pipeline = root['~unstable']
267263
.with(
268264
boxSizeAccessor,
269-
tgpu['~unstable']
270-
.fn(
271-
[],
272-
d.u32,
273-
)('() -> u32 { return boxSize; }')
274-
.$uses({ boxSize: boxSizeUniform }),
265+
tgpu['~unstable'].fn([], d.u32)`() -> u32 { return boxSize; }`.$uses({
266+
boxSize: boxSizeUniform,
267+
}),
275268
)
276269
.with(canvasDimsAccessor, canvasDimsUniform)
277270
.withVertex(vertexFunction, {})

apps/typegpu-docs/src/content/examples/simple/triangle/index.ts

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,20 @@ context.configure({
1616
alphaMode: 'premultiplied',
1717
});
1818

19-
const getGradientColor = tgpu['~unstable']
20-
.fn(
21-
{ ratio: d.f32 },
22-
d.vec4f,
23-
)(/* wgsl */ `{
19+
const getGradientColor = tgpu['~unstable'].fn(
20+
{ ratio: d.f32 },
21+
d.vec4f,
22+
) /* wgsl */`{
2423
let color = mix(purple, blue, ratio);
2524
return color;
26-
}`)
25+
}`
2726
.$uses({ purple, blue })
2827
.$name('getGradientColor');
2928

3029
const mainVertex = tgpu['~unstable'].vertexFn({
3130
in: { vertexIndex: d.builtin.vertexIndex },
3231
out: { outPos: d.builtin.position, uv: d.vec2f },
33-
})(/* wgsl */ `{
32+
}) /* wgsl */`{
3433
var pos = array<vec2f, 3>(
3534
vec2(0.0, 0.5),
3635
vec2(-0.5, -0.5),
@@ -44,16 +43,14 @@ const mainVertex = tgpu['~unstable'].vertexFn({
4443
);
4544
4645
return Out(vec4f(pos[in.vertexIndex], 0.0, 1.0), uv[in.vertexIndex]);
47-
}`);
46+
}`;
4847

49-
const mainFragment = tgpu['~unstable']
50-
.fragmentFn({
51-
in: { uv: d.vec2f },
52-
out: d.vec4f,
53-
})(/* wgsl */ `{
48+
const mainFragment = tgpu['~unstable'].fragmentFn({
49+
in: { uv: d.vec2f },
50+
out: d.vec4f,
51+
}) /* wgsl */`{
5452
return getGradientColor((in.uv[0] + in.uv[1]) / 2);
55-
}`)
56-
.$uses({ getGradientColor });
53+
}`.$uses({ getGradientColor });
5754

5855
const pipeline = root['~unstable']
5956
.withVertex(mainVertex, {})

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

Lines changed: 54 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -90,75 +90,67 @@ const dataLayout = tgpu
9090
const rotate = tgpu['~unstable'].fn(
9191
{ v: d.vec2f, angle: d.f32 },
9292
d.vec2f,
93-
)(/* wgsl */ `{
94-
let pos = vec2(
95-
(v.x * cos(angle)) - (v.y * sin(angle)),
96-
(v.x * sin(angle)) + (v.y * cos(angle))
97-
);
93+
) /* wgsl */`{
94+
let pos = vec2(
95+
(v.x * cos(angle)) - (v.y * sin(angle)),
96+
(v.x * sin(angle)) + (v.y * cos(angle))
97+
);
98+
99+
return pos;
100+
}`;
98101

99-
return pos;
102+
const mainVert = tgpu['~unstable'].vertexFn({
103+
in: {
104+
tilt: d.f32,
105+
angle: d.f32,
106+
color: d.vec4f,
107+
center: d.vec2f,
108+
index: d.builtin.vertexIndex,
109+
},
110+
out: VertexOutput,
111+
}) /* wgsl */`{
112+
let width = in.tilt;
113+
let height = in.tilt / 2;
114+
115+
var pos = rotate(array<vec2f, 4>(
116+
vec2f(0, 0),
117+
vec2f(width, 0),
118+
vec2f(0, height),
119+
vec2f(width, height),
120+
)[in.index] / 350, in.angle) + in.center;
121+
122+
if (canvasAspectRatio < 1) {
123+
pos.x /= canvasAspectRatio;
124+
} else {
125+
pos.y *= canvasAspectRatio;
100126
}
101-
`);
102-
103-
const mainVert = tgpu['~unstable']
104-
.vertexFn({
105-
in: {
106-
tilt: d.f32,
107-
angle: d.f32,
108-
color: d.vec4f,
109-
center: d.vec2f,
110-
index: d.builtin.vertexIndex,
111-
},
112-
out: VertexOutput,
113-
})(
114-
/* wgsl */ `{
115-
let width = in.tilt;
116-
let height = in.tilt / 2;
117-
118-
var pos = rotate(array<vec2f, 4>(
119-
vec2f(0, 0),
120-
vec2f(width, 0),
121-
vec2f(0, height),
122-
vec2f(width, height),
123-
)[in.index] / 350, in.angle) + in.center;
124-
125-
if (canvasAspectRatio < 1) {
126-
pos.x /= canvasAspectRatio;
127-
} else {
128-
pos.y *= canvasAspectRatio;
129-
}
130127
131-
return Out(vec4f(pos, 0.0, 1.0), in.color);
132-
}`,
133-
)
134-
.$uses({
135-
rotate,
136-
canvasAspectRatio: canvasAspectRatioUniform,
137-
});
128+
return Out(vec4f(pos, 0.0, 1.0), in.color);
129+
}`.$uses({
130+
rotate,
131+
canvasAspectRatio: canvasAspectRatioUniform,
132+
});
138133

139134
const mainFrag = tgpu['~unstable'].fragmentFn({
140135
in: VertexOutput,
141136
out: d.vec4f,
142-
})(/* wgsl */ `{
143-
return in.color;
144-
}`);
145-
146-
const mainCompute = tgpu['~unstable']
147-
.computeFn({ in: { gid: d.builtin.globalInvocationId }, workgroupSize: [1] })(
148-
/* wgsl */ `{
149-
let index = in.gid.x;
150-
if index == 0 {
151-
time += deltaTime;
152-
}
153-
let phase = (time / 300) + particleData[index].seed;
154-
particleData[index].position += particleData[index].velocity * deltaTime / 20 + vec2f(sin(phase) / 600, cos(phase) / 500);
155-
}`,
156-
)
157-
.$uses({
158-
particleData: particleDataStorage,
159-
deltaTime: deltaTimeUniform,
160-
time: timeStorage,
161-
});
137+
}) /* wgsl */`{ return in.color; }`;
138+
139+
const mainCompute = tgpu['~unstable'].computeFn({
140+
in: { gid: d.builtin.globalInvocationId },
141+
workgroupSize: [1],
142+
}) /* wgsl */`{
143+
let index = in.gid.x;
144+
if index == 0 {
145+
time += deltaTime;
146+
}
147+
let phase = (time / 300) + particleData[index].seed;
148+
particleData[index].position += particleData[index].velocity * deltaTime / 20 + vec2f(sin(phase) / 600, cos(phase) / 500);
149+
}`.$uses({
150+
particleData: particleDataStorage,
151+
deltaTime: deltaTimeUniform,
152+
time: timeStorage,
153+
});
162154

163155
// pipelines
164156

0 commit comments

Comments
 (0)