Skip to content

Instantly share code, notes, and snippets.

@danmaas
Created January 19, 2022 22:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danmaas/4f3341b75a147d0c3ffc588a0f4c9b92 to your computer and use it in GitHub Desktop.
Save danmaas/4f3341b75a147d0c3ffc588a0f4c9b92 to your computer and use it in GitHub Desktop.
expo-gl 11.0.3 patch / concurrency and background rendering
diff --git a/node_modules/expo-gl/ios/EXGL/EXGLContext.mm b/node_modules/expo-gl/ios/EXGL/EXGLContext.mm
index 0a904ab..2bd2fdf 100644
--- a/node_modules/expo-gl/ios/EXGL/EXGLContext.mm
+++ b/node_modules/expo-gl/ios/EXGL/EXGLContext.mm
@@ -18,6 +18,7 @@ @interface EXGLContext ()
@property (nonatomic, strong) dispatch_queue_t glQueue;
@property (nonatomic, weak) EXModuleRegistry *moduleRegistry;
@property (nonatomic, weak) EXGLObjectManager *objectManager;
+@property (atomic) BOOL appIsBackground; // tracks the app's foreground/background state
@end
@@ -32,6 +33,7 @@ - (instancetype)initWithDelegate:(id<EXGLContextDelegate>)delegate andModuleRegi
_objectManager = (EXGLObjectManager *)[_moduleRegistry getExportedModuleOfClass:[EXGLObjectManager class]];
_glQueue = dispatch_queue_create("host.exp.gl", DISPATCH_QUEUE_SERIAL);
_eaglCtx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3] ?: [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
+ _appIsBackground = NO;
}
return self;
}
@@ -48,6 +50,11 @@ - (EAGLContext *)createSharedEAGLContext
- (void)runInEAGLContext:(EAGLContext*)context callback:(void(^)(void))callback
{
+ if (_appIsBackground) {
+ // iOS does not allow calls to OpenGL when the app is in background
+ return;
+ }
+
[EAGLContext setCurrentContext:context];
callback();
glFlush();
@@ -86,6 +93,16 @@ - (void)initialize:(void(^)(BOOL))callback
self->_contextId = UEXGLContextCreate(jsRuntimePtr);
[self->_objectManager saveContext:self];
+ // listen for foreground/background transitions
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(onApplicationDidBecomeActive:)
+ name:UIApplicationDidBecomeActiveNotification
+ object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(onApplicationWillResignActive:)
+ name:UIApplicationWillResignActiveNotification
+ object:nil];
+
UEXGLContextSetFlushMethodObjc(self->_contextId, ^{
[self flush];
});
@@ -112,6 +129,21 @@ - (void)flush
}];
}
+- (void) onApplicationWillResignActive:(NSNotification *) note
+{
+ // flush all pending GL activity, wait for completion, and then set the appIsBackground flag
+ [self runAsync:^{
+ glFinish();
+ self->_appIsBackground = YES;
+ }];
+}
+
+- (void) onApplicationDidBecomeActive:(NSNotification *) note {
+ // unset the appIsBackground flag so that rendering can resume
+ _appIsBackground = NO;
+ // [self flush];
+}
+
- (void)destroy
{
[self runAsync:^{
@@ -119,6 +151,10 @@ - (void)destroy
[self.delegate glContextWillDestroy:self];
}
+ // stop listening for foreground/background transitions
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
+
// Flush all the stuff
UEXGLContextFlush(self->_contextId);
diff --git a/node_modules/expo-gl/ios/EXGL/EXGLView.mm b/node_modules/expo-gl/ios/EXGL/EXGLView.mm
index e7ca617..324e148 100644
--- a/node_modules/expo-gl/ios/EXGL/EXGLView.mm
+++ b/node_modules/expo-gl/ios/EXGL/EXGLView.mm
@@ -235,6 +235,8 @@ - (void)removeFromSuperview
- (void)draw
{
+ @synchronized(self) { // DJM
+
// exglCtxId may be unset if we get here (on the UI thread) before UEXGLContextCreate(...) is
// called on the JS thread to create the EXGL context and save its id (see EXGLContext.initializeContextWithBridge method).
// In this case no GL work has been sent yet so we skip this frame.
@@ -248,7 +250,7 @@ - (void)draw
// This happens exactly at `gl.endFrameEXP()` in the queue
if (_viewColorbuffer != 0 && !_renderbufferPresented) {
// bind renderbuffer and present it on the layer
- [_glContext runInEAGLContext:_uiEaglCtx callback:^{
+ [_glContext runAsync:^{
glBindRenderbuffer(GL_RENDERBUFFER, self->_viewColorbuffer);
[self->_uiEaglCtx presentRenderbuffer:GL_RENDERBUFFER];
}];
@@ -257,12 +259,14 @@ - (void)draw
_renderbufferPresented = YES;
}
}
+ } // @synchronized(self)
}
// [GL thread] blits framebuffers and then sets a flag that informs UI thread
// about presenting the new content of the renderbuffer on the next draw call
- (void)blitFramebuffers
{
+ @synchronized(self) { // DJM
if (_glContext.isInitialized && _viewFramebuffer != 0 && _viewColorbuffer != 0) {
// Save surrounding framebuffer
GLint prevFramebuffer;
@@ -290,6 +294,7 @@ - (void)blitFramebuffers
// mark renderbuffer as not presented
_renderbufferPresented = NO;
}
+ } // @synchronized(self)
}
#pragma mark - EXGLContextDelegate
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment