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
createWebGLRenderer(jsi::Runtime & runtime,EXGLContext * ctx,glesContext viewport,jsi::Object && global)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
createWebGLObject(jsi::Runtime & runtime,EXWebGLClass webglClass,std::initializer_list<jsi::Value> && args)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
getConstructorName(EXWebGLClass value)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
attachClass(jsi::Runtime & runtime,EXWebGLClass webglClass,std::function<void (EXWebGLClass webglClass)> installPrototypes)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 });
jsClassExtend(jsi::Runtime & runtime,jsi::Object & baseClass,jsi::PropNameID derivedProp)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
ensurePrototypes(jsi::Runtime & runtime)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
installConstants(jsi::Runtime & runtime,jsi::Object & gl)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
installWebGLMethods(jsi::Runtime & runtime,jsi::Object & gl)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
installWebGL2Methods(jsi::Runtime & runtime,jsi::Object & gl)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