1import { Asset } from 'expo-asset'; 2import ExpoCheckbox from 'expo-checkbox'; 3import { ExpoWebGLRenderingContext, GLView } from 'expo-gl'; 4import React, { useEffect, useState } from 'react'; 5import { Text, StyleSheet, View, TouchableHighlight } from 'react-native'; 6import { runOnUI } from 'react-native-reanimated'; 7 8async function onContextCreate(gl: ExpoWebGLRenderingContext) { 9 const vert = gl.createShader(gl.VERTEX_SHADER)!; 10 gl.shaderSource( 11 vert, 12 ` 13 precision highp float; 14 attribute vec2 position; 15 varying vec2 uv; 16 void main () { 17 uv = position; 18 gl_Position = vec4(1.0 - 2.0 * position, 0, 1); 19 }` 20 ); 21 gl.compileShader(vert); 22 const frag = gl.createShader(gl.FRAGMENT_SHADER)!; 23 gl.shaderSource( 24 frag, 25 ` 26 precision highp float; 27 uniform sampler2D texture; 28 varying vec2 uv; 29 void main () { 30 gl_FragColor = texture2D(texture, vec2(uv.x, uv.y)); 31 }` 32 ); 33 gl.compileShader(frag); 34 35 const program = gl.createProgram()!; 36 gl.attachShader(program, vert); 37 gl.attachShader(program, frag); 38 gl.linkProgram(program); 39 gl.useProgram(program); 40 41 const buffer = gl.createBuffer(); 42 gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 43 const verts = new Float32Array([-2, 0, 0, -2, 2, 2]); 44 gl.bufferData(gl.ARRAY_BUFFER, verts, gl.STATIC_DRAW); 45 const positionAttrib = gl.getAttribLocation(program, 'position'); 46 gl.enableVertexAttribArray(positionAttrib); 47 gl.vertexAttribPointer(positionAttrib, 2, gl.FLOAT, false, 0, 0); 48 49 const asset = Asset.fromModule(require('../../../assets/images/nikki.png')); 50 await asset.downloadAsync(); 51 const texture = gl.createTexture(); 52 gl.activeTexture(gl.TEXTURE0); 53 gl.bindTexture(gl.TEXTURE_2D, texture); 54 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 55 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 56 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, asset as any); 57 gl.uniform1i(gl.getUniformLocation(program, 'texture'), 0); 58 gl.clearColor(0, 0, 1, 1); 59 // tslint:disable-next-line: no-bitwise 60 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 61 gl.drawArrays(gl.TRIANGLES, 0, verts.length / 2); 62 gl.endFrameEXP(); 63} 64 65function BusyJSThreadSelector() { 66 const [fakeBusyJSThread, setFakeBusyJSThread] = useState(false); 67 useEffect(() => { 68 if (!fakeBusyJSThread) { 69 return; 70 } 71 const interval = setInterval(() => { 72 let test_value = 0; 73 const start = Date.now(); 74 while (Date.now() - start < 990) { 75 test_value += Math.random(); 76 } 77 console.log(`js - ${test_value}`); 78 }, 1000); 79 return () => clearInterval(interval); 80 }, [fakeBusyJSThread]); 81 82 return ( 83 <View style={styles.checkboxRow}> 84 <ExpoCheckbox 85 onValueChange={() => setFakeBusyJSThread(!fakeBusyJSThread)} 86 value={fakeBusyJSThread} 87 /> 88 <Text style={styles.text}>fake work on js thread</Text> 89 </View> 90 ); 91} 92 93function BusyWorkletThreadSelector() { 94 const [fakeBusyWorkletThread, setFakeBusyWorkletThread] = useState(true); 95 useEffect(() => { 96 if (!fakeBusyWorkletThread) { 97 return; 98 } 99 const interval = setInterval(() => { 100 runOnUI(() => { 101 'worklet'; 102 let test_value = 0; 103 const start = Date.now(); 104 while (Date.now() - start < 990) { 105 test_value += Math.random(); 106 } 107 console.log(`worklet - ${test_value}`); 108 })(); 109 }, 1000); 110 return () => clearInterval(interval); 111 }, [fakeBusyWorkletThread]); 112 113 return ( 114 <View style={styles.checkboxRow}> 115 <ExpoCheckbox 116 onValueChange={() => setFakeBusyWorkletThread(!fakeBusyWorkletThread)} 117 value={fakeBusyWorkletThread} 118 /> 119 <Text style={styles.text}>fake work on worklet thread</Text> 120 </View> 121 ); 122} 123 124export default function GLViewOnBusyThread() { 125 const [show, setShow] = useState(true); 126 return ( 127 <View style={styles.flex}> 128 <Text style={styles.text}> 129 This screen is expected to lag. It's faking work on JS and worklet threads. Toggle GLView 130 few times to make sure it does no crash. 131 </Text> 132 <BusyWorkletThreadSelector /> 133 <BusyJSThreadSelector /> 134 135 {show ? ( 136 <GLView 137 style={styles.flex} 138 onContextCreate={onContextCreate} 139 enableExperimentalWorkletSupport 140 /> 141 ) : ( 142 <View style={styles.placeholder}> 143 <Text>no gl view</Text> 144 </View> 145 )} 146 <TouchableHighlight 147 underlayColor="#0176d3" 148 style={styles.button} 149 onPress={() => setShow(!show)}> 150 <Text style={styles.buttonText}>TOGGLE</Text> 151 </TouchableHighlight> 152 </View> 153 ); 154} 155 156GLViewOnBusyThread.title = 'Creating GLView when a thread is busy'; 157 158const styles = StyleSheet.create({ 159 flex: { 160 flex: 1, 161 }, 162 checkboxRow: { 163 flexDirection: 'row', 164 alignItems: 'center', 165 marginLeft: 10, 166 }, 167 placeholder: { 168 flex: 1, 169 justifyContent: 'center', 170 alignItems: 'center', 171 }, 172 text: { 173 padding: 10, 174 fontSize: 16, 175 }, 176 buttonText: { 177 fontSize: 22, 178 }, 179 button: { 180 height: 100, 181 alignItems: 'center', 182 justifyContent: 'center', 183 backgroundColor: '#2196f3', 184 marginTop: 10, 185 }, 186}); 187