1921 lines
67 KiB
C++
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "EXGL.h"
#ifdef __ANDROID__
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <android/log.h>
#endif
#ifdef __APPLE__
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#endif
#include <exception>
#include <future>
#include <sstream>
#include <unordered_map>
#include <vector>
#include <JavaScriptCore/JSContextRef.h>
#include <JavaScriptCore/JSObjectRef.h>
#include <JavaScriptCore/JSValueRef.h>
#include "GLImages.h"
#include "EXJSUtils.h"
#include "EXJSConvertTypedArray.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<void(void)>;
// 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<Op>;
Batch nextBatch;
std::vector<Batch> backlog;
std::mutex backlogMutex;
// [JS thread] Add an Op to the 'next' batch -- the arguments are any form of
// constructor arguments for Op
template<typename... Args>
inline void addToNextBatch(Args &&...args) noexcept {
nextBatch.emplace_back(std::forward<Args>(args)...);
}
// [JS thread] Send the current 'next' batch to GL and make a new 'next' batch
void endNextBatch() noexcept {
std::lock_guard<decltype(backlogMutex)> 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<typename F>
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<decltype(mutex)> lock(mutex);
done = true;
}
cv.notify_all();
});
{
std::unique_lock<decltype(mutex)> lock(mutex);
endNextBatch();
cv.wait(lock, [&] { return done; });
}
#else
std::packaged_task<decltype(f())(void)> 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<Future, GLuint> 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<typename F>
inline JSValueRef addFutureToNextBatch(JSContextRef jsCtx, F &&f) noexcept {
auto future = nextFuture++;
addToNextBatch([=] {
//assert(futures.find(future) == futures.end()); // FIXME gre commented for some reason, breaks in android
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<decltype(backlogMutex)> 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<char> jsValueToSharedStr(JSContextRef jsCtx, JSValueRef jsVal) noexcept {
return std::shared_ptr<char>(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<void> jsValueToSharedArray(JSContextRef jsCtx, JSValueRef jsVal,
size_t *pByteLength) noexcept {
if (usingTypedArrayHack) {
return std::shared_ptr<void>(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<void>(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<void>(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<typename T, size_t dim, typename F>
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<GLfloat, 2>(jsCtx, kJSTypedArrayTypeFloat32Array, &glGetFloatv, pname);
// FLoat32Array[4]
case GL_BLEND_COLOR:
case GL_COLOR_CLEAR_VALUE:
return getParameterArray<GLfloat, 4>(jsCtx, kJSTypedArrayTypeFloat32Array, &glGetFloatv, pname);
// Int32Array[2]
case GL_MAX_VIEWPORT_DIMS:
return getParameterArray<GLint, 2>(jsCtx, kJSTypedArrayTypeInt32Array, &glGetIntegerv, pname);
// Int32Array[4]
case GL_SCISSOR_BOX:
case GL_VIEWPORT:
return getParameterArray<GLint, 4>(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<void>(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)
_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;
}
}
throw std::runtime_error("EXGL: Invalid pixel data argument for"
" gl.texImage2D()!");
} else if (jsArgc == 6) {
EXJS_UNPACK_ARGV(GLenum target, GLint level, GLint internalformat,
GLenum format, GLenum type);
JSObjectRef jsPixels = (JSObjectRef) jsArgv[5];
// GLAsset object ?
JSValueRef jsGlAssetId = EXJSObjectGetPropertyNamed(jsCtx, jsPixels, "glAssetId");
if (jsGlAssetId && JSValueIsNumber(jsCtx, jsGlAssetId)) {
int glAssetId = (int)JSValueToNumber(jsCtx, jsGlAssetId, NULL);
GLAsset *asset = GLImagesGet(glAssetId);
if (!asset) {
throw std::runtime_error("EXGL: gl.texImage2D: asset not found");
}
void *data;
int width = asset->width;
int height = asset->height;
if (unpackFLipY) {
data = asset->lazyFlippedData;
if (!data) {
int bpp = 4; // GLAsset convention
int bytes = width * height * bpp;
data = malloc(bytes);
memcpy(data, asset->data, bytes);
flipPixels((GLubyte *) data, width * bpp, height);
asset->lazyFlippedData = data;
}
}
else {
data = asset->data;
}
addToNextBatch([=] {
glTexImage2D(target, level, internalformat,
width, height, 0,
format, type, data);
});
return nullptr;
}
throw std::runtime_error("EXGL: Invalid pixel data argument for"
" gl.texImage2D()!");
} 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<GLuint> 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<typename F, typename G>
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<typename F>
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<EXGLContextId, EXGLContext *> EXGLContextMap;
static std::mutex EXGLContextMapMutex;
static EXGLContextId EXGLContextNextId = 1;
static EXGLContext *EXGLContextGet(EXGLContextId exglCtxId) {
std::lock_guard<decltype(EXGLContextMapMutex)> 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<EXGLContextId>::max()) {
EXGLSysLog("Ran out of EXGLContext ids!");
return 0;
}
// Create C++ object
EXGLContext *exglCtx;
EXGLContextId exglCtxId;
{
std::lock_guard<decltype(EXGLContextMapMutex)> 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<decltype(EXGLContextMapMutex)> 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);
}
}