Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
[ios] Enable/disable presentsWithTransaction to address #14232 (#14307)
Browse files Browse the repository at this point in the history
  • Loading branch information
julianrex authored and Julian Rex committed Apr 3, 2019
1 parent ec3f115 commit c279059
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 12 deletions.
2 changes: 2 additions & 0 deletions platform/ios/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT

* Added `MGLOrnamentPosition` enum and margin properties to customize scale bar, compass, logo, and attribution position within the map view. ([#13911](https://github.com/mapbox/mapbox-gl-native/pull/13911))
* Added an `MGLMapView.prefetchesTiles` property to configure lower-resolution tile prefetching behavior. ([#14031](https://github.com/mapbox/mapbox-gl-native/pull/14031))
* Speculatively fixed a performance issue seen on iOS 12.2, when an `MGLMapView` is repeatedly removed and re-added in a view hierarchy. ([#14264](https://github.com/mapbox/mapbox-gl-native/pull/14264))


## 4.9.0 - February 27, 2019

Expand Down
152 changes: 140 additions & 12 deletions platform/ios/src/MGLMapView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ typedef NS_ENUM(NSUInteger, MGLUserTrackingState) {

const CGSize MGLAnnotationAccessibilityElementMinimumSize = CGSizeMake(10, 10);

/// The number of view annotations (excluding the user location view) that must
/// be descendents of `MGLMapView` before presentsWithTransaction is enabled.
static const NSUInteger MGLPresentsWithTransactionAnnotationCount = 0;

/// An indication that the requested annotation was not found or is nonexistent.
enum { MGLAnnotationTagNotFound = UINT32_MAX };

Expand Down Expand Up @@ -252,6 +256,7 @@ @interface MGLMapView () <UIGestureRecognizerDelegate,
@property (nonatomic) MGLAnnotationContainerView *annotationContainerView;
@property (nonatomic) MGLUserLocation *userLocation;
@property (nonatomic) NSMutableDictionary<NSString *, NSMutableArray<MGLAnnotationView *> *> *annotationViewReuseQueueByIdentifier;
@property (nonatomic) BOOL enablePresentsWithTransaction;

/// Experimental rendering performance measurement.
@property (nonatomic) BOOL experimental_enableFrameRateMeasurement;
Expand Down Expand Up @@ -328,6 +333,9 @@ @implementation MGLMapView
CFTimeInterval _frameCounterStartTime;
NSInteger _frameCount;
CFTimeInterval _frameDurations;

BOOL _atLeastiOS_12_2_0;

}

#pragma mark - Setup & Teardown -
Expand Down Expand Up @@ -441,6 +449,7 @@ - (void)commonInit
{
_isTargetingInterfaceBuilder = NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent;
_opaque = NO;
_atLeastiOS_12_2_0 = [NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){12,2,0}];

BOOL background = [UIApplication sharedApplication].applicationState == UIApplicationStateBackground;
if (!background)
Expand Down Expand Up @@ -675,6 +684,20 @@ - (void)commonInit
static_cast<uint32_t>(self.glView.drawableHeight) };
}

+ (GLKView *)GLKViewWithFrame:(CGRect)frame context:(EAGLContext *)context opaque:(BOOL)opaque
{
GLKView *glView = [[GLKView alloc] initWithFrame:frame context:context];
glView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
glView.enableSetNeedsDisplay = NO;
glView.drawableStencilFormat = GLKViewDrawableStencilFormat8;
glView.drawableDepthFormat = GLKViewDrawableDepthFormat16;
glView.contentScaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale];
glView.layer.opaque = opaque;
glView.contentMode = UIViewContentModeCenter;

return glView;
}

- (void)createGLView
{
if (_context) return;
Expand All @@ -686,21 +709,14 @@ - (void)createGLView

// create GL view
//
_glView = [[GLKView alloc] initWithFrame:self.bounds context:_context];
_glView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_glView.enableSetNeedsDisplay = NO;
_glView.drawableStencilFormat = GLKViewDrawableStencilFormat8;
_glView.drawableDepthFormat = GLKViewDrawableDepthFormat16;
_glView.contentScaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale];
_glView.layer.opaque = _opaque;
_glView = [MGLMapView GLKViewWithFrame:self.bounds context:_context opaque:_opaque];
_glView.delegate = self;

CAEAGLLayer *eaglLayer = MGL_OBJC_DYNAMIC_CAST(_glView.layer, CAEAGLLayer);
eaglLayer.presentsWithTransaction = YES;
eaglLayer.presentsWithTransaction = NO;

[_glView bindDrawable];
[self insertSubview:_glView atIndex:0];
_glView.contentMode = UIViewContentModeCenter;
}

- (UIImage *)compassImage
Expand Down Expand Up @@ -1095,6 +1111,13 @@ - (void)updateFromDisplayLink:(CADisplayLink *)displayLink
{
MGLAssertIsMainThread();

// Not "visible" - this isn't a full definition of visibility, but if
// the map view doesn't have a window then it *cannot* be visible.
if (!self.window) {
return;
}

// Mismatched display link
if (displayLink && displayLink != _displayLink) {
return;
}
Expand All @@ -1107,8 +1130,28 @@ - (void)updateFromDisplayLink:(CADisplayLink *)displayLink
[self updateUserLocationAnnotationView];
[self updateAnnotationViews];
[self updateCalloutView];

[self.glView display];

#ifdef MGL_RECREATE_GL_IN_AN_EMERGENCY
// See https://github.com/mapbox/mapbox-gl-native/issues/14232
// glClear can be blocked for 1 second. This code is an "escape hatch",
// an attempt to detect this situation and rebuild the GL views.
if (self.enablePresentsWithTransaction && _atLeastiOS_12_2_0)
{
CFTimeInterval before = CACurrentMediaTime();
[self.glView display];
CFTimeInterval after = CACurrentMediaTime();

if (after-before >= 1.0) {
dispatch_async(dispatch_get_main_queue(), ^{
[self emergencyRecreateGL];
});
}
}
else
#endif
{
[self.glView display];
}
}

if (self.experimental_enableFrameRateMeasurement)
Expand Down Expand Up @@ -1225,15 +1268,96 @@ - (void)setPreferredFramesPerSecond:(MGLMapViewPreferredFramesPerSecond)preferre
[self updateDisplayLinkPreferredFramesPerSecond];
}

- (void)setEnablePresentsWithTransaction:(BOOL)enablePresentsWithTransaction
{
if (_enablePresentsWithTransaction == enablePresentsWithTransaction)
{
return;
}

_enablePresentsWithTransaction = enablePresentsWithTransaction;

// If the map is visible, change the layer property too
if (self.window) {
CAEAGLLayer *eaglLayer = MGL_OBJC_DYNAMIC_CAST(_glView.layer, CAEAGLLayer);
eaglLayer.presentsWithTransaction = enablePresentsWithTransaction;
}
}

#ifdef MGL_RECREATE_GL_IN_AN_EMERGENCY
// See https://github.com/mapbox/mapbox-gl-native/issues/14232
- (void)emergencyRecreateGL {
MGLLogError(@"Rendering took too long - creating GL views");

CAEAGLLayer *eaglLayer = MGL_OBJC_DYNAMIC_CAST(_glView.layer, CAEAGLLayer);
eaglLayer.presentsWithTransaction = NO;

[self sleepGL:nil];

// Just performing a sleepGL/wakeGL pair isn't sufficient - in this case
// we can still get errors when calling bindDrawable. Here we completely
// recreate the GLKView

[self.userLocationAnnotationView removeFromSuperview];
[_glView removeFromSuperview];

_glView = [MGLMapView GLKViewWithFrame:self.bounds context:_context opaque:_opaque];
_glView.delegate = self;

[self insertSubview:_glView atIndex:0];

if (self.annotationContainerView)
{
[_glView insertSubview:self.annotationContainerView atIndex:0];
}

[self updateUserLocationAnnotationView];

// Do not bind...yet

if (self.window) {
[self wakeGL:nil];
CAEAGLLayer *eaglLayer = MGL_OBJC_DYNAMIC_CAST(_glView.layer, CAEAGLLayer);
eaglLayer.presentsWithTransaction = self.enablePresentsWithTransaction;
}
else {
MGLLogDebug(@"No window - skipping wakeGL");
}
}
#endif

- (void)willMoveToWindow:(UIWindow *)newWindow {
[super willMoveToWindow:newWindow];
[self refreshSupportedInterfaceOrientationsWithWindow:newWindow];

if (!newWindow)
{
// See https://github.com/mapbox/mapbox-gl-native/issues/14232
// In iOS 12.2, CAEAGLLayer.presentsWithTransaction can cause dramatic
// slow down. The exact cause of this is unknown, but this work around
// appears to lessen the effects.
//
// Also, consider calling the new mbgl::Renderer::flush()
CAEAGLLayer *eaglLayer = MGL_OBJC_DYNAMIC_CAST(_glView.layer, CAEAGLLayer);
eaglLayer.presentsWithTransaction = NO;

// Moved from didMoveToWindow
[self validateDisplayLink];
}
}

- (void)didMoveToWindow
{
[self validateDisplayLink];
[super didMoveToWindow];

if (self.window)
{
// See above comment
CAEAGLLayer *eaglLayer = MGL_OBJC_DYNAMIC_CAST(_glView.layer, CAEAGLLayer);
eaglLayer.presentsWithTransaction = self.enablePresentsWithTransaction;

[self validateDisplayLink];
}
}

- (void)didMoveToSuperview
Expand Down Expand Up @@ -4145,6 +4269,8 @@ - (void)updateAnnotationContainerViewWithAnnotationViews:(NSArray<MGLAnnotationV
[newAnnotationContainerView addSubviews:annotationViews];
[_glView insertSubview:newAnnotationContainerView atIndex:0];
self.annotationContainerView = newAnnotationContainerView;

self.enablePresentsWithTransaction = (self.annotationContainerView.annotationViews.count > MGLPresentsWithTransactionAnnotationCount);
}

/// Initialize and return a default annotation image that depicts a round pin
Expand Down Expand Up @@ -4318,6 +4444,8 @@ - (void)removeAnnotations:(NSArray<id <MGLAnnotation>> *)annotations
annotationView.annotation = nil;
[annotationView removeFromSuperview];
[self.annotationContainerView.annotationViews removeObject:annotationView];

self.enablePresentsWithTransaction = (self.annotationContainerView.annotationViews.count > MGLPresentsWithTransactionAnnotationCount);

if (annotationTag == _selectedAnnotationTag)
{
Expand Down

0 comments on commit c279059

Please sign in to comment.