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