1 #include "EXWebGLMethods.h"
2 #include "EXGLContextManager.h"
3 #include "EXGLImageUtils.h"
4 #include "EXJsiArgsTransform.h"
5 #include "EXWebGLMethodsHelpers.h"
6 #include "EXWebGLRenderer.h"
7 
8 #include <algorithm>
9 
10 #define ARG(index, type)                                   \
11   (argc > index ? unpackArg<type>(runtime, jsArgv + index) \
12                 : throw std::runtime_error("EXGL: Too few arguments"))
13 
14 #define CTX()                                \
15   auto result = getContext(runtime, jsThis); \
16   auto ctx = result.first;                   \
17   if (ctx == nullptr) {                      \
18     return jsi::Value::undefined();          \
19   }
20 
21 #define NATIVE_METHOD(name, ...)    \
22   jsi::Value glNativeMethod_##name( \
23       jsi::Runtime &runtime, const jsi::Value &jsThis, const jsi::Value *jsArgv, size_t argc)
24 
25 #define SIMPLE_NATIVE_METHOD(name, func)                                    \
26   NATIVE_METHOD(name) {                                                     \
27     CTX();                                                                  \
28     ctx->addToNextBatch(generateNativeMethod(runtime, func, jsArgv, argc)); \
29     return nullptr;                                                         \
30   }
31 
32 #define UNIMPL_NATIVE_METHOD(name)   \
33   NATIVE_METHOD(name) {              \
34     return exglUnimplemented(#name); \
35   }
36 
37 namespace expo {
38 namespace gl_cpp {
39 namespace method {
40 
41 ContextWithLock getContext(jsi::Runtime &runtime, const jsi::Value &jsThis) {
42   double exglCtxId = jsThis.asObject(runtime).getProperty(runtime, "contextId").asNumber();
43   return ContextGet(static_cast<EXGLContextId>(exglCtxId));
44 }
45 
46 // This listing follows the order in
47 // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext
48 // https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext
49 
50 // The WebGL context
51 // -----------------
52 
53 NATIVE_METHOD(getContextAttributes) {
54   jsi::Object jsResult(runtime);
55   jsResult.setProperty(runtime, "alpha", true);
56   jsResult.setProperty(runtime, "depth", true);
57   jsResult.setProperty(runtime, "stencil", true);
58   jsResult.setProperty(runtime, "antialias", false);
59   jsResult.setProperty(runtime, "premultipliedAlpha", false);
60   return jsResult;
61 }
62 
63 NATIVE_METHOD(isContextLost) {
64   return false;
65 }
66 
67 // Viewing and clipping
68 // --------------------
69 
70 SIMPLE_NATIVE_METHOD(scissor, glScissor); // x, y, width, height
71 
72 SIMPLE_NATIVE_METHOD(viewport, glViewport); // x, y, width, height
73 
74 // State information
75 // -----------------
76 
77 SIMPLE_NATIVE_METHOD(activeTexture, glActiveTexture); // texture
78 
79 SIMPLE_NATIVE_METHOD(blendColor, glBlendColor); // red, green, blue, alpha
80 
81 SIMPLE_NATIVE_METHOD(blendEquation, glBlendEquation); // mode
82 
83 SIMPLE_NATIVE_METHOD(blendEquationSeparate, glBlendEquationSeparate); // modeRGB, modeAlpha
84 
85 SIMPLE_NATIVE_METHOD(blendFunc, glBlendFunc); // sfactor, dfactor
86 
87 SIMPLE_NATIVE_METHOD(blendFuncSeparate, glBlendFuncSeparate); // srcRGB, dstRGB, srcAlpha, dstAlpha
88 
89 SIMPLE_NATIVE_METHOD(clearColor, glClearColor); // red, green, blue, alpha
90 
91 SIMPLE_NATIVE_METHOD(clearDepth, glClearDepthf); // depth
92 
93 SIMPLE_NATIVE_METHOD(clearStencil, glClearStencil); // s
94 
95 SIMPLE_NATIVE_METHOD(colorMask, glColorMask); // red, green, blue, alpha
96 
97 SIMPLE_NATIVE_METHOD(cullFace, glCullFace); // mode
98 
99 SIMPLE_NATIVE_METHOD(depthFunc, glDepthFunc); // func
100 
101 SIMPLE_NATIVE_METHOD(depthMask, glDepthMask); // flag
102 
103 SIMPLE_NATIVE_METHOD(depthRange, glDepthRangef); // zNear, zFar
104 
105 SIMPLE_NATIVE_METHOD(disable, glDisable); // cap
106 
107 SIMPLE_NATIVE_METHOD(enable, glEnable); // cap
108 
109 SIMPLE_NATIVE_METHOD(frontFace, glFrontFace); // mode
110 
111 NATIVE_METHOD(getParameter) {
112   CTX();
113   auto pname = ARG(0, GLenum);
114 
115   switch (pname) {
116       // Float32Array[0]
117     case GL_COMPRESSED_TEXTURE_FORMATS:
118       return TypedArray<TypedArrayKind::Float32Array>(runtime, {});
119 
120       // FLoat32Array[2]
121     case GL_ALIASED_LINE_WIDTH_RANGE:
122     case GL_ALIASED_POINT_SIZE_RANGE:
123     case GL_DEPTH_RANGE: {
124       std::vector<TypedArrayBase::ContentType<TypedArrayKind::Float32Array>> glResults(2);
125       ctx->addBlockingToNextBatch([&] { glGetFloatv(pname, glResults.data()); });
126       return TypedArray<TypedArrayKind::Float32Array>(runtime, glResults);
127     }
128       // FLoat32Array[4]
129     case GL_BLEND_COLOR:
130     case GL_COLOR_CLEAR_VALUE: {
131       std::vector<TypedArrayBase::ContentType<TypedArrayKind::Float32Array>> glResults(4);
132       ctx->addBlockingToNextBatch([&] { glGetFloatv(pname, glResults.data()); });
133       return TypedArray<TypedArrayKind::Float32Array>(runtime, glResults);
134     }
135       // Int32Array[2]
136     case GL_MAX_VIEWPORT_DIMS: {
137       std::vector<TypedArrayBase::ContentType<TypedArrayKind::Int32Array>> glResults(2);
138       ctx->addBlockingToNextBatch([&] { glGetIntegerv(pname, glResults.data()); });
139       return TypedArray<TypedArrayKind::Int32Array>(runtime, glResults);
140     }
141       // Int32Array[4]
142     case GL_SCISSOR_BOX:
143     case GL_VIEWPORT: {
144       std::vector<TypedArrayBase::ContentType<TypedArrayKind::Int32Array>> glResults(4);
145       ctx->addBlockingToNextBatch([&] { glGetIntegerv(pname, glResults.data()); });
146       return TypedArray<TypedArrayKind::Int32Array>(runtime, glResults);
147     }
148       // boolean[4]
149     case GL_COLOR_WRITEMASK: {
150       GLint glResults[4];
151       ctx->addBlockingToNextBatch([&] { glGetIntegerv(pname, glResults); });
152       return jsi::Array::createWithElements(
153           runtime,
154           {jsi::Value(glResults[0]),
155            jsi::Value(glResults[1]),
156            jsi::Value(glResults[2]),
157            jsi::Value(glResults[3])});
158     }
159 
160       // boolean
161     case GL_UNPACK_FLIP_Y_WEBGL:
162       return ctx->unpackFLipY;
163     case GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL:
164     case GL_UNPACK_COLORSPACE_CONVERSION_WEBGL:
165       return false;
166     case GL_RASTERIZER_DISCARD:
167     case GL_SAMPLE_ALPHA_TO_COVERAGE:
168     case GL_SAMPLE_COVERAGE:
169     case GL_TRANSFORM_FEEDBACK_ACTIVE:
170     case GL_TRANSFORM_FEEDBACK_PAUSED: {
171       GLint glResult;
172       ctx->addBlockingToNextBatch([&] { glGetIntegerv(pname, &glResult); });
173       return jsi::Value(glResult);
174     }
175 
176       // string
177     case GL_RENDERER:
178     case GL_SHADING_LANGUAGE_VERSION:
179     case GL_VENDOR:
180     case GL_VERSION: {
181       const GLubyte *glStr;
182       ctx->addBlockingToNextBatch([&] { glStr = glGetString(pname); });
183       return jsi::String::createFromUtf8(
184           runtime, std::string(reinterpret_cast<const char *>(glStr)));
185     }
186 
187       // float
188     case GL_DEPTH_CLEAR_VALUE:
189     case GL_LINE_WIDTH:
190     case GL_POLYGON_OFFSET_FACTOR:
191     case GL_POLYGON_OFFSET_UNITS:
192     case GL_SAMPLE_COVERAGE_VALUE:
193     case GL_MAX_TEXTURE_LOD_BIAS: {
194       GLfloat glFloat;
195       ctx->addBlockingToNextBatch([&] { glGetFloatv(pname, &glFloat); });
196       return static_cast<double>(glFloat);
197     }
198 
199       // EXGLObjectId
200     case GL_ARRAY_BUFFER_BINDING:
201     case GL_ELEMENT_ARRAY_BUFFER_BINDING: {
202       GLint glInt;
203       ctx->addBlockingToNextBatch([&] { glGetIntegerv(pname, &glInt); });
204       for (const auto &pair : ctx->objects) {
205         if (static_cast<int>(pair.second) == glInt) {
206           return createWebGLObject(
207               runtime, EXWebGLClass::WebGLBuffer, {static_cast<double>(pair.first)});
208         }
209       }
210       return nullptr;
211     }
212 
213     case GL_CURRENT_PROGRAM: {
214       GLint glInt;
215       ctx->addBlockingToNextBatch([&] { glGetIntegerv(pname, &glInt); });
216       for (const auto &pair : ctx->objects) {
217         if (static_cast<int>(pair.second) == glInt) {
218           return createWebGLObject(
219               runtime, EXWebGLClass::WebGLProgram, {static_cast<double>(pair.first)});
220         }
221       }
222       return nullptr;
223     }
224 
225       // Unimplemented...
226     case GL_COPY_READ_BUFFER_BINDING:
227     case GL_COPY_WRITE_BUFFER_BINDING:
228     case GL_DRAW_FRAMEBUFFER_BINDING:
229     case GL_READ_FRAMEBUFFER_BINDING:
230     case GL_RENDERBUFFER_BINDING:
231     case GL_SAMPLER_BINDING:
232     case GL_TEXTURE_BINDING_2D_ARRAY:
233     case GL_TEXTURE_BINDING_2D:
234     case GL_TEXTURE_BINDING_3D:
235     case GL_TEXTURE_BINDING_CUBE_MAP:
236     case GL_TRANSFORM_FEEDBACK_BINDING:
237     case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
238     case GL_UNIFORM_BUFFER_BINDING:
239     case GL_VERTEX_ARRAY_BINDING:
240       throw std::runtime_error(
241           "EXGL: getParameter() doesn't support gl." + std::to_string(pname) + " yet!");
242 
243       // int
244     default: {
245       GLint glInt;
246       ctx->addBlockingToNextBatch([&] { glGetIntegerv(pname, &glInt); });
247       return jsi::Value(glInt);
248     }
249   }
250 }
251 
252 NATIVE_METHOD(getError) {
253   CTX();
254   GLenum glResult;
255   ctx->addBlockingToNextBatch([&] { glResult = glGetError(); });
256   return static_cast<double>(glResult);
257 }
258 
259 SIMPLE_NATIVE_METHOD(hint, glHint); // target, mode
260 
261 NATIVE_METHOD(isEnabled) {
262   CTX();
263   auto cap = ARG(0, GLenum);
264   GLboolean glResult;
265   ctx->addBlockingToNextBatch([&] { glResult = glIsEnabled(cap); });
266   return glResult == GL_TRUE;
267 }
268 
269 SIMPLE_NATIVE_METHOD(lineWidth, glLineWidth); // width
270 
271 NATIVE_METHOD(pixelStorei) {
272   CTX();
273   auto pname = ARG(0, GLenum);
274   switch (pname) {
275     case GL_UNPACK_FLIP_Y_WEBGL: {
276       ctx->unpackFLipY = ARG(1, GLboolean);
277       break;
278     }
279     default:
280       jsConsoleLog(runtime, { jsi::String::createFromUtf8(runtime, "EXGL: gl.pixelStorei() doesn't support this parameter yet!") });
281   }
282   return nullptr;
283 }
284 
285 SIMPLE_NATIVE_METHOD(polygonOffset, glPolygonOffset); // factor, units
286 
287 SIMPLE_NATIVE_METHOD(sampleCoverage, glSampleCoverage); // value, invert
288 
289 SIMPLE_NATIVE_METHOD(stencilFunc, glStencilFunc); // func, ref, mask
290 
291 SIMPLE_NATIVE_METHOD(stencilFuncSeparate, glStencilFuncSeparate); // face, func, ref, mask
292 
293 SIMPLE_NATIVE_METHOD(stencilMask, glStencilMask); // mask
294 
295 SIMPLE_NATIVE_METHOD(stencilMaskSeparate, glStencilMaskSeparate); // face, mask
296 
297 SIMPLE_NATIVE_METHOD(stencilOp, glStencilOp) // fail, zfail, zpass
298 
299 SIMPLE_NATIVE_METHOD(stencilOpSeparate, glStencilOpSeparate); // face, fail, zfail, zpass
300 
301 // Buffers
302 // -------
303 
304 NATIVE_METHOD(bindBuffer) {
305   CTX();
306   auto target = ARG(0, GLenum);
307   auto buffer = ARG(1, EXWebGLClass);
308   ctx->addToNextBatch([=] { glBindBuffer(target, ctx->lookupObject(buffer)); });
309   return nullptr;
310 }
311 
312 NATIVE_METHOD(bufferData) {
313   CTX();
314   auto target = ARG(0, GLenum);
315   auto &sizeOrData = ARG(1, const jsi::Value &);
316   auto usage = ARG(2, GLenum);
317 
318   if (sizeOrData.isNumber()) {
319     GLsizeiptr length = sizeOrData.getNumber();
320     ctx->addToNextBatch([=] { glBufferData(target, length, nullptr, usage); });
321   } else if (sizeOrData.isNull() || sizeOrData.isUndefined()) {
322     ctx->addToNextBatch([=] { glBufferData(target, 0, nullptr, usage); });
323   } else if (sizeOrData.isObject()) {
324     auto data = rawTypedArray(runtime, sizeOrData.getObject(runtime));
325     ctx->addToNextBatch(
326         [=, data{std::move(data)}] { glBufferData(target, data.size(), data.data(), usage); });
327   }
328   return nullptr;
329 }
330 
331 NATIVE_METHOD(bufferSubData) {
332   CTX();
333   auto target = ARG(0, GLenum);
334   auto offset = ARG(1, GLintptr);
335   if (ARG(2, const jsi::Value &).isNull()) {
336     ctx->addToNextBatch([=] { glBufferSubData(target, offset, 0, nullptr); });
337   } else {
338     auto data = rawTypedArray(runtime, ARG(2, jsi::Object));
339     ctx->addToNextBatch(
340         [=, data{std::move(data)}] { glBufferSubData(target, offset, data.size(), data.data()); });
341   }
342   return nullptr;
343 }
344 
345 NATIVE_METHOD(createBuffer) {
346   CTX();
347   return exglGenObject(ctx, runtime, glGenBuffers, EXWebGLClass::WebGLBuffer);
348 }
349 
350 NATIVE_METHOD(deleteBuffer) {
351   CTX();
352   return exglDeleteObject(ctx, ARG(0, EXWebGLClass), glDeleteBuffers);
353 }
354 
355 NATIVE_METHOD(getBufferParameter) {
356   CTX();
357   auto target = ARG(0, GLenum);
358   auto pname = ARG(1, GLenum);
359   GLint glResult;
360   ctx->addBlockingToNextBatch([&] { glGetBufferParameteriv(target, pname, &glResult); });
361   return jsi::Value(glResult);
362 }
363 
364 NATIVE_METHOD(isBuffer) {
365   CTX();
366   return exglIsObject(ctx, ARG(0, EXWebGLClass), glIsBuffer);
367 }
368 
369 // Buffers (WebGL2)
370 
371 SIMPLE_NATIVE_METHOD(
372     copyBufferSubData,
373     glCopyBufferSubData) // readTarget, writeTarget, readOffset, writeOffset, size
374 
375 // glGetBufferSubData is not available in OpenGL ES
376 UNIMPL_NATIVE_METHOD(getBufferSubData);
377 
378 // Framebuffers
379 // ------------
380 
381 NATIVE_METHOD(bindFramebuffer) {
382   CTX();
383   auto target = ARG(0, GLenum);
384   auto framebuffer = ARG(1, EXWebGLClass);
385   ctx->addToNextBatch([=] {
386     glBindFramebuffer(
387         target, framebuffer == 0 ? ctx->defaultFramebuffer : ctx->lookupObject(framebuffer));
388   });
389   return nullptr;
390 }
391 
392 NATIVE_METHOD(checkFramebufferStatus) {
393   CTX();
394   auto target = ARG(0, GLenum);
395   GLenum glResult;
396   ctx->addBlockingToNextBatch([&] { glResult = glCheckFramebufferStatus(target); });
397   return static_cast<double>(glResult);
398 }
399 
400 NATIVE_METHOD(createFramebuffer) {
401   CTX();
402   return exglGenObject(ctx, runtime, glGenFramebuffers, EXWebGLClass::WebGLFramebuffer);
403 }
404 
405 NATIVE_METHOD(deleteFramebuffer) {
406   CTX();
407   return exglDeleteObject(ctx, ARG(0, EXWebGLClass), glDeleteFramebuffers);
408 }
409 
410 NATIVE_METHOD(framebufferRenderbuffer) {
411   CTX();
412   auto target = ARG(0, GLenum);
413   auto attachment = ARG(1, GLenum);
414   auto renderbuffertarget = ARG(2, GLenum);
415   auto fRenderbuffer = ARG(3, EXWebGLClass);
416   ctx->addToNextBatch([=] {
417     GLuint renderbuffer = ctx->lookupObject(fRenderbuffer);
418     glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer);
419   });
420   return nullptr;
421 }
422 
423 NATIVE_METHOD(framebufferTexture2D, 5) {
424   CTX();
425   auto target = ARG(0, GLenum);
426   auto attachment = ARG(1, GLenum);
427   auto textarget = ARG(2, GLenum);
428   auto fTexture = ARG(3, EXWebGLClass);
429   auto level = ARG(4, GLint);
430   ctx->addToNextBatch([=] {
431     glFramebufferTexture2D(target, attachment, textarget, ctx->lookupObject(fTexture), level);
432   });
433   return nullptr;
434 }
435 
436 UNIMPL_NATIVE_METHOD(getFramebufferAttachmentParameter)
437 
438 NATIVE_METHOD(isFramebuffer) {
439   CTX();
440   return exglIsObject(ctx, ARG(0, EXWebGLClass), glIsFramebuffer);
441 }
442 
443 NATIVE_METHOD(readPixels) {
444   CTX();
445   auto x = ARG(0, GLint);
446   auto y = ARG(1, GLint);
447   auto width = ARG(2, GLuint); // GLsizei allows negative values
448   auto height = ARG(3, GLuint);
449   auto format = ARG(4, GLenum);
450   auto type = ARG(5, GLenum);
451   size_t byteLength = width * height * bytesPerPixel(type, format);
452   std::vector<uint8_t> pixels(byteLength);
453   ctx->addBlockingToNextBatch(
454       [&] { glReadPixels(x, y, width, height, format, type, pixels.data()); });
455 
456   TypedArrayBase arr = ARG(6, TypedArrayBase);
457   jsi::ArrayBuffer buffer = arr.getBuffer(runtime);
458   arrayBufferUpdate(runtime, buffer, pixels, arr.byteOffset(runtime));
459   return nullptr;
460 }
461 
462 // Framebuffers (WebGL2)
463 // ---------------------
464 
465 SIMPLE_NATIVE_METHOD(blitFramebuffer, glBlitFramebuffer);
466 // srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter
467 
468 NATIVE_METHOD(framebufferTextureLayer) {
469   CTX();
470   auto target = ARG(0, GLenum);
471   auto attachment = ARG(1, GLenum);
472   auto texture = ARG(2, EXWebGLClass);
473   auto level = ARG(3, GLint);
474   auto layer = ARG(4, GLint);
475   ctx->addToNextBatch([=] {
476     glFramebufferTextureLayer(target, attachment, ctx->lookupObject(texture), level, layer);
477   });
478   return nullptr;
479 }
480 
481 NATIVE_METHOD(invalidateFramebuffer) {
482   CTX();
483   auto target = ARG(0, GLenum);
484   auto jsAttachments = ARG(1, jsi::Array);
485 
486   std::vector<GLenum> attachments(jsAttachments.size(runtime));
487   for (size_t i = 0; i < attachments.size(); i++) {
488     attachments[i] = jsAttachments.getValueAtIndex(runtime, i).asNumber();
489   }
490   ctx->addToNextBatch([=, attachaments{std::move(attachments)}] {
491     glInvalidateFramebuffer(target, static_cast<GLsizei>(attachments.size()), attachments.data());
492   });
493   return nullptr; // breaking change TypedArray -> Array (bug in previous implementation)
494 }
495 
496 NATIVE_METHOD(invalidateSubFramebuffer) {
497   CTX();
498   auto target = ARG(0, GLenum);
499   auto jsAttachments = ARG(1, jsi::Array);
500   auto x = ARG(2, GLint);
501   auto y = ARG(3, GLint);
502   auto width = ARG(4, GLint);
503   auto height = ARG(5, GLint);
504   std::vector<GLenum> attachments(jsAttachments.size(runtime));
505   for (size_t i = 0; i < attachments.size(); i++) {
506     attachments[i] = jsAttachments.getValueAtIndex(runtime, i).asNumber();
507   }
508   ctx->addToNextBatch([=, attachments{std::move(attachments)}] {
509     glInvalidateSubFramebuffer(
510         target, static_cast<GLsizei>(attachments.size()), attachments.data(), x, y, width, height);
511   });
512   return nullptr;
513 }
514 
515 SIMPLE_NATIVE_METHOD(readBuffer, glReadBuffer); // mode
516 
517 // Renderbuffers
518 // -------------
519 
520 NATIVE_METHOD(bindRenderbuffer) {
521   CTX();
522   auto target = ARG(0, GLenum);
523   auto fRenderbuffer = ARG(1, EXWebGLClass);
524   ctx->addToNextBatch([=] { glBindRenderbuffer(target, ctx->lookupObject(fRenderbuffer)); });
525   return nullptr;
526 }
527 
528 NATIVE_METHOD(createRenderbuffer) {
529   CTX();
530   return exglGenObject(ctx, runtime, glGenRenderbuffers, EXWebGLClass::WebGLRenderbuffer);
531 }
532 
533 NATIVE_METHOD(deleteRenderbuffer) {
534   CTX();
535   return exglDeleteObject(ctx, ARG(0, EXWebGLClass), glDeleteRenderbuffers);
536 }
537 
538 UNIMPL_NATIVE_METHOD(getRenderbufferParameter)
539 
540 NATIVE_METHOD(isRenderbuffer) {
541   CTX();
542   return exglIsObject(ctx, ARG(0, EXWebGLClass), glIsRenderbuffer);
543 }
544 
545 NATIVE_METHOD(renderbufferStorage) {
546   CTX();
547   auto target = ARG(0, GLenum);
548   auto internalformat = ARG(1, GLint);
549   auto width = ARG(2, GLsizei);
550   auto height = ARG(3, GLsizei);
551 
552   // WebGL allows `GL_DEPTH_STENCIL` flag to be passed here,
553   // however OpenGL ES seems to require sized format, so we fall back to `GL_DEPTH24_STENCIL8`.
554   internalformat = internalformat == GL_DEPTH_STENCIL ? GL_DEPTH24_STENCIL8 : internalformat;
555 
556   ctx->addToNextBatch([=] { glRenderbufferStorage(target, internalformat, width, height); });
557   return nullptr;
558 }
559 
560 // Renderbuffers (WebGL2)
561 // ----------------------
562 
563 NATIVE_METHOD(getInternalformatParameter) {
564   CTX();
565   auto target = ARG(0, GLenum);
566   auto internalformat = ARG(1, GLenum);
567   auto pname = ARG(2, GLenum);
568 
569   std::vector<TypedArrayBase::ContentType<TypedArrayKind::Int32Array>> glResults;
570   ctx->addBlockingToNextBatch([&] {
571     GLint count;
572     glGetInternalformativ(target, internalformat, GL_NUM_SAMPLE_COUNTS, 1, &count);
573     glResults.resize(count);
574     glGetInternalformativ(target, internalformat, pname, count, glResults.data());
575   });
576 
577   return TypedArray<TypedArrayKind::Int32Array>(runtime, glResults);
578 }
579 
580 UNIMPL_NATIVE_METHOD(renderbufferStorageMultisample)
581 
582 // Textures
583 // --------
584 
585 NATIVE_METHOD(bindTexture) {
586   CTX();
587   auto target = ARG(0, GLenum);
588   auto texture = ARG(1, EXWebGLClass);
589   ctx->addToNextBatch([=] { glBindTexture(target, ctx->lookupObject(texture)); });
590   return nullptr;
591 }
592 
593 UNIMPL_NATIVE_METHOD(compressedTexImage2D)
594 
595 UNIMPL_NATIVE_METHOD(compressedTexSubImage2D)
596 
597 SIMPLE_NATIVE_METHOD(
598     copyTexImage2D,
599     glCopyTexImage2D); // target, level, internalformat, x, y, width, height, border
600 
601 SIMPLE_NATIVE_METHOD(
602     copyTexSubImage2D,
603     glCopyTexSubImage2D) // target, level, xoffset, yoffset, x, y, width, height
604 
605 NATIVE_METHOD(createTexture) {
606   CTX();
607   return exglGenObject(ctx, runtime, glGenTextures, EXWebGLClass::WebGLTexture);
608 }
609 
610 NATIVE_METHOD(deleteTexture) {
611   CTX();
612   return exglDeleteObject(ctx, ARG(0, EXWebGLClass), glDeleteTextures);
613 }
614 
615 SIMPLE_NATIVE_METHOD(generateMipmap, glGenerateMipmap) // target
616 
617 UNIMPL_NATIVE_METHOD(getTexParameter)
618 
619 NATIVE_METHOD(isTexture) {
620   CTX();
621   return exglIsObject(ctx, ARG(0, EXWebGLClass), glIsTexture);
622 }
623 
624 NATIVE_METHOD(texImage2D, 6) {
625   CTX();
626   auto target = ARG(0, GLenum);
627   auto level = ARG(1, GLint);
628   auto internalformat = ARG(2, GLint);
629   if (argc == 9) {
630     auto width = ARG(3, GLsizei);
631     auto height = ARG(4, GLsizei);
632     auto border = ARG(5, GLsizei);
633     auto format = ARG(6, GLenum);
634     auto type = ARG(7, GLenum);
635     if (ARG(8, const jsi::Value &).isNull()) {
636       ctx->addToNextBatch([=] {
637         glTexImage2D(target, level, internalformat, width, height, border, format, type, nullptr);
638       });
639       return nullptr;
640     }
641     auto data = ARG(8, jsi::Object);
642 
643     if (data.isArrayBuffer(runtime) || isTypedArray(runtime, data)) {
644       std::vector<uint8_t> vec = rawTypedArray(runtime, std::move(data));
645       if (ctx->unpackFLipY) {
646         flipPixels(vec.data(), width * bytesPerPixel(type, format), height);
647       }
648       ctx->addToNextBatch([=, vec{std::move(vec)}] {
649         glTexImage2D(
650             target, level, internalformat, width, height, border, format, type, vec.data());
651       });
652     } else {
653       auto image = loadImage(runtime, data, &width, &height, nullptr);
654       if (ctx->unpackFLipY) {
655         flipPixels(image.get(), width * bytesPerPixel(type, format), height);
656       }
657       ctx->addToNextBatch([=] {
658         glTexImage2D(
659             target, level, internalformat, width, height, border, format, type, image.get());
660       });
661     }
662   } else if (argc == 6) {
663     auto format = ARG(3, GLenum);
664     auto type = ARG(4, GLenum);
665     auto data = ARG(5, jsi::Object);
666     GLsizei width = 0, height = 0, border = 0;
667     auto image = loadImage(runtime, data, &width, &height, nullptr);
668     if (ctx->unpackFLipY) {
669       flipPixels(image.get(), width * bytesPerPixel(type, format), height);
670     }
671     ctx->addToNextBatch([=] {
672       glTexImage2D(target, level, internalformat, width, height, border, format, type, image.get());
673     });
674   } else {
675     throw std::runtime_error("EXGL: Invalid number of arguments to gl.texImage2D()!");
676   }
677   return nullptr;
678 }
679 
680 NATIVE_METHOD(texSubImage2D, 6) {
681   CTX();
682   auto target = ARG(0, GLenum);
683   auto level = ARG(1, GLint);
684   auto xoffset = ARG(2, GLint);
685   auto yoffset = ARG(3, GLint);
686   if (argc == 9) {
687     auto width = ARG(4, GLsizei);
688     auto height = ARG(5, GLsizei);
689     auto format = ARG(6, GLenum);
690     auto type = ARG(7, GLenum);
691     if (ARG(8, const jsi::Value &).isNull()) {
692       ctx->addToNextBatch([=] {
693         auto empty = std::make_unique<uint8_t>(width * height * bytesPerPixel(type, format));
694         std::memset(empty.get(), 0, width * height * bytesPerPixel(type, format));
695         glTexImage2D(target, level, xoffset, yoffset, width, height, format, type, empty.get());
696       });
697       return nullptr;
698     }
699 
700     auto data = ARG(8, jsi::Object);
701 
702     if (data.isArrayBuffer(runtime) || isTypedArray(runtime, data)) {
703       std::vector<uint8_t> vec = rawTypedArray(runtime, std::move(data));
704       if (ctx->unpackFLipY) {
705         flipPixels(vec.data(), width * bytesPerPixel(type, format), height);
706       }
707       ctx->addToNextBatch([=, vec{std::move(vec)}] {
708         glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, vec.data());
709       });
710     } else {
711       auto image = loadImage(runtime, data, &width, &height, nullptr);
712       if (ctx->unpackFLipY) {
713         flipPixels(image.get(), width * bytesPerPixel(type, format), height);
714       }
715       ctx->addToNextBatch([=] {
716         glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, image.get());
717       });
718     }
719   } else if (argc == 7) {
720     auto format = ARG(4, GLenum);
721     auto type = ARG(5, GLenum);
722     auto data = ARG(6, jsi::Object);
723     GLsizei width = 0, height = 0;
724     auto image = loadImage(runtime, data, &width, &height, nullptr);
725     if (ctx->unpackFLipY) {
726       flipPixels(image.get(), width * bytesPerPixel(type, format), height);
727     }
728     ctx->addToNextBatch([=] {
729       glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, image.get());
730     });
731   } else {
732     throw std::runtime_error("EXGL: Invalid number of arguments to gl.texSubImage2D()!");
733   }
734   return nullptr;
735 }
736 
737 SIMPLE_NATIVE_METHOD(texParameterf, glTexParameterf); // target, pname, param
738 
739 SIMPLE_NATIVE_METHOD(texParameteri, glTexParameteri); // target, pname, param
740 
741 // Textures (WebGL2)
742 // -----------------
743 
744 SIMPLE_NATIVE_METHOD(texStorage2D, glTexStorage2D); // target, levels, internalformat, width, height
745 
746 SIMPLE_NATIVE_METHOD(
747     texStorage3D,
748     glTexStorage3D); // target, levels, internalformat, width, height, depth
749 
750 NATIVE_METHOD(texImage3D) {
751   CTX();
752   auto target = ARG(0, GLenum);
753   auto level = ARG(1, GLint);
754   auto internalformat = ARG(2, GLint);
755   auto width = ARG(3, GLsizei);
756   auto height = ARG(4, GLsizei);
757   auto depth = ARG(5, GLsizei);
758   auto border = ARG(6, GLsizei);
759   auto format = ARG(7, GLenum);
760   auto type = ARG(8, GLenum);
761 
762   if (ARG(9, const jsi::Value &).isNull()) {
763     ctx->addToNextBatch([=] {
764       glTexImage3D(
765           target, level, internalformat, width, height, depth, border, format, type, nullptr);
766     });
767     return nullptr;
768   }
769   auto data = ARG(9, jsi::Object);
770   auto flip = [&](uint8_t *data) {
771     GLubyte *texelLayer = data;
772     for (int z = 0; z < depth; z++) {
773       flipPixels(texelLayer, width * bytesPerPixel(type, format), height);
774       texelLayer += bytesPerPixel(type, format) * width * height;
775     }
776   };
777 
778   if (data.isArrayBuffer(runtime) || isTypedArray(runtime, data)) {
779     std::vector<uint8_t> vec = rawTypedArray(runtime, std::move(data));
780     if (ctx->unpackFLipY) {
781       flip(vec.data());
782     }
783     ctx->addToNextBatch([=, vec{std::move(vec)}] {
784       glTexImage3D(
785           target, level, internalformat, width, height, depth, border, format, type, vec.data());
786     });
787   } else {
788     auto image = loadImage(runtime, data, &width, &height, nullptr);
789     if (ctx->unpackFLipY) {
790       flip(image.get());
791     }
792     ctx->addToNextBatch([=] {
793       glTexImage3D(
794           target, level, internalformat, width, height, depth, border, format, type, image.get());
795     });
796   }
797   return nullptr;
798 }
799 
800 NATIVE_METHOD(texSubImage3D) {
801   CTX();
802   auto target = ARG(0, GLenum);
803   auto level = ARG(1, GLint);
804   auto xoffset = ARG(2, GLint);
805   auto yoffset = ARG(3, GLint);
806   auto zoffset = ARG(4, GLint);
807   auto width = ARG(5, GLsizei);
808   auto height = ARG(6, GLsizei);
809   auto depth = ARG(7, GLsizei);
810   auto format = ARG(8, GLenum);
811   auto type = ARG(9, GLenum);
812 
813   if (ARG(10, const jsi::Value &).isNull()) {
814     ctx->addToNextBatch([=] {
815       auto empty = std::make_unique<uint8_t>(width * height * depth * bytesPerPixel(type, format));
816       std::memset(empty.get(), 0, width * height * depth * bytesPerPixel(type, format));
817       auto ptr = empty.get();
818       glTexSubImage3D(
819           target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, ptr);
820     });
821     return nullptr;
822   }
823   auto data = ARG(10, jsi::Object);
824   auto flip = [&](uint8_t *data) {
825     GLubyte *texelLayer = data;
826     for (int z = 0; z < depth; z++) {
827       flipPixels(texelLayer, width * bytesPerPixel(type, format), height);
828       texelLayer += bytesPerPixel(type, format) * width * height;
829     }
830   };
831 
832   if (data.isArrayBuffer(runtime) || isTypedArray(runtime, data)) {
833     std::vector<uint8_t> vec = rawTypedArray(runtime, std::move(data));
834     if (ctx->unpackFLipY) {
835       flip(vec.data());
836     }
837     ctx->addToNextBatch([=, vec{std::move(vec)}] {
838       glTexSubImage3D(
839           target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, vec.data());
840     });
841   } else {
842     auto image = loadImage(runtime, data, &width, &height, nullptr);
843     if (ctx->unpackFLipY) {
844       flip(image.get());
845     }
846     ctx->addToNextBatch([=] {
847       glTexSubImage3D(
848           target,
849           level,
850           xoffset,
851           yoffset,
852           zoffset,
853           width,
854           height,
855           depth,
856           format,
857           type,
858           image.get());
859     });
860   }
861   return nullptr;
862 }
863 
864 SIMPLE_NATIVE_METHOD(
865     copyTexSubImage3D,
866     glCopyTexSubImage3D); // target, level, xoffset, yoffset, zoffset, x, y, width, height
867 
868 UNIMPL_NATIVE_METHOD(compressedTexImage3D)
869 
870 UNIMPL_NATIVE_METHOD(compressedTexSubImage3D)
871 
872 // Programs and shaders
873 // --------------------
874 
875 NATIVE_METHOD(attachShader) {
876   CTX();
877   auto program = ARG(0, EXWebGLClass);
878   auto shader = ARG(1, EXWebGLClass);
879   ctx->addToNextBatch(
880       [=] { glAttachShader(ctx->lookupObject(program), ctx->lookupObject(shader)); });
881   return nullptr;
882 }
883 
884 NATIVE_METHOD(bindAttribLocation) {
885   CTX();
886   auto program = ARG(0, EXWebGLClass);
887   auto index = ARG(1, GLuint);
888   auto name = ARG(2, std::string);
889   ctx->addToNextBatch([=, name{std::move(name)}] {
890     glBindAttribLocation(ctx->lookupObject(program), index, name.c_str());
891   });
892   return nullptr;
893 }
894 
895 NATIVE_METHOD(compileShader) {
896   CTX();
897   auto shader = ARG(0, EXWebGLClass);
898   ctx->addToNextBatch([=] { glCompileShader(ctx->lookupObject(shader)); });
899   return nullptr;
900 }
901 
902 NATIVE_METHOD(createProgram) {
903   CTX();
904   return exglCreateObject(ctx, runtime, glCreateProgram, EXWebGLClass::WebGLProgram);
905 }
906 
907 NATIVE_METHOD(createShader) {
908   CTX();
909   auto type = ARG(0, GLenum);
910   if (type == GL_VERTEX_SHADER || type == GL_FRAGMENT_SHADER) {
911     return exglCreateObject(
912         ctx, runtime, std::bind(glCreateShader, type), EXWebGLClass::WebGLShader);
913   } else {
914     throw std::runtime_error("unknown shader type passed to function");
915   }
916 }
917 
918 NATIVE_METHOD(deleteProgram) {
919   CTX();
920   return exglDeleteObject(ctx, ARG(0, EXWebGLClass), glDeleteProgram);
921 }
922 
923 NATIVE_METHOD(deleteShader) {
924   CTX();
925   return exglDeleteObject(ctx, ARG(0, EXWebGLClass), glDeleteShader);
926 }
927 
928 NATIVE_METHOD(detachShader) {
929   CTX();
930   auto program = ARG(0, EXWebGLClass);
931   auto shader = ARG(1, EXWebGLClass);
932   ctx->addToNextBatch(
933       [=] { glDetachShader(ctx->lookupObject(program), ctx->lookupObject(shader)); });
934   return nullptr;
935 }
936 
937 NATIVE_METHOD(getAttachedShaders) {
938   CTX();
939   auto fProgram = ARG(0, EXWebGLClass);
940 
941   GLint count;
942   std::vector<GLuint> glResults;
943   ctx->addBlockingToNextBatch([&] {
944     GLuint program = ctx->lookupObject(fProgram);
945     glGetProgramiv(program, GL_ATTACHED_SHADERS, &count);
946     glResults.resize(count);
947     glGetAttachedShaders(program, count, nullptr, glResults.data());
948   });
949 
950   jsi::Array jsResults(runtime, count);
951   for (auto i = 0; i < count; ++i) {
952     EXGLObjectId exglObjId = 0;
953     for (const auto &pair : ctx->objects) {
954       if (pair.second == glResults[i]) {
955         exglObjId = pair.first;
956       }
957     }
958     if (exglObjId == 0) {
959       throw std::runtime_error(
960           "EXGL: Internal error: couldn't find EXGLObjectId "
961           "associated with shader in getAttachedShaders()!");
962     }
963     jsResults.setValueAtIndex(
964         runtime,
965         i,
966         createWebGLObject(runtime, EXWebGLClass::WebGLShader, {static_cast<double>(exglObjId)}));
967   }
968   return jsResults;
969 }
970 
971 NATIVE_METHOD(getProgramParameter) {
972   CTX();
973   auto fProgram = ARG(0, EXWebGLClass);
974   auto pname = ARG(1, GLenum);
975   GLint glResult;
976   ctx->addBlockingToNextBatch(
977       [&] { glGetProgramiv(ctx->lookupObject(fProgram), pname, &glResult); });
978   if (pname == GL_DELETE_STATUS || pname == GL_LINK_STATUS || pname == GL_VALIDATE_STATUS) {
979     return glResult == GL_TRUE;
980   } else {
981     return glResult;
982   }
983 }
984 
985 NATIVE_METHOD(getShaderParameter) {
986   CTX();
987   auto fShader = ARG(0, EXWebGLClass);
988   auto pname = ARG(1, GLenum);
989   GLint glResult;
990   ctx->addBlockingToNextBatch([&] { glGetShaderiv(ctx->lookupObject(fShader), pname, &glResult); });
991   if (pname == GL_DELETE_STATUS || pname == GL_COMPILE_STATUS) {
992     return glResult == GL_TRUE;
993   } else {
994     return glResult;
995   }
996 }
997 
998 NATIVE_METHOD(getShaderPrecisionFormat) {
999   CTX();
1000   auto shaderType = ARG(0, GLenum);
1001   auto precisionType = ARG(1, GLenum);
1002 
1003   GLint range[2], precision;
1004   ctx->addBlockingToNextBatch(
1005       [&] { glGetShaderPrecisionFormat(shaderType, precisionType, range, &precision); });
1006 
1007   jsi::Object jsResult =
1008       createWebGLObject(runtime, EXWebGLClass::WebGLShaderPrecisionFormat, {}).asObject(runtime);
1009   jsResult.setProperty(runtime, "rangeMin", jsi::Value(range[0]));
1010   jsResult.setProperty(runtime, "rangeMax", jsi::Value(range[1]));
1011   jsResult.setProperty(runtime, "precision", jsi::Value(precision));
1012   return jsResult;
1013 }
1014 
1015 NATIVE_METHOD(getProgramInfoLog) {
1016   CTX();
1017   auto fObj = ARG(0, EXWebGLClass);
1018   std::string str;
1019   ctx->addBlockingToNextBatch([&] {
1020     GLuint obj = ctx->lookupObject(fObj);
1021     GLint length;
1022     glGetProgramiv(obj, GL_INFO_LOG_LENGTH, &length);
1023     str.resize(length > 0 ? length - 1 : 0);
1024     glGetProgramInfoLog(obj, length, nullptr, &str[0]);
1025   });
1026   return jsi::String::createFromUtf8(runtime, str);
1027 }
1028 
1029 NATIVE_METHOD(getShaderInfoLog) {
1030   CTX();
1031   auto fObj = ARG(0, EXWebGLClass);
1032   std::string str;
1033   ctx->addBlockingToNextBatch([&] {
1034     GLuint obj = ctx->lookupObject(fObj);
1035     GLint length;
1036     glGetShaderiv(obj, GL_INFO_LOG_LENGTH, &length);
1037     str.resize(length > 0 ? length - 1 : 0);
1038     glGetShaderInfoLog(obj, length, nullptr, &str[0]);
1039   });
1040   return jsi::String::createFromUtf8(runtime, str);
1041 }
1042 
1043 NATIVE_METHOD(getShaderSource) {
1044   CTX();
1045   auto fObj = ARG(0, EXWebGLClass);
1046   std::string str;
1047   ctx->addBlockingToNextBatch([&] {
1048     GLuint obj = ctx->lookupObject(fObj);
1049     GLint length;
1050     glGetShaderiv(obj, GL_SHADER_SOURCE_LENGTH, &length);
1051     str.resize(length > 0 ? length - 1 : 0);
1052     glGetShaderSource(obj, length, nullptr, &str[0]);
1053   });
1054   return jsi::String::createFromUtf8(runtime, str);
1055 }
1056 
1057 NATIVE_METHOD(isShader) {
1058   CTX();
1059   return exglIsObject(ctx, ARG(0, EXWebGLClass), glIsShader);
1060 }
1061 
1062 NATIVE_METHOD(isProgram) {
1063   CTX();
1064   return exglIsObject(ctx, ARG(0, EXWebGLClass), glIsProgram);
1065 }
1066 
1067 NATIVE_METHOD(linkProgram) {
1068   CTX();
1069   auto fProgram = ARG(0, EXWebGLClass);
1070   ctx->addToNextBatch([=] { glLinkProgram(ctx->lookupObject(fProgram)); });
1071   return nullptr;
1072 }
1073 
1074 NATIVE_METHOD(shaderSource) {
1075   CTX();
1076   auto fShader = ARG(0, EXWebGLClass);
1077   auto str = ARG(1, std::string);
1078   ctx->addToNextBatch([=, str{std::move(str)}] {
1079     const char *cstr = str.c_str();
1080     glShaderSource(ctx->lookupObject(fShader), 1, &cstr, nullptr);
1081   });
1082   return nullptr;
1083 }
1084 
1085 NATIVE_METHOD(useProgram) {
1086   CTX();
1087   auto program = ARG(0, EXWebGLClass);
1088   ctx->addToNextBatch([=] { glUseProgram(ctx->lookupObject(program)); });
1089   return nullptr;
1090 }
1091 
1092 NATIVE_METHOD(validateProgram) {
1093   CTX();
1094   auto program = ARG(0, EXWebGLClass);
1095   ctx->addToNextBatch([=] { glValidateProgram(ctx->lookupObject(program)); });
1096   return nullptr;
1097 }
1098 
1099 // Programs and shaders (WebGL2)
1100 
1101 NATIVE_METHOD(getFragDataLocation) {
1102   CTX();
1103   auto program = ARG(0, EXWebGLClass);
1104   auto name = ARG(1, std::string);
1105   GLint location;
1106   ctx->addBlockingToNextBatch(
1107       [&] { location = glGetFragDataLocation(ctx->lookupObject(program), name.c_str()); });
1108   return location == -1 ? jsi::Value::null() : jsi::Value(location);
1109 }
1110 
1111 // Uniforms and attributes
1112 // -----------------------
1113 
1114 SIMPLE_NATIVE_METHOD(disableVertexAttribArray, glDisableVertexAttribArray); // index
1115 
1116 SIMPLE_NATIVE_METHOD(enableVertexAttribArray, glEnableVertexAttribArray); // index
1117 
1118 NATIVE_METHOD(getActiveAttrib) {
1119   CTX();
1120   return exglGetActiveInfo(
1121       ctx,
1122       runtime,
1123       ARG(0, EXWebGLClass),
1124       ARG(1, GLuint),
1125       GL_ACTIVE_ATTRIBUTE_MAX_LENGTH,
1126       glGetActiveAttrib);
1127 }
1128 
1129 NATIVE_METHOD(getActiveUniform) {
1130   CTX();
1131   return exglGetActiveInfo(
1132       ctx,
1133       runtime,
1134       ARG(0, EXWebGLClass),
1135       ARG(1, GLuint),
1136       GL_ACTIVE_UNIFORM_MAX_LENGTH,
1137       glGetActiveUniform);
1138 }
1139 
1140 NATIVE_METHOD(getAttribLocation) {
1141   CTX();
1142   auto program = ARG(0, EXWebGLClass);
1143   auto name = ARG(1, std::string);
1144   GLint location;
1145   ctx->addBlockingToNextBatch(
1146       [&] { location = glGetAttribLocation(ctx->lookupObject(program), name.c_str()); });
1147   return jsi::Value(location);
1148 }
1149 
1150 UNIMPL_NATIVE_METHOD(getUniform)
1151 
1152 NATIVE_METHOD(getUniformLocation) {
1153   CTX();
1154   auto program = ARG(0, EXWebGLClass);
1155   auto name = ARG(1, std::string);
1156   GLint location;
1157   ctx->addBlockingToNextBatch(
1158       [&] { location = glGetUniformLocation(ctx->lookupObject(program), name.c_str()); });
1159   return location == -1
1160       ? jsi::Value::null()
1161       : createWebGLObject(runtime, EXWebGLClass::WebGLUniformLocation, {location});
1162 }
1163 
1164 UNIMPL_NATIVE_METHOD(getVertexAttrib)
1165 
1166 UNIMPL_NATIVE_METHOD(getVertexAttribOffset)
1167 
1168 NATIVE_METHOD(uniform1f) {
1169   CTX();
1170   auto uniform = ARG(0, EXWebGLClass);
1171   auto x = ARG(1, GLfloat);
1172   ctx->addToNextBatch([uniform, x]() { glUniform1f(uniform, x); });
1173   return nullptr;
1174 }
1175 
1176 NATIVE_METHOD(uniform2f) {
1177   CTX();
1178   auto uniform = ARG(0, EXWebGLClass);
1179   auto x = ARG(1, GLfloat);
1180   auto y = ARG(2, GLfloat);
1181   ctx->addToNextBatch([uniform, x, y]() { glUniform2f(uniform, x, y); });
1182   return nullptr;
1183 }
1184 
1185 NATIVE_METHOD(uniform3f) {
1186   CTX();
1187   auto uniform = ARG(0, EXWebGLClass);
1188   auto x = ARG(1, GLfloat);
1189   auto y = ARG(2, GLfloat);
1190   auto z = ARG(3, GLfloat);
1191   ctx->addToNextBatch([uniform, x, y, z]() { glUniform3f(uniform, x, y, z); });
1192   return nullptr;
1193 }
1194 
1195 NATIVE_METHOD(uniform4f) {
1196   CTX();
1197   auto uniform = ARG(0, EXWebGLClass);
1198   auto x = ARG(1, GLfloat);
1199   auto y = ARG(2, GLfloat);
1200   auto z = ARG(3, GLfloat);
1201   auto w = ARG(4, GLfloat);
1202   ctx->addToNextBatch([uniform, x, y, z, w]() { glUniform4f(uniform, x, y, z, w); });
1203   return nullptr;
1204 }
1205 
1206 NATIVE_METHOD(uniform1i) {
1207   CTX();
1208   auto uniform = ARG(0, EXWebGLClass);
1209   auto x = ARG(1, GLint);
1210   ctx->addToNextBatch([uniform, x]() { glUniform1i(uniform, x); });
1211   return nullptr;
1212 }
1213 
1214 NATIVE_METHOD(uniform2i) {
1215   CTX();
1216   auto uniform = ARG(0, EXWebGLClass);
1217   auto x = ARG(1, GLint);
1218   auto y = ARG(2, GLint);
1219   ctx->addToNextBatch([uniform, x, y]() { glUniform2i(uniform, x, y); });
1220   return nullptr;
1221 }
1222 
1223 NATIVE_METHOD(uniform3i) {
1224   CTX();
1225   auto uniform = ARG(0, EXWebGLClass);
1226   auto x = ARG(1, GLint);
1227   auto y = ARG(2, GLint);
1228   auto z = ARG(3, GLint);
1229   ctx->addToNextBatch([uniform, x, y, z]() { glUniform3i(uniform, x, y, z); });
1230   return nullptr;
1231 }
1232 
1233 NATIVE_METHOD(uniform4i) {
1234   CTX();
1235   auto uniform = ARG(0, EXWebGLClass);
1236   auto x = ARG(1, GLint);
1237   auto y = ARG(2, GLint);
1238   auto z = ARG(3, GLint);
1239   auto w = ARG(4, GLint);
1240   ctx->addToNextBatch([uniform, x, y, z, w]() { glUniform4i(uniform, x, y, z, w); });
1241   return nullptr;
1242 }
1243 
1244 NATIVE_METHOD(uniform1fv) {
1245   CTX();
1246   return exglUniformv(ctx, glUniform1fv, ARG(0, EXWebGLClass), 1, ARG(1, std::vector<float>));
1247 };
1248 
1249 NATIVE_METHOD(uniform2fv) {
1250   CTX();
1251   return exglUniformv(ctx, glUniform2fv, ARG(0, EXWebGLClass), 2, ARG(1, std::vector<float>));
1252 };
1253 
1254 NATIVE_METHOD(uniform3fv) {
1255   CTX();
1256   return exglUniformv(ctx, glUniform3fv, ARG(0, EXWebGLClass), 3, ARG(1, std::vector<float>));
1257 };
1258 
1259 NATIVE_METHOD(uniform4fv) {
1260   CTX();
1261   return exglUniformv(ctx, glUniform4fv, ARG(0, EXWebGLClass), 4, ARG(1, std::vector<float>));
1262 };
1263 
1264 NATIVE_METHOD(uniform1iv) {
1265   CTX();
1266   return exglUniformv(ctx, glUniform1iv, ARG(0, EXWebGLClass), 1, ARG(1, std::vector<int32_t>));
1267 };
1268 
1269 NATIVE_METHOD(uniform2iv) {
1270   CTX();
1271   return exglUniformv(ctx, glUniform2iv, ARG(0, EXWebGLClass), 2, ARG(1, std::vector<int32_t>));
1272 };
1273 
1274 NATIVE_METHOD(uniform3iv) {
1275   CTX();
1276   return exglUniformv(ctx, glUniform3iv, ARG(0, EXWebGLClass), 3, ARG(1, std::vector<int32_t>));
1277 };
1278 
1279 NATIVE_METHOD(uniform4iv) {
1280   CTX();
1281   return exglUniformv(ctx, glUniform4iv, ARG(0, EXWebGLClass), 4, ARG(1, std::vector<int32_t>));
1282 };
1283 
1284 NATIVE_METHOD(uniformMatrix2fv) {
1285   CTX();
1286   return exglUniformMatrixv(
1287       ctx,
1288       glUniformMatrix2fv,
1289       ARG(0, EXWebGLClass),
1290       ARG(1, GLboolean),
1291       4,
1292       ARG(2, std::vector<float>));
1293 }
1294 
1295 NATIVE_METHOD(uniformMatrix3fv) {
1296   CTX();
1297   return exglUniformMatrixv(
1298       ctx,
1299       glUniformMatrix3fv,
1300       ARG(0, EXWebGLClass),
1301       ARG(1, GLboolean),
1302       9,
1303       ARG(2, std::vector<float>));
1304 }
1305 
1306 NATIVE_METHOD(uniformMatrix4fv) {
1307   CTX();
1308   return exglUniformMatrixv(
1309       ctx,
1310       glUniformMatrix4fv,
1311       ARG(0, EXWebGLClass),
1312       ARG(1, GLboolean),
1313       16,
1314       ARG(2, std::vector<float>));
1315 }
1316 
1317 NATIVE_METHOD(vertexAttrib1fv) {
1318   CTX();
1319   return exglVertexAttribv(
1320       ctx, glVertexAttrib1fv, ARG(0, EXWebGLClass), ARG(1, std::vector<float>));
1321 }
1322 
1323 NATIVE_METHOD(vertexAttrib2fv) {
1324   CTX();
1325   return exglVertexAttribv(
1326       ctx, glVertexAttrib2fv, ARG(0, EXWebGLClass), ARG(1, std::vector<float>));
1327 }
1328 
1329 NATIVE_METHOD(vertexAttrib3fv) {
1330   CTX();
1331   return exglVertexAttribv(
1332       ctx, glVertexAttrib3fv, ARG(0, EXWebGLClass), ARG(1, std::vector<float>));
1333 }
1334 
1335 NATIVE_METHOD(vertexAttrib4fv) {
1336   CTX();
1337   return exglVertexAttribv(
1338       ctx, glVertexAttrib4fv, ARG(0, EXWebGLClass), ARG(1, std::vector<float>));
1339 }
1340 
1341 SIMPLE_NATIVE_METHOD(vertexAttrib1f, glVertexAttrib1f); // index, x
1342 SIMPLE_NATIVE_METHOD(vertexAttrib2f, glVertexAttrib2f); // index, x, y
1343 SIMPLE_NATIVE_METHOD(vertexAttrib3f, glVertexAttrib3f); // index, x, y, z
1344 SIMPLE_NATIVE_METHOD(vertexAttrib4f, glVertexAttrib4f); // index, x, y, z, w
1345 
1346 SIMPLE_NATIVE_METHOD(
1347     vertexAttribPointer,
1348     glVertexAttribPointer); // index, itemSize, type, normalized, stride, const void *
1349 
1350 // Uniforms and attributes (WebGL2)
1351 // --------------------------------
1352 
1353 NATIVE_METHOD(uniform1ui) {
1354   CTX();
1355   auto uniform = ARG(0, EXWebGLClass);
1356   auto x = ARG(1, GLuint);
1357   ctx->addToNextBatch([uniform, x]() { glUniform1ui(uniform, x); });
1358   return nullptr;
1359 }
1360 
1361 NATIVE_METHOD(uniform2ui) {
1362   CTX();
1363   auto uniform = ARG(0, EXWebGLClass);
1364   auto x = ARG(1, GLuint);
1365   auto y = ARG(2, GLuint);
1366   ctx->addToNextBatch([uniform, x, y]() { glUniform2ui(uniform, x, y); });
1367   return nullptr;
1368 }
1369 
1370 NATIVE_METHOD(uniform3ui) {
1371   CTX();
1372   auto uniform = ARG(0, EXWebGLClass);
1373   auto x = ARG(1, GLuint);
1374   auto y = ARG(2, GLuint);
1375   auto z = ARG(3, GLuint);
1376   ctx->addToNextBatch([uniform, x, y, z]() { glUniform3ui(uniform, x, y, z); });
1377   return nullptr;
1378 }
1379 
1380 NATIVE_METHOD(uniform4ui) {
1381   CTX();
1382   auto uniform = ARG(0, EXWebGLClass);
1383   auto x = ARG(1, GLuint);
1384   auto y = ARG(2, GLuint);
1385   auto z = ARG(3, GLuint);
1386   auto w = ARG(4, GLuint);
1387   ctx->addToNextBatch([uniform, x, y, z, w]() { glUniform4ui(uniform, x, y, z, w); });
1388   return nullptr;
1389 }
1390 
1391 NATIVE_METHOD(uniform1uiv) {
1392   CTX();
1393   return exglUniformv(ctx, glUniform1uiv, ARG(0, EXWebGLClass), 1, ARG(1, std::vector<uint32_t>));
1394 };
1395 
1396 NATIVE_METHOD(uniform2uiv) {
1397   CTX();
1398   return exglUniformv(ctx, glUniform2uiv, ARG(0, EXWebGLClass), 2, ARG(1, std::vector<uint32_t>));
1399 };
1400 
1401 NATIVE_METHOD(uniform3uiv) {
1402   CTX();
1403   return exglUniformv(ctx, glUniform3uiv, ARG(0, EXWebGLClass), 3, ARG(1, std::vector<uint32_t>));
1404 };
1405 
1406 NATIVE_METHOD(uniform4uiv) {
1407   CTX();
1408   return exglUniformv(ctx, glUniform4uiv, ARG(0, EXWebGLClass), 4, ARG(1, std::vector<uint32_t>));
1409 };
1410 
1411 NATIVE_METHOD(uniformMatrix3x2fv) {
1412   CTX();
1413   return exglUniformMatrixv(
1414       ctx,
1415       glUniformMatrix3x2fv,
1416       ARG(0, EXWebGLClass),
1417       ARG(1, GLboolean),
1418       6,
1419       ARG(2, std::vector<float>));
1420 }
1421 
1422 NATIVE_METHOD(uniformMatrix4x2fv) {
1423   CTX();
1424   return exglUniformMatrixv(
1425       ctx,
1426       glUniformMatrix4x2fv,
1427       ARG(0, EXWebGLClass),
1428       ARG(1, GLboolean),
1429       8,
1430       ARG(2, std::vector<float>));
1431 }
1432 
1433 NATIVE_METHOD(uniformMatrix2x3fv) {
1434   CTX();
1435   return exglUniformMatrixv(
1436       ctx,
1437       glUniformMatrix2x3fv,
1438       ARG(0, EXWebGLClass),
1439       ARG(1, GLboolean),
1440       6,
1441       ARG(2, std::vector<float>));
1442 }
1443 
1444 NATIVE_METHOD(uniformMatrix4x3fv) {
1445   CTX();
1446   return exglUniformMatrixv(
1447       ctx,
1448       glUniformMatrix4x3fv,
1449       ARG(0, EXWebGLClass),
1450       ARG(1, GLboolean),
1451       12,
1452       ARG(2, std::vector<float>));
1453 }
1454 
1455 NATIVE_METHOD(uniformMatrix2x4fv) {
1456   CTX();
1457   return exglUniformMatrixv(
1458       ctx,
1459       glUniformMatrix2x4fv,
1460       ARG(0, EXWebGLClass),
1461       ARG(1, GLboolean),
1462       8,
1463       ARG(2, std::vector<float>));
1464 }
1465 
1466 NATIVE_METHOD(uniformMatrix3x4fv) {
1467   CTX();
1468   return exglUniformMatrixv(
1469       ctx,
1470       glUniformMatrix3x4fv,
1471       ARG(0, EXWebGLClass),
1472       ARG(1, GLboolean),
1473       12,
1474       ARG(2, std::vector<float>));
1475 }
1476 
1477 SIMPLE_NATIVE_METHOD(vertexAttribI4i, glVertexAttribI4i); // index, x, y, z, w
1478 SIMPLE_NATIVE_METHOD(vertexAttribI4ui, glVertexAttribI4ui); // index, x, y, z, w
1479 
1480 NATIVE_METHOD(vertexAttribI4iv) {
1481   CTX();
1482   return exglVertexAttribv(ctx, glVertexAttribI4iv, ARG(0, GLuint), ARG(1, std::vector<int32_t>));
1483 }
1484 
1485 NATIVE_METHOD(vertexAttribI4uiv) {
1486   CTX();
1487   return exglVertexAttribv(ctx, glVertexAttribI4uiv, ARG(0, GLuint), ARG(1, std::vector<uint32_t>));
1488 }
1489 
1490 SIMPLE_NATIVE_METHOD(
1491     vertexAttribIPointer,
1492     glVertexAttribIPointer); // index, size, type, stride, offset
1493 
1494 // Drawing buffers
1495 // ---------------
1496 
1497 SIMPLE_NATIVE_METHOD(clear, glClear); // mask
1498 
1499 SIMPLE_NATIVE_METHOD(drawArrays, glDrawArrays); // mode, first, count)
1500 
1501 SIMPLE_NATIVE_METHOD(drawElements, glDrawElements); // mode, count, type, offset
1502 
1503 SIMPLE_NATIVE_METHOD(finish, glFinish);
1504 
1505 SIMPLE_NATIVE_METHOD(flush, glFlush);
1506 
1507 // Drawing buffers (WebGL2)
1508 // ------------------------
1509 
1510 SIMPLE_NATIVE_METHOD(vertexAttribDivisor, glVertexAttribDivisor); // index, divisor
1511 
1512 SIMPLE_NATIVE_METHOD(
1513     drawArraysInstanced,
1514     glDrawArraysInstanced); // mode, first, count, instancecount
1515 
1516 SIMPLE_NATIVE_METHOD(
1517     drawElementsInstanced,
1518     glDrawElementsInstanced); // mode, count, type, offset, instanceCount
1519 
1520 SIMPLE_NATIVE_METHOD(
1521     drawRangeElements,
1522     glDrawRangeElements); // mode, start, end, count, type, offset
1523 
1524 NATIVE_METHOD(drawBuffers) {
1525   CTX();
1526   auto data = jsArrayToVector<GLenum>(runtime, ARG(0, jsi::Array));
1527   ctx->addToNextBatch(
1528       [data{std::move(data)}] { glDrawBuffers(static_cast<GLsizei>(data.size()), data.data()); });
1529   return nullptr;
1530 }
1531 
1532 NATIVE_METHOD(clearBufferfv) {
1533   CTX();
1534   auto buffer = ARG(0, GLenum);
1535   auto drawbuffer = ARG(1, GLint);
1536   auto values = ARG(2, TypedArrayKind::Float32Array).toVector(runtime);
1537   ctx->addToNextBatch(
1538       [=, values{std::move(values)}] { glClearBufferfv(buffer, drawbuffer, values.data()); });
1539   return nullptr;
1540 }
1541 
1542 NATIVE_METHOD(clearBufferiv) {
1543   CTX();
1544   auto buffer = ARG(0, GLenum);
1545   auto drawbuffer = ARG(1, GLint);
1546   auto values = ARG(2, TypedArrayKind::Int32Array).toVector(runtime);
1547   ctx->addToNextBatch(
1548       [=, values{std::move(values)}] { glClearBufferiv(buffer, drawbuffer, values.data()); });
1549   return nullptr;
1550 }
1551 
1552 NATIVE_METHOD(clearBufferuiv) {
1553   CTX();
1554   auto buffer = ARG(0, GLenum);
1555   auto drawbuffer = ARG(1, GLint);
1556   auto values = ARG(2, TypedArrayKind::Uint32Array).toVector(runtime);
1557   ctx->addToNextBatch(
1558       [=, values{std::move(values)}] { glClearBufferuiv(buffer, drawbuffer, values.data()); });
1559   return nullptr;
1560 }
1561 
1562 SIMPLE_NATIVE_METHOD(clearBufferfi, glClearBufferfi); // buffer, drawbuffer, depth, stencil
1563 
1564 // Query objects (WebGL2)
1565 // ----------------------
1566 
1567 NATIVE_METHOD(createQuery) {
1568   CTX();
1569   return exglGenObject(ctx, runtime, glGenQueries, EXWebGLClass::WebGLQuery);
1570 }
1571 
1572 NATIVE_METHOD(deleteQuery) {
1573   CTX();
1574   return exglDeleteObject(ctx, ARG(0, EXWebGLClass), glDeleteQueries);
1575 }
1576 
1577 NATIVE_METHOD(isQuery) {
1578   CTX();
1579   return exglIsObject(ctx, ARG(0, EXWebGLClass), glIsQuery);
1580 }
1581 
1582 NATIVE_METHOD(beginQuery) {
1583   CTX();
1584   auto target = ARG(0, GLenum);
1585   auto query = ARG(1, EXWebGLClass);
1586   ctx->addToNextBatch([=] { glBeginQuery(target, ctx->lookupObject(query)); });
1587   return nullptr;
1588 }
1589 
1590 SIMPLE_NATIVE_METHOD(endQuery, glEndQuery); // target
1591 
1592 NATIVE_METHOD(getQuery) {
1593   CTX();
1594   auto target = ARG(0, GLenum);
1595   auto pname = ARG(1, GLenum);
1596   GLint params;
1597   ctx->addBlockingToNextBatch([&] { glGetQueryiv(target, pname, &params); });
1598   return params == 0
1599       ? jsi::Value::null()
1600       : createWebGLObject(runtime, EXWebGLClass::WebGLQuery, {static_cast<double>(params)});
1601 }
1602 
1603 NATIVE_METHOD(getQueryParameter) {
1604   CTX();
1605   auto query = ARG(0, EXWebGLClass);
1606   auto pname = ARG(1, GLenum);
1607   GLuint params;
1608   ctx->addBlockingToNextBatch(
1609       [&] { glGetQueryObjectuiv(ctx->lookupObject(query), pname, &params); });
1610   return params == 0 ? jsi::Value::null() : static_cast<double>(params);
1611 }
1612 
1613 // Samplers (WebGL2)
1614 // -----------------
1615 
1616 NATIVE_METHOD(createSampler) {
1617   CTX();
1618   return exglGenObject(ctx, runtime, glGenSamplers, EXWebGLClass::WebGLSampler);
1619 }
1620 
1621 NATIVE_METHOD(deleteSampler) {
1622   CTX();
1623   return exglDeleteObject(ctx, ARG(0, EXWebGLClass), glDeleteSamplers);
1624 }
1625 
1626 NATIVE_METHOD(bindSampler) {
1627   CTX();
1628   auto unit = ARG(0, GLuint);
1629   auto sampler = ARG(1, EXWebGLClass);
1630   ctx->addToNextBatch([=] { glBindSampler(unit, ctx->lookupObject(sampler)); });
1631   return nullptr;
1632 }
1633 
1634 NATIVE_METHOD(isSampler) {
1635   CTX();
1636   return exglIsObject(ctx, ARG(0, EXWebGLClass), glIsSampler);
1637 }
1638 
1639 NATIVE_METHOD(samplerParameteri) {
1640   CTX();
1641   auto sampler = ARG(0, EXWebGLClass);
1642   auto pname = ARG(1, GLenum);
1643   auto param = ARG(2, GLfloat);
1644   ctx->addToNextBatch([=] { glSamplerParameteri(ctx->lookupObject(sampler), pname, param); });
1645   return nullptr;
1646 }
1647 
1648 NATIVE_METHOD(samplerParameterf) {
1649   CTX();
1650   auto sampler = ARG(0, EXWebGLClass);
1651   auto pname = ARG(1, GLenum);
1652   auto param = ARG(2, GLfloat);
1653   ctx->addToNextBatch([=] { glSamplerParameterf(ctx->lookupObject(sampler), pname, param); });
1654   return nullptr;
1655 }
1656 
1657 NATIVE_METHOD(getSamplerParameter) {
1658   CTX();
1659   auto sampler = ARG(0, EXWebGLClass);
1660   auto pname = ARG(1, GLenum);
1661   bool isFloatParam = pname == GL_TEXTURE_MAX_LOD || pname == GL_TEXTURE_MIN_LOD;
1662   union {
1663     GLfloat f;
1664     GLint i;
1665   } param;
1666 
1667   ctx->addBlockingToNextBatch([&] {
1668     if (isFloatParam) {
1669       glGetSamplerParameterfv(ctx->lookupObject(sampler), pname, &param.f);
1670     } else {
1671       glGetSamplerParameteriv(ctx->lookupObject(sampler), pname, &param.i);
1672     }
1673   });
1674   return isFloatParam ? static_cast<double>(param.f) : static_cast<double>(param.i);
1675 }
1676 
1677 // Sync objects (WebGL2)
1678 // ---------------------
1679 
1680 UNIMPL_NATIVE_METHOD(fenceSync)
1681 
1682 UNIMPL_NATIVE_METHOD(isSync)
1683 
1684 UNIMPL_NATIVE_METHOD(deleteSync)
1685 
1686 UNIMPL_NATIVE_METHOD(clientWaitSync)
1687 
1688 UNIMPL_NATIVE_METHOD(waitSync)
1689 
1690 UNIMPL_NATIVE_METHOD(getSyncParameter)
1691 
1692 // Transform feedback (WebGL2)
1693 // ---------------------------
1694 
1695 NATIVE_METHOD(createTransformFeedback) {
1696   CTX();
1697   return exglGenObject(ctx, runtime, glGenTransformFeedbacks, EXWebGLClass::WebGLTransformFeedback);
1698 }
1699 
1700 NATIVE_METHOD(deleteTransformFeedback) {
1701   CTX();
1702   return exglDeleteObject(ctx, ARG(0, EXWebGLClass), glDeleteTransformFeedbacks);
1703 }
1704 
1705 NATIVE_METHOD(isTransformFeedback) {
1706   CTX();
1707   return exglIsObject(ctx, ARG(0, EXWebGLClass), glIsTransformFeedback);
1708 }
1709 
1710 NATIVE_METHOD(bindTransformFeedback) {
1711   CTX();
1712   auto target = ARG(0, GLenum);
1713   auto transformFeedback = ARG(1, EXWebGLClass);
1714   ctx->addToNextBatch(
1715       [=] { glBindTransformFeedback(target, ctx->lookupObject(transformFeedback)); });
1716   return nullptr;
1717 }
1718 
1719 SIMPLE_NATIVE_METHOD(beginTransformFeedback, glBeginTransformFeedback); // primitiveMode
1720 
1721 SIMPLE_NATIVE_METHOD(endTransformFeedback, glEndTransformFeedback);
1722 
1723 NATIVE_METHOD(transformFeedbackVaryings) {
1724   CTX();
1725   auto program = ARG(0, EXWebGLClass);
1726   std::vector<std::string> varyings = jsArrayToVector<std::string>(runtime, ARG(1, jsi::Array));
1727   auto bufferMode = ARG(2, GLenum);
1728 
1729   ctx->addToNextBatch([=, varyings{std::move(varyings)}] {
1730     std::vector<const char *> varyingsRaw(varyings.size());
1731     std::transform(
1732         varyings.begin(), varyings.end(), varyingsRaw.begin(), [](const std::string &str) {
1733           return str.c_str();
1734         });
1735 
1736     glTransformFeedbackVaryings(
1737         ctx->lookupObject(program),
1738         static_cast<GLsizei>(varyingsRaw.size()),
1739         varyingsRaw.data(),
1740         bufferMode);
1741   });
1742   return nullptr;
1743 }
1744 
1745 NATIVE_METHOD(getTransformFeedbackVarying) {
1746   CTX();
1747   return exglGetActiveInfo(
1748       ctx,
1749       runtime,
1750       ARG(0, EXWebGLClass),
1751       ARG(1, GLuint),
1752       GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH,
1753       glGetTransformFeedbackVarying);
1754 }
1755 
1756 SIMPLE_NATIVE_METHOD(pauseTransformFeedback, glPauseTransformFeedback);
1757 
1758 SIMPLE_NATIVE_METHOD(resumeTransformFeedback, glResumeTransformFeedback);
1759 
1760 // Uniform buffer objects (WebGL2)
1761 // -------------------------------
1762 
1763 NATIVE_METHOD(bindBufferBase) {
1764   CTX();
1765   auto target = ARG(0, GLenum);
1766   auto index = ARG(1, GLuint);
1767   auto buffer = ARG(2, EXWebGLClass);
1768   ctx->addToNextBatch([=] { glBindBufferBase(target, index, ctx->lookupObject(buffer)); });
1769   return nullptr;
1770 }
1771 
1772 NATIVE_METHOD(bindBufferRange) {
1773   CTX();
1774   auto target = ARG(0, GLenum);
1775   auto index = ARG(1, GLuint);
1776   auto buffer = ARG(2, EXWebGLClass);
1777   auto offset = ARG(3, GLint);
1778   auto size = ARG(4, GLsizei);
1779   ctx->addToNextBatch(
1780       [=] { glBindBufferRange(target, index, ctx->lookupObject(buffer), offset, size); });
1781   return nullptr;
1782 }
1783 
1784 NATIVE_METHOD(getUniformIndices) {
1785   CTX();
1786   auto program = ARG(0, EXWebGLClass);
1787   std::vector<std::string> uniformNames = jsArrayToVector<std::string>(runtime, ARG(1, jsi::Array));
1788 
1789   std::vector<const char *> uniformNamesRaw(uniformNames.size());
1790   std::transform(
1791       uniformNames.begin(),
1792       uniformNames.end(),
1793       uniformNamesRaw.begin(),
1794       [](const std::string &str) { return str.c_str(); });
1795 
1796   std::vector<GLuint> indices(uniformNames.size());
1797   ctx->addBlockingToNextBatch([&] {
1798     glGetUniformIndices(
1799         ctx->lookupObject(program),
1800         static_cast<GLsizei>(uniformNames.size()),
1801         uniformNamesRaw.data(),
1802         &indices[0]);
1803   });
1804   jsi::Array jsResult(runtime, indices.size());
1805   for (unsigned int i = 0; i < indices.size(); i++) {
1806     jsResult.setValueAtIndex(runtime, i, static_cast<double>(indices[i]));
1807   }
1808   return jsResult;
1809 }
1810 
1811 NATIVE_METHOD(getActiveUniforms) {
1812   CTX();
1813   auto program = ARG(0, EXWebGLClass);
1814   auto uniformIndices = jsArrayToVector<GLuint>(runtime, ARG(1, jsi::Array));
1815   auto pname = ARG(2, GLenum);
1816   std::vector<GLint> params(uniformIndices.size());
1817 
1818   ctx->addBlockingToNextBatch([&] {
1819     glGetActiveUniformsiv(
1820         ctx->lookupObject(program),
1821         static_cast<GLsizei>(uniformIndices.size()),
1822         uniformIndices.data(),
1823         pname,
1824         &params[0]);
1825   });
1826   jsi::Array jsResult(runtime, params.size());
1827   for (unsigned int i = 0; i < params.size(); i++) {
1828     jsResult.setValueAtIndex(
1829         runtime,
1830         i,
1831         pname == GL_UNIFORM_IS_ROW_MAJOR ? params[i] != 0 : static_cast<double>(params[i]));
1832   }
1833   return jsResult;
1834 }
1835 
1836 NATIVE_METHOD(getUniformBlockIndex) {
1837   CTX();
1838   auto program = ARG(0, EXWebGLClass);
1839   auto uniformBlockName = ARG(1, std::string);
1840 
1841   GLuint blockIndex;
1842   ctx->addBlockingToNextBatch([&] {
1843     blockIndex = glGetUniformBlockIndex(ctx->lookupObject(program), uniformBlockName.c_str());
1844   });
1845   return static_cast<double>(blockIndex);
1846 }
1847 
1848 UNIMPL_NATIVE_METHOD(getActiveUniformBlockParameter)
1849 
1850 NATIVE_METHOD(getActiveUniformBlockName) {
1851   CTX();
1852   auto fProgram = ARG(0, EXWebGLClass);
1853   auto uniformBlockIndex = ARG(1, GLuint);
1854 
1855   std::string blockName;
1856   ctx->addBlockingToNextBatch([&] {
1857     GLuint program = ctx->lookupObject(fProgram);
1858     GLint bufSize;
1859     glGetActiveUniformBlockiv(program, uniformBlockIndex, GL_UNIFORM_BLOCK_NAME_LENGTH, &bufSize);
1860     blockName.resize(bufSize > 0 ? bufSize - 1 : 0);
1861     glGetActiveUniformBlockName(program, uniformBlockIndex, bufSize, NULL, &blockName[0]);
1862   });
1863   return jsi::String::createFromUtf8(runtime, blockName);
1864 }
1865 
1866 NATIVE_METHOD(uniformBlockBinding) {
1867   CTX();
1868   auto program = ARG(0, EXWebGLClass);
1869   auto uniformBlockIndex = ARG(1, GLuint);
1870   auto uniformBlockBinding = ARG(2, GLuint);
1871   ctx->addToNextBatch([=] {
1872     glUniformBlockBinding(ctx->lookupObject(program), uniformBlockIndex, uniformBlockBinding);
1873   });
1874   return nullptr;
1875 }
1876 
1877 // Vertex Array Object (WebGL2)
1878 // ----------------------------
1879 
1880 NATIVE_METHOD(createVertexArray) {
1881   CTX();
1882   return exglGenObject(ctx, runtime, glGenVertexArrays, EXWebGLClass::WebGLVertexArrayObject);
1883 }
1884 
1885 NATIVE_METHOD(deleteVertexArray) {
1886   CTX();
1887   return exglDeleteObject(ctx, ARG(0, EXWebGLClass), glDeleteVertexArrays);
1888 }
1889 
1890 NATIVE_METHOD(isVertexArray) {
1891   CTX();
1892   return exglIsObject(ctx, ARG(0, EXWebGLClass), glIsVertexArray);
1893 }
1894 
1895 NATIVE_METHOD(bindVertexArray) {
1896   CTX();
1897   auto vertexArray = ARG(0, EXWebGLClass);
1898   ctx->addToNextBatch([=] { glBindVertexArray(ctx->lookupObject(vertexArray)); });
1899   return nullptr;
1900 }
1901 
1902 // Extensions
1903 // ----------
1904 
1905 // It may return some extensions that are not specified by WebGL specification nor drafts.
1906 NATIVE_METHOD(getSupportedExtensions) {
1907   CTX();
1908   // Set with supported extensions is cached to make checks in `getExtension` faster.
1909   ctx->maybeReadAndCacheSupportedExtensions();
1910 
1911   jsi::Array extensions(runtime, ctx->supportedExtensions.size());
1912   int i = 0;
1913   for (auto const &extensionName : ctx->supportedExtensions) {
1914     extensions.setValueAtIndex(runtime, i++, jsi::String::createFromUtf8(runtime, extensionName));
1915   }
1916   return extensions;
1917 }
1918 
1919 #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
1920 #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
1921 
1922 NATIVE_METHOD(getExtension) {
1923   CTX();
1924   auto name = ARG(0, std::string);
1925 
1926   // There is no `getExtension` equivalent in OpenGL ES so return `null`
1927   // if requested extension is not returned by `getSupportedExtensions`.
1928   ctx->maybeReadAndCacheSupportedExtensions();
1929   if (ctx->supportedExtensions.find(name) == ctx->supportedExtensions.end()) {
1930     return nullptr;
1931   }
1932 
1933   if (name == "EXT_texture_filter_anisotropic") {
1934     jsi::Object result(runtime);
1935     result.setProperty(
1936         runtime, "TEXTURE_MAX_ANISOTROPY_EXT", jsi::Value(GL_TEXTURE_MAX_ANISOTROPY_EXT));
1937     result.setProperty(
1938         runtime, "MAX_TEXTURE_MAX_ANISOTROPY_EXT", jsi::Value(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT));
1939     return result;
1940   }
1941   return jsi::Object(runtime);
1942 }
1943 
1944 // Exponent extensions
1945 // -------------------
1946 
1947 NATIVE_METHOD(endFrameEXP) {
1948   CTX();
1949   ctx->addToNextBatch([=] { ctx->needsRedraw = true; });
1950   ctx->endNextBatch();
1951   ctx->flushOnGLThread();
1952   return nullptr;
1953 }
1954 
1955 NATIVE_METHOD(flushEXP) {
1956   CTX();
1957   // nothing, it's just a helper so that we can measure how much time some operations take
1958   ctx->addBlockingToNextBatch([&] {});
1959   return nullptr;
1960 }
1961 
1962 } // namespace method
1963 } // namespace gl_cpp
1964 } // namespace expo
1965