summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--platform/ios/CHANGELOG.md1
-rw-r--r--platform/ios/src/MGLMapView.mm80
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)