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

Optimize annotation view updates #5987

Merged
merged 9 commits into from
Nov 1, 2016

Conversation

boundsj
Copy link
Contributor

@boundsj boundsj commented Aug 13, 2016

Now that #5165 has landed, we can address the issues reported in #5787 and again in #5489 (comment). This PR will:

Fixes #5787

cc @incanus @1ec5

@boundsj boundsj added iOS Mapbox Maps SDK for iOS performance Speed, stability, CPU usage, memory usage, or power usage ⚠️ DO NOT MERGE Work in progress, proof of concept, or on hold labels Aug 13, 2016
@boundsj boundsj added this to the ios-v3.4.0 milestone Aug 13, 2016
@boundsj boundsj self-assigned this Aug 13, 2016
@boundsj
Copy link
Contributor Author

boundsj commented Aug 13, 2016

This first commit addresses #5787 specifically. After this change, profiling shows that drawing icons at the GL level is responsible for most of the
CPU usage when 10k annotations backed by sprites are added to the map and then the map is continuously panned.

screen shot 2016-08-12 at 5 15 17 pm

cc @incanus

@boundsj
Copy link
Contributor Author

boundsj commented Aug 29, 2016

Noting that the parts of this PR not blocked by #6055 have been PRd in #6199

@boundsj boundsj added bug release blocker Blocks the next final release labels Oct 27, 2016
@boundsj boundsj changed the title [WIP] Optimize annotation view updates Optimize annotation view updates Oct 28, 2016
- Get sets of visible and offscreen annotations and update and enqueue
as required
- Query viewport adjusted if tilted (avoid apparent issue with
queryPointAnnotations when the query box is larger than the actual
viewport)
- Add a small debugging display in iOS app to see annotations going in
and out of the reuse queue
This works around a performance issue that made getting an annotation
context expensive. It also works around an issue with the underlying
mbgl query so that even if it (rarely) returns an incorrect result,
the correct visual effect still occurs and the reuse queue is added
to and drained as expected.
But implement it with _annotationContextsByAnnotation to avoid the
expensive lookup in annotationTagForAnnotation:
@boundsj boundsj force-pushed the boundsj-optimize-annotation-updates branch from d374b45 to a8c481b Compare October 31, 2016 05:59
@boundsj boundsj changed the base branch from master to release-ios-v3.4.0 October 31, 2016 06:16
@boundsj boundsj added ✓ ready for review and removed ⚠️ DO NOT MERGE Work in progress, proof of concept, or on hold labels Oct 31, 2016
@boundsj
Copy link
Contributor Author

boundsj commented Oct 31, 2016

The fix of #6055 freed us up to make some long desired changes to the way annotation views are updated and queued for reuse. The summary of changes here so far is:

  • Use query point annotations to create subsets of visible and offscreen annotations
  • Update the centers of the visible annotations as the map is moved
  • Dequeue annotations that are offscreen
  • Work around and a few (what seems to be) remaining issues / limitations with symbol querying in mbgl core where the no annotations come back in the query -- sometimes this seems to happen randomly but rarely during rotation / changing zoom. It also seems to be a limitation that a query box larger than the viewport becomes increasingly problematic as pitch increases. But again, this PR works around those issues so that the performance benefit of using mbgl's query can still be used
  • A performance refactor to avoid MGLMapView annotationTagForAnnotation: since the linear performance characteristics of that method become problematic with thousands of annotation views. The tradeoff is space complexity and another std::map to make annotation context retrieval time constant

When testing for 1 minute with 10,000 annotation views, high zoom (to trigger heavy reuse queue usage), and a constant panning gesture, the performance increase is significant:

Before: total weight of ~50 seconds and updateAnnotationViews being ~88% of that

screenshot 2016-10-30 23 21 49

Now: total weight of ~15 seconds and updateAnnotationViews being ~50% of that

screenshot 2016-10-30 23 24 44

This commit also includes an optional debugger view in iosapp to confirm the reuse queue is working as expected:

fullsizerender

This addresses concerns raised in #5787 and #6173. I believe it is a more desirable and complete solution than the one proposed in #6840.

cc @1ec5 @incanus @frederoni

@boundsj
Copy link
Contributor Author

boundsj commented Oct 31, 2016

Noting also that while this is not a complete fix for #5489, I'm seeing much better results when scrolling since, I think, the main thread as a whole is taxed far less than before.

typedef std::map<MGLAnnotationTag, MGLAnnotationContext> MGLAnnotationContextMap;
typedef std::map<MGLAnnotationTag, MGLAnnotationContext> MGLAnnotationTagContextMap;

typedef std::map<id<MGLAnnotation>, MGLAnnotationContext> MGLAnnotationObjectContextMap;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we instead have a mapping from annotation objects to annotation tags? It would be an additional hop, but also less parallel state to keep in sync.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. Done in fc8d77a

@@ -129,7 +129,9 @@ typedef NS_ENUM(NSUInteger, MGLUserTrackingState) {

/// Mapping from an annotation tag to metadata about that annotation, including
/// the annotation itself.
typedef std::map<MGLAnnotationTag, MGLAnnotationContext> MGLAnnotationContextMap;
typedef std::map<MGLAnnotationTag, MGLAnnotationContext> MGLAnnotationTagContextMap;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename this type in the macOS implementation as well to keep the two implementations from diverging too much.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in fc8d77a

This creates an extra hop to get the context but eliminates the
need to sync two sets of references to each context object.

This also syncs up the ios and macos names (and types) for
MGLAnnotationTagContextMap
@@ -189,6 +192,7 @@ - (void)accessibilityDecrement
MGLAnnotationAccessibilityElement *accessibilityElement;
MGLAnnotationView *annotationView;
NSString *viewReuseIdentifier;
MGLAnnotationTag annotationTag;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unused, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 8e67bef

@boundsj boundsj merged commit 2901bc0 into release-ios-v3.4.0 Nov 1, 2016
@boundsj boundsj deleted the boundsj-optimize-annotation-updates branch November 1, 2016 19:01
@@ -1506,9 +1527,24 @@ - (void)mapView:(MGLMapView *)mapView tapOnCalloutForAnnotation:(id <MGLAnnotati
- (void)mapView:(MGLMapView *)mapView didFinishLoadingStyle:(MGLStyle *)style
{
// Default Mapbox styles use {name_en} as their label language, which means
NSUInteger queuedAnnotations = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a bad merge occurred.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Fixed in #6882

1ec5 added a commit that referenced this pull request Nov 8, 2016
Streamlined -[MGLMapView annotationTagForAnnotation:] to use the mapping added in #5987. Reverted an unsafe access into that mapping that was added in #5987; returning nil is preferable when no such annotation can be found. Return the user location annotation view when given the user location annotation.
1ec5 added a commit that referenced this pull request Nov 9, 2016
Streamlined -[MGLMapView annotationTagForAnnotation:] to use the mapping added in #5987. Reverted an unsafe access into that mapping that was added in #5987; returning nil is preferable when no such annotation can be found. Return the user location annotation view when given the user location annotation.
yojique added a commit to yojique/mapbox-gl-native that referenced this pull request Dec 1, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
annotations Annotations on iOS and macOS or markers on Android bug iOS Mapbox Maps SDK for iOS performance Speed, stability, CPU usage, memory usage, or power usage refactor release blocker Blocks the next final release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants