Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,6 @@ export function HelloTriangle() {
passEncoder.end();

device.queue.submit([commandEncoder.finish()]);

context.present();
};
helloTriangle();
}, [ref]);
Expand Down Expand Up @@ -174,17 +172,28 @@ ctx.canvas.height = ctx.canvas.clientHeight * PixelRatio.get();

### Frame Scheduling

In React Native, we want to keep frame presentation as a manual operation as we plan to provide more advanced rendering options that are React Native specific.
This means that when you are ready to present a frame, you need to call `present` on the context.
On the **main JS runtime** and the **Reanimated UI runtime**, frame presentation is automatic: once you acquire the frame's texture with `context.getCurrentTexture()` and submit your commands, the frame is presented on the next display refresh (driven by a global vsync source: `CADisplayLink` on iOS, `Choreographer` on Android). There is no `present()` call.

```tsx
// draw
// submit to the queue
device.queue.submit([commandEncoder.finish()]);
// This method is React Native only
context.present();
// The frame is presented automatically on the next vsync.
```

When you render from a **dedicated worklet runtime** (e.g. `createWorkletRuntime` / `runOnRuntime`, or a Vision Camera frame processor), it runs on its own thread where present can't be driven automatically. Call `context.present()` yourself after submitting:

```tsx
const onFrame = () => {
"worklet";
// draw on the dedicated runtime's thread
device.queue.submit([commandEncoder.finish()]);
context.present(); // required on dedicated worklet runtimes; a no-op on JS/UI
};
```

`present()` is safe to call from a worklet that runs on either the UI runtime or a dedicated runtime: it presents on the dedicated runtime and does nothing on the JS/UI runtime (which auto-present).

### Canvas Transparency

On Android, the `alphaMode` property is ignored when configuring the canvas.
Expand Down Expand Up @@ -296,7 +305,6 @@ const render = () => {

// Release the surface's access window right after the submit that sampled it.
externalTexture.destroy();
context.present();
};
```

Expand Down Expand Up @@ -328,7 +336,6 @@ const renderFrame = (device: GPUDevice, context: GPUCanvasContext) => {
const commandEncoder = device.createCommandEncoder();
// ... render ...
device.queue.submit([commandEncoder.finish()]);
context.present();
};

// Initialize WebGPU on main thread, then run on UI thread
Expand Down
4 changes: 2 additions & 2 deletions apps/example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1924,7 +1924,7 @@ PODS:
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- react-native-wgpu (0.5.12):
- react-native-wgpu (0.5.13):
- boost
- DoubleConversion
- fast_float
Expand Down Expand Up @@ -3074,7 +3074,7 @@ SPEC CHECKSUMS:
React-microtasksnativemodule: 75b6604b667d297292345302cc5bfb6b6aeccc1b
react-native-safe-area-context: c00143b4823773bba23f2f19f85663ae89ceb460
react-native-skia: fc73e9bdc46ebb420a98c9c2be29fee80f565e79
react-native-wgpu: 274ffec11ee3a082260d9f3d1fb54030a5ca0873
react-native-wgpu: 0496e9efeb4c3939ab56371005ede4e1468591d1
React-NativeModulesApple: 879fbdc5dcff7136abceb7880fe8a2022a1bd7c3
React-oscompat: 93b5535ea7f7dff46aaee4f78309a70979bdde9d
React-perflogger: 5536d2df3d18fe0920263466f7b46a56351c0510
Expand Down
2 changes: 0 additions & 2 deletions apps/example/src/CanvasAPI/CanvasAPI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,6 @@ export const CanvasAPI = () => {
passEncoder.end();

device.queue.submit([commandEncoder.finish()]);

context.present();
})()
}
title="check surface"
Expand Down
1 change: 0 additions & 1 deletion apps/example/src/ComputeToys/engine/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,6 @@ fn passSampleLevelBilinearRepeat(pass_index: int, uv: float2, lod: float) -> flo

// Submit command buffer
this.device.queue.submit([encoder.finish()]);
this.surface!.present();

// Update frame counter
this.bindings!.time.host.frame += 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,6 @@ export const ImportExternalTexture = () => {
// Now that the work sampling it has been submitted, end the external
// texture's access window so the frame's surface is released promptly.
externalTex?.destroy();
context.present();
rafRef.current = requestAnimationFrame(render);
};
rafRef.current = requestAnimationFrame(render);
Expand Down
4 changes: 3 additions & 1 deletion apps/example/src/Reanimated/Reanimated.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,10 @@ export const webGPUDemo = (
passEncoder.end();

device.queue.submit([commandEncoder.finish()]);

// Needed on a dedicated worklet runtime (DedicatedThread); a no-op on the
// UI runtime (UIThread), where present is automatic.
context.present();

if (runAnimation.value) {
requestAnimationFrame(frame);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,6 @@ export const SharedTextureMemory = () => {
}
pass.end();
device.queue.submit([encoder.finish()]);
context.present();
rafRef.current = requestAnimationFrame(render);
};
rafRef.current = requestAnimationFrame(render);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,6 @@ export function StorageBufferVertices() {

const commandBuffer = encoder.finish();
device.queue.submit([commandBuffer]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(context as any).present();
});

return (
Expand Down
1 change: 0 additions & 1 deletion apps/example/src/ThreeJS/Backdrop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ export const Backdrop = () => {
}

renderer.render(scene, camera);
context!.present();
}
return () => {
renderer.setAnimationLoop(null);
Expand Down
1 change: 0 additions & 1 deletion apps/example/src/ThreeJS/Cube.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ export const Cube = () => {
mesh.rotation.y = time / 1000;

renderer.render(scene, camera);
context.present();
}
renderer.setAnimationLoop(animate);
return () => {
Expand Down
1 change: 0 additions & 1 deletion apps/example/src/ThreeJS/Helmet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ export const Helmet = () => {
function animate() {
animateCamera();
renderer.render(scene, camera);
context!.present();
}

return () => {
Expand Down
1 change: 0 additions & 1 deletion apps/example/src/ThreeJS/InstancedMesh.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ export const InstancedMesh = () => {

function animate() {
render();
context!.present();
}

function render() {
Expand Down
1 change: 0 additions & 1 deletion apps/example/src/ThreeJS/PostProcessing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ export const PostProcessing = () => {
mixer.update(delta);
}
postProcessing.render();
context!.present();
}
return () => {
renderer.setAnimationLoop(null);
Expand Down
1 change: 0 additions & 1 deletion apps/example/src/ThreeJS/Retargeting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,6 @@ export const Retargeting = () => {
source.mixer.update(delta);
mixer.update(delta);
renderer.render(scene, camera);
context.present();
});

return () => {
Expand Down
1 change: 0 additions & 1 deletion apps/example/src/ThreeJS/components/FiberCanvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ export const FiberCanvas = ({
const renderFrame = state.gl.render.bind(state.gl);
state.gl.render = (s: THREE.Scene, c: THREE.Camera) => {
renderFrame(s, c);
context?.present();
};
},
});
Expand Down
2 changes: 0 additions & 2 deletions apps/example/src/Triangle/HelloTriangle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ export function HelloTriangle() {
passEncoder.end();

device.queue.submit([commandEncoder.finish()]);

context.present();
})();
}, [ref]);

Expand Down
1 change: 0 additions & 1 deletion apps/example/src/Triangle/HelloTriangleMSAA.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ export function HelloTriangleMSAA() {
}

frame();
context.present();
})();
}, [ref]);

Expand Down
4 changes: 3 additions & 1 deletion apps/example/src/VisionCamera/VisionCamera.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -613,11 +613,13 @@ const CameraView = () => {
pass.draw(3);
pass.end();
device.queue.submit([encoder.finish()]);
// Vision Camera frame processors run on a dedicated worklet runtime,
// so present explicitly (auto-present only covers the JS/UI runtime).
context.present();
// The work sampling it is submitted, so end the external texture's
// access window now to release the camera frame's surface promptly
// (don't wait for GC, which would starve the frame buffer pool).
externalTex.destroy();
context.present();
} finally {
videoFrame.release();
}
Expand Down
1 change: 0 additions & 1 deletion apps/example/src/components/Texture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ export const Texture = ({ texture, style, device }: GPUTextureProps) => {
renderPass.end();

device.queue.submit([commandEncoder.finish()]);
context.present();
}, [device, state, texture, ref]);
return <Canvas ref={ref} style={style} />;
};
1 change: 0 additions & 1 deletion apps/example/src/components/useWebGPU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ export const useWebGPU = (scene: Scene) => {
const render = () => {
const timestamp = Date.now();
renderScene(timestamp);
context.present();
animationFrameId.current = requestAnimationFrame(render);
};

Expand Down
Loading
Loading