1 #include "EXWebGLRenderer.h" 2 #include "EXGLNativeContext.h" 3 #include "EXGLContextManager.h" 4 #include "EXJsiUtils.h" 5 #include "EXWebGLMethods.h" 6 #include <jsi/jsi.h> 7 8 namespace expo { 9 namespace gl_cpp { 10 11 constexpr const char *EXGLContextsMapPropertyName = "__EXGLContexts"; 12 13 // There is no way to crate function that can be used as constructor 14 // using jsi api, so we need to set it up via eval, it will be called 15 // only once so perofmance impact should be minimal 16 constexpr const char *evalStubConstructors = R"( 17 WebGLRenderingContext = function() {}; 18 WebGL2RenderingContext = function() {}; 19 WebGLObject = function() {}; 20 WebGLBuffer = function() {}; 21 WebGLFramebuffer = function() {}; 22 WebGLProgram = function() {}; 23 WebGLRenderbuffer = function() {}; 24 WebGLShader = function() {}; 25 WebGLTexture = function() {}; 26 WebGLUniformLocation = function() {}; 27 WebGLActiveInfo = function() {}; 28 WebGLShaderPrecisionFormat = function() {}; 29 WebGLQuery = function() {}; 30 WebGLSampler = function() {}; 31 WebGLSync = function() {}; 32 WebGLTransformFeedback = function() {}; 33 WebGLVertexArrayObject = function() {}; 34 )"; 35 36 void installConstants(jsi::Runtime &runtime, jsi::Object &gl); 37 void installWebGLMethods(jsi::Runtime &runtime, jsi::Object &gl); 38 void installWebGL2Methods(jsi::Runtime &runtime, jsi::Object &gl); 39 40 void createWebGLRenderer(jsi::Runtime &runtime, EXGLContext *ctx, glesContext viewport, jsi::Object&& global) { 41 ensurePrototypes(runtime); 42 jsi::Object gl = ctx->supportsWebGL2 43 ? createWebGLObject( 44 runtime, EXWebGLClass::WebGL2RenderingContext, {static_cast<double>(ctx->ctxId)}) 45 .asObject(runtime) 46 : createWebGLObject( 47 runtime, EXWebGLClass::WebGLRenderingContext, {static_cast<double>(ctx->ctxId)}) 48 .asObject(runtime); 49 50 gl.setProperty(runtime, "drawingBufferWidth", viewport.viewportWidth); 51 gl.setProperty(runtime, "drawingBufferHeight", viewport.viewportHeight); 52 gl.setProperty(runtime, "supportsWebGL2", ctx->supportsWebGL2); 53 gl.setProperty(runtime, "contextId", static_cast<double>(ctx->ctxId)); 54 55 jsi::Value jsContextMap = global.getProperty(runtime, EXGLContextsMapPropertyName); 56 if (jsContextMap.isNull() || jsContextMap.isUndefined()) { 57 global.setProperty(runtime, EXGLContextsMapPropertyName, jsi::Object(runtime)); 58 } 59 global.getProperty(runtime, EXGLContextsMapPropertyName) 60 .asObject(runtime) 61 .setProperty(runtime, jsi::PropNameID::forUtf8(runtime, std::to_string(ctx->ctxId)), gl); 62 } 63 64 // We are assuming that eval was called before first object 65 // is created and all global.WebGL... stub functions already exist 66 jsi::Value createWebGLObject( 67 jsi::Runtime &runtime, 68 EXWebGLClass webglClass, 69 std::initializer_list<jsi::Value> &&args) { 70 jsi::Object webglObject = runtime.global() 71 .getProperty(runtime, jsi::PropNameID::forUtf8(runtime, getConstructorName(webglClass))) 72 .asObject(runtime) 73 .asFunction(runtime) 74 .callAsConstructor(runtime, {}) 75 .asObject(runtime); 76 jsi::Value id = args.size() > 0 ? jsi::Value(runtime, *args.begin()) : jsi::Value::undefined(); 77 webglObject.setProperty(runtime, "id", id); 78 return webglObject; 79 } 80 81 std::string getConstructorName(EXWebGLClass value) { 82 switch (value) { 83 case EXWebGLClass::WebGLRenderingContext: 84 return "WebGLRenderingContext"; 85 case EXWebGLClass::WebGL2RenderingContext: 86 return "WebGL2RenderingContext"; 87 case EXWebGLClass::WebGLObject: 88 return "WebGLObject"; 89 case EXWebGLClass::WebGLBuffer: 90 return "WebGLBuffer"; 91 case EXWebGLClass::WebGLFramebuffer: 92 return "WebGLFramebuffer"; 93 case EXWebGLClass::WebGLProgram: 94 return "WebGLProgram"; 95 case EXWebGLClass::WebGLRenderbuffer: 96 return "WebGLRenderbuffer"; 97 case EXWebGLClass::WebGLShader: 98 return "WebGLShader"; 99 case EXWebGLClass::WebGLTexture: 100 return "WebGLTexture"; 101 case EXWebGLClass::WebGLUniformLocation: 102 return "WebGLUniformLocation"; 103 case EXWebGLClass::WebGLActiveInfo: 104 return "WebGLActiveInfo"; 105 case EXWebGLClass::WebGLShaderPrecisionFormat: 106 return "WebGLShaderPrecisionFormat"; 107 case EXWebGLClass::WebGLQuery: 108 return "WebGLQuery"; 109 case EXWebGLClass::WebGLSampler: 110 return "WebGLSampler"; 111 case EXWebGLClass::WebGLSync: 112 return "WebGLSync"; 113 case EXWebGLClass::WebGLTransformFeedback: 114 return "WebGLTransformFeedback"; 115 case EXWebGLClass::WebGLVertexArrayObject: 116 return "WebGLVertexArrayObject"; 117 } 118 } 119 120 void attachClass( 121 jsi::Runtime &runtime, 122 EXWebGLClass webglClass, 123 std::function<void(EXWebGLClass webglClass)> installPrototypes) { 124 jsi::PropNameID name = jsi::PropNameID::forUtf8(runtime, getConstructorName(webglClass)); 125 installPrototypes(webglClass); 126 } 127 128 // https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Inheritance#setting_teachers_prototype_and_constructor_reference 129 // 130 // Below implementation is equivalent of `class WebGLBuffer extends WebGLObject {}` 131 // where baseClass=global.WebGLObject and derivedProp="WebGLBuffer" 132 // 133 // WebGLBuffer.prototype = Object.create(WebGLObject.prototype); 134 // Object.defineProperty(WebGLBuffer.prototype, 'constructor', { 135 // value: WebGLBuffer, 136 // enumerable: false, 137 // configurable: true, 138 // writable: true }); 139 void jsClassExtend(jsi::Runtime &runtime, jsi::Object &baseClass, jsi::PropNameID derivedProp) { 140 jsi::PropNameID prototype = jsi::PropNameID::forUtf8(runtime, "prototype"); 141 jsi::Object objectClass = runtime.global().getPropertyAsObject(runtime, "Object"); 142 jsi::Function createMethod = objectClass.getPropertyAsFunction(runtime, "create"); 143 jsi::Function definePropertyMethod = objectClass.getPropertyAsFunction(runtime, "defineProperty"); 144 jsi::Object derivedClass = runtime.global().getProperty(runtime, derivedProp).asObject(runtime); 145 146 // WebGLBuffer.prototype = Object.create(WebGLObject.prototype); 147 derivedClass.setProperty( 148 runtime, 149 prototype, 150 createMethod.callWithThis(runtime, objectClass, {baseClass.getProperty(runtime, prototype)})); 151 152 jsi::Object propertyOptions(runtime); 153 propertyOptions.setProperty(runtime, "value", derivedClass); 154 propertyOptions.setProperty(runtime, "enumerable", false); 155 propertyOptions.setProperty(runtime, "configurable", true); 156 propertyOptions.setProperty(runtime, "writable", true); 157 158 // Object.defineProperty ... 159 definePropertyMethod.callWithThis( 160 runtime, 161 objectClass, 162 { 163 derivedClass.getProperty(runtime, prototype), 164 jsi::String::createFromUtf8(runtime, "constructor"), 165 std::move(propertyOptions), 166 }); 167 } 168 169 void ensurePrototypes(jsi::Runtime &runtime) { 170 if (runtime.global().hasProperty(runtime, "WebGLRenderingContext")) { 171 return; 172 } 173 runtime.global().setProperty(runtime, "__EXGLConstructorReady", true); 174 175 auto evalBuffer = std::make_shared<jsi::StringBuffer>(evalStubConstructors); 176 runtime.evaluateJavaScript(evalBuffer, "expo-gl"); 177 178 auto inheritFromJsObject = [&runtime](EXWebGLClass classEnum) { 179 auto objectClass = runtime.global().getPropertyAsObject(runtime, "Object"); 180 jsClassExtend( 181 runtime, objectClass, jsi::PropNameID::forUtf8(runtime, getConstructorName(classEnum))); 182 }; 183 184 // configure WebGLRenderingContext 185 { 186 inheritFromJsObject(EXWebGLClass::WebGLRenderingContext); 187 auto prototype = 188 runtime.global() 189 .getProperty(runtime, jsi::PropNameID::forUtf8(runtime, getConstructorName(EXWebGLClass::WebGLRenderingContext))) 190 .asObject(runtime) 191 .getPropertyAsObject(runtime, "prototype"); 192 installConstants(runtime, prototype); 193 installWebGLMethods(runtime, prototype); 194 } 195 196 // configure WebGL2RenderingContext 197 { 198 inheritFromJsObject(EXWebGLClass::WebGL2RenderingContext); 199 auto prototype = 200 runtime.global() 201 .getProperty(runtime, jsi::PropNameID::forUtf8(runtime, getConstructorName(EXWebGLClass::WebGL2RenderingContext))) 202 .asObject(runtime) 203 .getPropertyAsObject(runtime, "prototype"); 204 installConstants(runtime, prototype); 205 installWebGL2Methods(runtime, prototype); 206 } 207 208 // Configure rest of WebGL objects 209 inheritFromJsObject(EXWebGLClass::WebGLObject); 210 211 jsi::Object webglObjectClass = 212 runtime.global() 213 .getProperty( 214 runtime, 215 jsi::PropNameID::forUtf8(runtime, getConstructorName(EXWebGLClass::WebGLObject))) 216 .asObject(runtime); 217 auto inheritFromWebGLObject = [&runtime, &webglObjectClass](EXWebGLClass classEnum) { 218 jsClassExtend( 219 runtime, 220 webglObjectClass, 221 jsi::PropNameID::forUtf8(runtime, getConstructorName(classEnum))); 222 }; 223 224 inheritFromWebGLObject(EXWebGLClass::WebGLBuffer); 225 inheritFromWebGLObject(EXWebGLClass::WebGLFramebuffer); 226 inheritFromWebGLObject(EXWebGLClass::WebGLProgram); 227 inheritFromWebGLObject(EXWebGLClass::WebGLRenderbuffer); 228 inheritFromWebGLObject(EXWebGLClass::WebGLShader); 229 inheritFromWebGLObject(EXWebGLClass::WebGLTexture); 230 inheritFromJsObject(EXWebGLClass::WebGLUniformLocation); 231 inheritFromJsObject(EXWebGLClass::WebGLActiveInfo); 232 inheritFromJsObject(EXWebGLClass::WebGLShaderPrecisionFormat); 233 inheritFromWebGLObject(EXWebGLClass::WebGLQuery); 234 inheritFromWebGLObject(EXWebGLClass::WebGLSampler); 235 inheritFromWebGLObject(EXWebGLClass::WebGLSync); 236 inheritFromWebGLObject(EXWebGLClass::WebGLTransformFeedback); 237 inheritFromWebGLObject(EXWebGLClass::WebGLVertexArrayObject); 238 } 239 240 void installConstants(jsi::Runtime &runtime, jsi::Object &gl) { 241 #define GL_CONSTANT(name) gl.setProperty(runtime, #name, static_cast<double>(GL_##name)); 242 #include "EXWebGLConstants.def" 243 #undef GL_CONSTANT 244 } 245 246 void installWebGLMethods(jsi::Runtime &runtime, jsi::Object &gl) { 247 #define NATIVE_METHOD(name) setFunctionOnObject(runtime, gl, #name, method::glNativeMethod_##name); 248 249 #define NATIVE_WEBGL2_METHOD(name) ; 250 #include "EXWebGLMethods.def" 251 #undef NATIVE_WEBGL2_METHOD 252 #undef NATIVE_METHOD 253 } 254 255 void installWebGL2Methods(jsi::Runtime &runtime, jsi::Object &gl) { 256 #define CREATE_METHOD(name) setFunctionOnObject(runtime, gl, #name, method::glNativeMethod_##name); 257 258 #define NATIVE_METHOD(name) CREATE_METHOD(name) 259 #define NATIVE_WEBGL2_METHOD(name) CREATE_METHOD(name) 260 #include "EXWebGLMethods.def" 261 #undef NATIVE_WEBGL2_METHOD 262 #undef NATIVE_METHOD 263 #undef CREATE_METHOD 264 } 265 266 } // namespace gl_cpp 267 } // namespace expo 268