Skip to content

Commit 9e4ce90

Browse files
committed
Merge branch 'release'
2 parents e068100 + a18f2f1 commit 9e4ce90

File tree

5 files changed

+152
-116
lines changed

5 files changed

+152
-116
lines changed

apps/typegpu-docs/src/components/shaderhunt/ChallengesSignupPopover.astro

Lines changed: 74 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
<div data-challenges-signup-popover="true" class="contents">
55
<slot />
6-
<dialog class="fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 border border-gray-300 dark:border-gray-700 p-6 rounded-none shadow-lg max-w-md w-full backdrop:bg-black/20 dark:backdrop:bg-black/50 backdrop:backdrop-blur-md">
7-
<form method="dialog">
6+
<dialog data-success="false" class="group fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 border border-gray-300 dark:border-gray-700 p-6 rounded-none shadow-lg max-w-md w-full backdrop:bg-black/20 dark:backdrop:bg-black/50 backdrop:backdrop-blur-md">
7+
<form method="dialog" class="hidden group-[[data-success='false']]:block">
88
<p class="text-justify text-base mb-12">Sign up to be notified when the <strong>ShaderHunt</strong> platform is available, along with interactive examples teaching TypeGPU from the ground up.</p>
99
<label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Your name (required)</label>
1010
<input type="text" id="name" name="name" required class="w-full mb-8 px-3 py-2 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 rounded-none focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400" />
@@ -22,64 +22,88 @@
2222
</div>
2323
<button type="submit" class="mt-4 w-full px-4 py-2 bg-accent-600 dark:bg-accent-700 text-white font-medium border-none rounded-none hover:bg-accent-700 dark:hover:bg-accent-800 focus:outline-none focus:ring-2 focus:ring-accent-500 dark:focus:ring-accent-400">Get Notified</button>
2424
</form>
25+
<div class="hidden group-[[data-success='true']]:block">
26+
<p class="text-2xl font-bold text-center text-gray-700 dark:text-gray-300 mb-5">Thank you for signing up for ShaderHunt updates!</p>
27+
<p class="text-sm text-center text-gray-700 dark:text-gray-300 my-3">You should receive a confirmation email shortly.</p>
28+
<button data-close type="button" class="mt-4 w-full px-4 py-2 bg-accent-600 dark:bg-accent-700 text-white font-medium border-none rounded-none hover:bg-accent-700 dark:hover:bg-accent-800 focus:outline-none focus:ring-2 focus:ring-accent-500 dark:focus:ring-accent-400">Close</button>
29+
</div>
2530
</dialog>
2631
</div>
2732

2833
<script>
2934
const popovers = document.querySelectorAll('[data-challenges-signup-popover="true"]');
30-
popovers.forEach((popover) => {
31-
const linkElement: HTMLAnchorElement|null = popover.querySelector('a');
32-
const dialogElement: HTMLDialogElement|null = popover.querySelector('dialog');
33-
const formElement: HTMLFormElement|null = popover.querySelector('form');
3435

35-
if (!linkElement || !dialogElement || !formElement) {
36-
return;
37-
}
38-
39-
// Check if URL hash indicates popover should be open
40-
if (window.location.hash === '#challenges-signup') {
41-
dialogElement?.showModal();
42-
}
36+
class DialogBox {
37+
readonly dialogElement: HTMLDialogElement;
4338

44-
linkElement.addEventListener('click', (e) => {
45-
e.preventDefault();
46-
window.location.hash = '#challenges-signup';
47-
dialogElement?.showModal();
48-
});
39+
constructor(popover: Element) {
40+
const linkElement: HTMLAnchorElement|null = popover.querySelector('a');
41+
const dialogElement: HTMLDialogElement|null = popover.querySelector('dialog');
42+
const formElement: HTMLFormElement|null = popover.querySelector('form');
43+
const closeButton: HTMLButtonElement|null = popover.querySelector('button[data-close]');
4944

50-
dialogElement.addEventListener('close', () => {
51-
if (window.location.hash === '#challenges-signup') {
52-
window.location.hash = '';
45+
if (!linkElement || !dialogElement || !formElement || !closeButton) {
46+
throw new Error(`Missing required elements: link: ${linkElement ? '✅' : '🚫'}, dialog: ${dialogElement ? '✅' : '🚫'}, form: ${formElement ? '✅' : '🚫'}, closeButton: ${closeButton ? '✅' : '🚫'}`);
5347
}
54-
});
5548

56-
formElement.addEventListener('submit', async (e) => {
57-
e.preventDefault();
58-
const formData = new FormData(formElement);
59-
const data = {
60-
name: formData.get('name'),
61-
email: formData.get('email'),
62-
newsletterAgree: formData.get('newsletter-agree') === 'on'
63-
};
49+
this.dialogElement = dialogElement;
50+
this.dialogElement.dataset.success = 'false';
51+
52+
linkElement.addEventListener('click', (e) => {
53+
e.preventDefault();
54+
window.location.hash = '#challenges-signup';
55+
dialogElement?.showModal();
56+
});
6457

65-
try {
66-
const response = await fetch('https://swmansion.dev/api/shaderhunt/signin', {
67-
method: 'POST',
68-
headers: {
69-
'Content-Type': 'application/json'
70-
},
71-
body: JSON.stringify(data)
72-
});
73-
console.log(await response.text());
74-
if (response.ok) {
75-
formElement.reset();
76-
dialogElement.close();
77-
} else {
78-
alert('Error submitting form. Please try again.');
58+
dialogElement.addEventListener('close', () => {
59+
if (window.location.hash === '#challenges-signup') {
60+
window.location.hash = '';
7961
}
80-
} catch (error) {
81-
alert('Network error. Please try again.');
82-
}
83-
});
84-
});
62+
});
63+
64+
formElement.addEventListener('submit', async (e) => {
65+
e.preventDefault();
66+
const formData = new FormData(formElement);
67+
const data = {
68+
name: formData.get('name'),
69+
email: formData.get('email'),
70+
newsletterAgree: formData.get('newsletter-agree') === 'on'
71+
};
72+
73+
try {
74+
const response = await fetch('https://swmansion.dev/api/shaderhunt/signin', {
75+
method: 'POST',
76+
headers: {
77+
'Content-Type': 'application/json'
78+
},
79+
body: JSON.stringify(data)
80+
});
81+
console.log(await response.text());
82+
if (response.ok) {
83+
formElement.reset();
84+
dialogElement.dataset.success = 'true';
85+
} else {
86+
alert('Error submitting form. Please try again.');
87+
}
88+
} catch (error) {
89+
alert('Network error. Please try again.');
90+
}
91+
});
92+
93+
closeButton.addEventListener('click', () => {
94+
dialogElement.close();
95+
});
96+
}
97+
}
98+
99+
const dialogBoxes = Array.from(popovers).map((popover) => new DialogBox(popover));
100+
101+
// Check if URL hash indicates popover should be open
102+
if (window.location.hash === '#challenges-signup') {
103+
const firstDialogBox = dialogBoxes[0]
104+
if (!firstDialogBox) {
105+
throw new Error('Expected at least one dialog box');
106+
}
107+
firstDialogBox.dialogElement.showModal();
108+
}
85109
</script>

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

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Imported functions from external sources are automatically resolved and embedded
1717
## Defining a function
1818

1919
:::note[WGSL enthusiasts!]
20-
Don't let the JavaScript scare you! TypeGPU functions can be implemented using either WGSL or JS, both being able to call one another.
20+
Don't let the JavaScript discourage you! TypeGPU functions can be implemented using either WGSL or JS, both being able to call one another.
2121
If you're planning on only using WGSL, you can skip right over to [Implementing functions in WGSL](#implementing-functions-in-wgsl),
2222
though we recommend reading through anyway.
2323
:::
@@ -53,7 +53,6 @@ const neighborhood = (a: number, r: number) => {
5353
// ---cut---
5454
const main = () => {
5555
'use gpu';
56-
// Call from another function
5756
return neighborhood(1.1, 0.5);
5857
};
5958

@@ -91,12 +90,27 @@ You can already notice a few things about TypeGPU functions:
9190
- TS types are properly inferred, feel free to hover over the variables to see their types.
9291
- The generated code closely matches your source code.
9392
93+
:::caution[Using numeric literals]
94+
Be mindful when using numeric literals. Numbers with nothing after the decimal point (those that pass the `Number.isInteger` test) are inferred as integers, the rest as floats.
95+
```js
96+
const foo = 1.1; // generates: const foo = 1.1f;
97+
const bar = 1.0; // generates: const bar = 1i;
98+
const baz = 123; // generates: const baz = 123i;
99+
```
100+
To ensure that a literal will be coerced to a specific type, you can wrap them in a schema:
101+
```js
102+
const foo = d.u32(1.1); // generates: const foo = 1u;
103+
const bar = d.f32(1.0); // generates: const bar = 1f;
104+
const baz = d.f16(123); // generates: const baz = 123h;
105+
```
106+
:::
107+
94108
### Code transformation
95109

96-
To make this all work, we perform a small transformation to functions marked with `'use gpu'`, leaving all other code untouched. Every project's setup is different, and we want to be as non-invasive as possible. The [unplugin-typegpu](/TypeGPU/tooling/unplugin-typegpu) package hooks into existing bundlers and build tools, extracts ASTs from TypeGPU functions and compacts them into our custom format called [tinyest](https://www.npmjs.com/package/tinyest). This metadata is injected into the final JS bundle, then used to efficiently generate equivalent WGSL at runtime.
110+
To make this all work, we perform a small transformation to functions marked with `'use gpu'`. Every project's setup is different, and we want to be as non-invasive as possible. The [unplugin-typegpu](/TypeGPU/tooling/unplugin-typegpu) package hooks into existing bundlers and build tools, extracts ASTs from TypeGPU functions and compacts them into our custom format called [tinyest](https://www.npmjs.com/package/tinyest). This metadata is injected into the final JS bundle, then used to efficiently generate equivalent WGSL at runtime.
97111

98112
:::tip
99-
If you wish for all WGSL generation to happen at build time, combine [unplugin-macros](https://github.com/unplugin/unplugin-macros) and our [resolve API](/TypeGPU/fundamentals/resolve).
113+
If all your shader code is predetermined, or you want to precompute a set of variants ahead of time, you can combine [unplugin-macros](https://github.com/unplugin/unplugin-macros) and our [resolve API](/TypeGPU/fundamentals/resolve).
100114
:::
101115

102116

@@ -193,25 +207,6 @@ const double = <T extends d.v2f | d.v3f | d.v4f>(a: T): T => {
193207
};
194208
```
195209

196-
### Standard library
197-
198-
TypeGPU provides a set of standard functions under `typegpu/std`, which you can use in your own TypeGPU functions. Our goal is for all functions to have matching
199-
behavior on the CPU and GPU, which unlocks many possibilities (shader unit testing, shared business logic, and more...).
200-
201-
```ts twoslash
202-
import * as d from 'typegpu/data';
203-
import * as std from 'typegpu/std';
204-
205-
function manhattanDistance(a: d.v3f, b: d.v3f) {
206-
'use gpu';
207-
const dx = std.abs(a.x - b.x);
208-
const dy = std.abs(a.y - b.y);
209-
const dz = std.abs(a.z - b.z);
210-
211-
return std.max(dx, std.max(dy, dz));
212-
}
213-
```
214-
215210
You can explore the set of standard functions in the [API Reference](/TypeGPU/api/typegpu/std/functions/abs/).
216211

217212
### The outer scope
@@ -310,6 +305,27 @@ you have to use supplementary functions from `typegpu/std` (*add, mul, eq, lt, g
310305

311306
* **Math.\*** --
312307
Utility functions on the `Math` object can't automatically run on the GPU, but can usually be swapped with functions exported from `typegpu/std`.
308+
Additionally, if you're able to pull the call to `Math.*` out of the function, you can store the result in a constant and use it in the function
309+
no problem.
310+
311+
### Standard library
312+
313+
TypeGPU provides a set of standard functions under `typegpu/std`, which you can use in your own TypeGPU functions. Our goal is for all functions to have matching
314+
behavior on the CPU and GPU, which unlocks many possibilities (shader unit testing, shared business logic, and more...).
315+
316+
```ts twoslash
317+
import * as d from 'typegpu/data';
318+
import * as std from 'typegpu/std';
319+
320+
function manhattanDistance(a: d.v3f, b: d.v3f) {
321+
'use gpu';
322+
const dx = std.abs(a.x - b.x);
323+
const dy = std.abs(a.y - b.y);
324+
const dz = std.abs(a.z - b.z);
325+
326+
return std.max(dx, std.max(dy, dz));
327+
}
328+
```
313329

314330
## Function shells
315331

apps/typegpu-docs/src/content/docs/getting-started.mdx

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,23 +27,45 @@ Install TypeGPU using the package manager of your choice.
2727
</TabItem>
2828
</Tabs>
2929

30-
You can then import it in JavaScript/TypeScript code.
30+
TypeScript doesn't yet ship WebGPU types by default, so we install them separately:
3131

32-
```ts
33-
// main.ts
32+
<Tabs syncKey="package-manager">
33+
<TabItem label="npm" icon="seti:npm">
34+
```sh frame=none
35+
npm install --save-dev @webgpu/types
36+
```
37+
</TabItem>
38+
<TabItem label="pnpm" icon="pnpm">
39+
```sh frame=none
40+
pnpm add -D @webgpu/types
41+
```
42+
</TabItem>
43+
<TabItem label="yarn" icon="seti:yarn">
44+
```sh frame=none
45+
yarn add -D @webgpu/types
46+
```
47+
</TabItem>
48+
</Tabs>
3449

35-
import tgpu from 'typegpu';
50+
Then add the following to your `tsconfig.json`:
3651

37-
// ...
52+
```json
53+
{
54+
"compilerOptions": {
55+
"types": ["@webgpu/types"],
56+
// ...
57+
},
58+
// ...
59+
}
3860
```
3961

40-
## Live Examples
41-
42-
Our [live examples](/TypeGPU/examples) showcase many use-cases of TypeGPU. Feel free to check them out! You can also open each of them on StackBlitz in order to edit the code and see the preview update live.
62+
:::note
63+
TypeGPU features related to writing shaders in JavaScript/TypeScript rely on an [additional build tool that hooks into existing bundlers called unplugin-typegpu](/TypeGPU/tooling/unplugin-typegpu). Make sure to set it up as well if you plan on using these features.
64+
:::
4365

44-
## StackBlitz
66+
## Live Examples
4567

46-
You can play with TypeGPU right in the browser using our [StackBlitz example](https://stackblitz.com/edit/typegpu-example?file=src%2Fmain.ts).
68+
Our [live examples](/TypeGPU/examples) showcase many use-cases of TypeGPU. Feel free to check them out! You can also open each of them on StackBlitz in order to edit the code and see the preview update live, or to bootstrap your own project.
4769

4870
## Troubleshooting
4971

apps/typegpu-docs/src/content/docs/tooling/unplugin-typegpu.mdx

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,47 +7,23 @@ description: A guide on how to use the optional build plugin for TypeGPU
77

88
The package includes the following functionalities:
99

10-
- **TGSL functions**
10+
- **TypeGPU functions implemented in JS/TS**
1111

12-
TypeGPU allows running a subset of JavaScript (which we call TGSL) on the GPU. It is achieved by transpiling JS functions into WGSL.
13-
This can be done via a Just-In-Time compiler or at build time, using the plugin.
14-
Transpiling at build time reduces the performance overhead at runtime,
15-
and it is also the only possible approach on React Native, as the function code string is not available during runtime there.
16-
17-
_unplugin-typegpu_ scans the project files looking for the tgpu function shell implementations.
18-
It transpiles the JS code into a compact AST format, called [tinyest](https://www.npmjs.com/package/tinyest).
19-
20-
When an implementation function is passed directly to the shell creation, the plugin should have no problem identifying and processing it.
12+
The plugin allows transpiling a subset of JavaScript to WebGPU Shading Language (WGSL).
13+
The parsing of JavaScript happens ahead of time, emitting a highly compact AST format, called [tinyest](https://www.npmjs.com/package/tinyest).
2114

2215
```ts
2316
import tgpu from 'typegpu';
2417
import * as d from 'typegpu/data';
2518

26-
const add = tgpu.fn([d.u32, d.u32], d.u32)((a, b) => a + b);
27-
```
28-
29-
However, if the implementation function, or the shell, is referenced via a variable, the plugin will not recognize it as TGSL,
30-
thus to make it work, the function needs to be marked with a `'use gpu'` directive.
31-
32-
```ts
33-
const addFn = tgpu.fn([d.u32, d.u32], d.u32);
34-
35-
const add = addFn((a, b) => {
36-
'use gpu';
37-
return a + b;
38-
});
39-
```
40-
41-
```ts
42-
const addImpl = (a, b) => {
19+
// Found by the plugin and decorated with 'tinyest' metadata
20+
const add = (a: number, b: number) => {
4321
'use gpu';
4422
return a + b;
4523
};
46-
47-
const add = tgpu.fn([d.u32, d.u32], d.u32)(addImpl);
4824
```
4925

50-
Besides transpiling JS into AST, the plugin also collects external references, so it is not necessary to pass them to the `$uses` method anymore.
26+
Besides parsing JS into an AST, the plugin also collects external references, so it is not necessary to pass them to the `$uses` method (as is required when implementing functions in WGSL).
5127

5228
- **Automatic naming of tgpu objects**
5329

@@ -96,7 +72,7 @@ import { defineConfig } from 'vite';
9672
import typegpuPlugin from 'unplugin-typegpu/vite';
9773

9874
export default defineConfig({
99-
plugins: [typegpuPlugin()],
75+
plugins: [typegpuPlugin({})],
10076
});
10177
```
10278

apps/typegpu-docs/src/examples/simulation/slime-mold/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,6 @@ function frame(now: number) {
289289
.with(renderBindGroups[1 - currentTexture])
290290
.draw(3);
291291

292-
root['~unstable'].flush();
293-
294292
currentTexture = 1 - currentTexture;
295293

296294
requestAnimationFrame(frame);

0 commit comments

Comments
 (0)