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

[core] Prefetch low resolution tiles #7741

Merged
merged 3 commits into from
Jul 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmake/test-files.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ set(MBGL_TEST_FILES

# map
test/map/map.test.cpp
test/map/prefetch.test.cpp
test/map/transform.test.cpp

# math
Expand Down
9 changes: 9 additions & 0 deletions include/mbgl/map/map.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,15 @@ class Map : private util::noncopyable {

AnnotationIDs queryPointAnnotations(const ScreenBox&);

// Tile prefetching
//
// When loading a map, if `PrefetchZoomDelta` is set to any number greater than 0, the map will
// first request a tile for `zoom = getZoom() - delta` in a attempt to display a full map at
// lower resolution as quick as possible. It will get clamped at the tile source minimum zoom.
// The default `delta` is 4.
void setPrefetchZoomDelta(uint8_t delta);
uint8_t getPrefetchZoomDelta() const;

// Memory
void onLowMemory();

Expand Down
2 changes: 2 additions & 0 deletions include/mbgl/util/constants.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ constexpr double MAX_ZOOM = 25.5;
constexpr float MIN_ZOOM_F = MIN_ZOOM;
constexpr float MAX_ZOOM_F = MAX_ZOOM;

constexpr uint8_t DEFAULT_PREFETCH_ZOOM_DELTA = 4;

constexpr uint64_t DEFAULT_MAX_CACHE_SIZE = 50 * 1024 * 1024;

constexpr Duration DEFAULT_TRANSITION_DURATION = Milliseconds(300);
Expand Down
15 changes: 14 additions & 1 deletion src/mbgl/map/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <mbgl/renderer/render_source.hpp>
#include <mbgl/renderer/render_style.hpp>
#include <mbgl/renderer/render_style_observer.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/exception.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/exception.hpp>
Expand Down Expand Up @@ -92,6 +93,9 @@ class Map::Impl : public style::Observer,
std::unique_ptr<RenderStyle> renderStyle;

bool cameraMutated = false;

uint8_t prefetchZoomDelta = util::DEFAULT_PREFETCH_ZOOM_DELTA;

bool loading = false;

util::AsyncTask asyncInvalidate;
Expand Down Expand Up @@ -245,7 +249,8 @@ void Map::Impl::render(View& view) {
style->impl->getLayerImpls(),
scheduler,
fileSource,
annotationManager
annotationManager,
prefetchZoomDelta
});

bool loaded = style->impl->isLoaded() && renderStyle->isLoaded();
Expand Down Expand Up @@ -823,6 +828,14 @@ bool Map::isFullyLoaded() const {
return impl->style->impl->isLoaded() && impl->renderStyle && impl->renderStyle->isLoaded();
}

void Map::setPrefetchZoomDelta(uint8_t delta) {
impl->prefetchZoomDelta = delta;
}

uint8_t Map::getPrefetchZoomDelta() const {
return impl->prefetchZoomDelta;
}

void Map::onLowMemory() {
if (impl->painter) {
BackendScope guard(impl->backend);
Expand Down
6 changes: 5 additions & 1 deletion src/mbgl/renderer/render_style.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ void RenderStyle::update(const UpdateParameters& parameters) {
parameters.mode,
parameters.annotationManager,
*imageManager,
*glyphManager
*glyphManager,
parameters.prefetchZoomDelta
};

glyphManager->setURL(parameters.glyphURL);
Expand Down Expand Up @@ -325,6 +326,9 @@ RenderData RenderStyle::getRenderData(MapDebugOptions debugOptions, float angle)

return std::tie(par.y, par.x) < std::tie(pbr.y, pbr.x);
});
} else {
std::sort(sortedTiles.begin(), sortedTiles.end(),
[](const auto& a, const auto& b) { return a.get().id < b.get().id; });
}

std::vector<std::reference_wrapper<RenderTile>> sortedTilesForInsertion;
Expand Down
1 change: 1 addition & 0 deletions src/mbgl/renderer/tile_parameters.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class TileParameters {
AnnotationManager& annotationManager;
ImageManager& imageManager;
GlyphManager& glyphManager;
const uint8_t prefetchZoomDelta = 0;
};

} // namespace mbgl
28 changes: 26 additions & 2 deletions src/mbgl/renderer/tile_pyramid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,30 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
// Determine the overzooming/underzooming amounts and required tiles.
int32_t overscaledZoom = util::coveringZoomLevel(parameters.transformState.getZoom(), type, tileSize);
int32_t tileZoom = overscaledZoom;
int32_t panZoom = zoomRange.max;

std::vector<UnwrappedTileID> idealTiles;
std::vector<UnwrappedTileID> panTiles;

