Skip to content

Instantly share code, notes, and snippets.

@D1no
Last active October 3, 2017 07:52
Show Gist options
  • Save D1no/70043ea86df1036cde0647a4268e8e14 to your computer and use it in GitHub Desktop.
Save D1no/70043ea86df1036cde0647a4268e8e14 to your computer and use it in GitHub Desktop.
React-Native WebGL Bridge
#include "UEXGL.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 "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(UEXGLContextId exglCtxId);
// --- EXGLContext -------------------------------------------------------------
// Class of the C++ object representing an EXGL rendering context.
class EXGLContext {
// --- 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
private:
// 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] 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 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] 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
}
// [JS thread] Enqueue a function and return an EXGL object that will get mapped
// to the function's return value when it is called on the GL thread.
//
// We call these 'futures': a 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.
template<typename F>
inline JSValueRef addFutureToNextBatch(JSContextRef jsCtx, F &&f) noexcept {
auto exglObjId = createObject();
addToNextBatch([=] {
assert(objects.find(exglObjId) == objects.end());
mapObject(exglObjId, f());
});
return JSValueMakeNumber(jsCtx, exglObjId);
}
// [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();
}
}
}
// --- Object mapping --------------------------------------------------------
// We err on the side of performance and hope that a global incrementing atomic
// unsigned int is enough for object ids. On 'creating' an object we simply
// 'reserve' the id by incrementing the atomic counter. Since the mapping is only
// set and read on the GL thread, this prevents us from having to maintain a
// mutex on the mapping.
private:
std::unordered_map<UEXGLObjectId, GLuint> objects;
static std::atomic_uint nextObjectId;
inline GLuint lookupObject(UEXGLObjectId exglObjId) noexcept {
auto iter = objects.find(exglObjId);
return iter == objects.end() ? 0 : iter->second;
}
public:
inline UEXGLObjectId createObject(void) noexcept {
return nextObjectId++;
}
inline void destroyObject(UEXGLObjectId exglObjId) noexcept {
objects.erase(exglObjId);
}
inline void mapObject(UEXGLObjectId exglObjId, GLuint glObj) noexcept {
objects[exglObjId] = glObj;
}
// --- Init/destroy and JS object binding ------------------------------------
private:
JSObjectRef jsGl;
public:
EXGLContext(JSGlobalContextRef jsCtx, UEXGLContextId 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 = (GLuint)rows / 2;
GLuint intsPerRow = (GLuint)bytesPerRow / sizeof(GLuint);
GLuint remainingBytes = (GLuint)bytesPerRow - intsPerRow * sizeof(GLuint);
for (GLuint rowTop = 0, rowBottom = (GLuint)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((UEXGLContextId) (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);
}
// UEXGLObjectId
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 : objects) {
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, UEXGLObjectId fBuffer);
addToNextBatch([=] { glBindBuffer(target, lookupObject(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(UEXGLObjectId fBuffer);
addToNextBatch([=] {
GLuint buffer = lookupObject(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(UEXGLObjectId f); \
GLboolean glResult; \
addBlockingToNextBatch([&] { \
glResult = glIs ## type(lookupObject(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 {
UEXGLObjectId fFramebuffer = EXJSValueToNumberFast(jsCtx, jsArgv[1]);
addToNextBatch([=] { glBindFramebuffer(target, lookupObject(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(UEXGLObjectId fFramebuffer);
addToNextBatch([=] {
GLuint framebuffer = lookupObject(fFramebuffer);
glDeleteFramebuffers(1, &framebuffer);
});
return nullptr;
}
_WRAP_METHOD_UNIMPL(framebufferRenderbuffer)
_WRAP_METHOD(framebufferTexture2D, 5) {
EXJS_UNPACK_ARGV(GLenum target, GLenum attachment, GLenum textarget, UEXGLObjectId fTexture, GLint level);
addToNextBatch([=] {
glFramebufferTexture2D(target, attachment, textarget, lookupObject(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 {
UEXGLObjectId fTexture = EXJSValueToNumberFast(jsCtx, jsArgv[1]);
addToNextBatch([=] { glBindTexture(target, lookupObject(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(UEXGLObjectId fTexture);
addToNextBatch([=] {
GLuint texture = lookupObject(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<void> 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(UEXGLObjectId fProgram, UEXGLObjectId fShader);
addToNextBatch([=] { glAttachShader(lookupObject(fProgram), lookupObject(fShader)); });
return nullptr;
}
_WRAP_METHOD(bindAttribLocation, 3) {
EXJS_UNPACK_ARGV(UEXGLObjectId fProgram, GLuint index);
auto name = jsValueToSharedStr(jsCtx, jsArgv[2]);
addToNextBatch([=] { glBindAttribLocation(lookupObject(fProgram), index, name.get()); });
return nullptr;
}
_WRAP_METHOD(compileShader, 1) {
EXJS_UNPACK_ARGV(UEXGLObjectId fShader);
addToNextBatch([=] { glCompileShader(lookupObject(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(UEXGLObjectId fProgram);
addToNextBatch([=] { glDeleteProgram(lookupObject(fProgram)); });
return nullptr;
}
_WRAP_METHOD(deleteShader, 1) {
EXJS_UNPACK_ARGV(UEXGLObjectId fShader);
addToNextBatch([=] { glDeleteShader(lookupObject(fShader)); });
return nullptr;
}
_WRAP_METHOD(detachShader, 2) {
EXJS_UNPACK_ARGV(UEXGLObjectId fProgram, UEXGLObjectId fShader);
addToNextBatch([=] { glDetachShader(lookupObject(fProgram), lookupObject(fShader)); });
return nullptr;
}
_WRAP_METHOD(getAttachedShaders, 1) {
EXJS_UNPACK_ARGV(UEXGLObjectId fProgram);
GLint count;
std::vector<GLuint> glResults;
addBlockingToNextBatch([&] {
GLuint program = lookupObject(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) {
UEXGLObjectId exglObjId = 0;
for (const auto &pair : objects) {
if (pair.second == glResults[i]) {
exglObjId = pair.first;
}
}
if (exglObjId == 0) {
throw new std::runtime_error("EXGL: Internal error: couldn't find UEXGLObjectId "
"associated with shader in getAttachedShaders()!");
}
jsResults[i] = JSValueMakeNumber(jsCtx, exglObjId);
}
return JSObjectMakeArray(jsCtx, count, jsResults, nullptr);
}
_WRAP_METHOD(getProgramParameter, 2) {
EXJS_UNPACK_ARGV(UEXGLObjectId fProgram, GLenum pname);
GLint glResult;
addBlockingToNextBatch([&] { glGetProgramiv(lookupObject(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(UEXGLObjectId fShader, GLenum pname);
GLint glResult;
addBlockingToNextBatch([&] { glGetShaderiv(lookupObject(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(UEXGLObjectId fObj);
GLint length;
std::string str;
addBlockingToNextBatch([&] {
GLuint obj = lookupObject(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(UEXGLObjectId fProgram);
addToNextBatch([=] { glLinkProgram(lookupObject(fProgram)); });
return nullptr;
}
_WRAP_METHOD(shaderSource, 2) {
EXJS_UNPACK_ARGV(UEXGLObjectId fShader);
auto str = jsValueToSharedStr(jsCtx, jsArgv[1]);
addToNextBatch([=] {
char *pstr = str.get();
glShaderSource(lookupObject(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(UEXGLObjectId fProgram);
addToNextBatch([=] { glUseProgram(lookupObject(fProgram)); });
}
return nullptr;
}
_WRAP_METHOD(validateProgram, 1) {
EXJS_UNPACK_ARGV(UEXGLObjectId fProgram);
addToNextBatch([=] { glValidateProgram(lookupObject(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(UEXGLObjectId fProgram, GLuint index);
GLsizei length;
GLint size;
GLenum type;
std::string name;
GLint maxNameLength;
addBlockingToNextBatch([&] {
GLuint program = lookupObject(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(UEXGLObjectId fProgram);
auto name = jsValueToSharedStr(jsCtx, jsArgv[1]);
GLint location;
addBlockingToNextBatch([&] {
location = glGetAttribLocation(lookupObject(fProgram), name.get());
});
return JSValueMakeNumber(jsCtx, location);
}
_WRAP_METHOD_UNIMPL(getUniform)
_WRAP_METHOD(getUniformLocation, 2) {
EXJS_UNPACK_ARGV(UEXGLObjectId fProgram);
auto name = jsValueToSharedStr(jsCtx, jsArgv[1]);
GLint location;
addBlockingToNextBatch([&] {
location = glGetUniformLocation(lookupObject(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
}
};
std::atomic_uint EXGLContext::nextObjectId { 1 };
// --- C interface -------------------------------------------------------------
static std::unordered_map<UEXGLContextId, EXGLContext *> EXGLContextMap;
static std::mutex EXGLContextMapMutex;
static UEXGLContextId EXGLContextNextId = 1;
static EXGLContext *EXGLContextGet(UEXGLContextId exglCtxId) {
std::lock_guard<decltype(EXGLContextMapMutex)> lock(EXGLContextMapMutex);
auto iter = EXGLContextMap.find(exglCtxId);
if (iter != EXGLContextMap.end()) {
return iter->second;
}
return nullptr;
}
UEXGLContextId UEXGLContextCreate(JSGlobalContextRef jsCtx) {
// Out of ids?
if (EXGLContextNextId >= std::numeric_limits<UEXGLContextId>::max()) {
EXGLSysLog("Ran out of EXGLContext ids!");
return 0;
}
// Create C++ object
EXGLContext *exglCtx;
UEXGLContextId 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 UEXGLContextDestroy(UEXGLContextId 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 UEXGLContextFlush(UEXGLContextId exglCtxId) {
auto exglCtx = EXGLContextGet(exglCtxId);
if (exglCtx) {
exglCtx->flush();
}
}
void UEXGLContextSetDefaultFramebuffer(UEXGLContextId exglCtxId, GLint framebuffer) {
auto exglCtx = EXGLContextGet(exglCtxId);
if (exglCtx) {
exglCtx->setDefaultFramebuffer(framebuffer);
}
}
UEXGLObjectId UEXGLContextCreateObject(UEXGLContextId exglCtxId) {
auto exglCtx = EXGLContextGet(exglCtxId);
if (exglCtx) {
return exglCtx->createObject();
}
return 0;
}
void UEXGLContextDestroyObject(UEXGLContextId exglCtxId, UEXGLObjectId exglObjId) {
auto exglCtx = EXGLContextGet(exglCtxId);
if (exglCtx) {
exglCtx->destroyObject(exglObjId);
}
}
void UEXGLContextMapObject(UEXGLContextId exglCtxId, UEXGLObjectId exglObjId, GLuint glObj) {
auto exglCtx = EXGLContextGet(exglCtxId);
if (exglCtx) {
exglCtx->mapObject(exglObjId, glObj);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment