expo-gl
Version:
Provides GLView that acts as OpenGL ES render target and gives GL context object implementing WebGL 2.0 specification.
156 lines • 6.9 kB
JavaScript
import { NativeModulesProxy, UnavailabilityError, requireNativeModule, requireNativeViewManager, CodedError, } from 'expo-modules-core';
import * as React from 'react';
import { Platform, View, findNodeHandle } from 'react-native';
import { configureLogging } from './GLUtils';
import { createWorkletContextManager } from './GLWorkletContextManager';
const ExponentGLObjectManager = requireNativeModule('ExponentGLObjectManager');
const { ExponentGLViewManager } = NativeModulesProxy;
const NativeView = requireNativeViewManager('ExponentGLView');
const workletContextManager = createWorkletContextManager();
// @needsAudit
/**
* A View that acts as an OpenGL ES render target. On mounting, an OpenGL ES context is created.
* Its drawing buffer is presented as the contents of the View every frame.
*/
export class GLView extends React.Component {
static NativeView;
static defaultProps = {
msaaSamples: 4,
enableExperimentalWorkletSupport: false,
};
/**
* Imperative API that creates headless context which is devoid of underlying view.
* It's useful for headless rendering or in case you want to keep just one context per application and share it between multiple components.
* It is slightly faster than usual context as it doesn't swap framebuffers and doesn't present them on the canvas,
* however it may require you to take a snapshot in order to present its results.
* 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.
* @return A promise that resolves to WebGL context object. See [WebGL API](#webgl-api) for more details.
*/
static async createContextAsync() {
const { exglCtxId } = await ExponentGLObjectManager.createContextAsync();
return getGl(exglCtxId);
}
/**
* Destroys given context.
* @param exgl WebGL context to destroy.
* @return A promise that resolves to boolean value that is `true` if given context existed and has been destroyed successfully.
*/
static async destroyContextAsync(exgl) {
const exglCtxId = getContextId(exgl);
unregisterGLContext(exglCtxId);
return ExponentGLObjectManager.destroyContextAsync(exglCtxId);
}
/**
* Takes a snapshot of the framebuffer and saves it as a file to app's cache directory.
* @param exgl WebGL context to take a snapshot from.
* @param options
* @return A promise that resolves to `GLSnapshot` object.
*/
static async takeSnapshotAsync(exgl, options = {}) {
const exglCtxId = getContextId(exgl);
return ExponentGLObjectManager.takeSnapshotAsync(exglCtxId, options);
}
static getWorkletContext = workletContextManager.getContext;
nativeRef = null;
exglCtxId;
render() {
const { onContextCreate, // eslint-disable-line no-unused-vars
msaaSamples, enableExperimentalWorkletSupport, ...viewProps } = this.props;
return (<View {...viewProps}>
<NativeView ref={this._setNativeRef} style={{
flex: 1,
...(Platform.OS === 'ios'
? {
backgroundColor: 'transparent',
}
: {}),
}} onSurfaceCreate={this._onSurfaceCreate} enableExperimentalWorkletSupport={enableExperimentalWorkletSupport} msaaSamples={Platform.OS === 'ios' ? msaaSamples : undefined}/>
</View>);
}
_setNativeRef = (nativeRef) => {
if (this.props.nativeRef_EXPERIMENTAL) {
this.props.nativeRef_EXPERIMENTAL(nativeRef);
}
this.nativeRef = nativeRef;
};
_onSurfaceCreate = ({ nativeEvent: { exglCtxId } }) => {
const gl = getGl(exglCtxId);
this.exglCtxId = exglCtxId;
if (this.props.onContextCreate) {
this.props.onContextCreate(gl);
}
};
componentWillUnmount() {
if (this.exglCtxId) {
unregisterGLContext(this.exglCtxId);
}
}
componentDidUpdate(prevProps) {
if (this.props.enableExperimentalWorkletSupport !== prevProps.enableExperimentalWorkletSupport) {
console.warn('Updating prop enableExperimentalWorkletSupport is not supported');
}
}
// @docsMissing
async startARSessionAsync() {
if (!ExponentGLViewManager.startARSessionAsync) {
throw new UnavailabilityError('expo-gl', 'startARSessionAsync');
}
return await ExponentGLViewManager.startARSessionAsync(findNodeHandle(this.nativeRef));
}
// @docsMissing
async createCameraTextureAsync(cameraRefOrHandle) {
if (!ExponentGLObjectManager.createCameraTextureAsync) {
throw new UnavailabilityError('expo-gl', 'createCameraTextureAsync');
}
const { exglCtxId } = this;
if (!exglCtxId) {
throw new Error("GLView's surface is not created yet!");
}
const cameraTag = findNodeHandle(cameraRefOrHandle);
const { exglObjId } = await ExponentGLObjectManager.createCameraTextureAsync(exglCtxId, cameraTag);
return { id: exglObjId };
}
// @docsMissing
async destroyObjectAsync(glObject) {
if (!ExponentGLObjectManager.destroyObjectAsync) {
throw new UnavailabilityError('expo-gl', 'destroyObjectAsync');
}
return await ExponentGLObjectManager.destroyObjectAsync(glObject.id);
}
/**
* Same as static [`takeSnapshotAsync()`](#takesnapshotasyncoptions),
* but uses WebGL context that is associated with the view on which the method is called.
* @param options
*/
async takeSnapshotAsync(options = {}) {
if (!GLView.takeSnapshotAsync) {
throw new UnavailabilityError('expo-gl', 'takeSnapshotAsync');
}
const { exglCtxId } = this;
return await GLView.takeSnapshotAsync(exglCtxId, options);
}
}
GLView.NativeView = NativeView;
function unregisterGLContext(exglCtxId) {
if (global.__EXGLContexts) {
delete global.__EXGLContexts[String(exglCtxId)];
}
workletContextManager.unregister?.(exglCtxId);
}
// Get the GL interface from an EXGLContextId
const getGl = (exglCtxId) => {
if (!global.__EXGLContexts) {
throw new CodedError('ERR_GL_NOT_AVAILABLE', 'GL is currently not available. (Have you enabled remote debugging? GL is not available while debugging remotely.)');
}
const gl = global.__EXGLContexts[String(exglCtxId)];
configureLogging(gl);
return gl;
};
const getContextId = (exgl) => {
const exglCtxId = exgl && typeof exgl === 'object' ? exgl.contextId : exgl;
if (!exglCtxId || typeof exglCtxId !== 'number') {
throw new Error(`Invalid EXGLContext id: ${String(exglCtxId)}`);
}
return exglCtxId;
};
//# sourceMappingURL=GLView.js.map