Skip to content

Commit f4e6494

Browse files
authored
docs: Functions and triangle example (#849)
1 parent 0534b76 commit f4e6494

File tree

6 files changed

+221
-2
lines changed

6 files changed

+221
-2
lines changed

apps/typegpu-docs/astro.config.mjs

+4-2
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ export default defineConfig({
111111
label: 'Roots',
112112
slug: 'fundamentals/roots',
113113
},
114+
{
115+
label: 'Functions',
116+
slug: 'fundamentals/functions',
117+
},
114118
{
115119
label: 'Buffers',
116120
slug: 'fundamentals/buffers',
@@ -126,7 +130,6 @@ export default defineConfig({
126130
{
127131
label: 'Resolve',
128132
slug: 'fundamentals/resolve',
129-
badge: { text: '0.3' },
130133
},
131134
{
132135
label: 'Vertex Layouts',
@@ -157,7 +160,6 @@ export default defineConfig({
157160
{
158161
label: 'WebGPU Interoperability',
159162
slug: 'integration/webgpu-interoperability',
160-
badge: { text: 'new' },
161163
},
162164
{
163165
label: 'Working with wgpu-matrix',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
---
2+
title: Functions
3+
description: A guide on how to create and use the TypeGPU typed functions.
4+
---
5+
6+
:::caution[Experimental]
7+
Functions are an *unstable* feature. The API may be subject to change in the near future.
8+
:::
9+
10+
:::note[Recommended reading]
11+
We assume that you are familiar with the following concepts:
12+
- <a href="https://webgpufundamentals.org/webgpu/lessons/webgpu-fundamentals.html" target="_blank" rel="noopener noreferrer">WebGPU Fundamentals</a>
13+
- <a href="https://webgpufundamentals.org/webgpu/lessons/webgpu-wgsl.html" target="_blank" rel="noopener noreferrer">WebGPU Shading Language</a>
14+
:::
15+
16+
TypeGPU allows writing shaders by composing typed functions, which are special wrappers around WGSL code.
17+
These functions can reference outside resources, like other user-defined or helper functions, buffers, bind group layouts etc.
18+
19+
## Creating a function
20+
21+
Functions are constructed by first defining their shells, which specify their inputs and outputs.
22+
Then the actual WGSL implementation is passed in through the `does` method.
23+
24+
The following code defines a function that accepts one argument and returns one value.
25+
26+
```ts
27+
const getGradientColor = tgpu['~unstable']
28+
.fn([d.f32], d.vec4f)
29+
.does(`(ratio: f32) -> vec4f {
30+
let color = mix(vec4f(0.769, 0.392, 1.0, 1), d.vec4f(0.114, 0.447, 0.941, 1), ratio);
31+
return color;
32+
}`)
33+
.$name('getGradientColor');
34+
```
35+
36+
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.
37+
38+
## External resources
39+
40+
Functions can use external resources passed inside a record via the `$uses` method.
41+
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.).
42+
43+
```ts
44+
const getBlue = tgpu['~unstable']
45+
.fn([], d.f32)
46+
.does('() -> f32 { return 0.941; }');
47+
48+
const purple = d.vec4f(0.769, 0.392, 1.0, 1);
49+
50+
const getGradientColor = tgpu['~unstable']
51+
.fn([d.f32], d.vec4f)
52+
.does(`(ratio: f32) -> vec4f {
53+
let color = mix(purple, getBlue(), ratio);
54+
return color;
55+
}`)
56+
.$uses({ purple, getBlue })
57+
.$name('getGradientColor');
58+
```
59+
60+
The `getGradientColor` function, when resolved to WGSL, includes the definitions of all used external resources:
61+
62+
```wgsl
63+
fn getBlue_1() -> f32 { return 0.941; }
64+
65+
fn getGradientColor_0(ratio: f32) -> vec4f {
66+
let color = mix(vec4f(0.769, 0.392, 1, 1), getBlue_1(), ratio);
67+
return color;
68+
}
69+
```
70+
71+
## Entry functions
72+
73+
Defining entry functions is similar to regular ones, but is done through dedicated constructors:
74+
- `tgpu['~unstable'].vertexFn`
75+
- `tgpu['~unstable'].fragmentFn`
76+
- `tgpu['~unstable'].computeFn`
77+
78+
They can be passed to root-defined pipelines and they accept special arguments like builtins (`d.builtin`) and decorated data (`d.location`).
79+
80+
```ts
81+
const mainVertex = tgpu['~unstable']
82+
.vertexFn(
83+
{ vertexIndex: d.builtin.vertexIndex },
84+
{ outPos: d.builtin.position, uv: d.vec2f },
85+
)
86+
.does(/* wgsl */ `(input: VertexInput) -> VertexOutput {
87+
var pos = array<vec2f, 3>(
88+
vec2(0.0, 0.5),
89+
vec2(-0.5, -0.5),
90+
vec2(0.5, -0.5)
91+
);
92+
93+
var uv = array<vec2f, 3>(
94+
vec2(0.5, 1.0),
95+
vec2(0.0, 0.0),
96+
vec2(1.0, 0.0),
97+
);
98+
99+
return VertexOutput(vec4f(pos[input.vertexIndex], 0.0, 1.0), uv[input.vertexIndex]);
100+
}`);
101+
102+
const mainFragment = tgpu['~unstable']
103+
.fragmentFn({ uv: d.vec2f }, d.vec4f)
104+
.does(/* wgsl */ `(input: FragmentInput) -> @location(0) vec4f {
105+
return getGradientColor((input.uv[0] + input.uv[1]) / 2);
106+
}`)
107+
.$uses({ getGradientColor });
108+
```
109+
110+
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 (see *VertexInput*, *VertexOutput*, *FragmentInput*).
111+
TypeGPU schemas for these structs are created automatically by the library and their definitions are included when resolving the functions. The names referenced in the implementations do not matter.
112+
113+
## Usage in pipelines
114+
115+
Typed functions are crucial for simplified pipeline creation offered by TypeGPU. You can define and run pipelines as follows:
116+
117+
```ts
118+
const pipeline = root['~unstable']
119+
.withVertex(mainVertex, {})
120+
.withFragment(mainFragment, { format: presentationFormat })
121+
.createPipeline();
122+
123+
pipeline
124+
.withColorAttachment({
125+
view: context.getCurrentTexture().createView(),
126+
clearValue: [0, 0, 0, 0],
127+
loadOp: 'clear',
128+
storeOp: 'store',
129+
})
130+
.draw(3);
131+
132+
root['~unstable'].flush();
133+
```
134+
135+
The rendering result looks like this:
136+
![rendering result - gradient triangle](./triangle-result.png)
137+
138+
You can check out the full example on [our examples page](/TypeGPU/examples#example=simple--triangle).
139+
140+
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<canvas></canvas>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import tgpu from 'typegpu';
2+
import * as d from 'typegpu/data';
3+
4+
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
5+
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
6+
const context = canvas.getContext('webgpu') as GPUCanvasContext;
7+
8+
const root = await tgpu.init();
9+
10+
context.configure({
11+
device: root.device,
12+
format: presentationFormat,
13+
alphaMode: 'premultiplied',
14+
});
15+
16+
const purple = d.vec4f(0.769, 0.392, 1.0, 1);
17+
const blue = d.vec4f(0.114, 0.447, 0.941, 1);
18+
19+
const getGradientColor = tgpu['~unstable']
20+
.fn([d.f32], d.vec4f)
21+
.does(`(ratio: f32) -> vec4f {
22+
let color = mix(purple, blue, ratio);
23+
return color;
24+
}`)
25+
.$uses({ purple, blue })
26+
.$name('getGradientColor');
27+
28+
const mainVertex = tgpu['~unstable']
29+
.vertexFn(
30+
{ vertexIndex: d.builtin.vertexIndex },
31+
{ outPos: d.builtin.position, uv: d.vec2f },
32+
)
33+
.does(/* wgsl */ `(input: VertexInput) -> VertexOutput {
34+
var pos = array<vec2f, 3>(
35+
vec2(0.0, 0.5),
36+
vec2(-0.5, -0.5),
37+
vec2(0.5, -0.5)
38+
);
39+
40+
var uv = array<vec2f, 3>(
41+
vec2(0.5, 1.0),
42+
vec2(0.0, 0.0),
43+
vec2(1.0, 0.0),
44+
);
45+
46+
return VertexOutput(vec4f(pos[input.vertexIndex], 0.0, 1.0), uv[input.vertexIndex]);
47+
}`);
48+
49+
const mainFragment = tgpu['~unstable']
50+
.fragmentFn({ uv: d.vec2f }, d.vec4f)
51+
.does(/* wgsl */ `(input: FragmentInput) -> @location(0) vec4f {
52+
return getGradientColor((input.uv[0] + input.uv[1]) / 2);
53+
}`)
54+
.$uses({ getGradientColor });
55+
56+
const pipeline = root['~unstable']
57+
.withVertex(mainVertex, {})
58+
.withFragment(mainFragment, { format: presentationFormat })
59+
.createPipeline();
60+
61+
pipeline
62+
.withColorAttachment({
63+
view: context.getCurrentTexture().createView(),
64+
clearValue: [0, 0, 0, 0],
65+
loadOp: 'clear',
66+
storeOp: 'store',
67+
})
68+
.draw(3);
69+
70+
root['~unstable'].flush();
71+
root.destroy();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"title": "Triangle",
3+
"category": "simple",
4+
"tags": ["experimental"]
5+
}

0 commit comments

Comments
 (0)