diff options
-rw-r--r-- | platform/ios/CHANGELOG.md | 1 | ||||
-rw-r--r-- | platform/ios/src/MGLMapView.mm | 80 |
2 files changed, 73 insertions, 8 deletions
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 16cfcd365a..d20967c600 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -7,6 +7,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Fixed an Interface Builder crash when using an `MGLMapView` in a storyboard. ([#14379](https://github.com/mapbox/mapbox-gl-native/pull/14379)) * Fix a bug that wrong position of attribution dialog after rotation. ([#14185](https://github.com/mapbox/mapbox-gl-native/pull/14185)) * Fixed a bug where non-opaque `UIColor` values were ignored when assigned to a style layer color property. ([#14406](https://github.com/mapbox/mapbox-gl-native/pull/14406)) +* Speculatively fixed a bug where GL rendering could occur in the background. ([#14439](https://github.com/mapbox/mapbox-gl-native/pull/14439)) ## 4.10.0 diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index bbce6be961..9a0fcd242a 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -256,6 +256,7 @@ public: @property (nonatomic) MGLUserLocation *userLocation; @property (nonatomic) NSMutableDictionary<NSString *, NSMutableArray<MGLAnnotationView *> *> *annotationViewReuseQueueByIdentifier; @property (nonatomic) BOOL enablePresentsWithTransaction; +@property (nonatomic) UIImage *lastSnapshotImage; /// Experimental rendering performance measurement. @property (nonatomic) BOOL experimental_enableFrameRateMeasurement; @@ -635,9 +636,11 @@ public: // observe app activity // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willTerminate) name:UIApplicationWillTerminateNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sleepGL:) name:UIApplicationDidEnterBackgroundNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wakeGL:) name:UIApplicationWillEnterForegroundNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wakeGL:) name:UIApplicationDidBecomeActiveNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willResignActive:) name:UIApplicationWillResignActiveNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil]; + // As of 3.7.5, we intentionally do not listen for `UIApplicationWillResignActiveNotification` or call `sleepGL:` in response to it, as doing // so causes a loop when asking for location permission. See: https://github.com/mapbox/mapbox-gl-native/issues/11225 @@ -831,10 +834,12 @@ public: { MGLAssertIsMainThread(); - if ( ! self.dormant) + if ( ! self.dormant && _rendererFrontend) { _rendererFrontend->reduceMemoryUse(); } + + self.lastSnapshotImage = nil; } #pragma mark - Layout - @@ -984,7 +989,7 @@ public: // This is the delegate of the GLKView object's display call. - (void)glkView:(__unused GLKView *)view drawInRect:(__unused CGRect)rect { - if ( ! self.dormant || ! _rendererFrontend) + if ( ! self.dormant && _rendererFrontend) { _rendererFrontend->render(); } @@ -1127,6 +1132,13 @@ public: if (displayLink && displayLink != _displayLink) { return; } + + // Check to ensure rendering doesn't occur in the background + if (([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) && + ![self supportsBackgroundRendering]) + { + return; + } if (_needsDisplayRefresh) { @@ -1465,6 +1477,55 @@ public: [self setNeedsLayout]; } +#pragma mark - Application lifecycle +- (void)willResignActive:(NSNotification *)notification +{ + if ([self supportsBackgroundRendering]) + { + return; + } + + self.lastSnapshotImage = self.glView.snapshot; + + // For OpenGL this calls glFinish as recommended in + // https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/ImplementingaMultitasking-awareOpenGLESApplication/ImplementingaMultitasking-awareOpenGLESApplication.html#//apple_ref/doc/uid/TP40008793-CH5-SW1 + // reduceMemoryUse(), calls performCleanup(), which calls glFinish + if (_rendererFrontend) + { + _rendererFrontend->reduceMemoryUse(); + } +} + +- (void)didEnterBackground:(NSNotification *)notification +{ + [self sleepGL:notification]; +} + +- (void)willEnterForeground:(NSNotification *)notification +{ + // Do nothing, currently if wakeGL is called here it's a no-op. +} + +- (void)didBecomeActive:(NSNotification *)notification +{ + [self wakeGL:notification]; + self.lastSnapshotImage = nil; +} + +#pragma mark - GL / display link wake/sleep + +- (BOOL)supportsBackgroundRendering +{ + // If this view targets an external display, such as AirPlay or CarPlay, we + // can safely continue to render OpenGL content without tripping + // gpus_ReturnNotPermittedKillClient in libGPUSupportMercury, because the + // external connection keeps the application from truly receding to the + // background. + return (self.window.screen != [UIScreen mainScreen]); +} + + + - (void)sleepGL:(__unused NSNotification *)notification { // If this view targets an external display, such as AirPlay or CarPlay, we @@ -1472,7 +1533,7 @@ public: // gpus_ReturnNotPermittedKillClient in libGPUSupportMercury, because the // external connection keeps the application from truly receding to the // background. - if (self.window.screen != [UIScreen mainScreen]) + if ([self supportsBackgroundRendering]) { return; } @@ -1485,7 +1546,10 @@ public: // Compromise position: release everything but currently rendering tiles // A possible improvement would be to store a copy of the GL buffers that we could use to rapidly // restart, but that we could also discard in response to a memory warning. - _rendererFrontend->reduceMemoryUse(); + if (_rendererFrontend) + { + _rendererFrontend->reduceMemoryUse(); + } if ( ! self.dormant) { @@ -1505,7 +1569,7 @@ public: [self insertSubview:self.glSnapshotView aboveSubview:self.glView]; } - self.glSnapshotView.image = self.glView.snapshot; + self.glSnapshotView.image = self.lastSnapshotImage; self.glSnapshotView.hidden = NO; if (self.debugMask && [self.glSnapshotView.subviews count] == 0) |