UNPKG

20.9 kBJavaScriptView Raw
1import * as React from 'react';
2import PropTypes from 'prop-types';
3import { NativeModulesProxy, requireNativeViewManager } from '@unimodules/core';
4import { Platform, View, ViewPropTypes, findNodeHandle } from 'react-native';
5const packageJSON = require('../package.json');
6import { UnavailabilityError } from '@unimodules/core';
7const { ExponentGLObjectManager, ExponentGLViewManager } = NativeModulesProxy;
8const NativeView = requireNativeViewManager('ExponentGLView');
9/**
10 * A component that acts as an OpenGL render target
11 */
12export class GLView extends React.Component {
13 constructor() {
14 super(...arguments);
15 this.nativeRef = null;
16 this._setNativeRef = (nativeRef) => {
17 if (this.props.nativeRef_EXPERIMENTAL) {
18 this.props.nativeRef_EXPERIMENTAL(nativeRef);
19 }
20 this.nativeRef = nativeRef;
21 };
22 this._onSurfaceCreate = ({ nativeEvent: { exglCtxId } }) => {
23 const gl = getGl(exglCtxId);
24 this.exglCtxId = exglCtxId;
25 if (this.props.onContextCreate) {
26 this.props.onContextCreate(gl);
27 }
28 };
29 }
30 static async createContextAsync() {
31 const { exglCtxId } = await ExponentGLObjectManager.createContextAsync();
32 return getGl(exglCtxId);
33 }
34 static async destroyContextAsync(exgl) {
35 const exglCtxId = getContextId(exgl);
36 return ExponentGLObjectManager.destroyContextAsync(exglCtxId);
37 }
38 static async takeSnapshotAsync(exgl, options = {}) {
39 const exglCtxId = getContextId(exgl);
40 return ExponentGLObjectManager.takeSnapshotAsync(exglCtxId, options);
41 }
42 render() {
43 const { onContextCreate, // eslint-disable-line no-unused-vars
44 msaaSamples, ...viewProps } = this.props;
45 return (<View {...viewProps}>
46 <NativeView ref={this._setNativeRef} style={{
47 flex: 1,
48 ...(Platform.OS === 'ios'
49 ? {
50 backgroundColor: 'transparent',
51 }
52 : {}),
53 }} onSurfaceCreate={this._onSurfaceCreate} msaaSamples={Platform.OS === 'ios' ? msaaSamples : undefined}/>
54 </View>);
55 }
56 async startARSessionAsync() {
57 if (!ExponentGLViewManager.startARSessionAsync) {
58 throw new UnavailabilityError('expo-gl', 'startARSessionAsync');
59 }
60 return await ExponentGLViewManager.startARSessionAsync(findNodeHandle(this.nativeRef));
61 }
62 async createCameraTextureAsync(cameraRefOrHandle) {
63 if (!ExponentGLObjectManager.createCameraTextureAsync) {
64 throw new UnavailabilityError('expo-gl', 'createCameraTextureAsync');
65 }
66 const { exglCtxId } = this;
67 if (!exglCtxId) {
68 throw new Error("GLView's surface is not created yet!");
69 }
70 const cameraTag = findNodeHandle(cameraRefOrHandle);
71 const { exglObjId } = await ExponentGLObjectManager.createCameraTextureAsync(exglCtxId, cameraTag);
72 return new WebGLTexture(exglObjId);
73 }
74 async destroyObjectAsync(glObject) {
75 if (!ExponentGLObjectManager.destroyObjectAsync) {
76 throw new UnavailabilityError('expo-gl', 'destroyObjectAsync');
77 }
78 return await ExponentGLObjectManager.destroyObjectAsync(glObject.id);
79 }
80 async takeSnapshotAsync(options = {}) {
81 if (!GLView.takeSnapshotAsync) {
82 throw new UnavailabilityError('expo-gl', 'takeSnapshotAsync');
83 }
84 const { exglCtxId } = this;
85 return await GLView.takeSnapshotAsync(exglCtxId, options);
86 }
87}
88GLView.propTypes = {
89 onContextCreate: PropTypes.func,
90 msaaSamples: PropTypes.number,
91 nativeRef_EXPERIMENTAL: PropTypes.func,
92 ...ViewPropTypes,
93};
94GLView.defaultProps = {
95 msaaSamples: 4,
96};
97GLView.NativeView = NativeView;
98// JavaScript WebGL types to wrap around native objects
99class WebGLRenderingContext {
100}
101class WebGL2RenderingContext extends WebGLRenderingContext {
102}
103const idToObject = {};
104class WebGLObject {
105 constructor(id) {
106 if (idToObject[id]) {
107 throw new Error(`WebGL object with underlying EXGLObjectId '${id}' already exists!`);
108 }
109 this.id = id; // Native GL object id
110 }
111 toString() {
112 return `[WebGLObject ${this.id}]`;
113 }
114}
115const wrapObject = (type, id) => {
116 const found = idToObject[id];
117 if (found) {
118 return found;
119 }
120 return (idToObject[id] = new type(id));
121};
122const objectId = (obj) => obj && obj.id;
123class WebGLBuffer extends WebGLObject {
124}
125class WebGLFramebuffer extends WebGLObject {
126}
127class WebGLProgram extends WebGLObject {
128}
129class WebGLRenderbuffer extends WebGLObject {
130}
131class WebGLShader extends WebGLObject {
132}
133class WebGLTexture extends WebGLObject {
134}
135class WebGLUniformLocation {
136 constructor(id) {
137 this.id = id; // Native GL object id
138 }
139}
140class WebGLActiveInfo {
141 constructor(obj) {
142 Object.assign(this, obj);
143 }
144}
145class WebGLShaderPrecisionFormat {
146 constructor(obj) {
147 Object.assign(this, obj);
148 }
149}
150// WebGL2 classes
151class WebGLQuery extends WebGLObject {
152}
153class WebGLSampler extends WebGLObject {
154}
155class WebGLSync extends WebGLObject {
156}
157class WebGLTransformFeedback extends WebGLObject {
158}
159class WebGLVertexArrayObject extends WebGLObject {
160}
161// Many functions need wrapping/unwrapping of arguments and return value. We handle each case
162// specifically so we can write the tightest code for better performance.
163const wrapMethods = gl => {
164 const wrap = (methodNames, wrapper) => (Array.isArray(methodNames) ? methodNames : [methodNames]).forEach(methodName => (gl[methodName] = wrapper(gl[methodName])));
165 // We can be slow in `gl.getParameter(...)` since it's a blocking call anyways
166 const getParameterTypes = {
167 [gl.ARRAY_BUFFER_BINDING]: WebGLBuffer,
168 [gl.COPY_READ_BUFFER_BINDING]: WebGLBuffer,
169 [gl.COPY_WRITE_BUFFER_BINDING]: WebGLBuffer,
170 [gl.CURRENT_PROGRAM]: WebGLProgram,
171 [gl.DRAW_FRAMEBUFFER_BINDING]: WebGLFramebuffer,
172 [gl.ELEMENT_ARRAY_BUFFER_BINDING]: WebGLBuffer,
173 [gl.READ_FRAMEBUFFER_BINDING]: WebGLFramebuffer,
174 [gl.RENDERBUFFER_BINDING]: WebGLRenderbuffer,
175 [gl.SAMPLER_BINDING]: WebGLSampler,
176 [gl.TEXTURE_BINDING_2D_ARRAY]: WebGLTexture,
177 [gl.TEXTURE_BINDING_2D]: WebGLTexture,
178 [gl.TEXTURE_BINDING_3D]: WebGLTexture,
179 [gl.TEXTURE_BINDING_CUBE_MAP]: WebGLTexture,
180 [gl.TRANSFORM_FEEDBACK_BINDING]: WebGLTransformFeedback,
181 [gl.TRANSFORM_FEEDBACK_BUFFER_BINDING]: WebGLBuffer,
182 [gl.UNIFORM_BUFFER_BINDING]: WebGLBuffer,
183 [gl.VERTEX_ARRAY_BINDING]: WebGLVertexArrayObject,
184 };
185 wrap('getParameter', orig => pname => {
186 let ret = orig.call(gl, pname);
187 if (pname === gl.VERSION) {
188 // Wrap native version name
189 ret = `WebGL 2.0 (Expo-${Platform.OS}-${packageJSON.version}) (${ret})`;
190 }
191 const type = getParameterTypes[pname];
192 return type ? wrapObject(type, ret) : ret;
193 });
194 // Buffers
195 wrap('bindBuffer', orig => (target, buffer) => orig.call(gl, target, buffer && buffer.id));
196 wrap('createBuffer', orig => () => wrapObject(WebGLBuffer, orig.call(gl)));
197 wrap('deleteBuffer', orig => buffer => orig.call(gl, buffer && buffer.id));
198 wrap('isBuffer', orig => buffer => buffer instanceof WebGLBuffer && orig.call(gl, buffer.id));
199 // Framebuffers
200 wrap('bindFramebuffer', orig => (target, framebuffer) => orig.call(gl, target, framebuffer && framebuffer.id));
201 wrap('createFramebuffer', orig => () => wrapObject(WebGLFramebuffer, orig.call(gl)));
202 wrap('deleteFramebuffer', orig => framebuffer => orig.call(gl, framebuffer && framebuffer.id));
203 wrap('framebufferRenderbuffer', orig => (target, attachment, rbtarget, rb) => orig.call(gl, target, attachment, rbtarget, rb && rb.id));
204 wrap('framebufferTexture2D', orig => (target, attachment, textarget, tex, level) => orig.call(gl, target, attachment, textarget, tex && tex.id, level));
205 wrap('isFramebuffer', orig => framebuffer => framebuffer instanceof WebGLFramebuffer && orig.call(gl, framebuffer.id));
206 wrap('framebufferTextureLayer', orig => (target, attachment, texture, level, layer) => {
207 return orig.call(gl, target, attachment, objectId(texture), level, layer);
208 });
209 // Renderbuffers
210 wrap('bindRenderbuffer', orig => (target, renderbuffer) => orig.call(gl, target, renderbuffer && renderbuffer.id));
211 wrap('createRenderbuffer', orig => () => wrapObject(WebGLRenderbuffer, orig.call(gl)));
212 wrap('deleteRenderbuffer', orig => renderbuffer => orig.call(gl, renderbuffer && renderbuffer.id));
213 wrap('isRenderbuffer', orig => renderbuffer => renderbuffer instanceof WebGLRenderbuffer && orig.call(gl, renderbuffer.id));
214 // Textures
215 wrap('bindTexture', orig => (target, texture) => orig.call(gl, target, texture && texture.id));
216 wrap('createTexture', orig => () => wrapObject(WebGLTexture, orig.call(gl)));
217 wrap('deleteTexture', orig => texture => orig.call(gl, texture && texture.id));
218 wrap('isTexture', orig => texture => texture instanceof WebGLTexture && orig.call(gl, texture.id));
219 // Programs and shaders
220 wrap('attachShader', orig => (program, shader) => orig.call(gl, program && program.id, shader && shader.id));
221 wrap('bindAttribLocation', orig => (program, index, name) => orig.call(gl, program && program.id, index, name));
222 wrap('compileShader', orig => shader => orig.call(gl, shader && shader.id));
223 wrap('createProgram', orig => () => wrapObject(WebGLProgram, orig.call(gl)));
224 wrap('createShader', orig => type => wrapObject(WebGLShader, orig.call(gl, type)));
225 wrap('deleteProgram', orig => program => orig.call(gl, program && program.id));
226 wrap('deleteShader', orig => shader => orig.call(gl, shader && shader.id));
227 wrap('detachShader', orig => (program, shader) => orig.call(gl, program && program.id, shader && shader.id));
228 wrap('getAttachedShaders', orig => program => orig.call(gl, program && program.id).map(id => wrapObject(WebGLShader, id)));
229 wrap('getProgramParameter', orig => (program, pname) => orig.call(gl, program && program.id, pname));
230 wrap('getProgramInfoLog', orig => program => orig.call(gl, program && program.id));
231 wrap('getShaderParameter', orig => (shader, pname) => orig.call(gl, shader && shader.id, pname));
232 wrap('getShaderPrecisionFormat', orig => (shadertype, precisiontype) => new WebGLShaderPrecisionFormat(orig.call(gl, shadertype, precisiontype)));
233 wrap('getShaderInfoLog', orig => shader => orig.call(gl, shader && shader.id));
234 wrap('getShaderSource', orig => shader => orig.call(gl, shader && shader.id));
235 wrap('linkProgram', orig => program => orig.call(gl, program && program.id));
236 wrap('shaderSource', orig => (shader, source) => orig.call(gl, shader && shader.id, source));
237 wrap('useProgram', orig => program => orig.call(gl, program && program.id));
238 wrap('validateProgram', orig => program => orig.call(gl, program && program.id));
239 wrap('isShader', orig => shader => shader instanceof WebGLShader && orig.call(gl, shader.id));
240 wrap('isProgram', orig => program => program instanceof WebGLProgram && orig.call(gl, program.id));
241 wrap('getFragDataLocation', orig => program => orig.call(gl, objectId(program)));
242 // Uniforms and attributes
243 wrap('getActiveAttrib', orig => (program, index) => new WebGLActiveInfo(orig.call(gl, program && program.id, index)));
244 wrap('getActiveUniform', orig => (program, index) => new WebGLActiveInfo(orig.call(gl, program && program.id, index)));
245 wrap('getAttribLocation', orig => (program, name) => orig.call(gl, program && program.id, name));
246 wrap('getUniform', orig => (program, location) => orig.call(gl, program && program.id, location && location.id));
247 wrap('getUniformLocation', orig => (program, name) => new WebGLUniformLocation(orig.call(gl, program && program.id, name)));
248 wrap(['uniform1f', 'uniform1i', 'uniform1ui'], orig => (loc, x) => orig.call(gl, objectId(loc), x));
249 wrap(['uniform2f', 'uniform2i', 'uniform2ui'], orig => (loc, x, y) => orig.call(gl, objectId(loc), x, y));
250 wrap(['uniform3f', 'uniform3i', 'uniform3ui'], orig => (loc, x, y, z) => orig.call(gl, objectId(loc), x, y, z));
251 wrap(['uniform4f', 'uniform4i', 'uniform4ui'], orig => (loc, x, y, z, w) => orig.call(gl, objectId(loc), x, y, z, w));
252 wrap(['uniform1fv', 'uniform2fv', 'uniform3fv', 'uniform4fv'], orig => (loc, val) => orig.call(gl, objectId(loc), new Float32Array(val)));
253 wrap(['uniform1iv', 'uniform2iv', 'uniform3iv', 'uniform4iv'], orig => (loc, val) => orig.call(gl, objectId(loc), new Int32Array(val)));
254 wrap(['uniform1uiv', 'uniform2uiv', 'uniform3uiv', 'uniform4uiv'], orig => (loc, val) => orig.call(gl, objectId(loc), new Uint32Array(val)));
255 wrap([
256 'uniformMatrix2fv',
257 'uniformMatrix3fv',
258 'uniformMatrix4fv',
259 'uniformMatrix3x2fv',
260 'uniformMatrix4x2fv',
261 'uniformMatrix2x3fv',
262 'uniformMatrix4x3fv',
263 'uniformMatrix2x4fv',
264 'uniformMatrix3x4fv',
265 ], orig => (loc, transpose, val) => orig.call(gl, loc && loc.id, transpose, new Float32Array(val)));
266 wrap(['vertexAttrib1fv', 'vertexAttrib2fv', 'vertexAttrib3fv', 'vertexAttrib4fv'], orig => (index, val) => orig.call(gl, index, new Float32Array(val)));
267 wrap('vertexAttribI4iv', orig => (index, val) => orig.call(gl, index, new Int32Array(val)));
268 wrap('vertexAttribI4uiv', orig => (index, val) => orig.call(gl, index, new Uint32Array(val)));
269 // Query objects
270 wrap('createQuery', orig => () => wrapObject(WebGLQuery, orig.call(gl)));
271 wrap('deleteQuery', orig => query => orig.call(gl, objectId(query)));
272 wrap('beginQuery', orig => (target, query) => orig.call(gl, target, objectId(query)));
273 wrap('getQuery', orig => (target, pname) => {
274 const id = orig.call(gl, target, pname);
275 return id ? wrapObject(WebGLQuery, id) : id;
276 });
277 wrap('getQueryParameter', orig => (query, pname) => orig.call(gl, objectId(query), pname));
278 // Samplers
279 wrap('bindSampler', orig => (unit, sampler) => orig.call(gl, unit, objectId(sampler)));
280 wrap('createSampler', orig => () => wrapObject(WebGLSampler, orig.call(gl)));
281 wrap('deleteSampler', orig => sampler => orig.call(gl, objectId(sampler)));
282 wrap('isSampler', orig => sampler => sampler instanceof WebGLSampler && orig.call(gl, sampler.id));
283 wrap(['samplerParameteri', 'samplerParameterf'], orig => (sampler, pname, param) => {
284 return orig.call(gl, objectId(sampler), pname, param);
285 });
286 wrap('getSamplerParameter', orig => (sampler, pname) => {
287 return orig.call(gl, objectId(sampler), pname);
288 });
289 // Transform feedback
290 wrap('bindTransformFeedback', orig => (target, transformFeedback) => {
291 return orig.call(gl, target, objectId(transformFeedback));
292 });
293 wrap('createTransformFeedback', orig => () => wrapObject(WebGLTransformFeedback, orig.call(gl)));
294 wrap('deleteTransformFeedback', orig => transformFeedback => {
295 return orig.call(gl, objectId(transformFeedback));
296 });
297 wrap('transformFeedbackVaryings', orig => (program, varyings, bufferMode) => {
298 return orig.call(gl, objectId(program), varyings, bufferMode);
299 });
300 wrap('getTransformFeedbackVarying', orig => (program, index) => {
301 return new WebGLActiveInfo(orig.call(gl, objectId(program), index));
302 });
303 // Uniforms and attributes
304 wrap(['bindBufferBase', 'bindBufferRange'], orig => (target, index, buffer, ...rest) => {
305 return orig.call(gl, target, index, objectId(buffer), ...rest);
306 });
307 wrap('getUniformIndices', orig => (program, uniformNames) => {
308 // according to WebGL2 specs, it returns Array instead of Uint32Array
309 const uintArray = orig.call(gl, objectId(program), uniformNames);
310 return Array.from(uintArray);
311 });
312 wrap('getActiveUniforms', orig => (program, uniformIndices, pname) => {
313 // according to WebGL2 specs, it returns Array instead of Int32Array
314 const intArray = orig.call(gl, objectId(program), new Uint32Array(uniformIndices), pname);
315 const boolResult = pname === gl.UNIFORM_IS_ROW_MAJOR;
316 const arr = Array.from(intArray);
317 return boolResult ? arr.map(val => !!val) : arr;
318 });
319 wrap('getUniformBlockIndex', orig => (program, uniformBlockName) => orig.call(gl, objectId(program), uniformBlockName));
320 wrap('getActiveUniformBlockName', orig => (program, uniformBlockIndex) => orig.call(gl, objectId(program), uniformBlockIndex));
321 wrap('uniformBlockBinding', orig => (program, uniformBlockIndex, uniformBlockBinding) => {
322 return orig.call(gl, objectId(program), uniformBlockIndex, uniformBlockBinding);
323 });
324 // Vertex array objects
325 wrap('bindVertexArray', orig => vertexArray => orig.call(gl, vertexArray && vertexArray.id));
326 wrap('createVertexArray', orig => () => wrapObject(WebGLVertexArrayObject, orig.call(gl)));
327 wrap('deleteVertexArray', orig => vertexArray => orig.call(gl, vertexArray && vertexArray.id));
328 wrap('isVertexArray', orig => vertexArray => vertexArray instanceof WebGLVertexArrayObject && orig.call(gl, vertexArray.id));
329};
330// Get the GL interface from an EXGLContextID and do JS-side setup
331const getGl = (exglCtxId) => {
332 const gl = global.__EXGLContexts[exglCtxId];
333 gl.__exglCtxId = exglCtxId;
334 delete global.__EXGLContexts[exglCtxId];
335 // determine the prototype to use, depending on OpenGL ES version
336 const glesVersion = gl.getParameter(gl.VERSION);
337 const supportsWebGL2 = parseFloat(glesVersion.split(/[^\d.]+/g).join(' ')) >= 3.0;
338 const prototype = supportsWebGL2
339 ? global.WebGL2RenderingContext.prototype
340 : global.WebGLRenderingContext.prototype;
341 if (Object.setPrototypeOf) {
342 Object.setPrototypeOf(gl, prototype);
343 }
344 else {
345 // Delete this path when we are competely sure we're using modern JSC on Android. iOS 9+
346 // supports Object.setPrototypeOf.
347 gl.__proto__ = prototype; // eslint-disable-line no-proto
348 }
349 wrapMethods(gl);
350 // No canvas yet...
351 gl.canvas = null;
352 // Drawing buffer width/height
353 // TODO(nikki): Make this dynamic
354 const viewport = gl.getParameter(gl.VIEWPORT);
355 gl.drawingBufferWidth = viewport[2];
356 gl.drawingBufferHeight = viewport[3];
357 // Enable/disable logging of all GL function calls
358 let enableLogging = false;
359 // $FlowIssue: Flow wants a "value" field
360 Object.defineProperty(gl, 'enableLogging', {
361 configurable: true,
362 get() {
363 return enableLogging;
364 },
365 set(enable) {
366 if (enable === enableLogging) {
367 return;
368 }
369 if (enable) {
370 Object.keys(gl).forEach(key => {
371 if (typeof gl[key] === 'function') {
372 const original = gl[key];
373 gl[key] = (...args) => {
374 console.log(`EXGL: ${key}(${args.join(', ')})`);
375 const r = original.apply(gl, args);
376 console.log(`EXGL: = ${r}`);
377 return r;
378 };
379 gl[key].original = original;
380 }
381 });
382 }
383 else {
384 Object.keys(gl).forEach(key => {
385 if (typeof gl[key] === 'function' && gl[key].original) {
386 gl[key] = gl[key].original;
387 }
388 });
389 }
390 enableLogging = enable;
391 },
392 });
393 return gl;
394};
395const getContextId = (exgl) => {
396 const exglCtxId = exgl && typeof exgl === 'object' ? exgl.__exglCtxId : exgl;
397 if (!exglCtxId || typeof exglCtxId !== 'number') {
398 throw new Error(`Invalid EXGLContext id: ${String(exglCtxId)}`);
399 }
400 return exglCtxId;
401};
402global.WebGLRenderingContext = WebGLRenderingContext;
403global.WebGL2RenderingContext = WebGL2RenderingContext;
404global.WebGLObject = WebGLObject;
405global.WebGLBuffer = WebGLBuffer;
406global.WebGLFramebuffer = WebGLFramebuffer;
407global.WebGLProgram = WebGLProgram;
408global.WebGLRenderbuffer = WebGLRenderbuffer;
409global.WebGLShader = WebGLShader;
410global.WebGLTexture = WebGLTexture;
411global.WebGLUniformLocation = WebGLUniformLocation;
412global.WebGLActiveInfo = WebGLActiveInfo;
413global.WebGLShaderPrecisionFormat = WebGLShaderPrecisionFormat;
414global.WebGLQuery = WebGLQuery;
415global.WebGLSampler = WebGLSampler;
416global.WebGLSync = WebGLSync;
417global.WebGLTransformFeedback = WebGLTransformFeedback;
418global.WebGLVertexArrayObject = WebGLVertexArrayObject;
419//# sourceMappingURL=GLView.js.map
\No newline at end of file