1 #pragma once 2 3 #include "EXGLNativeApi.h" 4 5 #ifdef __ANDROID__ 6 #include <GLES3/gl3.h> 7 #include <GLES3/gl3ext.h> 8 #endif 9 #ifdef __APPLE__ 10 #include <OpenGLES/EAGL.h> 11 #include <OpenGLES/ES3/gl.h> 12 #include <OpenGLES/ES3/glext.h> 13 #endif 14 15 #include "EXTypedArrayApi.h" 16 17 #include <exception> 18 #include <future> 19 #include <set> 20 #include <sstream> 21 #include <unordered_map> 22 #include <vector> 23 24 #include <jsi/jsi.h> 25 26 #include "EXJsiUtils.h" 27 #include "EXPlatformUtils.h" 28 #include "EXWebGLRenderer.h" 29 30 // Constants in WebGL that aren't in OpenGL ES 31 // https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Constants 32 33 #define GL_UNPACK_FLIP_Y_WEBGL 0x9240 34 #define GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL 0x9241 35 #define GL_CONTEXT_LOST_WEBGL 0x9242 36 #define GL_UNPACK_COLORSPACE_CONVERSION_WEBGL 0x9243 37 #define GL_BROWSER_DEFAULT_WEBGL 0x9244 38 #define GL_MAX_CLIENT_WAIT_TIMEOUT_WEBGL 0x9247 39 40 #define GL_STENCIL_INDEX 0x1901 41 #define GL_DEPTH_STENCIL 0x84F9 42 #define GL_DEPTH_STENCIL_ATTACHMENT 0x821A 43 44 namespace expo { 45 namespace gl_cpp { 46 47 class EXGLContext { 48 using Op = std::function<void(void)>; 49 using Batch = std::vector<Op>; 50 51 public: EXGLContext(EXGLContextId ctxId)52 EXGLContext(EXGLContextId ctxId) : ctxId(ctxId) {} 53 void prepareContext(jsi::Runtime &runtime, std::function<void(void)> flushMethod); 54 void maybeResolveWorkletContext(jsi::Runtime &runtime); 55 void prepareWorkletContext(); 56 57 // --- Queue handling -------------------------------------------------------- 58 59 // There are two threads: the input thread (henceforth "JS thread") feeds new GL 60 // work, the output thread (henceforth "GL thread", typically UI thread on iOS, 61 // GL thread on Android) reads GL work and performs it 62 63 // Ops are combined into batches: 64 // 1. A batch is always executed entirely in one go on the GL thread 65 // 2. The last add to a batch always precedes the first remove 66 // #2 means that it's good to use an std::vector<...> for this 67 68 // [JS thread] Send the current 'next' batch to GL and make a new 'next' batch 69 void endNextBatch() noexcept; 70 // [JS thread] Add an Op to the 'next' batch 71 void addToNextBatch(Op &&op) noexcept; 72 // [JS thread] Add a blocking operation to the 'next' batch -- waits for the 73 // queued function to run before returning 74 void addBlockingToNextBatch(Op &&op); 75 76 // [JS thread] Enqueue a function and return an EXGL object that will get mapped 77 // to the function's return value when it is called on the GL thread. 78 // 79 // We call these 'futures': a return value from a GL method call that is simply 80 // fed to other GL method calls. The value is never inspected in JS. This 81 // allows us to continue queueing method calls when a method call with a 82 // 'future' return value is encountered: its value won't immediately matter 83 // and is only needed when method calls after it ask for the value, and those 84 // are queued for even later. 85 // 86 // To make it work lookupObject can be called only on GL thread 87 // 88 jsi::Value addFutureToNextBatch( 89 jsi::Runtime &runtime, 90 std::function<unsigned int(void)> &&op) noexcept; 91 92 // [GL thread] Do all the remaining work we can do on the GL thread 93 // triggered by call to flushOnGLThread 94 void flush(void); 95 96 // --- Object mapping -------------------------------------------------------- 97 98 // We err on the side of performance and hope that a global incrementing atomic 99 // unsigned int is enough for object ids. On 'creating' an object we simply 100 // 'reserve' the id by incrementing the atomic counter. Since the mapping is only 101 // set and read on the GL thread, this prevents us from having to maintain a 102 // mutex on the mapping. 103 104 EXGLObjectId createObject(void) noexcept; 105 void destroyObject(EXGLObjectId exglObjId) noexcept; 106 void mapObject(EXGLObjectId exglObjId, GLuint glObj) noexcept; 107 GLuint lookupObject(EXGLObjectId exglObjId) noexcept; 108 109 void tryRegisterOnJSRuntimeDestroy(jsi::Runtime &runtime); 110 glesContext prepareOpenGLESContext(); 111 void maybeReadAndCacheSupportedExtensions(); 112 113 private: 114 // Queue 115 Batch nextBatch; 116 std::vector<Batch> backlog; 117 std::mutex backlogMutex; 118 119 public: 120 EXGLContextId ctxId; 121 // Worklet runtime is stored here only to avoid it passing through Java/Obj-C. 122 // It should only be used in prepareContext and prepareWorkletContext. 123 jsi::Runtime *maybeWorkletRuntime = nullptr; 124 glesContext initialGlesContext; 125 126 // Object mapping 127 std::unordered_map<EXGLObjectId, GLuint> objects; 128 std::atomic_uint nextObjectId = 1; 129 130 bool supportsWebGL2 = false; 131 std::set<const std::string> supportedExtensions; 132 133 // function that calls flush on GL thread - on Android it is passed by JNI 134 std::function<void(void)> flushOnGLThread = [&] {}; 135 136 // OpenGLES state 137 bool needsRedraw = false; 138 GLint defaultFramebuffer = 0; 139 bool unpackFLipY = false; 140 }; 141 142 } // namespace gl_cpp 143 } // namespace expo 144