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

Tile coverage updates #12310

Closed
wants to merge 6 commits into from
Closed

Tile coverage updates #12310

wants to merge 6 commits into from

Conversation

brunoabinader
Copy link
Member

This PR:

  • Limits the amount of covered tiles for TileCoordinate-based coverage: we are now clamping the coverage to up to 4 (value is arbitrary) neighbor tiles in each direction
  • Treats pan (prefetch) tiles as ideal, so they can be used to render the "horizon" in higher pitch values
  • Increases the maximum pitch value to 67.5 degrees

screenshot from 2018-07-04 20-52-34

From the example above, the topmost tile is a higher zoom tile (configurable via Map.prefetchZoomDelta() that covers the remaining visible area.

Using pan tiles to cover the horizon reduces the amount of rendered tiles, and because they are barely visible, the lower fidelity doesn't account much. This PR is an ongoing effort to eventually support modifying the projection matrix eye according to the edge insets (#12107) - modifying the eye position can also cause more tiles to be visible.

Most rendering failures in render tests are caused by setting a pitch value higher than maximum pitch, which causes different results between GL native (max pitch: 67.5) and GL JS (max pitch: 60.0).

/cc @asheemmamoowala @kkaefer @tmpsantos

@brunoabinader brunoabinader added feature Core The cross-platform C++ core, aka mbgl Google Maps parity For feature parity with the Google Maps SDK for Android or iOS labels Jul 4, 2018
@brunoabinader brunoabinader force-pushed the pitch-67.5-degrees branch 5 times, most recently from 0c503c5 to ed972b2 Compare July 5, 2018 12:41
@mb12
Copy link

mb12 commented Jul 5, 2018

@brunoabinader Can you please kindly clarify the following?

  1. With this change does it mean that for pitch > maxpitch irrespective of the width and height of the viewport, only 64 tiles at the most will be loaded?

  2. Can you please clarify why do tl, tr etc have to be rotated for the specific case of >maxpitch but not otherwise? Also why does the transformation include only the angle of rotation but it does not include the pitch? Also why is the rotation w.r.t. to (0, 0) and not the center of the map?

Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

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

The new maximum pitch would be a good change to add to the Android, iOS, and macOS changelogs. It improves not only the developer experience but also potentially the end user experience.

@brunoabinader
Copy link
Member Author

  1. With this change does it mean that for pitch > maxpitch irrespective of the width and height of the viewport, only 64 tiles at the most will be loaded?

We start limit (clamping the range of) tile coverage when pitch trespasses maxPitchForUnlimitedCoverage. When that happens, we clamp the amount of covered tiles to the range [CenterX-4...CenterX+4][CenterY-4...CenterY+4], which can give up to 9x9=81 tiles.

Notice, however, that in continuous rendering mode, we also calculate pan tiles, which throughputs bigger tiles that should cover the remaining part of the visible map - more about this in #7741.

  1. Can you please clarify why do tl, tr etc have to be rotated for the specific case of >maxpitch but not otherwise?

We only need the rotated values to calculate clamping when limiting (and we rotate back after). Otherwise, we proceed with the original tile coverage algorithm.

Also why does the transformation include only the angle of rotation but it does not include the pitch?

We don't need pitch because it has been already taken in consideration when calculating the TileCoordinate points received by tileCover(). We rotate these coordinates because we want a viewport axis-aligned TileCoordinate bbox to apply proper clamping - see screenshots below for the difference between rotated and non-rotated clamping:

viewport axis-aligned non-rotated
rotated non-rotated

Also why is the rotation w.r.t. to (0, 0) and not the center of the map?

Not sure what you mean here - in tileCover(), c is the center tile coordinate of the viewport center - see https://github.com/mapbox/mapbox-gl-native/blob/master/src/mbgl/util/tile_cover.cpp#L157 and https://github.com/mapbox/mapbox-gl-native/blob/master/src/mbgl/util/tile_cover.cpp#L171.

@brunoabinader
Copy link
Member Author

Worth mentioning that using an arbitrary fixed limiting factor of 4 is a simpler solution to a somewhat complex problem. When implementing this, I've also considered the following:

  • Calculate different limiting factors for x and y, taking width and height ratio in consideration.
  • Take pitch in consideration when calculating the limiting factor (example below)
double rangeX = (topRight.x - topLeft.x) * std::cos(pitch);

For future consideration, we can also think of:

  • Instead of two fixed tile layers (ideal and pan tileS), apply instead a mipmap-like approach that dynamically decreases the tile zoom as it is more distant from the center.

Copy link
Contributor

@asheemmamoowala asheemmamoowala left a comment

Choose a reason for hiding this comment

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

@brunoabinader In playing with this branch I see that the order in which tiles are fetched and rendered is prioritizing the panTiles over the idealTiles. This works great for satellite imagery. With vector tiles, Im sometimes seeing that low zoom tiles at the top edge of the screen are being loaded before tiles in the center or bottom of the screen. It's rare, but wondering if there's something to be done about it ?
Is there a reason for enabling the clamping only past 60 degrees pitch ? It seems like it would be useful starting ~ 45 degrees.

@brunoabinader
Copy link
Member Author

@brunoabinader In playing with this branch I see that the order in which tiles are fetched and rendered is prioritizing the panTiles over the idealTiles. This works great for satellite imagery. With vector tiles, Im sometimes seeing that low zoom tiles at the top edge of the screen are being loaded before tiles in the center or bottom of the screen. It's rare, but wondering if there's something to be done about it ?

I believe that's intended - my understanding of pan tiles is that they prevent empty areas when panning - so we want to start showing off these faster than the higher zoomed tiles. However, we could add some logic to prevent this prioritization when not gesturing (TransformState knows about that). Let's try out this logic and observe behavior.

Is there a reason for enabling the clamping only past 60 degrees pitch ? It seems like it would be useful starting ~ 45 degrees.

We can modify that 👍 However, clamping past 60 degrees preserves the current behavior (because we've been limiting pitch to 60 degrees until now).

Also, the amount of visible tiles also depends whether your zoom level is close to its integer value (e.g. zoom 10.1 = less tiles), or closer its ceiling value (zoom 10.95 = more tiles).

@brunoabinader
Copy link
Member Author

Render tests regressions/mapbox-gl-js#5911 and regressions/mapbox-gl-js#5911a are now failing when setting the tilt threshold for limiting the tile coverage to 45 instead of 60 degrees - this is actually one of the reasons why I selected 60 degrees as minimum threshold beforehand 😅

Copy link
Contributor

@asheemmamoowala asheemmamoowala left a comment

Choose a reason for hiding this comment

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

This looks good to me. Any chance you can add a render test too?

@brunoabinader
Copy link
Member Author

This looks good to me. Any chance you can add a render test too

Good idea, I'll add one 👍

@brunoabinader
Copy link
Member Author

@asheemmamoowala could you please re-review?

Copy link
Contributor

@asheemmamoowala asheemmamoowala left a comment

Choose a reason for hiding this comment

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

@brunoabinader Im not sure I understand why Symbol layers are skipped on the pan tiles, and how that relates to this PR.

@@ -43,6 +45,22 @@ TEST(TileCover, Pitch) {
{ 2, 1, 2 }, { 2, 1, 1 }, { 2, 2, 2 }, { 2, 2, 1 }, { 2, 3, 2 }
}),
util::tileCover(transform.getState(), 2));

