Skip to content

Commit 2cacfe5

Browse files
committed
chore(πŸ™): update to TypeGPU 0.11
1 parent d188e19 commit 2cacfe5

5 files changed

Lines changed: 230 additions & 192 deletions

File tree

β€Žapps/example/babel.config.jsβ€Ž

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ module.exports = {
22
presets: ['module:@react-native/babel-preset'],
33
plugins: [
44
'react-native-reanimated/plugin',
5-
"transform-inline-environment-variables",
5+
'transform-inline-environment-variables',
6+
'@babel/plugin-transform-class-static-block',
7+
'unplugin-typegpu/babel',
68
],
79
};

β€Žapps/example/package.jsonβ€Ž

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"@tensorflow/tfjs": "^4.22.0",
2626
"@tensorflow/tfjs-backend-webgpu": "^4.22.0",
2727
"@tensorflow/tfjs-vis": "^1.5.1",
28+
"@typegpu/react": "^0.11.0",
2829
"async-mutex": "^0.5.0",
2930
"babel-plugin-transform-inline-environment-variables": "^0.4.4",
3031
"fast-text-encoding": "^1.0.6",
@@ -40,7 +41,7 @@
4041
"react-native-worklets": "0.7.2",
4142
"teapot": "^1.0.0",
4243
"three": "0.172.0",
43-
"typegpu": "^0.3.2",
44+
"typegpu": "^0.11.4",
4445
"wgpu-matrix": "^3.0.2"
4546
},
4647
"devDependencies": {
@@ -66,7 +67,8 @@
6667
"prettier": "2.8.8",
6768
"react-native-test-app": "4.4.10",
6869
"react-test-renderer": "18.2.0",
69-
"typescript": "5.0.4"
70+
"typescript": "5.0.4",
71+
"unplugin-typegpu": "https://pkg.pr.new/unplugin-typegpu@bbce6b7.tgz"
7072
},
7173
"engines": {
7274
"node": ">=18"

β€Žapps/example/src/GradientTiles/GradientTiles.tsxβ€Ž

Lines changed: 32 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,159 +1,57 @@
1-
import { useEffect, useMemo, useRef, useState } from "react";
2-
import { Button, PixelRatio, StyleSheet, Text, View } from "react-native";
3-
import type { CanvasRef } from "react-native-wgpu";
4-
import { Canvas, useDevice } from "react-native-wgpu";
5-
import * as d from "typegpu/data";
6-
import tgpu, { type TgpuBindGroup, type TgpuBuffer } from "typegpu";
7-
8-
import { vertWGSL, fragWGSL } from "./gradientWgsl";
9-
10-
const Span = d.struct({
11-
x: d.u32,
12-
y: d.u32,
13-
});
14-
15-
const bindGroupLayout = tgpu.bindGroupLayout({
16-
span: { uniform: Span },
17-
});
18-
19-
interface RenderingState {
20-
pipeline: GPURenderPipeline;
21-
spanBuffer: TgpuBuffer<typeof Span>;
22-
bindGroup: TgpuBindGroup<(typeof bindGroupLayout)["entries"]>;
23-
}
24-
25-
function useRoot() {
26-
const { device } = useDevice();
27-
28-
return useMemo(
29-
() => (device ? tgpu.initFromDevice({ device }) : null),
30-
[device],
31-
);
32-
}
1+
import { useMemo, useState } from "react";
2+
import { Button, StyleSheet, Text, View } from "react-native";
3+
import { Canvas } from "react-native-wgpu";
4+
import { common, d, std } from "typegpu";
5+
import { useConfigureContext, useFrame, useMirroredUniform, useRoot } from "@typegpu/react";
336

347
export function GradientTiles() {
35-
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
36-
const [state, setState] = useState<null | RenderingState>(null);
37-
const [spanX, setSpanX] = useState(4);
38-
const [spanY, setSpanY] = useState(4);
398
const root = useRoot();
40-
const { device = null } = root ?? {};
41-
const ref = useRef<CanvasRef>(null);
429

43-
useEffect(() => {
44-
if (!device || !root || state !== null) {
45-
return;
46-
}
47-
const context = ref.current?.getContext("webgpu")!;
48-
49-
const canvas = context.canvas as HTMLCanvasElement;
50-
canvas.width = canvas.clientWidth * PixelRatio.get();
51-
canvas.height = canvas.clientHeight * PixelRatio.get();
52-
context.configure({
53-
device,
54-
format: presentationFormat,
55-
});
56-
57-
const spanBuffer = root
58-
.createBuffer(Span, { x: 10, y: 10 })
59-
.$usage("uniform");
60-
61-
const shader = device.createShaderModule({
62-
code: tgpu.resolve({
63-
template: `${vertWGSL} ${fragWGSL}`,
64-
externals: {
65-
_EXT_: {
66-
span: bindGroupLayout.bound.span,
67-
},
68-
},
69-
}),
70-
});
10+
const [spanX, setSpanX] = useState(4);
11+
const [spanY, setSpanY] = useState(4);
7112

72-
const pipeline = device.createRenderPipeline({
73-
layout: device.createPipelineLayout({
74-
bindGroupLayouts: [root.unwrap(bindGroupLayout)],
75-
}),
76-
vertex: {
77-
module: shader,
13+
// Mirroring React state on the GPU as a uniform
14+
const span = useMirroredUniform(d.vec2u, d.vec2u(spanX, spanY));
15+
16+
const pipeline = useMemo(() => {
17+
// Defining a full-screen shader
18+
return root.createRenderPipeline({
19+
vertex: common.fullScreenTriangle,
20+
fragment: ({ uv }) => {
21+
"use gpu";
22+
const red = std.floor(uv.x * d.f32(span.$.x)) / d.f32(span.$.x);
23+
const green = std.floor(uv.y * d.f32(span.$.y)) / d.f32(span.$.y);
24+
return d.vec4f(red, green, 0.5, 1.0);
7825
},
79-
fragment: {
80-
module: shader,
81-
targets: [
82-
{
83-
format: presentationFormat,
84-
},
85-
],
86-
},
87-
primitive: {
88-
topology: "triangle-strip",
89-
},
90-
});
91-
92-
const bindGroup = root.createBindGroup(bindGroupLayout, {
93-
span: spanBuffer,
9426
});
27+
}, [root, span]);
9528

96-
setState({ bindGroup, pipeline, spanBuffer });
97-
}, [ref, device, root, presentationFormat, state]);
98-
99-
useEffect(() => {
100-
if (!device || !root || !state) {
101-
return;
102-
}
103-
104-
const { bindGroup, pipeline, spanBuffer } = state;
105-
const context = ref.current?.getContext("webgpu")!;
106-
const textureView = context.getCurrentTexture().createView();
107-
const renderPassDescriptor: GPURenderPassDescriptor = {
108-
colorAttachments: [
109-
{
110-
view: textureView,
111-
clearValue: [0, 0, 0, 0],
112-
loadOp: "clear",
113-
storeOp: "store",
114-
},
115-
],
116-
};
29+
const { ref, ctxRef } = useConfigureContext();
11730

118-
spanBuffer.write({ x: spanX, y: spanY });
31+
useFrame(() => {
32+
const ctx = ctxRef.current;
33+
if (!ctx) return;
11934

120-
const commandEncoder = device.createCommandEncoder();
121-
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
122-
passEncoder.setPipeline(pipeline);
123-
passEncoder.setBindGroup(0, root.unwrap(bindGroup));
124-
passEncoder.draw(4);
125-
passEncoder.end();
35+
// Drawing to the canvas each frame
36+
pipeline.withColorAttachment({ view: ctx }).draw(3);
12637

127-
device.queue.submit([commandEncoder.finish()]);
128-
context.present();
129-
}, [ref, device, root, spanX, spanY, state]);
38+
ctx.present?.();
39+
});
13040

13141
return (
13242
<View style={style.container}>
13343
<Canvas ref={ref} style={style.webgpu} transparent />
13444
<View style={style.controls}>
13545
<View style={style.buttonRow}>
13646
<Text style={style.spanText}>span x: </Text>
137-
<Button
138-
title="βž–"
139-
onPress={() => setSpanX((prev) => Math.max(1, prev - 1))}
140-
/>
141-
<Button
142-
title="βž•"
143-
onPress={() => setSpanX((prev) => Math.min(prev + 1, 10))}
144-
/>
47+
<Button title="βž–" onPress={() => setSpanX((prev) => Math.max(1, prev - 1))} />
48+
<Button title="βž•" onPress={() => setSpanX((prev) => Math.min(prev + 1, 10))} />
14549
</View>
14650

14751
<View style={style.buttonRow}>
14852
<Text style={style.spanText}>span y: </Text>
149-
<Button
150-
title="βž–"
151-
onPress={() => setSpanY((prev) => Math.max(1, prev - 1))}
152-
/>
153-
<Button
154-
title="βž•"
155-
onPress={() => setSpanY((prev) => Math.min(prev + 1, 10))}
156-
/>
53+
<Button title="βž–" onPress={() => setSpanY((prev) => Math.max(1, prev - 1))} />
54+
<Button title="βž•" onPress={() => setSpanY((prev) => Math.min(prev + 1, 10))} />
15755
</View>
15856
</View>
15957
</View>

β€Žapps/example/src/GradientTiles/gradientWgsl.tsβ€Ž

Lines changed: 0 additions & 39 deletions
This file was deleted.

0 commit comments

Comments
Β (0)