[ios] Ensure glFinish is called when going into the background, and block rendering in the background (#14439)

This commit is contained in:
Julian Rex
2019-04-16 22:49:34 -04:00
committed by GitHub
parent 395f17f615
commit 425553646b
2 changed files with 73 additions and 8 deletions

View File

@@ -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

View File

@@ -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)