#include "EXGL.h" #ifdef __ANDROID__ #include #include #include #endif #ifdef __APPLE__ #include #include #endif #include #include #include #include #include #include #include #include #include "EXJSUtils.h" #include "EXJSConvertTypedArray.h" #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #ifdef __APPLE__ #include "EXiOSUtils.h" #endif // Debugging utilities #define EXGL_DEBUG // Whether debugging is on #ifdef EXGL_DEBUG #ifdef __ANDROID__ #define EXGLSysLog(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, "EXGL", fmt, ##__VA_ARGS__) #endif #ifdef __APPLE__ #define EXGLSysLog(fmt, ...) EXiOSLog("EXGL: " fmt, ##__VA_ARGS__) #endif #else #define EXGLSysLog(...) #endif // Forward declarations class EXGLContext; static EXGLContext *EXGLContextGet(EXGLContextId exglCtxId); // --- EXGLContext ------------------------------------------------------------- // Class of the C++ object representing an EXGL rendering context. class EXGLContext { private: // --- Queue handling -------------------------------------------------------- // There are two threads: the input thread (henceforth "JS thread") feeds new GL // work, the output thread (henceforth "GL thread", typically UI thread on iOS, // GL thread on Android) reads GL work and performs it // By not saving the JS{Global,}Context as a member variable we ensure that no // JS work is done on the GL thread // The smallest unit of work using Op = std::function; // Ops are combined into batches: // 1. A batch is always executed entirely in one go on the GL thread // 2. The last add to a batch always precedes the first remove // #2 means that it's good to use an std::vector<...> for this using Batch = std::vector; Batch nextBatch; std::vector backlog; std::mutex backlogMutex; // [JS thread] Add an Op to the 'next' batch -- the arguments are any form of // constructor arguments for Op template inline void addToNextBatch(Args &&...args) noexcept { nextBatch.emplace_back(std::forward(args)...); } // [JS thread] Send the current 'next' batch to GL and make a new 'next' batch void endNextBatch() noexcept { std::lock_guard lock(backlogMutex); backlog.emplace_back(); backlog.back().reserve(nextBatch.size()); backlog.back().swap(nextBatch); } // [JS thread] Add a blocking operation to the 'next' batch -- waits for the // queued function to run before returning template inline void addBlockingToNextBatch(F &&f) noexcept { #ifdef __ANDROID__ // std::packaged_task + std::future segfaults on Android... :| std::mutex mutex; std::condition_variable cv; auto done = false; addToNextBatch([&] { f(); { std::lock_guard lock(mutex); done = true; } cv.notify_all(); }); { std::unique_lock lock(mutex); endNextBatch(); cv.wait(lock, [&] { return done; }); } #else std::packaged_task task(std::move(f)); auto future = task.get_future(); addToNextBatch([&] { task(); }); endNextBatch(); future.wait(); #endif } // A 'Future' is an opaque return value from a GL method call that is simply // fed to other GL method calls. The value is never inspected in JS. This // allows us to continue queueing method calls when a method call with a // Future return value is encountered: its value won't immediately matter // and is only needed when method calls after it ask for the value, and those // are queued for even later. This works for GL object id return values such // as those from `createShader()`, `createProgram()`, etc. using Future = unsigned int; std::unordered_map futures; Future nextFuture = 1; // Start at 1 so that Futures are truthy in JS // [JS thread] Enqueue a function and return a Future that will have its // result when the function is called template inline JSValueRef addFutureToNextBatch(JSContextRef jsCtx, F &&f) noexcept { auto future = nextFuture++; addToNextBatch([=] { assert(futures.find(future) == futures.end()); futures[future] = f(); }); return JSValueMakeNumber(jsCtx, future); } // [GL thread] Get the value in a Future inline GLuint peekFuture(Future future) const { auto iter = futures.find(future); if (iter == futures.end()) { return 0; } return iter->second; } // [GL thread] Do all the remaining work we can do on the GL thread public: void flush(void) noexcept { // Keep a copy and clear backlog to minimize lock time decltype(backlog) copy; { std::lock_guard lock(backlogMutex); backlog.swap(copy); } for (const auto &batch : copy) { for (const auto &op : batch) { op(); } } } // --- Init/destroy and JS object binding ------------------------------------ private: JSObjectRef jsGl; public: EXGLContext(JSGlobalContextRef jsCtx, EXGLContextId exglCtxId) { // Prepare for TypedArray usage prepareTypedArrayAPI(jsCtx); // Create JS version of us auto jsClass = JSClassCreate(&kJSClassDefinitionEmpty); jsGl = JSObjectMake(jsCtx, jsClass, (void *) (intptr_t) exglCtxId); JSClassRelease(jsClass); installMethods(jsCtx); installConstants(jsCtx); // Clear everything to initial values addToNextBatch([this] { glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer); glClearColor(0, 0, 0, 0); glClearDepthf(1); glClearStencil(0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); }); } JSObjectRef getJSObject(void) const noexcept { return jsGl; } // --- GL state -------------------------------------------------------------- private: GLint defaultFramebuffer = 0; bool unpackFLipY = false; public: void setDefaultFramebuffer(GLint framebuffer) { defaultFramebuffer = framebuffer; } // --- Actual GL bindings ---------------------------------------------------- private: // Constants in WebGL that aren't in OpenGL ES // https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Constants #define GL_UNPACK_FLIP_Y_WEBGL 0x9240 #define GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL 0x9241 #define GL_CONTEXT_LOST_WEBGL 0x9242 #define GL_UNPACK_COLORSPACE_CONVERSION_WEBGL 0x9243 #define GL_BROWSER_DEFAULT_WEBGL 0x9244 #define GL_STENCIL_INDEX 0x1901 #define GL_DEPTH_STENCIL 0x84F9 #define GL_DEPTH_STENCIL_ATTACHMENT 0x821A // Utilities static inline void jsThrow(JSContextRef jsCtx, const char *msg, JSValueRef *jsException) { *jsException = JSValueToObject(jsCtx, EXJSValueMakeStringFromUTF8CString(jsCtx, msg), nullptr); } static inline std::shared_ptr jsValueToSharedStr(JSContextRef jsCtx, JSValueRef jsVal) noexcept { return std::shared_ptr(EXJSValueToUTF8CStringMalloc(jsCtx, jsVal, nullptr), free); } static inline void *bufferOffset(GLint offset) noexcept { return (char *) 0 + offset; } static inline GLuint bytesPerPixel(GLenum type, GLenum format) { int bytesPerComponent = 0; switch (type) { case GL_UNSIGNED_BYTE: bytesPerComponent = 1; break; case GL_FLOAT: bytesPerComponent = 4; break; case GL_HALF_FLOAT_OES: bytesPerComponent = 2; break; case GL_UNSIGNED_SHORT_5_6_5: case GL_UNSIGNED_SHORT_4_4_4_4: case GL_UNSIGNED_SHORT_5_5_5_1: return 2; } switch (format) { case GL_LUMINANCE: case GL_ALPHA: return 1 * bytesPerComponent; case GL_LUMINANCE_ALPHA: return 2 * bytesPerComponent; case GL_RGB: return 3 * bytesPerComponent; case GL_RGBA: return 4 * bytesPerComponent; } return 0; } static inline void flipPixels(GLubyte *pixels, size_t bytesPerRow, size_t rows) { if (!pixels) { return; } GLuint middle = rows / 2; GLuint intsPerRow = bytesPerRow / sizeof(GLuint); GLuint remainingBytes = bytesPerRow - intsPerRow * sizeof(GLuint); for (GLuint rowTop = 0, rowBottom = rows - 1; rowTop < middle; ++rowTop, --rowBottom) { // Swap in packs of sizeof(GLuint) bytes GLuint *iTop = (GLuint *) (pixels + rowTop * bytesPerRow); GLuint *iBottom = (GLuint *) (pixels + rowBottom * bytesPerRow); GLuint iTmp; GLuint n = intsPerRow; do { iTmp = *iTop; *iTop++ = *iBottom; *iBottom++ = iTmp; } while(--n > 0); // Swap remainder bytes GLubyte *bTop = (GLubyte *) iTop; GLubyte *bBottom = (GLubyte *) iBottom; GLubyte bTmp; switch (remainingBytes) { case 3: bTmp = *bTop; *bTop++ = *bBottom; *bBottom++ = bTmp; case 2: bTmp = *bTop; *bTop++ = *bBottom; *bBottom++ = bTmp; case 1: bTmp = *bTop; *bTop = *bBottom; *bBottom = bTmp; } } } // TypedArray API wrapping bool usingTypedArrayHack = false; inline void prepareTypedArrayAPI(JSContextRef jsCtx) { #ifdef __APPLE__ // iOS >= 10 has built-in C TypedArray API if (EXiOSGetOperatingSystemVersion().majorVersion >= 10) { return; } #endif JSContextPrepareTypedArrayAPI(jsCtx); usingTypedArrayHack = true; } inline std::shared_ptr jsValueToSharedArray(JSContextRef jsCtx, JSValueRef jsVal, size_t *pByteLength) noexcept { if (usingTypedArrayHack) { return std::shared_ptr(JSObjectGetTypedArrayDataMalloc(jsCtx, (JSObjectRef) jsVal, pByteLength), free); } else { JSObjectRef jsObject = (JSObjectRef) jsVal; size_t byteLength = JSObjectGetTypedArrayByteLength(jsCtx, jsObject, nullptr); if (pByteLength) { *pByteLength = byteLength; } void *data = JSObjectGetTypedArrayBytesPtr(jsCtx, jsObject, nullptr); if (!data) { return std::shared_ptr(nullptr); } // Copy data since it's unclear how long JavaScriptCore's buffer will live // TODO(nikki): See if we can just pin/unpin and not copy? void *dataMalloc = malloc(byteLength); memcpy(dataMalloc, data, byteLength); return std::shared_ptr(dataMalloc, free); } } static void jsTypedArrayFreeDeallocator(void *data, void *ctx) { free(data); } inline JSValueRef makeTypedArray(JSContextRef jsCtx, JSTypedArrayType arrayType, void *data, size_t byteLength) { if (data) { if (usingTypedArrayHack) { return JSObjectMakeTypedArrayWithData(jsCtx, arrayType, data, byteLength); } else { void *dataMalloc = malloc(byteLength); memcpy(dataMalloc, data, byteLength); return JSObjectMakeTypedArrayWithBytesNoCopy(jsCtx, arrayType, dataMalloc, byteLength, jsTypedArrayFreeDeallocator, nullptr, nullptr); } } else { if (usingTypedArrayHack) { return JSObjectMakeTypedArrayWithHack(jsCtx, arrayType, 0); } else { return JSObjectMakeTypedArray(jsCtx, arrayType, 0, nullptr); } } } // Standard method wrapper, run on JS thread, return a value #define _WRAP_METHOD(name, minArgc) \ static JSValueRef exglNativeStatic_##name(JSContextRef jsCtx, \ JSObjectRef jsFunction, \ JSObjectRef jsThis, \ size_t jsArgc, \ const JSValueRef jsArgv[], \ JSValueRef* jsException) \ { \ auto exglCtx = EXGLContextGet((EXGLContextId) (intptr_t) \ JSObjectGetPrivate(jsThis)); \ if (!exglCtx) { \ return nullptr; \ } \ try { \ if (jsArgc < minArgc) { \ throw std::runtime_error("EXGL: Too few arguments to " #name "()!"); \ } \ return exglCtx->exglNativeInstance_##name(jsCtx, jsFunction, jsThis, \ jsArgc, jsArgv, jsException); \ } catch (const std::exception &e) { \ exglCtx->jsThrow(jsCtx, e.what(), jsException); \ return nullptr; \ } \ } \ inline JSValueRef exglNativeInstance_##name(JSContextRef jsCtx, \ JSObjectRef jsFunction, \ JSObjectRef jsThis, \ size_t jsArgc, \ const JSValueRef jsArgv[], \ JSValueRef* jsException) // Wrapper raises an exception saying the function isn't implemented yet #define _WRAP_METHOD_UNIMPL(name) \ _WRAP_METHOD(name, 0) { \ throw std::runtime_error("EXGL: " #name "() isn't implemented yet!"); \ return nullptr; \ } // Wrapper that takes only scalar arguments and returns nothing #define _WRAP_METHOD_SIMPLE(name, glFunc, ...) \ _WRAP_METHOD(name, EXJS_ARGC(__VA_ARGS__)) { \ addToNextBatch(std::bind(glFunc, EXJS_MAP_EXT(0, _EXJS_COMMA, _WRAP_METHOD_SIMPLE_UNPACK, __VA_ARGS__))); \ return nullptr; \ } #define _WRAP_METHOD_SIMPLE_UNPACK(i, _) EXJSValueToNumberFast(jsCtx, jsArgv[i]) // This listing follows the order in // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext // The WebGL context // ----------------- _WRAP_METHOD(getContextAttributes, 0) { auto jsResult = JSObjectMake(jsCtx, nullptr, nullptr); EXJSObjectSetValueWithUTF8CStringName(jsCtx, jsResult, "alpha", JSValueMakeBoolean(jsCtx, true)); EXJSObjectSetValueWithUTF8CStringName(jsCtx, jsResult, "depth", JSValueMakeBoolean(jsCtx, true)); EXJSObjectSetValueWithUTF8CStringName(jsCtx, jsResult, "stencil", JSValueMakeBoolean(jsCtx, false)); EXJSObjectSetValueWithUTF8CStringName(jsCtx, jsResult, "antialias", JSValueMakeBoolean(jsCtx, false)); EXJSObjectSetValueWithUTF8CStringName(jsCtx, jsResult, "premultipliedAlpha", JSValueMakeBoolean(jsCtx, false)); return jsResult; } _WRAP_METHOD(isContextLost, 0) { return JSValueMakeBoolean(jsCtx, false); } // Viewing and clipping // -------------------- _WRAP_METHOD_SIMPLE(scissor, glScissor, x, y, width, height) _WRAP_METHOD_SIMPLE(viewport, glViewport, x, y, width, height) // State information // ----------------- _WRAP_METHOD_SIMPLE(activeTexture, glActiveTexture, texture) _WRAP_METHOD_SIMPLE(blendColor, glBlendColor, red, green, blue, alpha) _WRAP_METHOD_SIMPLE(blendEquation, glBlendEquation, mode) _WRAP_METHOD_SIMPLE(blendEquationSeparate, glBlendEquationSeparate, modeRGB, modeAlpha) _WRAP_METHOD_SIMPLE(blendFunc, glBlendFunc, sfactor, dfactor) _WRAP_METHOD_SIMPLE(blendFuncSeparate, glBlendFuncSeparate, srcRGB, dstRGB, srcAlpha, dstAlpha) _WRAP_METHOD_SIMPLE(clearColor, glClearColor, red, green, blue, alpha) _WRAP_METHOD_SIMPLE(clearDepth, glClearDepthf, depth) _WRAP_METHOD_SIMPLE(clearStencil, glClearStencil, s) _WRAP_METHOD_SIMPLE(colorMask, glColorMask, red, green, blue, alpha) _WRAP_METHOD_SIMPLE(cullFace, glCullFace, mode) _WRAP_METHOD_SIMPLE(depthFunc, glDepthFunc, func) _WRAP_METHOD_SIMPLE(depthMask, glDepthMask, flag) _WRAP_METHOD_SIMPLE(depthRange, glDepthRangef, zNear, zFar) _WRAP_METHOD_SIMPLE(disable, glDisable, cap) _WRAP_METHOD_SIMPLE(enable, glEnable, cap) _WRAP_METHOD_SIMPLE(frontFace, glFrontFace, mode) template inline JSValueRef getParameterArray(JSContextRef jsCtx, JSTypedArrayType arrayType, F &&glGetFunc, GLenum pname) { T glResults[dim]; addBlockingToNextBatch([&] { glGetFunc(pname, glResults); }); return makeTypedArray(jsCtx, arrayType, glResults, sizeof(glResults)); } _WRAP_METHOD(getParameter, 1) { EXJS_UNPACK_ARGV(GLenum pname); switch (pname) { // Float32Array[0] case GL_COMPRESSED_TEXTURE_FORMATS: return makeTypedArray(jsCtx, kJSTypedArrayTypeFloat32Array, nullptr, 0); // FLoat32Array[2] case GL_ALIASED_LINE_WIDTH_RANGE: case GL_ALIASED_POINT_SIZE_RANGE: case GL_DEPTH_RANGE: return getParameterArray(jsCtx, kJSTypedArrayTypeFloat32Array, &glGetFloatv, pname); // FLoat32Array[4] case GL_BLEND_COLOR: case GL_COLOR_CLEAR_VALUE: return getParameterArray(jsCtx, kJSTypedArrayTypeFloat32Array, &glGetFloatv, pname); // Int32Array[2] case GL_MAX_VIEWPORT_DIMS: return getParameterArray(jsCtx, kJSTypedArrayTypeInt32Array, &glGetIntegerv, pname); // Int32Array[4] case GL_SCISSOR_BOX: case GL_VIEWPORT: return getParameterArray(jsCtx, kJSTypedArrayTypeInt32Array, &glGetIntegerv, pname); // boolean[4] case GL_COLOR_WRITEMASK: { GLint glResults[4]; addBlockingToNextBatch([&] { glGetIntegerv(pname, glResults); }); JSValueRef jsResults[4]; for (unsigned int i = 0; i < 4; ++i) { jsResults[i] = JSValueMakeBoolean(jsCtx, glResults[i]); } return JSObjectMakeArray(jsCtx, 4, jsResults, nullptr); } // boolean case GL_UNPACK_FLIP_Y_WEBGL: return JSValueMakeBoolean(jsCtx, unpackFLipY); case GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL: case GL_UNPACK_COLORSPACE_CONVERSION_WEBGL: return JSValueMakeBoolean(jsCtx, false); // string case GL_RENDERER: case GL_SHADING_LANGUAGE_VERSION: case GL_VENDOR: case GL_VERSION: { const GLubyte *glStr; addBlockingToNextBatch([&] { glStr = glGetString(pname); }); return EXJSValueMakeStringFromUTF8CString(jsCtx, (const char *) glStr); } // float case GL_DEPTH_CLEAR_VALUE: case GL_LINE_WIDTH: case GL_POLYGON_OFFSET_FACTOR: case GL_POLYGON_OFFSET_UNITS: case GL_SAMPLE_COVERAGE_VALUE: { GLfloat glFloat; addBlockingToNextBatch([&] { glGetFloatv(pname, &glFloat); }); return JSValueMakeNumber(jsCtx, glFloat); } // Future case GL_ARRAY_BUFFER_BINDING: case GL_ELEMENT_ARRAY_BUFFER_BINDING: case GL_CURRENT_PROGRAM: { GLint glInt; addBlockingToNextBatch([&] { glGetIntegerv(pname, &glInt); }); for (const auto &pair : futures) { if (pair.second == glInt) { return JSValueMakeNumber(jsCtx, pair.first); } } return nullptr; } // Unimplemented... #define _GET_PARAMETER_UNIMPL(param) \ case GL_##param: \ throw std::runtime_error("EXGL: getParameter() doesn't support gl." \ #param " yet!"); _GET_PARAMETER_UNIMPL(FRAMEBUFFER_BINDING); _GET_PARAMETER_UNIMPL(RENDERBUFFER_BINDING); _GET_PARAMETER_UNIMPL(TEXTURE_BINDING_2D); _GET_PARAMETER_UNIMPL(TEXTURE_BINDING_CUBE_MAP); #undef _GET_PARAMETER_UNIMPL // int default: { GLint glInt; addBlockingToNextBatch([&] { glGetIntegerv(pname, &glInt); }); return JSValueMakeNumber(jsCtx, glInt); } } } _WRAP_METHOD(getError, 0) { GLenum glResult; addBlockingToNextBatch([&] { glResult = glGetError(); }); return JSValueMakeNumber(jsCtx, glResult); } _WRAP_METHOD_SIMPLE(hint, glHint, target, mode) _WRAP_METHOD(isEnabled, 1) { EXJS_UNPACK_ARGV(GLenum cap); GLboolean glResult; addBlockingToNextBatch([&] { glResult = glIsEnabled(cap); }); return JSValueMakeBoolean(jsCtx, glResult); } _WRAP_METHOD_SIMPLE(lineWidth, glLineWidth, width) _WRAP_METHOD(pixelStorei, 2) { EXJS_UNPACK_ARGV(GLenum pname, GLint param); switch (pname) { case GL_UNPACK_FLIP_Y_WEBGL: unpackFLipY = param; break; default: EXGLSysLog("EXGL: gl.pixelStorei() doesn't support this parameter yet!"); break; } return nullptr; } _WRAP_METHOD_SIMPLE(polygonOffset, glPolygonOffset, factor, units) _WRAP_METHOD_SIMPLE(sampleCoverage, glSampleCoverage, value, invert) _WRAP_METHOD_SIMPLE(stencilFunc, glStencilFunc, func, ref, mask) _WRAP_METHOD_SIMPLE(stencilFuncSeparate, glStencilFuncSeparate, face, func, ref, mask) _WRAP_METHOD_SIMPLE(stencilMask, glStencilMask, mask) _WRAP_METHOD_SIMPLE(stencilMaskSeparate, glStencilMaskSeparate, face, mask) _WRAP_METHOD_SIMPLE(stencilOp, glStencilOp, fail, zfail, zpass) _WRAP_METHOD_SIMPLE(stencilOpSeparate, glStencilOpSeparate, face, fail, zfail, zpass) // Buffers // ------- _WRAP_METHOD(bindBuffer, 2) { EXJS_UNPACK_ARGV(GLenum target, Future fBuffer); addToNextBatch([=] { glBindBuffer(target, peekFuture(fBuffer)); }); return nullptr; } _WRAP_METHOD(bufferData, 3) { GLenum target = EXJSValueToNumberFast(jsCtx, jsArgv[0]); auto jsSecond = jsArgv[1]; GLenum usage = EXJSValueToNumberFast(jsCtx, jsArgv[2]); if (JSValueIsNumber(jsCtx, jsSecond)) { GLsizeiptr length = EXJSValueToNumberFast(jsCtx, jsSecond); addToNextBatch([=] { glBufferData(target, length, nullptr, usage); }); } else if (JSValueIsNull(jsCtx, jsSecond)) { addToNextBatch([=] { glBufferData(target, 0, nullptr, usage); }); } else { size_t length; auto data = jsValueToSharedArray(jsCtx, jsSecond, &length); addToNextBatch([=] { glBufferData(target, length, data.get(), usage); }); } return nullptr; } _WRAP_METHOD(bufferSubData, 3) { if (!JSValueIsNull(jsCtx, jsArgv[2])) { EXJS_UNPACK_ARGV(GLenum target, GLintptr offset); size_t length; auto data = jsValueToSharedArray(jsCtx, jsArgv[2], &length); addToNextBatch([=] { glBufferSubData(target, offset, length, data.get()); }); } return nullptr; } _WRAP_METHOD(createBuffer, 0) { return addFutureToNextBatch(jsCtx, [] { GLuint buffer; glGenBuffers(1, &buffer); return buffer; }); } _WRAP_METHOD(deleteBuffer, 1) { EXJS_UNPACK_ARGV(Future fBuffer); addToNextBatch([=] { GLuint buffer = peekFuture(fBuffer); glDeleteBuffers(1, &buffer); }); return nullptr; } _WRAP_METHOD(getBufferParameter, 2) { EXJS_UNPACK_ARGV(GLenum target, GLenum pname); GLint glResult; addBlockingToNextBatch([&] { glGetBufferParameteriv(target, pname, &glResult); }); return JSValueMakeNumber(jsCtx, glResult); } #define _WRAP_METHOD_IS_OBJECT(type) \ _WRAP_METHOD(is ## type, 1) { \ EXJS_UNPACK_ARGV(Future f); \ GLboolean glResult; \ addBlockingToNextBatch([&] { \ glResult = glIs ## type(peekFuture(f)); \ }); \ return JSValueMakeBoolean(jsCtx, glResult); \ } _WRAP_METHOD_IS_OBJECT(Buffer) // Framebuffers // ------------ _WRAP_METHOD(bindFramebuffer, 2) { EXJS_UNPACK_ARGV(GLenum target); if (JSValueIsNull(jsCtx, jsArgv[1])) { addToNextBatch([=] { glBindFramebuffer(target, defaultFramebuffer); }); } else { Future fFramebuffer = EXJSValueToNumberFast(jsCtx, jsArgv[1]); addToNextBatch([=] { glBindFramebuffer(target, peekFuture(fFramebuffer)); }); } return nullptr; } _WRAP_METHOD(checkFramebufferStatus, 1) { GLenum glResult; EXJS_UNPACK_ARGV(GLenum target); addBlockingToNextBatch([&] { glResult = glCheckFramebufferStatus(target); }); return JSValueMakeNumber(jsCtx, glResult); } _WRAP_METHOD(createFramebuffer, 0) { return addFutureToNextBatch(jsCtx, [] { GLuint framebuffer; glGenFramebuffers(1, &framebuffer); return framebuffer; }); } _WRAP_METHOD(deleteFramebuffer, 1) { EXJS_UNPACK_ARGV(Future fFramebuffer); addToNextBatch([=] { GLuint framebuffer = peekFuture(fFramebuffer); glDeleteFramebuffers(1, &framebuffer); }); return nullptr; } _WRAP_METHOD_UNIMPL(framebufferRenderbuffer) _WRAP_METHOD(framebufferTexture2D, 5) { EXJS_UNPACK_ARGV(GLenum target, GLenum attachment, GLenum textarget, Future fTexture, GLint level); addToNextBatch([=] { glFramebufferTexture2D(target, attachment, textarget, peekFuture(fTexture), level); }); return nullptr; } _WRAP_METHOD_UNIMPL(getFramebufferAttachmentParameter) _WRAP_METHOD_IS_OBJECT(Framebuffer) _WRAP_METHOD(readPixels, 7) { EXJS_UNPACK_ARGV(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type); if (usingTypedArrayHack) { size_t byteLength = width * height * bytesPerPixel(type, format); auto pixels = std::shared_ptr(malloc(byteLength), free); addBlockingToNextBatch([&] { glReadPixels(x, y, width, height, format, type, pixels.get()); }); JSObjectSetTypedArrayData(jsCtx, (JSObjectRef) jsArgv[6], pixels.get(), byteLength); } else { void *pixels = JSObjectGetTypedArrayBytesPtr(jsCtx, (JSObjectRef) jsArgv[6], nullptr); addBlockingToNextBatch([&] { glReadPixels(x, y, width, height, format, type, pixels); }); } return nullptr; } // Renderbuffers // ------------- _WRAP_METHOD_UNIMPL(bindRenderbuffer) _WRAP_METHOD_UNIMPL(createRenderbuffer) _WRAP_METHOD_UNIMPL(deleteRenderbuffer) _WRAP_METHOD_UNIMPL(getRenderbufferParameter) _WRAP_METHOD_IS_OBJECT(Renderbuffer) _WRAP_METHOD_UNIMPL(renderbufferStorage) // Textures // -------- _WRAP_METHOD(bindTexture, 2) { EXJS_UNPACK_ARGV(GLenum target); if (JSValueIsNull(jsCtx, jsArgv[1])) { addToNextBatch(std::bind(glBindTexture, target, 0)); } else { Future fTexture = EXJSValueToNumberFast(jsCtx, jsArgv[1]); addToNextBatch([=] { glBindTexture(target, peekFuture(fTexture)); }); } return nullptr; } _WRAP_METHOD_UNIMPL(compressedTexImage2D) _WRAP_METHOD_UNIMPL(compressedTexSubImage2D) _WRAP_METHOD_SIMPLE(copyTexImage2D, glCopyTexImage2D, target, level, internalformat, x, y, width, height, border) _WRAP_METHOD_SIMPLE(copyTexSubImage2D, glCopyTexSubImage2D, target, level, xoffset, yoffset, x, y, width, height) _WRAP_METHOD(createTexture, 0) { return addFutureToNextBatch(jsCtx, [] { GLuint texture; glGenTextures(1, &texture); return texture; }); } _WRAP_METHOD(deleteTexture, 1) { EXJS_UNPACK_ARGV(Future fTexture); addToNextBatch([=] { GLuint texture = peekFuture(fTexture); glDeleteTextures(1, &texture); }); return nullptr; } _WRAP_METHOD_SIMPLE(generateMipmap, glGenerateMipmap, target) _WRAP_METHOD_UNIMPL(getTexParameter) _WRAP_METHOD_IS_OBJECT(Texture) inline void decodeURI(char *dst, const char *src) { char a, b; while (*src) { if ((*src == '%') && ((a = src[1]) && (b = src[2])) && (isxdigit(a) && isxdigit(b))) { if (a >= 'a') { a -= 'a' - 'A'; } if (a >= 'A') { a -= ('A' - 10); } else { a -= '0'; } if (b >= 'a') { b -= 'a' - 'A'; } if (b >= 'A') { b -= ('A' - 10); } else { b -= '0'; } *dst++ = 16 * a + b; src += 3; } else if (*src == '+') { *dst++ = ' '; src++; } else { *dst++ = *src++; } } *dst++ = '\0'; } _WRAP_METHOD(texImage2D, 6) { if (jsArgc == 9) { // 9-argument version EXJS_UNPACK_ARGV(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei border, GLenum format, GLenum type); // Null? if (JSValueIsNull(jsCtx, jsArgv[8])) { addToNextBatch([=] { glTexImage2D(target, level, internalformat, width, height, border, format, type, nullptr); }); return nullptr; } JSObjectRef jsPixels = (JSObjectRef) jsArgv[8]; // Raw texture data TypedArray? { auto data = jsValueToSharedArray(jsCtx, jsPixels, nullptr); if (data) { if (unpackFLipY) { flipPixels((GLubyte *) data.get(), width * bytesPerPixel(type, format), height); } addToNextBatch([=] { glTexImage2D(target, level, internalformat, width, height, border, format, type, data.get()); }); return nullptr; } } // Exponent.Asset object? JSValueRef jsLocalUri = EXJSObjectGetPropertyNamed(jsCtx, jsPixels, "localUri"); if (jsLocalUri && JSValueIsString(jsCtx, jsLocalUri)) { // TODO(nikki): Check that this file is in the right scope auto localUri = jsValueToSharedStr(jsCtx, jsLocalUri); if (strncmp(localUri.get(), "file://", 7) != 0) { throw std::runtime_error("EXGL: Asset doesn't have a cached local file for" " gl.texImage2D()!"); } char localPath[strlen(localUri.get())]; decodeURI(localPath, localUri.get() + 7); int fileWidth, fileHeight, fileComp; std::shared_ptr data(stbi_load(localPath, &fileWidth, &fileHeight, &fileComp, STBI_rgb_alpha), stbi_image_free); if (!data) { throw std::runtime_error("EXGL: Couldn't read image from Asset's local file" " for gl.texImage2D()!"); } if (width != fileWidth || height != fileHeight) { throw std::runtime_error("EXGL: Asset's width and height don't match" " given width and height for gl.texImage2D()!"); } if (unpackFLipY) { flipPixels((GLubyte *) data.get(), width * bytesPerPixel(type, format), height); } addToNextBatch([=] { glTexImage2D(target, level, internalformat, width, height, border, format, type, data.get()); }); return nullptr; } // None of the above? throw std::runtime_error("EXGL: Invalid pixel data argument for" " gl.texImage2D()!"); } else if (jsArgc == 6) { // 6-argument version (no width, height, border) throw std::runtime_error("EXGL: gl.texImage2D() does't support 6-argument" " version yet!"); } else { throw std::runtime_error("EXGL: Invalid number of arguments to gl.texImage2D()!"); } } _WRAP_METHOD_UNIMPL(texSubImage2D) _WRAP_METHOD_SIMPLE(texParameterf, glTexParameterf, target, pname, param) _WRAP_METHOD_SIMPLE(texParameteri, glTexParameteri, target, pname, param) // Programs and shaders // -------------------- _WRAP_METHOD(attachShader, 2) { EXJS_UNPACK_ARGV(Future fProgram, Future fShader); addToNextBatch([=] { glAttachShader(peekFuture(fProgram), peekFuture(fShader)); }); return nullptr; } _WRAP_METHOD(bindAttribLocation, 3) { EXJS_UNPACK_ARGV(Future fProgram, GLuint index); auto name = jsValueToSharedStr(jsCtx, jsArgv[2]); addToNextBatch([=] { glBindAttribLocation(peekFuture(fProgram), index, name.get()); }); return nullptr; } _WRAP_METHOD(compileShader, 1) { EXJS_UNPACK_ARGV(Future fShader); addToNextBatch([=] { glCompileShader(peekFuture(fShader)); }); return nullptr; } _WRAP_METHOD(createProgram, 0) { return addFutureToNextBatch(jsCtx, &glCreateProgram); } _WRAP_METHOD(createShader, 1) { EXJS_UNPACK_ARGV(GLenum type); if (type == GL_VERTEX_SHADER || type == GL_FRAGMENT_SHADER) { return addFutureToNextBatch(jsCtx, std::bind(glCreateShader, type)); } else { return JSValueMakeNull(jsCtx); } } _WRAP_METHOD(deleteProgram, 1) { EXJS_UNPACK_ARGV(Future fProgram); addToNextBatch([=] { glDeleteProgram(peekFuture(fProgram)); }); return nullptr; } _WRAP_METHOD(deleteShader, 1) { EXJS_UNPACK_ARGV(Future fShader); addToNextBatch([=] { glDeleteShader(peekFuture(fShader)); }); return nullptr; } _WRAP_METHOD(detachShader, 2) { EXJS_UNPACK_ARGV(Future fProgram, Future fShader); addToNextBatch([=] { glDetachShader(peekFuture(fProgram), peekFuture(fShader)); }); return nullptr; } _WRAP_METHOD(getAttachedShaders, 1) { EXJS_UNPACK_ARGV(Future fProgram); GLint count; std::vector glResults; addBlockingToNextBatch([&] { GLuint program = peekFuture(fProgram); glGetProgramiv(program, GL_ATTACHED_SHADERS, &count); glResults.resize(count); glGetAttachedShaders(program, count, nullptr, glResults.data()); }); JSValueRef jsResults[count]; for (auto i = 0; i < count; ++i) { Future future = 0; for (const auto &pair : futures) { if (pair.second == glResults[i]) { future = pair.first; } } if (future == 0) { throw new std::runtime_error("EXGL: Internal error: couldn't find Future " "associated with shader in getAttachedShaders()!"); } jsResults[i] = JSValueMakeNumber(jsCtx, future); } return JSObjectMakeArray(jsCtx, count, jsResults, nullptr); } _WRAP_METHOD(getProgramParameter, 2) { EXJS_UNPACK_ARGV(Future fProgram, GLenum pname); GLint glResult; addBlockingToNextBatch([&] { glGetProgramiv(peekFuture(fProgram), pname, &glResult); }); if (pname == GL_DELETE_STATUS || pname == GL_LINK_STATUS || pname == GL_VALIDATE_STATUS) { return JSValueMakeBoolean(jsCtx, glResult); } else { return JSValueMakeNumber(jsCtx, glResult); } } _WRAP_METHOD(getShaderParameter, 2) { EXJS_UNPACK_ARGV(Future fShader, GLenum pname); GLint glResult; addBlockingToNextBatch([&] { glGetShaderiv(peekFuture(fShader), pname, &glResult); }); if (pname == GL_DELETE_STATUS || pname == GL_COMPILE_STATUS) { return JSValueMakeBoolean(jsCtx, glResult); } else { return JSValueMakeNumber(jsCtx, glResult); } } _WRAP_METHOD(getShaderPrecisionFormat, 2) { EXJS_UNPACK_ARGV(GLenum shaderType, GLenum precisionType); GLint range[2], precision; addBlockingToNextBatch([&] { glGetShaderPrecisionFormat(shaderType, precisionType, range, &precision); }); JSObjectRef jsResult = JSObjectMake(jsCtx, nullptr, nullptr); EXJSObjectSetValueWithUTF8CStringName(jsCtx, jsResult, "rangeMin", JSValueMakeNumber(jsCtx, range[0])); EXJSObjectSetValueWithUTF8CStringName(jsCtx, jsResult, "rangeMax", JSValueMakeNumber(jsCtx, range[1])); EXJSObjectSetValueWithUTF8CStringName(jsCtx, jsResult, "precision", JSValueMakeNumber(jsCtx, precision)); return jsResult; } template inline JSValueRef getShaderOrProgramStr(JSContextRef jsCtx, const JSValueRef jsArgv[], F &&glGetLengthParam, GLenum glLengthParam, G &&glGetStr) { EXJS_UNPACK_ARGV(Future fObj); GLint length; std::string str; addBlockingToNextBatch([&] { GLuint obj = peekFuture(fObj); glGetLengthParam(obj, glLengthParam, &length); str.resize(length); glGetStr(obj, length, nullptr, &str[0]); }); return EXJSValueMakeStringFromUTF8CString(jsCtx, str.c_str()); } _WRAP_METHOD(getProgramInfoLog, 1) { return getShaderOrProgramStr(jsCtx, jsArgv, glGetProgramiv, GL_INFO_LOG_LENGTH, glGetProgramInfoLog); } _WRAP_METHOD(getShaderInfoLog, 1) { return getShaderOrProgramStr(jsCtx, jsArgv, glGetShaderiv, GL_INFO_LOG_LENGTH, glGetShaderInfoLog); } _WRAP_METHOD(getShaderSource, 1) { return getShaderOrProgramStr(jsCtx, jsArgv, glGetShaderiv, GL_SHADER_SOURCE_LENGTH, glGetShaderSource); } _WRAP_METHOD_IS_OBJECT(Program) _WRAP_METHOD_IS_OBJECT(Shader) _WRAP_METHOD(linkProgram, 1) { EXJS_UNPACK_ARGV(Future fProgram); addToNextBatch([=] { glLinkProgram(peekFuture(fProgram)); }); return nullptr; } _WRAP_METHOD(shaderSource, 2) { EXJS_UNPACK_ARGV(Future fShader); auto str = jsValueToSharedStr(jsCtx, jsArgv[1]); addToNextBatch([=] { char *pstr = str.get(); glShaderSource(peekFuture(fShader), 1, (const char **) &pstr, nullptr); }); return nullptr; } _WRAP_METHOD(useProgram, 1) { if (JSValueIsNull(jsCtx, jsArgv[0])) { addToNextBatch(std::bind(glUseProgram, 0)); } else { EXJS_UNPACK_ARGV(Future fProgram); addToNextBatch([=] { glUseProgram(peekFuture(fProgram)); }); } return nullptr; } _WRAP_METHOD(validateProgram, 1) { EXJS_UNPACK_ARGV(Future fProgram); addToNextBatch([=] { glValidateProgram(peekFuture(fProgram)); }); return nullptr; } // Uniforms and attributes // ----------------------- _WRAP_METHOD_SIMPLE(disableVertexAttribArray, glDisableVertexAttribArray, index) _WRAP_METHOD_SIMPLE(enableVertexAttribArray, glEnableVertexAttribArray, index) template inline JSValueRef getActiveInfo(JSContextRef jsCtx, const JSValueRef jsArgv[], GLenum lengthParam, F &&glFunc) { if (JSValueIsNull(jsCtx, jsArgv[0])) { return JSValueMakeNull(jsCtx); } EXJS_UNPACK_ARGV(Future fProgram, GLuint index); GLsizei length; GLint size; GLenum type; std::string name; GLint maxNameLength; addBlockingToNextBatch([&] { GLuint program = peekFuture(fProgram); glGetProgramiv(program, lengthParam, &maxNameLength); name.resize(maxNameLength); glFunc(program, index, maxNameLength, &length, &size, &type, &name[0]); }); if (strlen(name.c_str()) == 0) { // name.length() may be larger return JSValueMakeNull(jsCtx); } JSObjectRef jsResult = JSObjectMake(jsCtx, nullptr, nullptr); EXJSObjectSetValueWithUTF8CStringName(jsCtx, jsResult, "name", EXJSValueMakeStringFromUTF8CString(jsCtx, name.c_str())); EXJSObjectSetValueWithUTF8CStringName(jsCtx, jsResult, "size", JSValueMakeNumber(jsCtx, size)); EXJSObjectSetValueWithUTF8CStringName(jsCtx, jsResult, "type", JSValueMakeNumber(jsCtx, type)); return jsResult; } _WRAP_METHOD(getActiveAttrib, 2) { return getActiveInfo(jsCtx, jsArgv, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, glGetActiveAttrib); } _WRAP_METHOD(getActiveUniform, 2) { return getActiveInfo(jsCtx, jsArgv, GL_ACTIVE_UNIFORM_MAX_LENGTH, glGetActiveUniform); } _WRAP_METHOD(getAttribLocation, 2) { EXJS_UNPACK_ARGV(Future fProgram); auto name = jsValueToSharedStr(jsCtx, jsArgv[1]); GLint location; addBlockingToNextBatch([&] { location = glGetAttribLocation(peekFuture(fProgram), name.get()); }); return JSValueMakeNumber(jsCtx, location); } _WRAP_METHOD_UNIMPL(getUniform) _WRAP_METHOD(getUniformLocation, 2) { EXJS_UNPACK_ARGV(Future fProgram); auto name = jsValueToSharedStr(jsCtx, jsArgv[1]); GLint location; addBlockingToNextBatch([&] { location = glGetUniformLocation(peekFuture(fProgram), name.get()); }); return location == -1 ? JSValueMakeNull(jsCtx) : JSValueMakeNumber(jsCtx, location); } _WRAP_METHOD_UNIMPL(getVertexAttrib) _WRAP_METHOD_UNIMPL(getVertexAttribOffset) _WRAP_METHOD_SIMPLE(uniform1f, glUniform1f, uniform, x) _WRAP_METHOD_SIMPLE(uniform2f, glUniform2f, uniform, x, y) _WRAP_METHOD_SIMPLE(uniform3f, glUniform3f, uniform, x, y, z) _WRAP_METHOD_SIMPLE(uniform4f, glUniform4f, uniform, x, y, z, w) _WRAP_METHOD_SIMPLE(uniform1i, glUniform1i, uniform, x) _WRAP_METHOD_SIMPLE(uniform2i, glUniform2i, uniform, x, y) _WRAP_METHOD_SIMPLE(uniform3i, glUniform3i, uniform, x, y, z) _WRAP_METHOD_SIMPLE(uniform4i, glUniform4i, uniform, x, y, z, w) #define _WRAP_METHOD_UNIFORM_V(suffix, dim, Type) \ _WRAP_METHOD(uniform##suffix, 2) { \ GLuint uniform = EXJSValueToNumberFast(jsCtx, jsArgv[0]); \ size_t bytes; \ auto data = jsValueToSharedArray(jsCtx, jsArgv[1], &bytes); \ GLsizei count = (GLsizei) bytes / sizeof(Type); \ addToNextBatch([=] { \ glUniform##suffix(uniform, count / dim, (Type *) data.get()); \ }); \ return nullptr; \ } _WRAP_METHOD_UNIFORM_V(1fv, 1, GLfloat) _WRAP_METHOD_UNIFORM_V(2fv, 2, GLfloat) _WRAP_METHOD_UNIFORM_V(3fv, 3, GLfloat) _WRAP_METHOD_UNIFORM_V(4fv, 4, GLfloat) _WRAP_METHOD_UNIFORM_V(1iv, 1, GLint) _WRAP_METHOD_UNIFORM_V(2iv, 2, GLint) _WRAP_METHOD_UNIFORM_V(3iv, 3, GLint) _WRAP_METHOD_UNIFORM_V(4iv, 4, GLint) #undef _WRAP_METHOD_UNIFORM_V #define _WRAP_METHOD_UNIFORM_MATRIX(suffix, dim) \ _WRAP_METHOD(uniformMatrix##suffix, 3) { \ GLuint uniform = EXJSValueToNumberFast(jsCtx, jsArgv[0]); \ GLboolean transpose = JSValueToBoolean(jsCtx, jsArgv[1]); \ size_t bytes; \ auto data = jsValueToSharedArray(jsCtx, jsArgv[2], &bytes); \ GLsizei count = (GLsizei) bytes / sizeof(GLfloat); \ addToNextBatch([=] { \ glUniformMatrix##suffix(uniform, count / dim, transpose, (GLfloat *) data.get()); \ }); \ return nullptr; \ } _WRAP_METHOD_UNIFORM_MATRIX(2fv, 4) _WRAP_METHOD_UNIFORM_MATRIX(3fv, 9) _WRAP_METHOD_UNIFORM_MATRIX(4fv, 16) #undef _WRAP_METHOD_UNIFORM_MATRIX #define _WRAP_METHOD_VERTEX_ATTRIB_V(suffix, dim) \ _WRAP_METHOD(vertexAttrib##suffix, 2) { \ GLuint index = EXJSValueToNumberFast(jsCtx, jsArgv[0]); \ auto data = jsValueToSharedArray(jsCtx, jsArgv[1], nullptr); \ addToNextBatch([=] { glVertexAttrib##suffix(index, (GLfloat *) data.get());}); \ return nullptr; \ } _WRAP_METHOD_VERTEX_ATTRIB_V(1fv, 1) _WRAP_METHOD_VERTEX_ATTRIB_V(2fv, 1) _WRAP_METHOD_VERTEX_ATTRIB_V(3fv, 1) _WRAP_METHOD_VERTEX_ATTRIB_V(4fv, 1) #undef _WRAP_METHOD_VERTEX_ATTRIB_V _WRAP_METHOD_SIMPLE(vertexAttrib1f, glVertexAttrib1f, index, x) _WRAP_METHOD_SIMPLE(vertexAttrib2f, glVertexAttrib2f, index, x, y) _WRAP_METHOD_SIMPLE(vertexAttrib3f, glVertexAttrib3f, index, x, y, z) _WRAP_METHOD_SIMPLE(vertexAttrib4f, glVertexAttrib4f, index, x, y, z, w) _WRAP_METHOD(vertexAttribPointer, 6) { EXJS_UNPACK_ARGV(GLuint index, GLuint itemSize, GLenum type, GLboolean normalized, GLsizei stride, GLint offset); addToNextBatch(std::bind(glVertexAttribPointer, index, itemSize, type, normalized, stride, bufferOffset(offset))); return nullptr; } // Drawing buffers // --------------- _WRAP_METHOD_SIMPLE(clear, glClear, mask) _WRAP_METHOD_SIMPLE(drawArrays, glDrawArrays, mode, first, count) _WRAP_METHOD(drawElements, 4) { EXJS_UNPACK_ARGV(GLenum mode, GLsizei count, GLenum type, GLint offset); addToNextBatch(std::bind(glDrawElements, mode, count, type, bufferOffset(offset))); return nullptr; } _WRAP_METHOD(finish, 0) { addToNextBatch(glFinish); return nullptr; } _WRAP_METHOD(flush, 0) { addToNextBatch(glFlush); return nullptr; } // Extensions // ---------- _WRAP_METHOD(getSupportedExtensions, 0) { return JSObjectMakeArray(jsCtx, 0, NULL, NULL); } _WRAP_METHOD(getExtension, 1) { return JSValueMakeNull(jsCtx); } // Exponent extensions // ------------------- _WRAP_METHOD(endFrameEXP, 0) { endNextBatch(); return nullptr; } #undef _WRAP_METHOD_SIMPLE_UNPACK #undef _WRAP_METHOD_SIMPLE #undef _WRAP_METHOD_UNIMPL #undef _WRAP_METHOD void installMethods(JSContextRef jsCtx) { #define _INSTALL_METHOD(name) \ EXJSObjectSetFunctionWithUTF8CStringName(jsCtx, jsGl, #name, \ &EXGLContext::exglNativeStatic_##name) // This listing follows the order in // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext // The WebGL context _INSTALL_METHOD(getContextAttributes); _INSTALL_METHOD(isContextLost); // Viewing and clipping _INSTALL_METHOD(scissor); _INSTALL_METHOD(viewport); // State information _INSTALL_METHOD(activeTexture); _INSTALL_METHOD(blendColor); _INSTALL_METHOD(blendEquation); _INSTALL_METHOD(blendEquationSeparate); _INSTALL_METHOD(blendFunc); _INSTALL_METHOD(blendFuncSeparate); _INSTALL_METHOD(clearColor); _INSTALL_METHOD(clearDepth); _INSTALL_METHOD(clearStencil); _INSTALL_METHOD(colorMask); _INSTALL_METHOD(cullFace); _INSTALL_METHOD(depthFunc); _INSTALL_METHOD(depthMask); _INSTALL_METHOD(depthRange); _INSTALL_METHOD(disable); _INSTALL_METHOD(enable); _INSTALL_METHOD(frontFace); _INSTALL_METHOD(getParameter); _INSTALL_METHOD(getError); _INSTALL_METHOD(hint); _INSTALL_METHOD(isEnabled); _INSTALL_METHOD(lineWidth); _INSTALL_METHOD(pixelStorei); _INSTALL_METHOD(polygonOffset); _INSTALL_METHOD(sampleCoverage); _INSTALL_METHOD(stencilFunc); _INSTALL_METHOD(stencilFuncSeparate); _INSTALL_METHOD(stencilMask); _INSTALL_METHOD(stencilMaskSeparate); _INSTALL_METHOD(stencilOp); _INSTALL_METHOD(stencilOpSeparate); // Buffers _INSTALL_METHOD(bindBuffer); _INSTALL_METHOD(bufferData); _INSTALL_METHOD(bufferSubData); _INSTALL_METHOD(createBuffer); _INSTALL_METHOD(deleteBuffer); _INSTALL_METHOD(getBufferParameter); _INSTALL_METHOD(isBuffer); // Framebuffers _INSTALL_METHOD(bindFramebuffer); _INSTALL_METHOD(checkFramebufferStatus); _INSTALL_METHOD(createFramebuffer); _INSTALL_METHOD(deleteFramebuffer); _INSTALL_METHOD(framebufferRenderbuffer); _INSTALL_METHOD(framebufferTexture2D); _INSTALL_METHOD(getFramebufferAttachmentParameter); _INSTALL_METHOD(isFramebuffer); _INSTALL_METHOD(readPixels); // Renderbuffers _INSTALL_METHOD(bindRenderbuffer); _INSTALL_METHOD(createRenderbuffer); _INSTALL_METHOD(deleteRenderbuffer); _INSTALL_METHOD(getRenderbufferParameter); _INSTALL_METHOD(isRenderbuffer); _INSTALL_METHOD(renderbufferStorage); // Textures _INSTALL_METHOD(bindTexture); _INSTALL_METHOD(compressedTexImage2D); _INSTALL_METHOD(compressedTexSubImage2D); _INSTALL_METHOD(copyTexImage2D); _INSTALL_METHOD(copyTexSubImage2D); _INSTALL_METHOD(createTexture); _INSTALL_METHOD(deleteTexture); _INSTALL_METHOD(generateMipmap); _INSTALL_METHOD(getTexParameter); _INSTALL_METHOD(isTexture); _INSTALL_METHOD(texImage2D); _INSTALL_METHOD(texSubImage2D); _INSTALL_METHOD(texParameterf); _INSTALL_METHOD(texParameteri); // Programs and shaders _INSTALL_METHOD(attachShader); _INSTALL_METHOD(bindAttribLocation); _INSTALL_METHOD(compileShader); _INSTALL_METHOD(createProgram); _INSTALL_METHOD(createShader); _INSTALL_METHOD(deleteProgram); _INSTALL_METHOD(deleteShader); _INSTALL_METHOD(detachShader); _INSTALL_METHOD(getAttachedShaders); _INSTALL_METHOD(getProgramParameter); _INSTALL_METHOD(getProgramInfoLog); _INSTALL_METHOD(getShaderParameter); _INSTALL_METHOD(getShaderPrecisionFormat); _INSTALL_METHOD(getShaderInfoLog); _INSTALL_METHOD(getShaderSource); _INSTALL_METHOD(isProgram); _INSTALL_METHOD(isShader); _INSTALL_METHOD(linkProgram); _INSTALL_METHOD(shaderSource); _INSTALL_METHOD(useProgram); _INSTALL_METHOD(validateProgram); // Uniforms and attributes _INSTALL_METHOD(disableVertexAttribArray); _INSTALL_METHOD(enableVertexAttribArray); _INSTALL_METHOD(getActiveAttrib); _INSTALL_METHOD(getActiveUniform); _INSTALL_METHOD(getAttribLocation); _INSTALL_METHOD(getUniform); _INSTALL_METHOD(getUniformLocation); _INSTALL_METHOD(getVertexAttrib); _INSTALL_METHOD(getVertexAttribOffset); _INSTALL_METHOD(uniform1f); _INSTALL_METHOD(uniform1fv); _INSTALL_METHOD(uniform1i); _INSTALL_METHOD(uniform1iv); _INSTALL_METHOD(uniform2f); _INSTALL_METHOD(uniform2fv); _INSTALL_METHOD(uniform2i); _INSTALL_METHOD(uniform2iv); _INSTALL_METHOD(uniform3f); _INSTALL_METHOD(uniform3fv); _INSTALL_METHOD(uniform3i); _INSTALL_METHOD(uniform3iv); _INSTALL_METHOD(uniform4f); _INSTALL_METHOD(uniform4fv); _INSTALL_METHOD(uniform4i); _INSTALL_METHOD(uniform4iv); _INSTALL_METHOD(uniformMatrix2fv); _INSTALL_METHOD(uniformMatrix3fv); _INSTALL_METHOD(uniformMatrix4fv); _INSTALL_METHOD(vertexAttrib1f); _INSTALL_METHOD(vertexAttrib1fv); _INSTALL_METHOD(vertexAttrib2f); _INSTALL_METHOD(vertexAttrib2fv); _INSTALL_METHOD(vertexAttrib3f); _INSTALL_METHOD(vertexAttrib3fv); _INSTALL_METHOD(vertexAttrib4f); _INSTALL_METHOD(vertexAttrib4fv); _INSTALL_METHOD(vertexAttribPointer); // Drawing buffers _INSTALL_METHOD(clear); _INSTALL_METHOD(drawArrays); _INSTALL_METHOD(drawElements); _INSTALL_METHOD(finish); _INSTALL_METHOD(flush); // Extensions _INSTALL_METHOD(getSupportedExtensions); _INSTALL_METHOD(getExtension); // Exponent extensions _INSTALL_METHOD(endFrameEXP); #undef _INSTALL_METHOD } void installConstants(JSContextRef jsCtx) { #define _INSTALL_CONSTANT(name) \ EXJSObjectSetValueWithUTF8CStringName(jsCtx, jsGl, #name, \ JSValueMakeNumber(jsCtx, GL_ ## name)) _INSTALL_CONSTANT(ACTIVE_ATTRIBUTES); //35721 // _INSTALL_CONSTANT(ACTIVE_ATTRIBUTE_MAX_LENGTH); //35722 _INSTALL_CONSTANT(ACTIVE_TEXTURE); //34016 _INSTALL_CONSTANT(ACTIVE_UNIFORMS); //35718 // _INSTALL_CONSTANT(ACTIVE_UNIFORM_MAX_LENGTH); //35719 _INSTALL_CONSTANT(ALIASED_LINE_WIDTH_RANGE); //33902 _INSTALL_CONSTANT(ALIASED_POINT_SIZE_RANGE); //33901 _INSTALL_CONSTANT(ALPHA); //6406 _INSTALL_CONSTANT(ALPHA_BITS); //3413 _INSTALL_CONSTANT(ALWAYS); //519 _INSTALL_CONSTANT(ARRAY_BUFFER); //34962 _INSTALL_CONSTANT(ARRAY_BUFFER_BINDING); //34964 _INSTALL_CONSTANT(ATTACHED_SHADERS); //35717 _INSTALL_CONSTANT(BACK); //1029 _INSTALL_CONSTANT(BLEND); //3042 _INSTALL_CONSTANT(BLEND_COLOR); //32773 _INSTALL_CONSTANT(BLEND_DST_ALPHA); //32970 _INSTALL_CONSTANT(BLEND_DST_RGB); //32968 _INSTALL_CONSTANT(BLEND_EQUATION); //32777 _INSTALL_CONSTANT(BLEND_EQUATION_ALPHA); //34877 _INSTALL_CONSTANT(BLEND_EQUATION_RGB); //32777 _INSTALL_CONSTANT(BLEND_SRC_ALPHA); //32971 _INSTALL_CONSTANT(BLEND_SRC_RGB); //32969 _INSTALL_CONSTANT(BLUE_BITS); //3412 _INSTALL_CONSTANT(BOOL); //35670 _INSTALL_CONSTANT(BOOL_VEC2); //35671 _INSTALL_CONSTANT(BOOL_VEC3); //35672 _INSTALL_CONSTANT(BOOL_VEC4); //35673 _INSTALL_CONSTANT(BROWSER_DEFAULT_WEBGL); //37444 _INSTALL_CONSTANT(BUFFER_SIZE); //34660 _INSTALL_CONSTANT(BUFFER_USAGE); //34661 _INSTALL_CONSTANT(BYTE); //5120 _INSTALL_CONSTANT(CCW); //2305 _INSTALL_CONSTANT(CLAMP_TO_EDGE); //33071 _INSTALL_CONSTANT(COLOR_ATTACHMENT0); //36064 _INSTALL_CONSTANT(COLOR_BUFFER_BIT); //16384 _INSTALL_CONSTANT(COLOR_CLEAR_VALUE); //3106 _INSTALL_CONSTANT(COLOR_WRITEMASK); //3107 _INSTALL_CONSTANT(COMPILE_STATUS); //35713 _INSTALL_CONSTANT(COMPRESSED_TEXTURE_FORMATS); //34467 _INSTALL_CONSTANT(CONSTANT_ALPHA); //32771 _INSTALL_CONSTANT(CONSTANT_COLOR); //32769 _INSTALL_CONSTANT(CONTEXT_LOST_WEBGL); //37442 _INSTALL_CONSTANT(CULL_FACE); //2884 _INSTALL_CONSTANT(CULL_FACE_MODE); //2885 _INSTALL_CONSTANT(CURRENT_PROGRAM); //35725 _INSTALL_CONSTANT(CURRENT_VERTEX_ATTRIB); //34342 _INSTALL_CONSTANT(CW); //2304 _INSTALL_CONSTANT(DECR); //7683 _INSTALL_CONSTANT(DECR_WRAP); //34056 _INSTALL_CONSTANT(DELETE_STATUS); //35712 _INSTALL_CONSTANT(DEPTH_ATTACHMENT); //36096 _INSTALL_CONSTANT(DEPTH_BITS); //3414 _INSTALL_CONSTANT(DEPTH_BUFFER_BIT); //256 _INSTALL_CONSTANT(DEPTH_CLEAR_VALUE); //2931 _INSTALL_CONSTANT(DEPTH_COMPONENT); //6402 _INSTALL_CONSTANT(DEPTH_COMPONENT16); //33189 _INSTALL_CONSTANT(DEPTH_FUNC); //2932 _INSTALL_CONSTANT(DEPTH_RANGE); //2928 _INSTALL_CONSTANT(DEPTH_STENCIL); //34041 _INSTALL_CONSTANT(DEPTH_STENCIL_ATTACHMENT); //33306 _INSTALL_CONSTANT(DEPTH_TEST); //2929 _INSTALL_CONSTANT(DEPTH_WRITEMASK); //2930 _INSTALL_CONSTANT(DITHER); //3024 _INSTALL_CONSTANT(DONT_CARE); //4352 _INSTALL_CONSTANT(DST_ALPHA); //772 _INSTALL_CONSTANT(DST_COLOR); //774 _INSTALL_CONSTANT(DYNAMIC_DRAW); //35048 _INSTALL_CONSTANT(ELEMENT_ARRAY_BUFFER); //34963 _INSTALL_CONSTANT(ELEMENT_ARRAY_BUFFER_BINDING); //34965 _INSTALL_CONSTANT(EQUAL); //514 // _INSTALL_CONSTANT(FALSE); //0 _INSTALL_CONSTANT(FASTEST); //4353 _INSTALL_CONSTANT(FLOAT); //5126 _INSTALL_CONSTANT(FLOAT_MAT2); //35674 _INSTALL_CONSTANT(FLOAT_MAT3); //35675 _INSTALL_CONSTANT(FLOAT_MAT4); //35676 _INSTALL_CONSTANT(FLOAT_VEC2); //35664 _INSTALL_CONSTANT(FLOAT_VEC3); //35665 _INSTALL_CONSTANT(FLOAT_VEC4); //35666 _INSTALL_CONSTANT(FRAGMENT_SHADER); //35632 _INSTALL_CONSTANT(FRAMEBUFFER); //36160 _INSTALL_CONSTANT(FRAMEBUFFER_ATTACHMENT_OBJECT_NAME); //36049 _INSTALL_CONSTANT(FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE); //36048 _INSTALL_CONSTANT(FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE); //36051 _INSTALL_CONSTANT(FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL); //36050 _INSTALL_CONSTANT(FRAMEBUFFER_BINDING); //36006 _INSTALL_CONSTANT(FRAMEBUFFER_COMPLETE); //36053 _INSTALL_CONSTANT(FRAMEBUFFER_INCOMPLETE_ATTACHMENT); //36054 _INSTALL_CONSTANT(FRAMEBUFFER_INCOMPLETE_DIMENSIONS); //36057 _INSTALL_CONSTANT(FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT); //36055 _INSTALL_CONSTANT(FRAMEBUFFER_UNSUPPORTED); //36061 _INSTALL_CONSTANT(FRONT); //1028 _INSTALL_CONSTANT(FRONT_AND_BACK); //1032 _INSTALL_CONSTANT(FRONT_FACE); //2886 _INSTALL_CONSTANT(FUNC_ADD); //32774 _INSTALL_CONSTANT(FUNC_REVERSE_SUBTRACT); //32779 _INSTALL_CONSTANT(FUNC_SUBTRACT); //32778 _INSTALL_CONSTANT(GENERATE_MIPMAP_HINT); //33170 _INSTALL_CONSTANT(GEQUAL); //518 _INSTALL_CONSTANT(GREATER); //516 _INSTALL_CONSTANT(GREEN_BITS); //3411 _INSTALL_CONSTANT(HIGH_FLOAT); //36338 _INSTALL_CONSTANT(HIGH_INT); //36341 _INSTALL_CONSTANT(IMPLEMENTATION_COLOR_READ_TYPE); //35738 _INSTALL_CONSTANT(IMPLEMENTATION_COLOR_READ_FORMAT); //35739 _INSTALL_CONSTANT(INCR); //7682 _INSTALL_CONSTANT(INCR_WRAP); //34055 // _INSTALL_CONSTANT(INFO_LOG_LENGTH); //35716 _INSTALL_CONSTANT(INT); //5124 _INSTALL_CONSTANT(INT_VEC2); //35667 _INSTALL_CONSTANT(INT_VEC3); //35668 _INSTALL_CONSTANT(INT_VEC4); //35669 _INSTALL_CONSTANT(INVALID_ENUM); //1280 _INSTALL_CONSTANT(INVALID_FRAMEBUFFER_OPERATION); //1286 _INSTALL_CONSTANT(INVALID_OPERATION); //1282 _INSTALL_CONSTANT(INVALID_VALUE); //1281 _INSTALL_CONSTANT(INVERT); //5386 _INSTALL_CONSTANT(KEEP); //7680 _INSTALL_CONSTANT(LEQUAL); //515 _INSTALL_CONSTANT(LESS); //513 _INSTALL_CONSTANT(LINEAR); //9729 _INSTALL_CONSTANT(LINEAR_MIPMAP_LINEAR); //9987 _INSTALL_CONSTANT(LINEAR_MIPMAP_NEAREST); //9985 _INSTALL_CONSTANT(LINES); //1 _INSTALL_CONSTANT(LINE_LOOP); //2 _INSTALL_CONSTANT(LINE_STRIP); //3 _INSTALL_CONSTANT(LINE_WIDTH); //2849 _INSTALL_CONSTANT(LINK_STATUS); //35714 _INSTALL_CONSTANT(LOW_FLOAT); //36336 _INSTALL_CONSTANT(LOW_INT); //36339 _INSTALL_CONSTANT(LUMINANCE); //6409 _INSTALL_CONSTANT(LUMINANCE_ALPHA); //6410 _INSTALL_CONSTANT(MAX_COMBINED_TEXTURE_IMAGE_UNITS); //35661 _INSTALL_CONSTANT(MAX_CUBE_MAP_TEXTURE_SIZE); //34076 _INSTALL_CONSTANT(MAX_FRAGMENT_UNIFORM_VECTORS); //36349 _INSTALL_CONSTANT(MAX_RENDERBUFFER_SIZE); //34024 _INSTALL_CONSTANT(MAX_TEXTURE_IMAGE_UNITS); //34930 _INSTALL_CONSTANT(MAX_TEXTURE_SIZE); //3379 _INSTALL_CONSTANT(MAX_VARYING_VECTORS); //36348 _INSTALL_CONSTANT(MAX_VERTEX_ATTRIBS); //34921 _INSTALL_CONSTANT(MAX_VERTEX_TEXTURE_IMAGE_UNITS); //35660 _INSTALL_CONSTANT(MAX_VERTEX_UNIFORM_VECTORS); //36347 _INSTALL_CONSTANT(MAX_VIEWPORT_DIMS); //3386 _INSTALL_CONSTANT(MEDIUM_FLOAT); //36337 _INSTALL_CONSTANT(MEDIUM_INT); //36340 _INSTALL_CONSTANT(MIRRORED_REPEAT); //33648 _INSTALL_CONSTANT(NEAREST); //9728 _INSTALL_CONSTANT(NEAREST_MIPMAP_LINEAR); //9986 _INSTALL_CONSTANT(NEAREST_MIPMAP_NEAREST); //9984 _INSTALL_CONSTANT(NEVER); //512 _INSTALL_CONSTANT(NICEST); //4354 _INSTALL_CONSTANT(NONE); //0 _INSTALL_CONSTANT(NOTEQUAL); //517 _INSTALL_CONSTANT(NO_ERROR); //0 // _INSTALL_CONSTANT(NUM_COMPRESSED_TEXTURE_FORMATS); //34466 _INSTALL_CONSTANT(ONE); //1 _INSTALL_CONSTANT(ONE_MINUS_CONSTANT_ALPHA); //32772 _INSTALL_CONSTANT(ONE_MINUS_CONSTANT_COLOR); //32770 _INSTALL_CONSTANT(ONE_MINUS_DST_ALPHA); //773 _INSTALL_CONSTANT(ONE_MINUS_DST_COLOR); //775 _INSTALL_CONSTANT(ONE_MINUS_SRC_ALPHA); //771 _INSTALL_CONSTANT(ONE_MINUS_SRC_COLOR); //769 _INSTALL_CONSTANT(OUT_OF_MEMORY); //1285 _INSTALL_CONSTANT(PACK_ALIGNMENT); //3333 _INSTALL_CONSTANT(POINTS); //0 _INSTALL_CONSTANT(POLYGON_OFFSET_FACTOR); //32824 _INSTALL_CONSTANT(POLYGON_OFFSET_FILL); //32823 _INSTALL_CONSTANT(POLYGON_OFFSET_UNITS); //10752 _INSTALL_CONSTANT(RED_BITS); //3410 _INSTALL_CONSTANT(RENDERBUFFER); //36161 _INSTALL_CONSTANT(RENDERBUFFER_ALPHA_SIZE); //36179 _INSTALL_CONSTANT(RENDERBUFFER_BINDING); //36007 _INSTALL_CONSTANT(RENDERBUFFER_BLUE_SIZE); //36178 _INSTALL_CONSTANT(RENDERBUFFER_DEPTH_SIZE); //36180 _INSTALL_CONSTANT(RENDERBUFFER_GREEN_SIZE); //36177 _INSTALL_CONSTANT(RENDERBUFFER_HEIGHT); //36163 _INSTALL_CONSTANT(RENDERBUFFER_INTERNAL_FORMAT); //36164 _INSTALL_CONSTANT(RENDERBUFFER_RED_SIZE); //36176 _INSTALL_CONSTANT(RENDERBUFFER_STENCIL_SIZE); //36181 _INSTALL_CONSTANT(RENDERBUFFER_WIDTH); //36162 _INSTALL_CONSTANT(RENDERER); //7937 _INSTALL_CONSTANT(REPEAT); //10497 _INSTALL_CONSTANT(REPLACE); //7681 _INSTALL_CONSTANT(RGB); //6407 _INSTALL_CONSTANT(RGB5_A1); //32855 _INSTALL_CONSTANT(RGB565); //36194 _INSTALL_CONSTANT(RGBA); //6408 _INSTALL_CONSTANT(RGBA4); //32854 _INSTALL_CONSTANT(SAMPLER_2D); //35678 _INSTALL_CONSTANT(SAMPLER_CUBE); //35680 _INSTALL_CONSTANT(SAMPLES); //32937 _INSTALL_CONSTANT(SAMPLE_ALPHA_TO_COVERAGE); //32926 _INSTALL_CONSTANT(SAMPLE_BUFFERS); //32936 _INSTALL_CONSTANT(SAMPLE_COVERAGE); //32928 _INSTALL_CONSTANT(SAMPLE_COVERAGE_INVERT); //32939 _INSTALL_CONSTANT(SAMPLE_COVERAGE_VALUE); //32938 _INSTALL_CONSTANT(SCISSOR_BOX); //3088 _INSTALL_CONSTANT(SCISSOR_TEST); //3089 // _INSTALL_CONSTANT(SHADER_COMPILER); //36346 // _INSTALL_CONSTANT(SHADER_SOURCE_LENGTH); //35720 _INSTALL_CONSTANT(SHADER_TYPE); //35663 _INSTALL_CONSTANT(SHADING_LANGUAGE_VERSION); //35724 _INSTALL_CONSTANT(SHORT); //5122 _INSTALL_CONSTANT(SRC_ALPHA); //770 _INSTALL_CONSTANT(SRC_ALPHA_SATURATE); //776 _INSTALL_CONSTANT(SRC_COLOR); //768 _INSTALL_CONSTANT(STATIC_DRAW); //35044 _INSTALL_CONSTANT(STENCIL_ATTACHMENT); //36128 _INSTALL_CONSTANT(STENCIL_BACK_FAIL); //34817 _INSTALL_CONSTANT(STENCIL_BACK_FUNC); //34816 _INSTALL_CONSTANT(STENCIL_BACK_PASS_DEPTH_FAIL); //34818 _INSTALL_CONSTANT(STENCIL_BACK_PASS_DEPTH_PASS); //34819 _INSTALL_CONSTANT(STENCIL_BACK_REF); //36003 _INSTALL_CONSTANT(STENCIL_BACK_VALUE_MASK); //36004 _INSTALL_CONSTANT(STENCIL_BACK_WRITEMASK); //36005 _INSTALL_CONSTANT(STENCIL_BITS); //3415 _INSTALL_CONSTANT(STENCIL_BUFFER_BIT); //1024 _INSTALL_CONSTANT(STENCIL_CLEAR_VALUE); //2961 _INSTALL_CONSTANT(STENCIL_FAIL); //2964 _INSTALL_CONSTANT(STENCIL_FUNC); //2962 _INSTALL_CONSTANT(STENCIL_INDEX); //6401 _INSTALL_CONSTANT(STENCIL_INDEX8); //36168 _INSTALL_CONSTANT(STENCIL_PASS_DEPTH_FAIL); //2965 _INSTALL_CONSTANT(STENCIL_PASS_DEPTH_PASS); //2966 _INSTALL_CONSTANT(STENCIL_REF); //2967 _INSTALL_CONSTANT(STENCIL_TEST); //2960 _INSTALL_CONSTANT(STENCIL_VALUE_MASK); //2963 _INSTALL_CONSTANT(STENCIL_WRITEMASK); //2968 _INSTALL_CONSTANT(STREAM_DRAW); //35040 _INSTALL_CONSTANT(SUBPIXEL_BITS); //3408 _INSTALL_CONSTANT(TEXTURE); //5890 _INSTALL_CONSTANT(TEXTURE0); //33984 _INSTALL_CONSTANT(TEXTURE1); //33985 _INSTALL_CONSTANT(TEXTURE2); //33986 _INSTALL_CONSTANT(TEXTURE3); //33987 _INSTALL_CONSTANT(TEXTURE4); //33988 _INSTALL_CONSTANT(TEXTURE5); //33989 _INSTALL_CONSTANT(TEXTURE6); //33990 _INSTALL_CONSTANT(TEXTURE7); //33991 _INSTALL_CONSTANT(TEXTURE8); //33992 _INSTALL_CONSTANT(TEXTURE9); //33993 _INSTALL_CONSTANT(TEXTURE10); //33994 _INSTALL_CONSTANT(TEXTURE11); //33995 _INSTALL_CONSTANT(TEXTURE12); //33996 _INSTALL_CONSTANT(TEXTURE13); //33997 _INSTALL_CONSTANT(TEXTURE14); //33998 _INSTALL_CONSTANT(TEXTURE15); //33999 _INSTALL_CONSTANT(TEXTURE16); //34000 _INSTALL_CONSTANT(TEXTURE17); //34001 _INSTALL_CONSTANT(TEXTURE18); //34002 _INSTALL_CONSTANT(TEXTURE19); //34003 _INSTALL_CONSTANT(TEXTURE20); //34004 _INSTALL_CONSTANT(TEXTURE21); //34005 _INSTALL_CONSTANT(TEXTURE22); //34006 _INSTALL_CONSTANT(TEXTURE23); //34007 _INSTALL_CONSTANT(TEXTURE24); //34008 _INSTALL_CONSTANT(TEXTURE25); //34009 _INSTALL_CONSTANT(TEXTURE26); //34010 _INSTALL_CONSTANT(TEXTURE27); //34011 _INSTALL_CONSTANT(TEXTURE28); //34012 _INSTALL_CONSTANT(TEXTURE29); //34013 _INSTALL_CONSTANT(TEXTURE30); //34014 _INSTALL_CONSTANT(TEXTURE31); //34015 _INSTALL_CONSTANT(TEXTURE_2D); //3553 _INSTALL_CONSTANT(TEXTURE_BINDING_2D); //32873 _INSTALL_CONSTANT(TEXTURE_BINDING_CUBE_MAP); //34068 _INSTALL_CONSTANT(TEXTURE_CUBE_MAP); //34067 _INSTALL_CONSTANT(TEXTURE_CUBE_MAP_NEGATIVE_X); //34070 _INSTALL_CONSTANT(TEXTURE_CUBE_MAP_NEGATIVE_Y); //34072 _INSTALL_CONSTANT(TEXTURE_CUBE_MAP_NEGATIVE_Z); //34074 _INSTALL_CONSTANT(TEXTURE_CUBE_MAP_POSITIVE_X); //34069 _INSTALL_CONSTANT(TEXTURE_CUBE_MAP_POSITIVE_Y); //34071 _INSTALL_CONSTANT(TEXTURE_CUBE_MAP_POSITIVE_Z); //34073 _INSTALL_CONSTANT(TEXTURE_MAG_FILTER); //10240 _INSTALL_CONSTANT(TEXTURE_MIN_FILTER); //10241 _INSTALL_CONSTANT(TEXTURE_WRAP_S); //10242 _INSTALL_CONSTANT(TEXTURE_WRAP_T); //10243 _INSTALL_CONSTANT(TRIANGLES); //4 _INSTALL_CONSTANT(TRIANGLE_FAN); //6 _INSTALL_CONSTANT(TRIANGLE_STRIP); //5 // _INSTALL_CONSTANT(TRUE); //1 _INSTALL_CONSTANT(UNPACK_ALIGNMENT); //3317 _INSTALL_CONSTANT(UNPACK_COLORSPACE_CONVERSION_WEBGL); //37443 _INSTALL_CONSTANT(UNPACK_FLIP_Y_WEBGL); //37440 _INSTALL_CONSTANT(UNPACK_PREMULTIPLY_ALPHA_WEBGL); //37441 _INSTALL_CONSTANT(UNSIGNED_BYTE); //5121 _INSTALL_CONSTANT(UNSIGNED_INT); //5125 _INSTALL_CONSTANT(UNSIGNED_SHORT); //5123 _INSTALL_CONSTANT(UNSIGNED_SHORT_4_4_4_4); //32819 _INSTALL_CONSTANT(UNSIGNED_SHORT_5_5_5_1); //32820 _INSTALL_CONSTANT(UNSIGNED_SHORT_5_6_5); //33635 _INSTALL_CONSTANT(VALIDATE_STATUS); //35715 _INSTALL_CONSTANT(VENDOR); //7936 _INSTALL_CONSTANT(VERSION); //7938 _INSTALL_CONSTANT(VERTEX_ATTRIB_ARRAY_BUFFER_BINDING); //34975 _INSTALL_CONSTANT(VERTEX_ATTRIB_ARRAY_ENABLED); //34338 _INSTALL_CONSTANT(VERTEX_ATTRIB_ARRAY_NORMALIZED); //34922 _INSTALL_CONSTANT(VERTEX_ATTRIB_ARRAY_POINTER); //34373 _INSTALL_CONSTANT(VERTEX_ATTRIB_ARRAY_SIZE); //34339 _INSTALL_CONSTANT(VERTEX_ATTRIB_ARRAY_STRIDE); //34340 _INSTALL_CONSTANT(VERTEX_ATTRIB_ARRAY_TYPE); //34341 _INSTALL_CONSTANT(VERTEX_SHADER); //35633 _INSTALL_CONSTANT(VIEWPORT); //2978 _INSTALL_CONSTANT(ZERO); //0 #undef _INSTALL_CONSTANT } }; // --- C interface ------------------------------------------------------------- static std::unordered_map EXGLContextMap; static std::mutex EXGLContextMapMutex; static EXGLContextId EXGLContextNextId = 1; static EXGLContext *EXGLContextGet(EXGLContextId exglCtxId) { std::lock_guard lock(EXGLContextMapMutex); auto iter = EXGLContextMap.find(exglCtxId); if (iter != EXGLContextMap.end()) { return iter->second; } return nullptr; } EXGLContextId EXGLContextCreate(JSGlobalContextRef jsCtx) { // Out of ids? if (EXGLContextNextId >= std::numeric_limits::max()) { EXGLSysLog("Ran out of EXGLContext ids!"); return 0; } // Create C++ object EXGLContext *exglCtx; EXGLContextId exglCtxId; { std::lock_guard lock(EXGLContextMapMutex); exglCtxId = EXGLContextNextId++; if (EXGLContextMap.find(exglCtxId) != EXGLContextMap.end()) { EXGLSysLog("Tried to reuse an EXGLContext id. This shouldn't really happen..."); return 0; } exglCtx = new EXGLContext(jsCtx, exglCtxId); EXGLContextMap[exglCtxId] = exglCtx; } // Save JavaScript object auto jsGlobal = JSContextGetGlobalObject(jsCtx); auto jsEXGLContextMap = (JSObjectRef) EXJSObjectGetPropertyNamed(jsCtx, jsGlobal, "__EXGLContexts"); if (!JSValueToBoolean(jsCtx, jsEXGLContextMap)) { jsEXGLContextMap = JSObjectMake(jsCtx, nullptr, nullptr); EXJSObjectSetValueWithUTF8CStringName(jsCtx, jsGlobal, "__EXGLContexts", jsEXGLContextMap); } std::stringstream ss; ss << exglCtxId; auto exglCtxIdStr = ss.str(); EXJSObjectSetValueWithUTF8CStringName(jsCtx, jsEXGLContextMap, exglCtxIdStr.c_str(), exglCtx->getJSObject()); return exglCtxId; } void EXGLContextDestroy(EXGLContextId exglCtxId) { std::lock_guard lock(EXGLContextMapMutex); // Destroy C++ object, JavaScript side should just know... auto iter = EXGLContextMap.find(exglCtxId); if (iter != EXGLContextMap.end()) { delete iter->second; EXGLContextMap.erase(iter); } } void EXGLContextFlush(EXGLContextId exglCtxId) { EXGLContext *exglCtx = EXGLContextGet(exglCtxId); if (exglCtx) { exglCtx->flush(); } } void EXGLContextSetDefaultFramebuffer(EXGLContextId exglCtxId, GLint framebuffer) { EXGLContext *exglCtx = EXGLContextGet(exglCtxId); if (exglCtx) { exglCtx->setDefaultFramebuffer(framebuffer); } }