11import React , { useEffect , useState } from "react" ;
2- import { Dimensions , Text , View , Image } from "react-native" ;
3- import { GPUOffscreenCanvas , useDevice } from "react-native-wgpu" ;
2+ import { Dimensions , Platform , Text , View , Image } from "react-native" ;
3+ import { GPUOffscreenCanvas } from "react-native-wgpu" ;
44import { mat4 , vec3 , mat3 } from "wgpu-matrix" ;
55
66import { useClient } from "./useClient" ;
@@ -14,9 +14,72 @@ export const CI = process.env.CI === "true";
1414const { width } = Dimensions . get ( "window" ) ;
1515const presentationFormat = navigator . gpu . getPreferredCanvasFormat ( ) ;
1616
17+ // Platform-specific Dawn features that back importSharedTextureMemory. The
18+ // fence feature is paired with the memory feature because Dawn validates it
19+ // is enabled before letting BeginAccess/EndAccess succeed on the matching
20+ // backend, even when callers pass zero fences.
21+ const sharedTextureMemoryFeatures = ( ) : GPUFeatureName [ ] => {
22+ if ( Platform . OS === "ios" ) {
23+ return [
24+ "shared-texture-memory-iosurface" ,
25+ "shared-fence-mtl-shared-event" ,
26+ ] as unknown as GPUFeatureName [ ] ;
27+ }
28+ if ( Platform . OS === "android" ) {
29+ return [
30+ "shared-texture-memory-ahardware-buffer" ,
31+ "shared-fence-vk-semaphore-sync-fd" ,
32+ ] as unknown as GPUFeatureName [ ] ;
33+ }
34+ return [ ] ;
35+ } ;
36+ const OPTIONAL_SHARED_TEXTURE_MEMORY_FEATURES = sharedTextureMemoryFeatures ( ) ;
37+
1738export const Tests = ( { assets : { di3D, saturn, moon } } : AssetProps ) => {
1839 const [ texture , setTexture ] = useState < GPUTexture | null > ( null ) ;
19- const { adapter, device } = useDevice ( ) ;
40+ const [ adapter , setAdapter ] = useState < GPUAdapter | null > ( null ) ;
41+ const [ device , setDevice ] = useState < GPUDevice | null > ( null ) ;
42+ const [ setupError , setSetupError ] = useState < string | null > ( null ) ;
43+ useEffect ( ( ) => {
44+ let cancelled = false ;
45+ ( async ( ) => {
46+ try {
47+ const a = await navigator . gpu . requestAdapter ( ) ;
48+ if ( ! a ) {
49+ throw new Error ( "No appropriate GPUAdapter found." ) ;
50+ }
51+ const requiredFeatures = OPTIONAL_SHARED_TEXTURE_MEMORY_FEATURES . filter (
52+ ( f ) => a . features . has ( f ) ,
53+ ) ;
54+ console . log (
55+ `[Tests] adapter features (shared-*): ${ [ ...a . features ]
56+ . filter ( ( f ) => f . toString ( ) . startsWith ( "shared-" ) )
57+ . join ( ", " ) } `,
58+ ) ;
59+ console . log (
60+ `[Tests] requestDevice with requiredFeatures: ${ JSON . stringify ( requiredFeatures ) } ` ,
61+ ) ;
62+ const d = await a . requestDevice ( { requiredFeatures } ) ;
63+ if ( ! d ) {
64+ throw new Error ( "No appropriate GPUDevice found." ) ;
65+ }
66+ if ( cancelled ) {
67+ return ;
68+ }
69+ setAdapter ( a ) ;
70+ setDevice ( d ) ;
71+ } catch ( e ) {
72+ const msg = e instanceof Error ? e . message : String ( e ) ;
73+ console . warn ( `[Tests] device setup failed: ${ msg } ` ) ;
74+ if ( ! cancelled ) {
75+ setSetupError ( msg ) ;
76+ }
77+ }
78+ } ) ( ) ;
79+ return ( ) => {
80+ cancelled = true ;
81+ } ;
82+ } , [ ] ) ;
2083 const [ client , hostname ] = useClient ( ) ;
2184 useEffect ( ( ) => {
2285 if ( client !== null && adapter !== null && device !== null ) {
@@ -78,8 +141,21 @@ export const Tests = ({ assets: { di3D, saturn, moon } }: AssetProps) => {
78141 }
79142 return ;
80143 } , [ adapter , client , device , di3D , moon , saturn ] ) ;
144+ if ( setupError ) {
145+ return (
146+ < View style = { { flex : 1 , padding : 16 , backgroundColor : "white" } } >
147+ < Text style = { { color : "red" } } >
148+ 🔴 Test device setup failed: { setupError }
149+ </ Text >
150+ </ View >
151+ ) ;
152+ }
81153 if ( ! device ) {
82- return null ;
154+ return (
155+ < View style = { { flex : 1 , padding : 16 , backgroundColor : "white" } } >
156+ < Text style = { { color : "black" } } > ⏳ Initializing GPU device…</ Text >
157+ </ View >
158+ ) ;
83159 }
84160 return (
85161 < View style = { { flex : 1 , backgroundColor : "white" } } >
0 commit comments