UNPKG

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