if (overscaledZoom >= zoomRange.min) {
int32_t idealZoom = std::min<int32_t>(zoomRange.max, overscaledZoom);

// Make sure we're not reparsing overzoomed raster tiles.
if (type == SourceType::Raster) {
tileZoom = idealZoom;

// FIXME: Prefetching is only enabled for raster
// tiles until we fix #7026.

// Request lower zoom level tiles (if configure to do so) in an attempt
// to show something on the screen faster at the cost of a little of bandwidth.
if (parameters.prefetchZoomDelta) {
panZoom = std::max<int32_t>(tileZoom - parameters.prefetchZoomDelta, zoomRange.min);
}

if (panZoom < tileZoom) {
panTiles = util::tileCover(parameters.transformState, panZoom);
}
}

idealTiles = util::tileCover(parameters.transformState, idealZoom);
Expand All @@ -108,8 +124,10 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
std::set<OverscaledTileID> retain;

auto retainTileFn = [&](Tile& tile, Resource::Necessity necessity) -> void {
retain.emplace(tile.id);
tile.setNecessity(necessity);
if (retain.emplace(tile.id).second) {
tile.setNecessity(necessity);
}

if (needsRelayout) {
tile.setLayers(layers);
}
Expand Down Expand Up @@ -137,6 +155,12 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
};

renderTiles.clear();

if (!panTiles.empty()) {
algorithm::updateRenderables(getTileFn, createTileFn, retainTileFn,
[](const UnwrappedTileID&, Tile&) {}, panTiles, zoomRange, panZoom);
}

Copy link
Member

Choose a reason for hiding this comment

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

Can we merge this call to updateRenderables into the other one by merging the panTiles into idealTiles?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Capturing from the chat: we have a separated algorithm::updateRenderables because we don't create render tiles for pan tiles.

algorithm::updateRenderables(getTileFn, createTileFn, retainTileFn, renderTileFn,
idealTiles, zoomRange, tileZoom);

Expand Down
2 changes: 2 additions & 0 deletions src/mbgl/renderer/update_parameters.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class UpdateParameters {
Scheduler& scheduler;
FileSource& fileSource;
AnnotationManager& annotationManager;

const uint8_t prefetchZoomDelta = 0;
};

} // namespace mbgl
5 changes: 5 additions & 0 deletions test/fixtures/map/prefetch/empty.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"version": 8,
"sources": {},
"layers": []
}
Binary file added test/fixtures/map/prefetch/expected.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions test/fixtures/map/prefetch/style.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"version": 8,
"name": "Test",
"sources": {
"raster": {
"type": "raster",
"tiles": [ "{z}" ],
"tileSize": 256,
"maxzoom": 20,
"minzoom": 0
}
},
"layers": [{
"id": "background",
"type": "background",
"paint": {
"background-color": "blue"
}
}, {
"id": "raster",
"type": "raster",
"source": "raster"
}]
}
Binary file added test/fixtures/map/prefetch/tile_green.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/map/prefetch/tile_red.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
93 changes: 93 additions & 0 deletions test/map/prefetch.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include <mbgl/test/util.hpp>
#include <mbgl/test/stub_file_source.hpp>

#include <mbgl/gl/headless_backend.hpp>
#include <mbgl/gl/offscreen_view.hpp>
#include <mbgl/map/backend_scope.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/util/default_thread_pool.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/io.hpp>
#include <mbgl/util/run_loop.hpp>

#include <algorithm>
#include <string>
#include <vector>

using namespace mbgl;
using namespace mbgl::style;
using namespace std::literals::string_literals;

TEST(Map, PrefetchTiles) {
util::RunLoop runLoop;
HeadlessBackend backend(test::sharedDisplay());
BackendScope scope(backend);
OffscreenView view(backend.getContext(), { 512, 512 });
ThreadPool threadPool(4);
StubFileSource fileSource;
Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still);

std::vector<int> tiles;

fileSource.response = [&] (const Resource& res) -> optional<Response> {
Response response;

auto zoom = std::stoi(res.url);
tiles.push_back(zoom);

// Return a red tile for prefetched tiles or green to the actual tile.
// The end rendering result should be all green because the map is only
// considered fully rendered when only ideal tiles are shown.
if (zoom == int(map.getZoom()) + 1) {
response.data = std::make_shared<std::string>(
util::read_file("test/fixtures/map/prefetch/tile_green.png"));
} else {
response.data = std::make_shared<std::string>(
util::read_file("test/fixtures/map/prefetch/tile_red.png"));
}

return { std::move(response) };
};

auto checkTilesForZoom = [&](int zoom, const std::vector<int>& expected) {
tiles.clear();

// Force tile reloading.
map.getStyle().loadJSON(util::read_file("test/fixtures/map/prefetch/empty.json"));
map.getStyle().loadJSON(util::read_file("test/fixtures/map/prefetch/style.json"));

map.setLatLngZoom({ 40.726989, -73.992857 }, zoom); // Manhattan

// Should always render the ideal tiles (i.e. a green map)
test::checkImage("test/fixtures/map/prefetch", test::render(map, view));

ASSERT_TRUE(std::is_permutation(tiles.begin(), tiles.end(), expected.begin()));
ASSERT_FALSE(tiles.empty());
};

// Check defaults, should be 4.
ASSERT_EQ(map.getPrefetchZoomDelta(), 4);
checkTilesForZoom(12, { 13, 13, 13, 13, 13, 13, 13, 13, 13, 9 });

// Setting it to 0 disables prefetching.
map.setPrefetchZoomDelta(0);

// No prefetching, raster tiles will use ideal
// tiles instead of the actual zoom level, that is
// why the zoom levels for non-prefetched tiles are
// not the same.
checkTilesForZoom(10, { 11, 11, 11, 11, 11, 11, 11, 11, 11 });

map.setPrefetchZoomDelta(5);
checkTilesForZoom(12, { 13, 13, 13, 13, 13, 13, 13, 13, 13, 8 });

// Should clamp at `minzoom`.
map.setPrefetchZoomDelta(20);
checkTilesForZoom(10, { 11, 11, 11, 11, 11, 11, 11, 11, 11, 0 });

// Disabled again.
map.setPrefetchZoomDelta(0);
checkTilesForZoom(13, { 14, 14, 14, 14, 14, 14, 14, 14, 14 });
}