Skip to content

Commit 3e86d02

Browse files
committed
docs: TypeGPU 0.11 Blog Post
1 parent 826c689 commit 3e86d02

2 files changed

Lines changed: 330 additions & 0 deletions

File tree

145 KB
Loading
Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
---
2+
title: TypeGPU 0.11
3+
date: 2026-04-14
4+
tags:
5+
- Release notes
6+
authors:
7+
- name: Iwo Plaza
8+
title: TypeGPU developer
9+
picture: https://avatars.githubusercontent.com/u/7166752?s=200
10+
cover:
11+
alt: A collage of examples introduced alongside TypeGPU 0.11
12+
image: banner.webp
13+
---
14+
15+
Hello fellow GPU enthusiast!
16+
17+
Over the past 2 months, my team and I have been pulling on a few threads that we thought would improve TypeGPU in terms of efficiency, and as a byproduct, we actually made the APIs more convenient. We are also introducing a _lint plugin_ to further improve the diagnostics and feedback you receive while writing TypeGPU shaders, on top of the type safety we already provide.
18+
19+
- [New examples](#new-examples)
20+
- [Migration guide](#migration-guide)
21+
- [New and improved write APIs](#new-and-improved-write-apis)
22+
- [Shader code ergonomics](#shader-code-ergonomics)
23+
- [Ecosystem updates](#ecosystem-updates)
24+
- [What's next?](#whats-next)
25+
26+
27+
We have been pulling a few more threads than I mentioned here... but for those, you'll have to wait for the next blog post 🤐.
28+
29+
## New examples
30+
31+
My teammate Konrad Reczko [(@reczkok)](https://github.com/reczkok) has outdone himself again, and delivered 3 new examples that push TypeGPU APIs to their limits:
32+
33+
* ["Genetic Racing"](https://typegpu.com/examples/#example=algorithms--genetic-racing) - watch a swarm of cars learn to traverse a procedurally generated race track.
34+
* ["Mesh Skinning"](https://typegpu.com/examples/#example=simple--mesh-skinning) - an animation and skinning system built from scratch in TypeGPU.
35+
* ["Parallax Occlusion Mapping"](https://typegpu.com/examples/#example=rendering--pom) - squeezing amazing depth out of just two triangles and a set of textures.
36+
37+
## Migration guide
38+
39+
### Deprecated APIs
40+
41+
The `buffer.writePartial` API is being deprecated in favor of `buffer.patch` [(and here are the reasons why)](#a-better-partial-write).
42+
To migrate, simply replace any partial write of arrays in the form of `[{ idx: 2, value: foo }, /* ... */]` with `{ 2: foo, /* ... */ }`.
43+
44+
```diff lang=ts
45+
const buffer = root.createBuffer(d.arrayOf(d.vec3f, 5)).$usage('storage');
46+
47+
- buffer.writePartial([{ idx: 2, value: d.vec3f(1, 2, 3) }]);
48+
+ buffer.patch({ 2: d.vec3f(1, 2, 3) });
49+
```
50+
51+
### Stabilizing textures and samplers
52+
53+
One by one, we're making our APIs available without the `['~unstable']` prefix, and this time around, it's **textures** and **samplers**.
54+
Just drop the unstable prefix, and you're good to go.
55+
56+
```diff lang=ts
57+
- const sampler = root['~unstable'].createSampler({
58+
+ const sampler = root.createSampler({
59+
magFilter: 'linear',
60+
minFilter: 'linear',
61+
});
62+
63+
- const texture = root['~unstable'].createTexture({
64+
+ const texture = root.createTexture({
65+
size: [256, 256],
66+
format: 'rgba8unorm' as const,
67+
}).$usage('sampled');
68+
```
69+
70+
## New and improved write APIs
71+
72+
### Efficient data
73+
74+
When writing to a buffer with an array of vectors, it's no longer required to create vector instances (e.g. `d.vec3f()`).
75+
```ts
76+
const positionsMutable = root.createMutable(d.arrayOf(d.vec3f, 3));
77+
78+
// existing overload
79+
positionsMutable.write([d.vec3f(0, 1, 2), d.vec3f(3, 4, 5), d.vec3f(6, 7, 8)]);
80+
// new overloads ⚡
81+
positionsMutable.write([[0, 1, 2], [3, 4, 5], [6, 7, 8]]); // tuples
82+
positionsMutable.write(new Float32Array([0, 1, 2, 0, 3, 4, 5, 0, 6, 7, 8, 0])); // typed arrays (mind the padding)
83+
// and more...
84+
```
85+
Each one is more efficient than the previous, so you can choose the appropriate API for your efficiency needs.
86+
[More about these new APIs here](https://docs.swmansion.com/TypeGPU/fundamentals/buffers/#writing-to-a-buffer).
87+
88+
### A better partial write
89+
90+
When writing to a buffer, we require the passed in value to exactly match the schema. This specifically means that updating a single field of a single array item was very costly. The `buffer.writePartial` API remedied that by accepting partial records
91+
for structs, and a list of indices and values to update in arrays. This works fine, but doesn't compose well with more complex data structures:
92+
93+
```ts
94+
const Node = d.struct({
95+
color: d.vec3f,
96+
// Indices of neighboring nodes
97+
neighbors: d.arrayOf(d.u32, 4),
98+
});
99+
100+
const nodes = root.createUniform(d.arrayOf(Node, 100));
101+
102+
// Updating the 50th node
103+
nodes.writePartial([
104+
{
105+
idx: 50,
106+
value: {
107+
color: d.vec3f(1, 0, 1),
108+
// We cannot pass [48, 49, 51, 52], as we could with nodes.write()
109+
neighbors: [{ idx: 0, value: 48 }, { idx: 1, value: 49 }, { idx: 2, value: 51 }, { idx: 3, value: 52 }],
110+
},
111+
}
112+
]);
113+
```
114+
115+
If we loosened the type to accept either partial arrays or full arrays, then we would reach an ambiguity in the following case:
116+
117+
```ts
118+
const Foo = d.struct({
119+
idx: d.u32,
120+
value: d.f32,
121+
});
122+
123+
const foos = root.createUniform(d.arrayOf(Foo, 2));
124+
125+
foos.writePartial([{ idx: 1, value: /* ... */ }, { idx: 0, value: /* ... */ }]);
126+
```
127+
128+
We could traverse the value deeper to disambiguate, but for the sake of efficiency and being able to reuse optimizations added to `buffer.write` by [Konrad](https://github.com/reczkok), we chose to add a new API:
129+
130+
```diff lang=ts
131+
- foos.writePartial([{ idx: 1, value: /* ... */ }, { idx: 0, value: /* ... */ }]);
132+
+ foos.patch({ 1: /* ... */, 0: /* ... */ });
133+
```
134+
135+
[You can read more about .patch in the Buffers guide](/TypeGPU/fundamentals/buffers/#patching-buffers).
136+
137+
### Writing struct-of-arrays (SoA) data
138+
139+
When the buffer schema is an `array<struct<...>>`, you can write the data in a struct-of-arrays form with `writeSoA` from `typegpu/common`. This is useful when your CPU-side data is already stored per-field, such as simulation attributes kept in separate typed arrays.
140+
141+
```ts
142+
const Particle = d.struct({
143+
pos: d.vec3f,
144+
vel: d.f32,
145+
});
146+
147+
const particleBuffer = root.createBuffer(d.arrayOf(Particle, 2));
148+
149+
common.writeSoA(particleBuffer, {
150+
pos: new Float32Array([
151+
1, 2, 3,
152+
4, 5, 6,
153+
]),
154+
vel: new Float32Array([10, 20]),
155+
});
156+
```
157+
158+
[More about this API can be found in the Buffers guide.](/TypeGPU/fundamentals/buffers/#writing-struct-of-arrays-soa-data)
159+
160+
## Shader code ergonomics
161+
162+
There's been a lot of improvements to our shader generation, mainly in regards to compile-time execution and pruning of unreachable branches.
163+
I will highlight some of them in the following sections.
164+
165+
### std.range
166+
167+
The new `std.range` function works similarly to `range()` in Python, and returns an array that can be iterated over.
168+
When combined with `tgpu.unroll`, it's now very easy to produce a set amount of code blocks.
169+
170+
```ts
171+
let result = d.u32();
172+
for (const i of tgpu.unroll(std.range(3))) {
173+
// this block will be inlined 3 times
174+
result += i * 10;
175+
}
176+
```
177+
178+
Generates:
179+
```wgsl
180+
var result = 0u;
181+
// unrolled iteration #0
182+
{
183+
result += 0u;
184+
}
185+
// unrolled iteration #1
186+
{
187+
result += 10u;
188+
}
189+
// unrolled iteration #2
190+
{
191+
result += 20u;
192+
}
193+
````
194+
195+
Because `i` is known at compile-time, the `i * 10` is evaluated and injected into the generated code in each block.
196+
For more, refer to [tgpu.unroll documentation.](/TypeGPU/fundamentals/utils/#tgpuunroll).
197+
198+
### Boolean logic
199+
200+
Logical expressions are now short-circuited if we can determine the result early.
201+
```ts
202+
const clampingEnabled = tgpu.accessor(d.bool);
203+
204+
function lerp(a: number, b: number, t: number) {
205+
'use gpu';
206+
let value = a + (b - a) * t;
207+
if (clampingEnabled.$ && (value > 1 || value < 0)) {
208+
value = std.saturate(value);
209+
}
210+
return value;
211+
}
212+
```
213+
214+
Generated WGSL depending on the value of `clampingEnabled`:
215+
216+
```wgsl
217+
// clampingEnabled.$ === false
218+
fn lerp(a: f32, b: f32, t: f32) -> f32 {
219+
var value = a + (b - a) * t;
220+
return value;
221+
}
222+
223+
// clampingEnabled.$ === true
224+
fn lerp(a: f32, b: f32, t: f32) -> f32 {
225+
var value = a + (b - a) * t;
226+
if (value > 1 || value < 0) {
227+
value = std.saturate(value);
228+
}
229+
return value;
230+
}
231+
```
232+
233+
### Convenience overload for `tgpu.const` API
234+
235+
If you're defining a WGSL constant using an array schema, you no longer have to duplicate the array length both in the value and in the schema.
236+
The `tgpu.const` function now accepts dynamically-sized schemas.
237+
238+
```diff lang=ts
239+
const ColorStops = d.arrayOf(d.vec3f);
240+
241+
const colorStops = tgpu.const(
242+
- ColorStops(3),
243+
+ ColorStops,
244+
[d.vec3f(1, 0, 0), d.vec3f(0, 1, 0), d.vec3f(0, 0, 1)],
245+
);
246+
```
247+
248+
## Ecosystem updates
249+
250+
There has been a lot of work outside of the `typegpu` package, both internally and from the community.
251+
252+
### Lint plugin
253+
254+
Aleksander Katan ([@aleksanderkatan](https://github.com/aleksanderkatan)) has been working behind the scenes on an ESLint/Oxlint plugin, capable of catching user errors that types cannot.
255+
256+
```ts
257+
import tgpu, { d } from 'typegpu';
258+
259+
function increment(n: number) {
260+
'use gpu';
261+
return n++;
262+
// ^^^
263+
// Cannot assign to 'n' since WGSL parameters are immutable.
264+
// If you're using d.ref, please either use '.$' or disable this rule
265+
}
266+
267+
function createBoid() {
268+
'use gpu';
269+
const boid = { pos: d.vec2f(), size: 1 };
270+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
271+
// { pos: d.vec2f(), size: 1 } must be wrapped in a schema call
272+
return boid;
273+
}
274+
275+
function clampTo0(n: number) {
276+
'use gpu';
277+
let result;
278+
// ^^^^^^
279+
// 'result' must have an initial value
280+
if (n < 0) {
281+
result = 0;
282+
} else {
283+
result = n;
284+
}
285+
return result;
286+
}
287+
```
288+
289+
[For setup instructions and available rules, refer to the documentation](/TypeGPU/tooling/eslint-plugin-typegpu/)
290+
291+
### New @typegpu/color helpers
292+
293+
There are three new helper functions importable from `@typegpu/color` which can be called at compile-time to create
294+
color vectors from hexadecimal strings: `hexToRgb`, `hexToRgba` and `hexToOklab`.
295+
296+
```ts
297+
import { hexToRgb } from '@typegpu/color';
298+
299+
function getGradientColor(t: number) {
300+
'use gpu';
301+
const from = hexToRgb('#FF00FF');
302+
const to = hexToRgb('#00FF00');
303+
return std.mix(from, to, t);
304+
}
305+
```
306+
307+
Generated WGSL:
308+
```wgsl
309+
fn getGradientColor(t: f32) -> vec3f {
310+
var from = vec3f(1, 0, 1);
311+
var to = vec3f(0, 1, 0);
312+
return mix(from, to, t);
313+
}
314+
````
315+
316+
### Bundler plugin rewrite
317+
318+
The `unplugin-typegpu` package is what enables TypeScript shaders, and to support its continued development, we rewrote it from
319+
the ground up. It should now support more bundlers than ever before, out of the box, including `esbuild`.
320+
321+
### Motion GPU
322+
323+
A minimalist WebGPU framework called [Motion GPU](https://motion-gpu.dev/) introduced a way to integrate with TypeGPU, and wrote about it
324+
in their documentation [(Integrations / TypeGPU)](https://motion-gpu.dev/docs/integrations-typegpu). It's awesome to see the continued adoption
325+
of TypeGPU in other ecosystems and communities 🎉
326+
327+
## What's next?
328+
329+
There are many more things introduced in TypeGPU 0.11 that I haven't mentioned. If you're curious, you can
330+
read [the full 0.11.0 changelog](https://github.com/software-mansion/TypeGPU/compare/v0.10.2...v0.11.0).

0 commit comments

Comments
 (0)