Skip to content

Commit b326ac7

Browse files
authored
feat(🔺): new async handling (#275)
1 parent 43df0ea commit b326ac7

96 files changed

Lines changed: 2183 additions & 1569 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
diff --git a/package.json b/package.json
2-
index e866692ead26f118141e8a3c67374a09f8aab0e9..08997ecbf59a077d15d035aee4f1285a5da76a95 100644
2+
index 265e5707cc4c56a369d4c9db0d75e9077b12cdee..15f54ddb2ac2bd34b95545be06c589d04d70a5cb 100644
33
--- a/package.json
44
+++ b/package.json
5-
@@ -30,7 +30,7 @@
5+
@@ -31,7 +31,7 @@
66
"main": "dist/react-three-fiber.cjs.js",
77
"module": "dist/react-three-fiber.esm.js",
88
"types": "dist/react-three-fiber.cjs.d.ts",
99
- "react-native": "native/dist/react-three-fiber-native.cjs.js",
10-
+ "react-native": "dist/react-three-fiber.cjs.js",
10+
+ "react-native": "dist/react-three-fiber.esm.js",
1111
"sideEffects": false,
1212
"preconstruct": {
1313
"entrypoints": [

‎README.md‎

Lines changed: 84 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
React Native implementation of WebGPU using [Dawn](https://dawn.googlesource.com/dawn).
44
This is currently a technical preview for early adopters.
55

6+
React Native WebGPU requires React Native 0.81 or newer and doesn't run on legacy architecture.
7+
68
## Installation
79

810
Please note that the package name is `react-native-wgpu`.
@@ -15,19 +17,19 @@ Below are some examples from the [example app](/apps/example/).
1517

1618
https://github.com/user-attachments/assets/116a41b2-2cf8-49f1-9f16-a5c83637c198
1719

18-
## Three.js
19-
2020
Starting from `r168`, Three.js runs out of the box with React Native WebGPU.
2121
You need to have a slight modification of [the metro config](/apps/example/metro.config.js) to resolve Three.js to the WebGPU build.
22-
We also support [three-fiber](/apps/example/src/ThreeJS/Fiber.tsx).
23-
For model loading, we also need the following polyfill:
22+
We also support [react-three-fiber](/apps/example/src/ThreeJS/Fiber.tsx); to make it work, patch `node_modules/@react-three/fiber/package.json` (for instance via `patch-package`) so that it resolves to the WebGPU entry point instead of the React Native bundle:
2423

25-
```tsx
26-
// The two lines below are needed by three.js
27-
import "fast-text-encoding";
28-
window.parent = window;
24+
```diff
25+
diff --git a/node_modules/@react-three/fiber/package.json b/node_modules/@react-three/fiber/package.json
26+
@@
27+
- "react-native": "native/dist/react-three-fiber-native.cjs.js",
28+
+ "react-native": "dist/react-three-fiber.cjs.js",
2929
```
3030

31+
For model loading, we also need [the following polyfill](/apps/example/src/App.tsx#29).
32+
3133
https://github.com/user-attachments/assets/5b49ef63-0a3c-4679-aeb5-e4b4dddfcc1d
3234

3335
We also provide prebuilt binaries for visionOS and macOS.
@@ -36,87 +38,91 @@ https://github.com/user-attachments/assets/2d5c618e-5b15-4cef-8558-d4ddf8c70667
3638

3739
## Usage
3840

39-
Currently we recommend to use the `useCanvasEffect` to access the WebGPU context.
41+
Usage is identical to Web.
4042

4143
```tsx
4244
import React from "react";
4345
import { StyleSheet, View, PixelRatio } from "react-native";
44-
import { Canvas, useCanvasEffect } from "react-native-wgpu";
46+
import { Canvas, CanvasRef } from "react-native-wgpu";
4547

4648
import { redFragWGSL, triangleVertWGSL } from "./triangle";
4749

4850
export function HelloTriangle() {
49-
const ref = useCanvasEffect(async () => {
50-
const adapter = await navigator.gpu.requestAdapter();
51-
if (!adapter) {
52-
throw new Error("No adapter");
53-
}
54-
const device = await adapter.requestDevice();
55-
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
56-
57-
const context = ref.current!.getContext("webgpu")!;
58-
const canvas = context.canvas as HTMLCanvasElement;
59-
canvas.width = canvas.clientWidth * PixelRatio.get();
60-
canvas.height = canvas.clientHeight * PixelRatio.get();
61-
62-
if (!context) {
63-
throw new Error("No context");
64-
}
65-
66-
context.configure({
67-
device,
68-
format: presentationFormat,
69-
alphaMode: "opaque",
70-
});
71-
72-
const pipeline = device.createRenderPipeline({
73-
layout: "auto",
74-
vertex: {
75-
module: device.createShaderModule({
76-
code: triangleVertWGSL,
77-
}),
78-
entryPoint: "main",
79-
},
80-
fragment: {
81-
module: device.createShaderModule({
82-
code: redFragWGSL,
83-
}),
84-
entryPoint: "main",
85-
targets: [
51+
const ref = useRef<CanvasRef>(null);
52+
useEffect(() => {
53+
const helloTriangle = async () => {
54+
const adapter = await navigator.gpu.requestAdapter();
55+
if (!adapter) {
56+
throw new Error("No adapter");
57+
}
58+
const device = await adapter.requestDevice();
59+
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
60+
61+
const context = ref.current!.getContext("webgpu")!;
62+
const canvas = context.canvas as HTMLCanvasElement;
63+
canvas.width = canvas.clientWidth * PixelRatio.get();
64+
canvas.height = canvas.clientHeight * PixelRatio.get();
65+
66+
if (!context) {
67+
throw new Error("No context");
68+
}
69+
70+
context.configure({
71+
device,
72+
format: presentationFormat,
73+
alphaMode: "opaque",
74+
});
75+
76+
const pipeline = device.createRenderPipeline({
77+
layout: "auto",
78+
vertex: {
79+
module: device.createShaderModule({
80+
code: triangleVertWGSL,
81+
}),
82+
entryPoint: "main",
83+
},
84+
fragment: {
85+
module: device.createShaderModule({
86+
code: redFragWGSL,
87+
}),
88+
entryPoint: "main",
89+
targets: [
90+
{
91+
format: presentationFormat,
92+
},
93+
],
94+
},
95+
primitive: {
96+
topology: "triangle-list",
97+
},
98+
});
99+
100+
const commandEncoder = device.createCommandEncoder();
101+
102+
const textureView = context.getCurrentTexture().createView();
103+
104+
const renderPassDescriptor: GPURenderPassDescriptor = {
105+
colorAttachments: [
86106
{
87-
format: presentationFormat,
107+
view: textureView,
108+
clearValue: [0, 0, 0, 1],
109+
loadOp: "clear",
110+
storeOp: "store",
88111
},
89112
],
90-
},
91-
primitive: {
92-
topology: "triangle-list",
93-
},
94-
});
95-
96-
const commandEncoder = device.createCommandEncoder();
97-
98-
const textureView = context.getCurrentTexture().createView();
99-
100-
const renderPassDescriptor: GPURenderPassDescriptor = {
101-
colorAttachments: [
102-
{
103-
view: textureView,
104-
clearValue: [0, 0, 0, 1],
105-
loadOp: "clear",
106-
storeOp: "store",
107-
},
108-
],
109-
};
113+
};
110114

111-
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
112-
passEncoder.setPipeline(pipeline);
113-
passEncoder.draw(3);
114-
passEncoder.end();
115+
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
116+
passEncoder.setPipeline(pipeline);
117+
passEncoder.draw(3);
118+
passEncoder.end();
115119

116-
device.queue.submit([commandEncoder.finish()]);
120+
device.queue.submit([commandEncoder.finish()]);
117121

118-
context.present();
119-
});
122+
context.present();
123+
};
124+
helloTriangle();
125+
}, [ref]);
120126

121127
return (
122128
<View style={style.container}>
@@ -137,7 +143,7 @@ const style = StyleSheet.create({
137143

138144
## Example App
139145

140-
To run the example app you first need to [build Dawn or download the prebuilt binaries](#building-dawn).
146+
To run the example app you first need to [install Dawn](#installing-dawn).
141147

142148
From there you will be able to run the example app properly.
143149

@@ -212,9 +218,9 @@ git submodule update --init
212218

213219
Make sure you have all the tools required for building the Skia libraries (Android Studio, XCode, Ninja, CMake, Android NDK/build tools).
214220

215-
### Downloading Dawn
221+
### Installing Dawn
216222

217-
There is an alternative way which is to download the prebuilt binaries from GitHub.
223+
There is an alternative way which is to install the prebuilt binaries from GitHub.
218224

219225
```sh
220226
$ yarn

‎apps/example/ios/Podfile.lock‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1865,7 +1865,7 @@ PODS:
18651865
- ReactCommon/turbomodule/core
18661866
- SocketRocket
18671867
- Yoga
1868-
- react-native-wgpu (0.2.10):
1868+
- react-native-wgpu (0.3.0):
18691869
- boost
18701870
- DoubleConversion
18711871
- fast_float
@@ -2903,7 +2903,7 @@ SPEC CHECKSUMS:
29032903
React-microtasksnativemodule: 75b6604b667d297292345302cc5bfb6b6aeccc1b
29042904
react-native-safe-area-context: c6e2edd1c1da07bdce287fa9d9e60c5f7b514616
29052905
react-native-skia: 5bf2b2107cd7f2d806fd364f5e16b1c7554ed3cd
2906-
react-native-wgpu: 9c80c820813ecb17fe331794180a00fb21a33af2
2906+
react-native-wgpu: 6704c6ad8ef91fdbbad58543089ac50c0e6f3fa7
29072907
React-NativeModulesApple: 879fbdc5dcff7136abceb7880fe8a2022a1bd7c3
29082908
React-oscompat: 93b5535ea7f7dff46aaee4f78309a70979bdde9d
29092909
React-perflogger: 5536d2df3d18fe0920263466f7b46a56351c0510

‎apps/example/package.json‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@
1313
"build:android": "cd android && ./gradlew assembleDebug --warning-mode all",
1414
"build:ios": "react-native build-ios --scheme Example --mode Debug --extra-params \"-sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO\"",
1515
"build:macos": "react-native build-macos --scheme Example --mode Debug",
16-
"mkdist": "node -e \"require('node:fs').mkdirSync('dist', { recursive: true, mode: 0o755 })\""
16+
"mkdist": "node -e \"require('node:fs').mkdirSync('dist', { recursive: true, mode: 0o755 })\"",
17+
"postinstall": "yarn pod:install:ios"
1718
},
1819
"dependencies": {
1920
"@callstack/react-native-visionos": "^0.74.0",
2021
"@react-navigation/native": "^6.1.17",
2122
"@react-navigation/stack": "^6.4.0",
22-
"@react-three/fiber": "^8.17.6",
23+
"@react-three/fiber": "^9.4.0",
2324
"@shopify/react-native-skia": "2.2.14",
2425
"@tensorflow/tfjs": "^4.22.0",
2526
"@tensorflow/tfjs-backend-webgpu": "^4.22.0",

‎apps/example/src/App.tsx‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ import { GradientTiles } from "./GradientTiles";
3030
import { CanvasAPI } from "./CanvasAPI";
3131
import { Tensorflow } from "./Tensorflow";
3232
import { ComputeToys } from "./ComputeToys";
33+
import { Reanimated } from "./Reanimated";
34+
import { AsyncStarvation } from "./Diagnostics/AsyncStarvation";
35+
import { DeviceLostHang } from "./Diagnostics/DeviceLostHang";
3336

3437
// The two lines below are needed by three.js
3538
import "fast-text-encoding";
@@ -80,6 +83,9 @@ function App() {
8083
{(props) => <Tests {...props} assets={assets} />}
8184
</Stack.Screen>
8285
<Stack.Screen name="GradientTiles" component={GradientTiles} />
86+
<Stack.Screen name="Reanimated" component={Reanimated} />
87+
<Stack.Screen name="AsyncStarvation" component={AsyncStarvation} />
88+
<Stack.Screen name="DeviceLostHang" component={DeviceLostHang} />
8389
</Stack.Navigator>
8490
</NavigationContainer>
8591
</GestureHandlerRootView>

‎apps/example/src/CanvasAPI/CanvasAPI.tsx‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
import { Button, View } from "react-native";
2-
import { Canvas, useGPUContext } from "react-native-wgpu";
2+
import type { CanvasRef } from "react-native-wgpu";
3+
import { Canvas } from "react-native-wgpu";
4+
import { useRef } from "react";
35

46
import { redFragWGSL, triangleVertWGSL } from "../Triangle/triangle";
57

68
export const CanvasAPI = () => {
7-
const { ref, context } = useGPUContext();
8-
9+
const ref = useRef<CanvasRef>(null);
910
return (
1011
<View style={{ flex: 1 }}>
1112
<Button
1213
onPress={() =>
1314
(async () => {
15+
const context = ref.current?.getContext("webgpu");
1416
if (!context) {
1517
throw new Error("No context");
1618
}

‎apps/example/src/ComputeToys/ComputeToy.tsx‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useCallback, useEffect, useRef, useState } from "react";
2-
import { Canvas, useCanvasEffect } from "react-native-wgpu";
2+
import type { CanvasRef } from "react-native-wgpu";
3+
import { Canvas } from "react-native-wgpu";
34
import { useWindowDimensions } from "react-native";
45
import { useSharedValue } from "react-native-reanimated";
56
import { Gesture, GestureDetector } from "react-native-gesture-handler";
@@ -46,23 +47,24 @@ export interface ComputeToyProps {
4647
}
4748

4849
export const ComputeToy = ({ toy: { shader, uniforms } }: ComputeToyProps) => {
50+
const ref = useRef<CanvasRef>(null);
4951
const mouse = useSharedValue({ pos: { x: 0, y: 0 }, click: false });
5052
const { width, height } = useWindowDimensions();
5153
const [engine, setEngine] = useState<ComputeEngine | null>(null);
5254
const animationRef = useRef<number | null>(null);
5355
const lastTimeRef = useRef<number>(0);
5456

5557
// Initialize WebGPU and the compute engine
56-
const canvasRef = useCanvasEffect(() => {
58+
useEffect(() => {
5759
const initWebGPU = async () => {
5860
try {
5961
// Create the compute engine
6062
await ComputeEngine.create();
6163
const eng = ComputeEngine.getInstance();
6264

6365
// Set the canvas surface
64-
if (canvasRef.current) {
65-
eng.setSurface(canvasRef.current!);
66+
if (ref.current) {
67+
eng.setSurface(ref.current!);
6668

6769
// Set the canvas size based on your CANVAS constants
6870
// TODO: use PixelRatio.get()?
@@ -106,7 +108,7 @@ export const ComputeToy = ({ toy: { shader, uniforms } }: ComputeToyProps) => {
106108
cancelAnimationFrame(animationRef.current);
107109
}
108110
};
109-
});
111+
}, [height, ref, shader, uniforms, width]);
110112

111113
// Animation/render loop
112114
const renderLoop = useCallback(
@@ -163,7 +165,7 @@ export const ComputeToy = ({ toy: { shader, uniforms } }: ComputeToyProps) => {
163165

164166
return (
165167
<GestureDetector gesture={gesture}>
166-
<Canvas ref={canvasRef} style={{ width, height }} />
168+
<Canvas ref={ref} style={{ width, height }} />
167169
</GestureDetector>
168170
);
169171
};

0 commit comments

Comments
 (0)