// 2 tiles on the left, 1 center tiler, then 2 tiles on the right.
Copy link
Contributor

Choose a reason for hiding this comment

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

Break this into a separate test

uint8_t integerZoom = std::floor(zoom);
transform.setZoom(zoom);
auto fullTiles = util::tileCover(transform.getState(), integerZoom, util::TileCoverMode::Full);
auto limitedTiles = util::tileCover(transform.getState(), integerZoom, util::TileCoverMode::Limited5x5);
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be good to also check the correctness of the returned tiles as part of the test.

point = util::rotate(rotated, -bearing);
};

// Limit tile coverage in continuous mode.
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this comment still appropriate? How is continuous mode being checked here?

const Point<double>& tr,
const Point<double>& br,
const Point<double>& bl,
std::vector<UnwrappedTileID> tileCover(Point<double> tl,
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: non-uniform input arguments.

Would it be better to keep these as const references, and conditionally return a clamped point from limitTileCoordinate?

auto scanLine = [&](int32_t x0, int32_t x1, int32_t y) {
int32_t x;
if (y >= 0 && y <= tiles) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This condition is no longer necessary? I think it is necessary at the poles.

@stale
Copy link

stale bot commented Oct 24, 2018

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the archived Archived because of inactivity label Oct 24, 2018
@ChrisLoer
Copy link
Contributor

This would be great to resurrect! Related discussion is still going on the JS side: mapbox/mapbox-gl-js#3731

@stale stale bot removed the archived Archived because of inactivity label Oct 24, 2018
@stale stale bot added the archived Archived because of inactivity label Dec 23, 2018
@stale
Copy link

stale bot commented Dec 24, 2018

This pull request has been automatically detected as stale because it has not had recent activity and will be archived. Thank you for your contributions.

@stale stale bot closed this Dec 24, 2018
@brunoabinader brunoabinader deleted the pitch-67.5-degrees branch March 3, 2019 12:14
@friedbunny friedbunny removed needs changelog Indicates PR needs a changelog entry prior to merging. labels Jun 13, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
archived Archived because of inactivity Core The cross-platform C++ core, aka mbgl feature Google Maps parity For feature parity with the Google Maps SDK for Android or iOS
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants