UNPKG

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