1import { NativeModulesProxy, UnavailabilityError, requireNativeModule, requireNativeViewManager, CodedError, } from 'expo-modules-core'; 2import * as React from 'react'; 3import { Platform, View, findNodeHandle } from 'react-native'; 4import { configureLogging } from './GLUtils'; 5import { createWorkletContextManager } from './GLWorkletContextManager'; 6const ExponentGLObjectManager = requireNativeModule('ExponentGLObjectManager'); 7const { ExponentGLViewManager } = NativeModulesProxy; 8const NativeView = requireNativeViewManager('ExponentGLView'); 9const workletContextManager = createWorkletContextManager(); 10// @needsAudit 11/** 12 * A View that acts as an OpenGL ES render target. On mounting, an OpenGL ES context is created. 13 * Its drawing buffer is presented as the contents of the View every frame. 14 */ 15export class GLView extends React.Component { 16 static NativeView; 17 static defaultProps = { 18 msaaSamples: 4, 19 enableExperimentalWorkletSupport: false, 20 }; 21 /** 22 * Imperative API that creates headless context which is devoid of underlying view. 23 * It's useful for headless rendering or in case you want to keep just one context per application and share it between multiple components. 24 * It is slightly faster than usual context as it doesn't swap framebuffers and doesn't present them on the canvas, 25 * however it may require you to take a snapshot in order to present its results. 26 * Also, keep in mind that you need to set up a viewport and create your own framebuffer and texture that you will be drawing to, before you take a snapshot. 27 * @return A promise that resolves to WebGL context object. See [WebGL API](#webgl-api) for more details. 28 */ 29 static async createContextAsync() { 30 const { exglCtxId } = await ExponentGLObjectManager.createContextAsync(); 31 return getGl(exglCtxId); 32 } 33 /** 34 * Destroys given context. 35 * @param exgl WebGL context to destroy. 36 * @return A promise that resolves to boolean value that is `true` if given context existed and has been destroyed successfully. 37 */ 38 static async destroyContextAsync(exgl) { 39 const exglCtxId = getContextId(exgl); 40 unregisterGLContext(exglCtxId); 41 return ExponentGLObjectManager.destroyContextAsync(exglCtxId); 42 } 43 /** 44 * Takes a snapshot of the framebuffer and saves it as a file to app's cache directory. 45 * @param exgl WebGL context to take a snapshot from. 46 * @param options 47 * @return A promise that resolves to `GLSnapshot` object. 48 */ 49 static async takeSnapshotAsync(exgl, options = {}) { 50 const exglCtxId = getContextId(exgl); 51 return ExponentGLObjectManager.takeSnapshotAsync(exglCtxId, options); 52 } 53 static getWorkletContext = workletContextManager.getContext; 54 nativeRef = null; 55 exglCtxId; 56 render() { 57 const { onContextCreate, // eslint-disable-line no-unused-vars 58 msaaSamples, enableExperimentalWorkletSupport, ...viewProps } = this.props; 59 return (React.createElement(View, { ...viewProps }, 60 React.createElement(NativeView, { ref: this._setNativeRef, style: { 61 flex: 1, 62 ...(Platform.OS === 'ios' 63 ? { 64 backgroundColor: 'transparent', 65 } 66 : {}), 67 }, onSurfaceCreate: this._onSurfaceCreate, enableExperimentalWorkletSupport: enableExperimentalWorkletSupport, msaaSamples: Platform.OS === 'ios' ? msaaSamples : undefined }))); 68 } 69 _setNativeRef = (nativeRef) => { 70 if (this.props.nativeRef_EXPERIMENTAL) { 71 this.props.nativeRef_EXPERIMENTAL(nativeRef); 72 } 73 this.nativeRef = nativeRef; 74 }; 75 _onSurfaceCreate = ({ nativeEvent: { exglCtxId } }) => { 76 const gl = getGl(exglCtxId); 77 this.exglCtxId = exglCtxId; 78 if (this.props.onContextCreate) { 79 this.props.onContextCreate(gl); 80 } 81 }; 82 componentWillUnmount() { 83 if (this.exglCtxId) { 84 unregisterGLContext(this.exglCtxId); 85 } 86 } 87 componentDidUpdate(prevProps) { 88 if (this.props.enableExperimentalWorkletSupport !== prevProps.enableExperimentalWorkletSupport) { 89 console.warn('Updating prop enableExperimentalWorkletSupport is not supported'); 90 } 91 } 92 // @docsMissing 93 async startARSessionAsync() { 94 if (!ExponentGLViewManager.startARSessionAsync) { 95 throw new UnavailabilityError('expo-gl', 'startARSessionAsync'); 96 } 97 return await ExponentGLViewManager.startARSessionAsync(findNodeHandle(this.nativeRef)); 98 } 99 // @docsMissing 100 async createCameraTextureAsync(cameraRefOrHandle) { 101 if (!ExponentGLObjectManager.createCameraTextureAsync) { 102 throw new UnavailabilityError('expo-gl', 'createCameraTextureAsync'); 103 } 104 const { exglCtxId } = this; 105 if (!exglCtxId) { 106 throw new Error("GLView's surface is not created yet!"); 107 } 108 const cameraTag = findNodeHandle(cameraRefOrHandle); 109 const { exglObjId } = await ExponentGLObjectManager.createCameraTextureAsync(exglCtxId, cameraTag); 110 return { id: exglObjId }; 111 } 112 // @docsMissing 113 async destroyObjectAsync(glObject) { 114 if (!ExponentGLObjectManager.destroyObjectAsync) { 115 throw new UnavailabilityError('expo-gl', 'destroyObjectAsync'); 116 } 117 return await ExponentGLObjectManager.destroyObjectAsync(glObject.id); 118 } 119 /** 120 * Same as static [`takeSnapshotAsync()`](#glviewtakesnapshotasyncgl-options), 121 * but uses WebGL context that is associated with the view on which the method is called. 122 * @param options 123 */ 124 async takeSnapshotAsync(options = {}) { 125 if (!GLView.takeSnapshotAsync) { 126 throw new UnavailabilityError('expo-gl', 'takeSnapshotAsync'); 127 } 128 const { exglCtxId } = this; 129 return await GLView.takeSnapshotAsync(exglCtxId, options); 130 } 131} 132GLView.NativeView = NativeView; 133function unregisterGLContext(exglCtxId) { 134 if (global.__EXGLContexts) { 135 delete global.__EXGLContexts[String(exglCtxId)]; 136 } 137 workletContextManager.unregister?.(exglCtxId); 138} 139// Get the GL interface from an EXGLContextId 140const getGl = (exglCtxId) => { 141 if (!global.__EXGLContexts) { 142 throw new CodedError('ERR_GL_NOT_AVAILABLE', 'GL is currently not available. (Have you enabled remote debugging? GL is not available while debugging remotely.)'); 143 } 144 const gl = global.__EXGLContexts[String(exglCtxId)]; 145 configureLogging(gl); 146 return gl; 147}; 148const getContextId = (exgl) => { 149 const exglCtxId = exgl && typeof exgl === 'object' ? exgl.contextId : exgl; 150 if (!exglCtxId || typeof exglCtxId !== 'number') { 151 throw new Error(`Invalid EXGLContext id: ${String(exglCtxId)}`); 152 } 153 return exglCtxId; 154}; 155//# sourceMappingURL=GLView.js.map