55 type RNCanvasContext ,
66} from 'react-native-webgpu' ;
77import { useCallback , useEffect , useRef , useState } from 'react' ;
8- import { scheduleOnRuntime , type WorkletRuntime } from 'react-native-worklets' ;
8+ import { type WorkletRuntime } from 'react-native-worklets' ;
99import { BackgroundRuntime } from '../utils/backgroundRuntime' ;
10+ import { getSharedGPUDevice } from '../utils/gpuDevice' ;
1011
1112type GPUResources = {
1213 device : GPUDevice ;
@@ -38,27 +39,18 @@ export function useWGPUSetup(): WGPUSetupResult {
3839 const configuredSizeRef = useRef < { width : number ; height : number } | null > (
3940 null
4041 ) ;
41- // Kept so the device can be released on unmount.
42- const deviceRef = useRef < GPUDevice | null > ( null ) ;
4342
4443 useEffect ( ( ) => {
4544 let cancelled = false ;
4645
4746 ( async ( ) => {
48- const adapter = await navigator . gpu . requestAdapter ( ) ;
49- if ( ! adapter || cancelled ) {
47+ // Shared across every ShaderView — see getSharedGPUDevice. Returns null if
48+ // no adapter is available.
49+ const device = await getSharedGPUDevice ( ) ;
50+ if ( ! device || cancelled ) {
5051 return ;
5152 }
5253
53- const device = await adapter . requestDevice ( ) ;
54- if ( cancelled ) {
55- // Unmounted mid-init: the worklet never used this device, so it's safe
56- // to drop it right here on the JS thread.
57- device . destroy ( ) ;
58- return ;
59- }
60- deviceRef . current = device ;
61-
6254 const context = canvasRef . current ! . getContext ( 'webgpu' ) ! ;
6355 const canvas = context . canvas as CanvasWithSize ;
6456 const dpr = PixelRatio . get ( ) ;
@@ -82,19 +74,11 @@ export function useWGPUSetup(): WGPUSetupResult {
8274
8375 return ( ) => {
8476 cancelled = true ;
85- const device = deviceRef . current ;
86- deviceRef . current = null ;
87- if ( device ) {
88- // Release the device (and all GPU resources created from it: pipeline,
89- // buffers, ...) on the render runtime — the thread that used it — after
90- // the render loop has been signalled to stop, so we don't race an
91- // in-flight frame. Any frame that does slip through hits a destroyed
92- // device and is swallowed by the render loop's try/catch.
93- scheduleOnRuntime ( runtime , ( ) => {
94- 'worklet' ;
95- device . destroy ( ) ;
96- } ) ;
97- }
77+ // The device is shared across all ShaderViews and lives for the JS
78+ // runtime's lifetime, so it must NOT be destroyed here. This view's own
79+ // GPU resources (the uniform buffer) are torn down by the render loop when
80+ // it stops; the pipeline/shader modules are released once the loop's
81+ // worklet closure is garbage-collected.
9882 } ;
9983 // eslint-disable-next-line react-hooks/exhaustive-deps
10084 } , [ ] ) ;
0 commit comments