diff options
author | Julian Rex <julian.rex@mapbox.com> | 2019-04-14 00:25:18 -0400 |
---|---|---|
committer | Julian Rex <julian.rex@mapbox.com> | 2019-04-16 11:38:41 -0400 |
commit | 4ad8de0be6ea5e0a0b1395c05e222b9540b7b3e0 (patch) | |
tree | 08e2ff3a885c05220cef874a24fae68f9925d651 | |
parent | afbd6e8e630c901c57e09197606cd07c1115547d (diff) | |
download | qtlocation-mapboxgl-4ad8de0be6ea5e0a0b1395c05e222b9540b7b3e0.tar.gz |
[ios] Replace flush/glFinish call with call to reduceMemoryUse()
-rw-r--r-- | platform/ios/Integration Tests/MGLBackgroundIntegrationTest.m | 92 | ||||
-rw-r--r-- | platform/ios/src/MGLMapView.mm | 14 |
2 files changed, 97 insertions, 9 deletions
diff --git a/platform/ios/Integration Tests/MGLBackgroundIntegrationTest.m b/platform/ios/Integration Tests/MGLBackgroundIntegrationTest.m index b4d3eed4e8..a6932dd60c 100644 --- a/platform/ios/Integration Tests/MGLBackgroundIntegrationTest.m +++ b/platform/ios/Integration Tests/MGLBackgroundIntegrationTest.m @@ -266,4 +266,96 @@ typedef void (^MGLNotificationBlock)(NSNotification*); XCTAssert(self.mapView.application.applicationState == UIApplicationStateActive); } +- (void)testRendererDelayingAdjustingViewsWhenInBackground { + + XCTAssertFalse(self.mapView.isDormant); + XCTAssertFalse(self.mapView.displayLink.isPaused); + XCTAssert(self.mapView.application.applicationState == UIApplicationStateActive); + + __weak typeof(self) weakSelf = self; + + // + // Enter background + // + + XCTestExpectation *didEnterBackgroundExpectation = [self expectationWithDescription:@"didEnterBackground"]; + didEnterBackgroundExpectation.expectedFulfillmentCount = 1; + didEnterBackgroundExpectation.assertForOverFulfill = YES; + + XCTestExpectation *adjustedViewsExpectation = [self expectationWithDescription:@"adjustedViewsExpectation"]; + adjustedViewsExpectation.expectedFulfillmentCount = 1; + adjustedViewsExpectation.assertForOverFulfill = YES; + + __block NSInteger displayLinkCount = 0; + + self.displayLinkDidUpdate = ^{ + displayLinkCount++; + }; + + NSTimeInterval delay = 5.0; + + self.didEnterBackground = ^(__unused NSNotification *notification){ + typeof(self) strongSelf = weakSelf; + MGLMapView *mapView = strongSelf.mapView; + + // In general, because order of notifications is not guaranteed + // the following asserts are somewhat meaningless (don't do this in + // production) - however, because we're mocking their delivery (and + // we're tracking a bug)... + + // MGLMapView responds to UIApplicationDidEnterBackgroundNotification and + // marks the map view as dormant. However, depending on the order of + // creation it's totally possible for client code also responding to + // this notification to be called first - and then trigger a scenario where + // GL can be rendering in the background - which can cause crashes. + + MGLTestAssert(strongSelf, !mapView.isDormant); + + // However, the display should be paused (because this has now moved + // to ...WillResignActive... + MGLTestAssert(strongSelf, mapView.displayLink.isPaused); + + // Remove the map view, and re-add to try and force a bad situation + // This will delete/re-create the display link + UIView *parentView = mapView.superview; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + + displayLinkCount = 0; + + NSLog(@"Removing MGLMapView from super view"); + [mapView removeFromSuperview]; + + // Re-add + NSLog(@"Re-adding MGLMapView as child"); + [parentView addSubview:mapView]; + + MGLTestAssert(strongSelf, displayLinkCount == 0, @"updateDisplayLink was called %ld times", displayLinkCount); + + [mapView.topAnchor constraintEqualToAnchor:parentView.topAnchor].active = YES; + [mapView.leftAnchor constraintEqualToAnchor:parentView.leftAnchor].active = YES; + [mapView.rightAnchor constraintEqualToAnchor:parentView.rightAnchor].active = YES; + [mapView.bottomAnchor constraintEqualToAnchor:parentView.bottomAnchor].active = YES; + + [adjustedViewsExpectation fulfill]; + }); + + [didEnterBackgroundExpectation fulfill]; + }; + + [self.mockApplication enterBackground]; + [self waitForExpectations:@[didEnterBackgroundExpectation] timeout:1.0]; + + XCTAssert(self.mapView.isDormant); + + // TODO: What do we want here? + XCTAssert(!self.mapView.displayLink || self.mapView.displayLink.isPaused); + XCTAssert(self.mapView.application.applicationState == UIApplicationStateBackground); + + + [self waitForExpectations:@[adjustedViewsExpectation] timeout:delay]; + XCTAssert(self.mapView.isDormant); + XCTAssert(!self.mapView.displayLink || self.mapView.displayLink.isPaused); +} + @end diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 86b4c51c49..6f053f0ac2 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1524,7 +1524,7 @@ public: [self resumeGL]; } -- (BOOL)supportBackgroundRendering +- (BOOL)supportsBackgroundRendering { // If this view targets an external display, such as AirPlay or CarPlay, we // can safely continue to render OpenGL content without tripping @@ -1536,7 +1536,7 @@ public: - (void)resignGL { - if ([self supportBackgroundRendering]) + if ([self supportsBackgroundRendering]) { return; } @@ -1550,15 +1550,16 @@ public: // 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->flush(); + _rendererFrontend->reduceMemoryUse(); } } - (void)sleepGL:(__unused NSNotification *)notification { - if ([self supportBackgroundRendering]) + if ([self supportsBackgroundRendering]) { return; } @@ -1605,11 +1606,6 @@ public: [self.glView deleteDrawable]; } - - if (_rendererFrontend) - { - _rendererFrontend->flush(); - } } - (void)wakeGL:(__unused NSNotification *)notification |