expo-gl 15.0.0 → 15.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,16 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 15.0.2 — 2024-11-14
14
+
15
+ _This version does not introduce any user-facing changes._
16
+
17
+ ## 15.0.1 — 2024-11-07
18
+
19
+ ### 🐛 Bug fixes
20
+
21
+ - Converted the native view from Objective-C to Swift to fix support for the New Architecture. ([#32675](https://github.com/expo/expo/pull/32675) by [@tsapeta](https://github.com/tsapeta))
22
+
13
23
  ## 15.0.0 — 2024-10-22
14
24
 
15
25
  ### 🛠 Breaking changes
@@ -1,7 +1,7 @@
1
1
  apply plugin: 'com.android.library'
2
2
 
3
3
  group = 'host.exp.exponent'
4
- version = '15.0.0'
4
+ version = '15.0.2'
5
5
 
6
6
  def reactNativeArchitectures() {
7
7
  def value = project.getProperties().get("reactNativeArchitectures")
@@ -26,7 +26,7 @@ android {
26
26
  namespace "expo.modules.gl"
27
27
  defaultConfig {
28
28
  versionCode 31
29
- versionName "15.0.0"
29
+ versionName "15.0.2"
30
30
 
31
31
  externalNativeBuild {
32
32
  cmake {
package/ios/EXGLContext.h CHANGED
@@ -17,12 +17,11 @@
17
17
 
18
18
  @interface EXGLContext : NSObject
19
19
 
20
- - (nullable instancetype)initWithDelegate:(nullable id<EXGLContextDelegate>)delegate
21
- andModuleRegistry:(nonnull EXModuleRegistry *)moduleRegistry;
22
- - (void)initialize;
20
+ - (nonnull instancetype)initWithDelegate:(nullable id<EXGLContextDelegate>)delegate
21
+ andModuleRegistry:(nonnull EXModuleRegistry *)moduleRegistry;
23
22
  - (void)prepare:(nullable void(^)(BOOL))callback andEnableExperimentalWorkletSupport:(BOOL)enableExperimentalWorkletSupport;
24
23
  - (BOOL)isInitialized;
25
- - (nullable EAGLContext *)createSharedEAGLContext;
24
+ - (nonnull EAGLContext *)createSharedEAGLContext;
26
25
  - (void)runAsync:(nonnull void(^)(void))callback;
27
26
  - (void)runInEAGLContext:(nonnull EAGLContext*)context callback:(nonnull void(^)(void))callback;
28
27
  - (void)takeSnapshotWithOptions:(nonnull NSDictionary *)options resolve:(nonnull EXPromiseResolveBlock)resolve reject:(nonnull EXPromiseRejectBlock)reject;
@@ -26,8 +26,8 @@
26
26
 
27
27
  @implementation EXGLContext
28
28
 
29
- - (instancetype)initWithDelegate:(id<EXGLContextDelegate>)delegate
30
- andModuleRegistry:(nonnull EXModuleRegistry *)moduleRegistry
29
+ - (nonnull instancetype)initWithDelegate:(id<EXGLContextDelegate>)delegate
30
+ andModuleRegistry:(nonnull EXModuleRegistry *)moduleRegistry
31
31
  {
32
32
  if (self = [super init]) {
33
33
  self.delegate = delegate;
@@ -39,6 +39,8 @@
39
39
  _isContextReady = NO;
40
40
  _wasPrepareCalled = NO;
41
41
  _appIsBackgrounded = NO;
42
+
43
+ [self initialize];
42
44
  }
43
45
  return self;
44
46
  }
@@ -48,7 +50,7 @@
48
50
  return _isContextReady;
49
51
  }
50
52
 
51
- - (EAGLContext *)createSharedEAGLContext
53
+ - (nonnull EAGLContext *)createSharedEAGLContext
52
54
  {
53
55
  return [[EAGLContext alloc] initWithAPI:[_eaglCtx API] sharegroup:[_eaglCtx sharegroup]];
54
56
  }
@@ -6,7 +6,6 @@
6
6
 
7
7
  #import <ExpoGL/EXGLObjectManager.h>
8
8
  #import <ExpoGL/EXGLObject.h>
9
- #import <ExpoGL/EXGLView.h>
10
9
  #import <ExpoGL/EXGLCameraObject.h>
11
10
 
12
11
  @interface EXGLObjectManager ()
@@ -99,7 +98,6 @@ EX_EXPORT_METHOD_AS(createContextAsync,
99
98
  EXGLContext *glContext = [[EXGLContext alloc] initWithDelegate:nil
100
99
  andModuleRegistry:_moduleRegistry];
101
100
 
102
- [glContext initialize];
103
101
  [glContext prepare:^(BOOL success) {
104
102
  if (success) {
105
103
  resolve(@{ @"exglCtxId": @(glContext.contextId) });
@@ -150,7 +148,7 @@ EX_EXPORT_METHOD_AS(createCameraTextureAsync,
150
148
  id<EXCameraInterface> cameraView = (id<EXCameraInterface>)view;
151
149
 
152
150
  if (glContext == nil) {
153
- reject(@"E_GL_BAD_VIEW_TAG", nil, EXErrorWithMessage(@"ExponentGLObjectManager.createCameraTextureAsync: Expected an EXGLView"));
151
+ reject(@"E_GL_BAD_VIEW_TAG", nil, EXErrorWithMessage(@"ExponentGLObjectManager.createCameraTextureAsync: Expected a GLView"));
154
152
  return;
155
153
  }
156
154
  if (cameraView == nil) {
@@ -0,0 +1,329 @@
1
+ // Copyright 2024-present 650 Industries. All rights reserved.
2
+
3
+ import ExpoModulesCore
4
+
5
+ internal final class GLView: ExpoView, EXGLContextDelegate {
6
+ lazy var glContext: EXGLContext = {
7
+ guard let legacyModuleRegistry = appContext?.legacyModuleRegistry else {
8
+ fatalError("Legacy module registry is not available")
9
+ }
10
+ return EXGLContext(delegate: self, andModuleRegistry: legacyModuleRegistry)
11
+ }()
12
+
13
+ lazy var eaglContext: EAGLContext = glContext.createSharedEAGLContext()
14
+
15
+ // Props
16
+ var msaaSamples: Int = 0
17
+ var enableExperimentalWorkletSupport: Bool = false
18
+
19
+ // Events
20
+ var onSurfaceCreate: EventDispatcher? = EventDispatcher()
21
+
22
+ // GL
23
+ var layerWidth: GLint = 0
24
+ var layerHeight: GLint = 0
25
+ var viewFramebuffer: GLuint = 0
26
+ var viewColorbuffer: GLuint = 0
27
+ var viewDepthStencilbuffer: GLuint = 0
28
+ var msaaFramebuffer: GLuint = 0
29
+ var msaaRenderbuffer: GLuint = 0
30
+
31
+ var displayLink: CADisplayLink?
32
+ var isAfterLayout: Bool = false
33
+ var isRenderbufferPresented: Bool = true
34
+ var viewBuffersSize: CGSize = .zero
35
+
36
+ override static var layerClass: AnyClass {
37
+ return CAEAGLLayer.self
38
+ }
39
+
40
+ required init(appContext: AppContext? = nil) {
41
+ super.init(appContext: appContext)
42
+
43
+ // Set up a draw loop
44
+ displayLink = CADisplayLink(target: self, selector: #selector(drawGL))
45
+ displayLink?.add(to: RunLoop.main, forMode: .common)
46
+
47
+ contentScaleFactor = EXUtilities.screenScale()
48
+
49
+ // Initialize properties of our backing CAEAGLLayer
50
+ if let eaglLayer = layer as? CAEAGLLayer {
51
+ eaglLayer.isOpaque = false
52
+ eaglLayer.drawableProperties = [
53
+ kEAGLDrawablePropertyRetainedBacking: false,
54
+ kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8
55
+ ]
56
+ }
57
+ }
58
+
59
+ func runOnUIThread(_ callback: @escaping () -> Void) {
60
+ DispatchQueue.main.sync {
61
+ glContext.run(in: self.eaglContext, callback: callback)
62
+ }
63
+ }
64
+
65
+ // MARK: - UIView
66
+
67
+ override func layoutSubviews() {
68
+ resizeViewBuffersToWidth(width: contentScaleFactor * frame.size.width, height: contentScaleFactor * frame.size.height)
69
+
70
+ isAfterLayout = true
71
+ glContext.prepare(nil, andEnableExperimentalWorkletSupport: enableExperimentalWorkletSupport)
72
+ maybeCallSurfaceCreated()
73
+ }
74
+
75
+ override func removeFromSuperview() {
76
+ glContext.destroy()
77
+ displayLink?.invalidate()
78
+ displayLink = nil
79
+ super.removeFromSuperview()
80
+ }
81
+
82
+ // MARK: - GL
83
+
84
+ func resizeViewBuffersToWidth(width: Double, height: Double) {
85
+ let newViewBuffersSize = CGSize(width: width, height: height)
86
+
87
+ // Don't resize if size hasn't changed and the current size is not zero
88
+ if CGSizeEqualToSize(newViewBuffersSize, viewBuffersSize) && !CGSizeEqualToSize(viewBuffersSize, .zero) {
89
+ return
90
+ }
91
+
92
+ // update viewBuffersSize on UI thread (before actual resize takes place)
93
+ // to get rid of redundant resizes if layoutSubviews is called multiple times with the same frame size
94
+ viewBuffersSize = newViewBuffersSize
95
+
96
+ // swiftlint:disable:next closure_body_length
97
+ glContext.runAsync { [self] in
98
+ // Save surrounding framebuffer/renderbuffer
99
+ var prevFramebuffer: GLuint = 0
100
+ var prevRenderbuffer: GLuint = 0
101
+
102
+ glGetIntegerv(GLenum(GL_FRAMEBUFFER_BINDING), &prevFramebuffer)
103
+ glGetIntegerv(GLenum(GL_RENDERBUFFER_BINDING), &prevRenderbuffer)
104
+ if prevFramebuffer == viewFramebuffer {
105
+ prevFramebuffer = 0
106
+ }
107
+
108
+ // Delete old buffers if they exist
109
+ deleteViewBuffers()
110
+
111
+ // Set up view framebuffer
112
+ glGenFramebuffers(1, &viewFramebuffer)
113
+ glBindFramebuffer(GLenum(GL_FRAMEBUFFER), viewFramebuffer)
114
+
115
+ // Set up new color renderbuffer
116
+ glGenRenderbuffers(1, &viewColorbuffer)
117
+ glBindRenderbuffer(GLenum(GL_RENDERBUFFER), viewColorbuffer)
118
+
119
+ runOnUIThread { [self] in
120
+ glBindRenderbuffer(GLenum(GL_RENDERBUFFER), viewColorbuffer)
121
+ eaglContext.renderbufferStorage(Int(GL_RENDERBUFFER), from: self.layer as? CAEAGLLayer)
122
+ }
123
+
124
+ glFramebufferRenderbuffer(
125
+ GLenum(GL_FRAMEBUFFER),
126
+ GLenum(GL_COLOR_ATTACHMENT0),
127
+ GLenum(GL_RENDERBUFFER),
128
+ viewColorbuffer
129
+ )
130
+ glGetRenderbufferParameteriv(
131
+ GLenum(GL_RENDERBUFFER),
132
+ GLenum(GL_RENDERBUFFER_WIDTH),
133
+ &layerWidth
134
+ )
135
+ glGetRenderbufferParameteriv(
136
+ GLenum(GL_RENDERBUFFER),
137
+ GLenum(GL_RENDERBUFFER_HEIGHT),
138
+ &layerHeight
139
+ )
140
+
141
+ // Set up MSAA framebuffer/renderbuffer
142
+ glGenFramebuffers(1, &msaaFramebuffer)
143
+ glGenRenderbuffers(1, &msaaRenderbuffer)
144
+ glBindFramebuffer(GLenum(GL_FRAMEBUFFER), msaaFramebuffer)
145
+ glBindRenderbuffer(GLenum(GL_RENDERBUFFER), msaaRenderbuffer)
146
+ glRenderbufferStorageMultisample(
147
+ GLenum(GL_RENDERBUFFER),
148
+ GLsizei(msaaSamples),
149
+ GLenum(GL_RGBA8),
150
+ layerWidth,
151
+ layerHeight
152
+ )
153
+ glFramebufferRenderbuffer(
154
+ GLenum(GL_FRAMEBUFFER),
155
+ GLenum(GL_COLOR_ATTACHMENT0),
156
+ GLenum(GL_RENDERBUFFER),
157
+ msaaRenderbuffer
158
+ )
159
+
160
+ EXGLContextSetDefaultFramebuffer(glContext.contextId, GLint(msaaFramebuffer))
161
+
162
+ // Set up new depth+stencil renderbuffer
163
+ glGenRenderbuffers(1, &viewDepthStencilbuffer)
164
+ glBindRenderbuffer(GLenum(GL_RENDERBUFFER), viewDepthStencilbuffer)
165
+ glRenderbufferStorageMultisample(
166
+ GLenum(GL_RENDERBUFFER),
167
+ GLsizei(msaaSamples),
168
+ GLenum(GL_DEPTH24_STENCIL8),
169
+ layerWidth,
170
+ layerHeight
171
+ )
172
+ glFramebufferRenderbuffer(
173
+ GLenum(GL_FRAMEBUFFER),
174
+ GLenum(GL_DEPTH_ATTACHMENT),
175
+ GLenum(GL_RENDERBUFFER),
176
+ viewDepthStencilbuffer
177
+ )
178
+ glFramebufferRenderbuffer(
179
+ GLenum(GL_FRAMEBUFFER),
180
+ GLenum(GL_STENCIL_ATTACHMENT),
181
+ GLenum(GL_RENDERBUFFER),
182
+ viewDepthStencilbuffer
183
+ )
184
+
185
+ // Resize viewport
186
+ glViewport(0, 0, GLsizei(width), GLsizei(height))
187
+
188
+ // Restore surrounding framebuffer/renderbuffer
189
+ if prevFramebuffer != 0 {
190
+ glBindFramebuffer(GLenum(GL_FRAMEBUFFER), prevFramebuffer)
191
+ }
192
+ glBindRenderbuffer(GLenum(GL_RENDERBUFFER), prevRenderbuffer)
193
+ }
194
+ }
195
+
196
+ func maybeCallSurfaceCreated() {
197
+ // Because initialization things happen asynchronously,
198
+ // we need to be sure that they all are done before we pass GL object to JS.
199
+
200
+ if onSurfaceCreate != nil && glContext.isInitialized() && isAfterLayout {
201
+ onSurfaceCreate?([
202
+ "exglCtxId": glContext.contextId
203
+ ])
204
+
205
+ // unset onSurfaceCreate - it will not be needed anymore
206
+ onSurfaceCreate = nil
207
+ }
208
+ }
209
+
210
+ @objc
211
+ func drawGL() {
212
+ // exglCtxId may be unset if we get here (on the UI thread) before EXGLContextCreate(...) is
213
+ // called on the JS thread to create the EXGL context and save its id (see EXGLContext.initializeContextWithBridge method).
214
+ // In this case no GL work has been sent yet so we skip this frame.
215
+ //
216
+ // _viewFramebuffer may be 0 if we haven't had a layout event yet and so the size of the
217
+ // framebuffer to create is unknown. In this case we have nowhere to render to so we skip
218
+ // this frame (the GL work to run remains on the queue for next time).
219
+
220
+ if glContext.isInitialized() && viewFramebuffer != 0 {
221
+ // Present current state of view buffers
222
+ // This happens exactly at `gl.endFrameEXP()` in the queue
223
+ if viewColorbuffer != 0 && !isRenderbufferPresented {
224
+ // bind renderbuffer and present it on the layer
225
+ glContext.runAsync {
226
+ glBindRenderbuffer(GLenum(GL_RENDERBUFFER), self.viewColorbuffer)
227
+ self.eaglContext.presentRenderbuffer(Int(GL_RENDERBUFFER))
228
+ }
229
+
230
+ // mark renderbuffer as presented
231
+ isRenderbufferPresented = true
232
+ }
233
+ }
234
+ }
235
+
236
+ // [GL thread] blits framebuffers and then sets a flag that informs UI thread
237
+ // about presenting the new content of the renderbuffer on the next draw call
238
+ func blitFramebuffers() {
239
+ if glContext.isInitialized() && viewFramebuffer != 0 && viewColorbuffer != 0 {
240
+ // Save surrounding framebuffer
241
+ var prevFramebuffer: GLuint = 0
242
+ glGetIntegerv(GLenum(GL_FRAMEBUFFER_BINDING), &prevFramebuffer)
243
+ if prevFramebuffer == viewFramebuffer {
244
+ prevFramebuffer = 0
245
+ }
246
+
247
+ // Resolve multisampling and present
248
+ glBindFramebuffer(GLenum(GL_READ_FRAMEBUFFER), msaaFramebuffer)
249
+ glBindFramebuffer(GLenum(GL_DRAW_FRAMEBUFFER), viewFramebuffer)
250
+
251
+ // glBlitFramebuffer works only on OpenGL ES 3.0, so we need a fallback to Apple's extension for OpenGL ES 2.0
252
+ if glContext.eaglCtx.api == .openGLES3 {
253
+ glBlitFramebuffer(
254
+ 0,
255
+ 0,
256
+ layerWidth,
257
+ layerHeight,
258
+ 0,
259
+ 0,
260
+ layerWidth,
261
+ layerHeight,
262
+ GLbitfield(GL_COLOR_BUFFER_BIT),
263
+ GLenum(GL_NEAREST)
264
+ )
265
+ } else {
266
+ glResolveMultisampleFramebufferAPPLE()
267
+ }
268
+
269
+ // Restore surrounding framebuffer
270
+ if prevFramebuffer != 0 {
271
+ glBindFramebuffer(GLenum(GL_FRAMEBUFFER), prevFramebuffer)
272
+ }
273
+
274
+ // mark renderbuffer as not presented
275
+ isRenderbufferPresented = false
276
+ }
277
+ }
278
+
279
+ func deleteViewBuffers() {
280
+ if viewDepthStencilbuffer != 0 {
281
+ glDeleteRenderbuffers(1, &viewDepthStencilbuffer)
282
+ viewDepthStencilbuffer = 0
283
+ }
284
+ if viewColorbuffer != 0 {
285
+ glDeleteRenderbuffers(1, &viewColorbuffer)
286
+ viewColorbuffer = 0
287
+ }
288
+ if viewFramebuffer != 0 {
289
+ glDeleteFramebuffers(1, &viewFramebuffer)
290
+ viewFramebuffer = 0
291
+ }
292
+ if msaaRenderbuffer != 0 {
293
+ glDeleteRenderbuffers(1, &msaaRenderbuffer)
294
+ msaaRenderbuffer = 0
295
+ }
296
+ if msaaFramebuffer != 0 {
297
+ glDeleteFramebuffers(1, &msaaFramebuffer)
298
+ msaaFramebuffer = 0
299
+ }
300
+ }
301
+
302
+ // MARK: - EXGLContextDelegate
303
+
304
+ // [GL thread]
305
+ func glContextFlushed(_ context: EXGLContext) {
306
+ // blit framebuffers if endFrameEXP was called
307
+ if EXGLContextNeedsRedraw(glContext.contextId) {
308
+ // actually draw isn't yet finished, but it's here to prevent blitting the same thing multiple times
309
+ EXGLContextDrawEnded(glContext.contextId)
310
+
311
+ blitFramebuffers()
312
+ }
313
+ }
314
+
315
+ // [JS thread]
316
+ func glContextInitialized(_ context: EXGLContext) {
317
+ maybeCallSurfaceCreated()
318
+ }
319
+
320
+ // [GL thread]
321
+ func glContextWillDestroy(_ context: EXGLContext) {
322
+ // Destroy GL objects owned by us
323
+ deleteViewBuffers()
324
+ }
325
+
326
+ func glContextGetDefaultFramebuffer() -> EXGLObjectId {
327
+ return msaaFramebuffer
328
+ }
329
+ }
@@ -6,7 +6,7 @@ public final class GLViewModule: Module {
6
6
  public func definition() -> ModuleDefinition {
7
7
  Name("ExponentGLView")
8
8
 
9
- View(EXGLView.self) {
9
+ View(GLView.self) {
10
10
  Events("onSurfaceCreate")
11
11
 
12
12
  Prop("msaaSamples") { (view, msaaSamples: Int) in
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-gl",
3
- "version": "15.0.0",
3
+ "version": "15.0.2",
4
4
  "description": "Provides GLView that acts as OpenGL ES render target and gives GL context object implementing WebGL 2.0 specification.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -57,5 +57,5 @@
57
57
  "optional": true
58
58
  }
59
59
  },
60
- "gitHead": "7c7d2362fff23bec26cd145ed34edd9c403551bd"
60
+ "gitHead": "b7b95a93855cb4863b626c4524fc6e8b3a2305eb"
61
61
  }
package/ios/EXGLView.h DELETED
@@ -1,28 +0,0 @@
1
- // Copyright 2016-present 650 Industries. All rights reserved.
2
-
3
- #import <ExpoGL/EXGLNativeApi.h>
4
- #import <ExpoGL/EXGLContext.h>
5
- #import <ExpoModulesCore/EXModuleRegistry.h>
6
- #import <ExpoModulesCore/EXLegacyExpoViewProtocol.h>
7
-
8
- NS_ASSUME_NONNULL_BEGIN
9
-
10
- @interface EXGLView : UIView <EXGLContextDelegate, EXLegacyExpoViewProtocol>
11
-
12
- - (EXGLContextId)exglCtxId;
13
-
14
- // AR
15
- - (void)setArSessionManager:(id)arSessionManager;
16
- - (void)maybeStopARSession;
17
-
18
- @property (nonatomic, copy, nullable) EXDirectEventBlock onSurfaceCreate;
19
- @property (nonatomic, assign) NSInteger msaaSamples;
20
- @property (nonatomic, assign) BOOL enableExperimentalWorkletSupport;
21
-
22
- // "protected"
23
- @property (nonatomic, strong, nullable) EXGLContext *glContext;
24
- @property (nonatomic, strong, nullable) EAGLContext *uiEaglCtx;
25
-
26
- @end
27
-
28
- NS_ASSUME_NONNULL_END
package/ios/EXGLView.mm DELETED
@@ -1,343 +0,0 @@
1
- // Copyright 2016-present 650 Industries. All rights reserved.
2
-
3
- #import <ExpoModulesCore/EXUtilities.h>
4
- #import <ExpoGL/EXGLView.h>
5
- #import <ExpoGL/EXGLContext.h>
6
-
7
- #include <OpenGLES/ES2/glext.h>
8
- #include <OpenGLES/ES3/gl.h>
9
- #include <OpenGLES/ES3/glext.h>
10
-
11
- #define STRINGIZE(x) #x
12
- #define STRINGIZE2(x) STRINGIZE(x)
13
- #define SHADER_STRING(text) @ STRINGIZE2(text)
14
-
15
- @interface EXGLView ()
16
-
17
- @property (nonatomic, assign) GLint layerWidth;
18
- @property (nonatomic, assign) GLint layerHeight;
19
- @property (nonatomic, assign) GLuint viewFramebuffer;
20
- @property (nonatomic, assign) GLuint viewColorbuffer;
21
- @property (nonatomic, assign) GLuint viewDepthStencilbuffer;
22
- @property (nonatomic, assign) GLuint msaaFramebuffer;
23
- @property (nonatomic, assign) GLuint msaaRenderbuffer;
24
- @property (nonatomic, strong) dispatch_queue_t glQueue;
25
-
26
- @property (nonatomic, strong) CADisplayLink *displayLink;
27
-
28
- @property (nonatomic, assign) BOOL isLayouted;
29
- @property (nonatomic, assign) BOOL renderbufferPresented;
30
- @property (nonatomic, assign) CGSize viewBuffersSize;
31
-
32
- @property (nonatomic, weak) id arSessionManager;
33
-
34
- @end
35
-
36
- @implementation EXGLView
37
-
38
- // Specify that we want this UIView to be backed by a CAEAGLLayer
39
- + (Class)layerClass {
40
- return [CAEAGLLayer class];
41
- }
42
-
43
- - (instancetype)initWithModuleRegistry:(EXModuleRegistry *)moduleRegistry
44
- {
45
- if ((self = [super init])) {
46
- _isLayouted = NO;
47
- _renderbufferPresented = YES;
48
- _viewBuffersSize = CGSizeZero;
49
-
50
- self.contentScaleFactor = [EXUtilities screenScale];
51
-
52
- // Initialize properties of our backing CAEAGLLayer
53
- CAEAGLLayer *eaglLayer = (CAEAGLLayer *) self.layer;
54
- eaglLayer.opaque = YES;
55
- eaglLayer.drawableProperties = @{
56
- kEAGLDrawablePropertyRetainedBacking: @(YES),
57
- kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8,
58
- };
59
-
60
- // Initialize GL context
61
- _glContext = [[EXGLContext alloc] initWithDelegate:self andModuleRegistry:moduleRegistry];
62
- _uiEaglCtx = [_glContext createSharedEAGLContext];
63
- [_glContext initialize];
64
-
65
- // View buffers will be created on layout event
66
- _msaaFramebuffer = _msaaRenderbuffer = _viewFramebuffer = _viewColorbuffer = _viewDepthStencilbuffer = 0;
67
-
68
- // Set up a draw loop
69
- _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(draw)];
70
- // _displayLink.preferredFramesPerSecond = 60;
71
- [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
72
- }
73
- return self;
74
- }
75
-
76
- - (EXGLContextId)exglCtxId
77
- {
78
- return [_glContext contextId];
79
- }
80
-
81
- - (void)maybeCallSurfaceCreated
82
- {
83
- // Because initialization things happen asynchronously,
84
- // we need to be sure that they all are done before we pass GL object to JS.
85
-
86
- if (_onSurfaceCreate && _glContext.isInitialized && _isLayouted) {
87
- EXGLContextId exglCtxId = _glContext.contextId;
88
- _onSurfaceCreate(@{ @"exglCtxId": @(exglCtxId) });
89
-
90
- // unset onSurfaceCreate - it will not be needed anymore
91
- _onSurfaceCreate = nil;
92
- }
93
- }
94
-
95
- - (void)layoutSubviews
96
- {
97
- [self resizeViewBuffersToWidth:self.contentScaleFactor * self.frame.size.width
98
- height:self.contentScaleFactor * self.frame.size.height];
99
-
100
- _isLayouted = YES;
101
- [_glContext prepare:nil andEnableExperimentalWorkletSupport:self.enableExperimentalWorkletSupport];
102
- [self maybeCallSurfaceCreated];
103
- }
104
-
105
- - (void)setOnSurfaceCreate:(EXDirectEventBlock)onSurfaceCreate
106
- {
107
- _onSurfaceCreate = onSurfaceCreate;
108
- [self maybeCallSurfaceCreated];
109
- }
110
-
111
- - (void)runOnUIThread:(void(^)(void))callback
112
- {
113
- dispatch_sync(dispatch_get_main_queue(), ^{
114
- [self->_glContext runInEAGLContext:self->_uiEaglCtx callback:callback];
115
- });
116
- }
117
-
118
- - (void)deleteViewBuffers
119
- {
120
- if (_viewDepthStencilbuffer != 0) {
121
- glDeleteRenderbuffers(1, &_viewDepthStencilbuffer);
122
- _viewDepthStencilbuffer = 0;
123
- }
124
- if (_viewColorbuffer != 0) {
125
- glDeleteRenderbuffers(1, &_viewColorbuffer);
126
- _viewColorbuffer = 0;
127
- }
128
- if (_viewFramebuffer != 0) {
129
- glDeleteFramebuffers(1, &_viewFramebuffer);
130
- _viewFramebuffer = 0;
131
- }
132
- if (_msaaRenderbuffer != 0) {
133
- glDeleteRenderbuffers(1, &_msaaRenderbuffer);
134
- _msaaRenderbuffer = 0;
135
- }
136
- if (_msaaFramebuffer != 0) {
137
- glDeleteFramebuffers(1, &_msaaFramebuffer);
138
- _msaaFramebuffer = 0;
139
- }
140
- }
141
-
142
- - (void)resizeViewBuffersToWidth:(short)width height:(short)height
143
- {
144
- CGSize newViewBuffersSize = CGSizeMake(width, height);
145
-
146
- // Don't resize if size hasn't changed and the current size is not zero
147
- if (CGSizeEqualToSize(newViewBuffersSize, _viewBuffersSize) && !CGSizeEqualToSize(_viewBuffersSize, CGSizeZero)) {
148
- return;
149
- }
150
-
151
- // update viewBuffersSize on UI thread (before actual resize takes place)
152
- // to get rid of redundant resizes if layoutSubviews is called multiple times with the same frame size
153
- _viewBuffersSize = newViewBuffersSize;
154
-
155
- [_glContext runAsync:^{
156
- // Save surrounding framebuffer/renderbuffer
157
- GLint prevFramebuffer;
158
- GLint prevRenderbuffer;
159
- glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFramebuffer);
160
- glGetIntegerv(GL_RENDERBUFFER_BINDING, &prevRenderbuffer);
161
- if (prevFramebuffer == self->_viewFramebuffer) {
162
- prevFramebuffer = 0;
163
- }
164
-
165
- // Delete old buffers if they exist
166
- [self deleteViewBuffers];
167
-
168
- // Set up view framebuffer
169
- glGenFramebuffers(1, &self->_viewFramebuffer);
170
- glBindFramebuffer(GL_FRAMEBUFFER, self->_viewFramebuffer);
171
-
172
- // Set up new color renderbuffer
173
- glGenRenderbuffers(1, &self->_viewColorbuffer);
174
- glBindRenderbuffer(GL_RENDERBUFFER, self->_viewColorbuffer);
175
-
176
- [self runOnUIThread:^{
177
- glBindRenderbuffer(GL_RENDERBUFFER, self->_viewColorbuffer);
178
- [self->_uiEaglCtx renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
179
- }];
180
-
181
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
182
- GL_RENDERBUFFER, self->_viewColorbuffer);
183
- glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &self->_layerWidth);
184
- glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &self->_layerHeight);
185
-
186
- // Set up MSAA framebuffer/renderbuffer
187
- glGenFramebuffers(1, &self->_msaaFramebuffer);
188
- glGenRenderbuffers(1, &self->_msaaRenderbuffer);
189
- glBindFramebuffer(GL_FRAMEBUFFER, self->_msaaFramebuffer);
190
- glBindRenderbuffer(GL_RENDERBUFFER, self->_msaaRenderbuffer);
191
- glRenderbufferStorageMultisample(GL_RENDERBUFFER, self.msaaSamples, GL_RGBA8,
192
- self->_layerWidth, self->_layerHeight);
193
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
194
- GL_RENDERBUFFER, self->_msaaRenderbuffer);
195
-
196
- EXGLContextSetDefaultFramebuffer(self->_glContext.contextId, self->_msaaFramebuffer);
197
-
198
- // Set up new depth+stencil renderbuffer
199
- glGenRenderbuffers(1, &self->_viewDepthStencilbuffer);
200
- glBindRenderbuffer(GL_RENDERBUFFER, self->_viewDepthStencilbuffer);
201
- glRenderbufferStorageMultisample(GL_RENDERBUFFER, self.msaaSamples, GL_DEPTH24_STENCIL8,
202
- self->_layerWidth, self->_layerHeight);
203
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
204
- GL_RENDERBUFFER, self->_viewDepthStencilbuffer);
205
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
206
- GL_RENDERBUFFER, self->_viewDepthStencilbuffer);
207
-
208
- // Resize viewport
209
- glViewport(0, 0, width, height);
210
-
211
- // Restore surrounding framebuffer/renderbuffer
212
- if (prevFramebuffer != 0) {
213
- glBindFramebuffer(GL_FRAMEBUFFER, prevFramebuffer);
214
- }
215
- glBindRenderbuffer(GL_RENDERBUFFER, prevRenderbuffer);
216
-
217
- // TODO(nikki): Notify JS component of resize
218
- }];
219
- }
220
-
221
- // TODO(nikki): Should all this be done in `dealloc` instead?
222
- - (void)removeFromSuperview
223
- {
224
- // Destroy EXGLContext
225
- [_glContext destroy];
226
-
227
- // Stop draw loop
228
- [_displayLink invalidate];
229
- _displayLink = nil;
230
-
231
- [super removeFromSuperview];
232
- }
233
-
234
- - (void)draw
235
- {
236
- // exglCtxId may be unset if we get here (on the UI thread) before EXGLContextCreate(...) is
237
- // called on the JS thread to create the EXGL context and save its id (see EXGLContext.initializeContextWithBridge method).
238
- // In this case no GL work has been sent yet so we skip this frame.
239
- //
240
- // _viewFramebuffer may be 0 if we haven't had a layout event yet and so the size of the
241
- // framebuffer to create is unknown. In this case we have nowhere to render to so we skip
242
- // this frame (the GL work to run remains on the queue for next time).
243
-
244
- if (_glContext.isInitialized && _viewFramebuffer != 0) {
245
- // Present current state of view buffers
246
- // This happens exactly at `gl.endFrameEXP()` in the queue
247
- if (_viewColorbuffer != 0 && !_renderbufferPresented) {
248
- // bind renderbuffer and present it on the layer
249
- [_glContext runAsync:^{
250
- glBindRenderbuffer(GL_RENDERBUFFER, self->_viewColorbuffer);
251
- [self->_uiEaglCtx presentRenderbuffer:GL_RENDERBUFFER];
252
- }];
253
-
254
- // mark renderbuffer as presented
255
- _renderbufferPresented = YES;
256
- }
257
- }
258
- }
259
-
260
- // [GL thread] blits framebuffers and then sets a flag that informs UI thread
261
- // about presenting the new content of the renderbuffer on the next draw call
262
- - (void)blitFramebuffers
263
- {
264
- if (_glContext.isInitialized && _viewFramebuffer != 0 && _viewColorbuffer != 0) {
265
- // Save surrounding framebuffer
266
- GLint prevFramebuffer;
267
- glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFramebuffer);
268
- if (prevFramebuffer == _viewFramebuffer) {
269
- prevFramebuffer = 0;
270
- }
271
-
272
- // Resolve multisampling and present
273
- glBindFramebuffer(GL_READ_FRAMEBUFFER, _msaaFramebuffer);
274
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _viewFramebuffer);
275
-
276
- // glBlitFramebuffer works only on OpenGL ES 3.0, so we need a fallback to Apple's extension for OpenGL ES 2.0
277
- if (_glContext.eaglCtx.API == kEAGLRenderingAPIOpenGLES3) {
278
- glBlitFramebuffer(0, 0, _layerWidth, _layerHeight, 0, 0, _layerWidth, _layerHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
279
- } else {
280
- glResolveMultisampleFramebufferAPPLE();
281
- }
282
-
283
- // Restore surrounding framebuffer
284
- if (prevFramebuffer != 0) {
285
- glBindFramebuffer(GL_FRAMEBUFFER, prevFramebuffer);
286
- }
287
-
288
- // mark renderbuffer as not presented
289
- _renderbufferPresented = NO;
290
- }
291
- }
292
-
293
- #pragma mark - EXGLContextDelegate
294
-
295
- // [GL thread]
296
- - (void)glContextFlushed:(nonnull EXGLContext *)context
297
- {
298
- // blit framebuffers if endFrameEXP was called
299
- if (EXGLContextNeedsRedraw(_glContext.contextId)) {
300
- // actually draw isn't yet finished, but it's here to prevent blitting the same thing multiple times
301
- EXGLContextDrawEnded(_glContext.contextId);
302
-
303
- [self blitFramebuffers];
304
- }
305
- }
306
-
307
- // [JS thread]
308
- - (void)glContextInitialized:(nonnull EXGLContext *)context
309
- {
310
- [self maybeCallSurfaceCreated];
311
- }
312
-
313
- // [GL thread]
314
- - (void)glContextWillDestroy:(nonnull EXGLContext *)context
315
- {
316
- // Stop AR session if running
317
- [self maybeStopARSession];
318
-
319
- // Destroy GL objects owned by us
320
- [self deleteViewBuffers];
321
- }
322
-
323
- - (EXGLObjectId)glContextGetDefaultFramebuffer
324
- {
325
- return _msaaFramebuffer;
326
- }
327
-
328
- #pragma mark - maybe AR
329
-
330
- - (void)setArSessionManager:(id)arSessionManager
331
- {
332
- _arSessionManager = arSessionManager;
333
- }
334
-
335
- - (void)maybeStopARSession
336
- {
337
- if (_arSessionManager) {
338
- [_arSessionManager stop];
339
- _arSessionManager = nil;
340
- }
341
- }
342
-
343
- @end