From 7332ae00735a7cb1a0a4528d48e5956aa593b8b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Tue, 10 May 2016 11:48:22 +0200 Subject: [core] retain tiles differently and remove old TileID class --- test/algorithm/mock.hpp | 60 ++++++ test/algorithm/update_renderables.cpp | 367 ++++++++++++++++++++++++++++++++++ 2 files changed, 427 insertions(+) create mode 100644 test/algorithm/mock.hpp create mode 100644 test/algorithm/update_renderables.cpp (limited to 'test/algorithm') diff --git a/test/algorithm/mock.hpp b/test/algorithm/mock.hpp new file mode 100644 index 0000000000..89f51b15b8 --- /dev/null +++ b/test/algorithm/mock.hpp @@ -0,0 +1,60 @@ +#ifndef MBGL_TEST_MOCK +#define MBGL_TEST_MOCK + +#include +#include +#include +#include +#include + +#include + +struct MockSourceInfo { + uint8_t maxZoom = 16; + uint8_t minZoom = 0; +}; + +struct MockTileData; + +struct MockRenderable { + MockRenderable(mbgl::UnwrappedTileID id_, MockTileData& data_) : id(id_), data(data_) {} + + const mbgl::UnwrappedTileID id; + MockTileData& data; + + bool operator==(const MockRenderable& rhs) const { + return &data == &rhs.data; + } +}; + +::std::ostream& operator<<(::std::ostream& os, const MockRenderable&) { + return os << "Renderable{}"; +} + +struct MockSource { + MockSourceInfo info; + std::map> dataTiles; + std::set idealTiles; + std::map renderables; + + // Test API + inline MockTileData* createTileData(const mbgl::OverscaledTileID& tileID); +}; + +struct MockBucket {}; + + +struct MockTileData { + bool isReady() { + return ready; + } + + bool ready = false; +}; + +MockTileData* MockSource::createTileData(const mbgl::OverscaledTileID& tileID) { + // Replace the existing MockTileData object, if any. + return (dataTiles[tileID] = std::make_unique()).get(); +} + +#endif diff --git a/test/algorithm/update_renderables.cpp b/test/algorithm/update_renderables.cpp new file mode 100644 index 0000000000..860381bdfa --- /dev/null +++ b/test/algorithm/update_renderables.cpp @@ -0,0 +1,367 @@ +#include +#include "mock.hpp" + +#include + +using namespace mbgl; + +TEST(UpdateRenderables, SingleTile) { + MockSource source; + source.idealTiles.emplace(UnwrappedTileID{ 1, 1, 1 }); + + // Make sure that we're getting the tile back. + auto tile_1_1_1 = source.createTileData(OverscaledTileID{ 1, 1, 1 }); + tile_1_1_1->ready = true; + + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 1); + EXPECT_EQ(decltype(source.renderables)({ + { { 1, 1, 1 }, MockRenderable{ { 1, 1, 1 }, *tile_1_1_1 } }, + }), + source.renderables); + + // Check a repeated render with the same data. + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 1); + EXPECT_EQ(decltype(source.renderables)({ + { { 1, 1, 1 }, MockRenderable{ { 1, 1, 1 }, *tile_1_1_1 } }, + }), + source.renderables); + + // Insert a tile we don't have data for. + source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 1 }); + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 1); + EXPECT_EQ(decltype(source.renderables)({ + { { 1, 1, 1 }, MockRenderable{ { 1, 1, 1 }, *tile_1_1_1 } }, + }), + source.renderables); + + // Now insert the missing tile and check that we're rendering it. + auto tile_1_0_1 = source.createTileData(OverscaledTileID{ 1, 0, 1 }); + tile_1_0_1->ready = true; + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 1); + EXPECT_EQ(decltype(source.renderables)({ + { { 1, 0, 1 }, MockRenderable{ { 1, 0, 1 }, *tile_1_0_1 } }, + { { 1, 1, 1 }, MockRenderable{ { 1, 1, 1 }, *tile_1_1_1 } }, + }), + source.renderables); + + // Insert another tile, and another bucket that has a different name and check that we're not + // using it. + source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 0 }); + auto tile_1_0_0 = source.createTileData(OverscaledTileID{ 1, 0, 0 }); + + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 1); + EXPECT_EQ(decltype(source.renderables)({ + { { 1, 0, 1 }, MockRenderable{ { 1, 0, 1 }, *tile_1_0_1 } }, + { { 1, 1, 1 }, MockRenderable{ { 1, 1, 1 }, *tile_1_1_1 } }, + }), + source.renderables); + + // Then, add the bucket and check that it's getting used. + tile_1_0_0->ready = true; + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 1); + EXPECT_EQ(decltype(source.renderables)({ + { { 1, 0, 0 }, MockRenderable{ { 1, 0, 0 }, *tile_1_0_0 } }, + { { 1, 0, 1 }, MockRenderable{ { 1, 0, 1 }, *tile_1_0_1 } }, + { { 1, 1, 1 }, MockRenderable{ { 1, 1, 1 }, *tile_1_1_1 } }, + }), + source.renderables); +} + +TEST(UpdateRenderables, UseParentTile) { + MockSource source; + source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 1 }); + source.idealTiles.emplace(UnwrappedTileID{ 1, 1, 0 }); + source.idealTiles.emplace(UnwrappedTileID{ 1, 1, 1 }); + + // Make sure that we're getting the tile back. + auto tile_0_0_0 = source.createTileData(OverscaledTileID{ 0, 0, 0 }); + tile_0_0_0->ready = true; + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 1); + EXPECT_EQ(decltype(source.renderables)({ + { { 0, 0, 0 }, MockRenderable{ { 0, 0, 0 }, *tile_0_0_0 } }, + }), + source.renderables); +} + +TEST(UpdateRenderables, DontUseWrongParentTile) { + MockSource source; + source.idealTiles.emplace(UnwrappedTileID{ 2, 0, 0 }); + + auto tile_1_1_0 = source.createTileData(OverscaledTileID{ 1, 1, 0 }); + tile_1_1_0->ready = true; + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 2); + EXPECT_EQ(decltype(source.renderables)({}), source.renderables); + + // Add a new child tile and check that it is now used. + source.idealTiles.emplace(UnwrappedTileID{ 2, 2, 0 }); + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 2); + EXPECT_EQ(decltype(source.renderables)({ + { { 1, 1, 0 }, MockRenderable{ { 1, 1, 0 }, *tile_1_1_0 } }, + }), + source.renderables); +} + +TEST(UpdateRenderables, UseParentTileWhenChildNotReady) { + MockSource source; + source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 1 }); + + auto tile_0_0_0 = source.createTileData(OverscaledTileID{ 0, 0, 0 }); + tile_0_0_0->ready = true; + + auto tile_1_0_1 = source.createTileData(OverscaledTileID{ 1, 0, 1 }); + // Don't create bucket. + + // Make sure that it renders the parent tile. + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 1); + EXPECT_EQ(decltype(source.renderables)({ + { { 0, 0, 0 }, MockRenderable{ { 0, 0, 0 }, *tile_0_0_0 } }, + }), + source.renderables); + + // Now insert the bucket and make sure we're now using the matching tile + tile_1_0_1->ready = true; + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 1); + EXPECT_EQ(decltype(source.renderables)({ + { { 1, 0, 1 }, MockRenderable{ { 1, 0, 1 }, *tile_1_0_1 } }, + }), + source.renderables); +} + +TEST(UpdateRenderables, UseOverlappingParentTile) { + MockSource source; + source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 0 }); + source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 1 }); + + auto tile_0_0_0 = source.createTileData(OverscaledTileID{ 0, 0, 0 }); + tile_0_0_0->ready = true; + + auto tile_1_0_1 = source.createTileData(OverscaledTileID{ 1, 0, 1 }); + tile_1_0_1->ready = true; + + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 1); + EXPECT_EQ(decltype(source.renderables)({ + { { 0, 0, 0 }, MockRenderable{ { 0, 0, 0 }, *tile_0_0_0 } }, + { { 1, 0, 1 }, MockRenderable{ { 1, 0, 1 }, *tile_1_0_1 } }, + }), + source.renderables); +} + +TEST(UpdateRenderables, UseChildTiles) { + MockSource source; + source.idealTiles.emplace(UnwrappedTileID{ 0, 0, 0 }); + + auto tile_1_0_0 = source.createTileData(OverscaledTileID{ 1, 0, 0 }); + tile_1_0_0->ready = true; + auto tile_1_1_0 = source.createTileData(OverscaledTileID{ 1, 1, 0 }); + tile_1_1_0->ready = true; + + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 0); + EXPECT_EQ(decltype(source.renderables)({ + { { 1, 0, 0 }, MockRenderable{ { 1, 0, 0 }, *tile_1_0_0 } }, + { { 1, 1, 0 }, MockRenderable{ { 1, 1, 0 }, *tile_1_1_0 } }, + }), + source.renderables); +} + +TEST(UpdateRenderables, PreferChildTiles) { + MockSource source; + source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 0 }); + + auto tile_0_0_0 = source.createTileData(OverscaledTileID{ 0, 0, 0 }); + tile_0_0_0->ready = true; + auto tile_2_0_0 = source.createTileData(OverscaledTileID{ 2, 0, 0 }); + tile_2_0_0->ready = true; + + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 1); + EXPECT_EQ(decltype(source.renderables)({ + { { 0, 0, 0 }, MockRenderable{ { 0, 0, 0 }, *tile_0_0_0 } }, + { { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_2_0_0 } }, + }), + source.renderables); + + // Now add more children to cover the ideal tile fully, until it is covered fully, and verify + // that the parent doesn't get rendered. + auto tile_2_0_1 = source.createTileData(OverscaledTileID{ 2, 0, 1 }); + tile_2_0_1->ready = true; + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 1); + EXPECT_EQ(decltype(source.renderables)({ + { { 0, 0, 0 }, MockRenderable{ { 0, 0, 0 }, *tile_0_0_0 } }, + { { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_2_0_0 } }, + { { 2, 0, 1 }, MockRenderable{ { 2, 0, 1 }, *tile_2_0_1 } }, + }), + source.renderables); + + auto tile_2_1_0 = source.createTileData(OverscaledTileID{ 2, 1, 0 }); + tile_2_1_0->ready = true; + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 1); + EXPECT_EQ(decltype(source.renderables)({ + { { 0, 0, 0 }, MockRenderable{ { 0, 0, 0 }, *tile_0_0_0 } }, + { { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_2_0_0 } }, + { { 2, 0, 1 }, MockRenderable{ { 2, 0, 1 }, *tile_2_0_1 } }, + { { 2, 1, 0 }, MockRenderable{ { 2, 1, 0 }, *tile_2_1_0 } }, + }), + source.renderables); + + // Adding the last child tile covers 1/0/0 fully, so we don't need 0/0/0 anymore. + auto tile_2_1_1 = source.createTileData(OverscaledTileID{ 2, 1, 1 }); + tile_2_1_1->ready = true; + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 1); + EXPECT_EQ(decltype(source.renderables)({ + { { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_2_0_0 } }, + { { 2, 0, 1 }, MockRenderable{ { 2, 0, 1 }, *tile_2_0_1 } }, + { { 2, 1, 0 }, MockRenderable{ { 2, 1, 0 }, *tile_2_1_0 } }, + { { 2, 1, 1 }, MockRenderable{ { 2, 1, 1 }, *tile_2_1_1 } }, + }), + source.renderables); +} + +TEST(UpdateRenderables, UseParentAndChildTiles) { + MockSource source; + source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 0 }); + + auto tile_0_0_0 = source.createTileData(OverscaledTileID{ 0, 0, 0 }); + tile_0_0_0->ready = true; + auto tile_2_0_0 = source.createTileData(OverscaledTileID{ 2, 0, 0 }); + tile_2_0_0->ready = true; + + // Check that it uses the child tile, but not the parent tile. + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 1); + EXPECT_EQ(decltype(source.renderables)({ + { { 0, 0, 0 }, MockRenderable{ { 0, 0, 0 }, *tile_0_0_0 } }, + { { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_2_0_0 } }, + }), + source.renderables); + + // Then, remove the child tile and check that it now uses the parent tile. + source.dataTiles.erase(OverscaledTileID{ 2, 0, 0 }); + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 1); + EXPECT_EQ(decltype(source.renderables)({ + { { 0, 0, 0 }, MockRenderable{ { 0, 0, 0 }, *tile_0_0_0 } }, + }), + source.renderables); +} + +TEST(UpdateRenderables, DontUseTilesLowerThanMinzoom) { + MockSource source; + source.info.minZoom = 2; + source.idealTiles.emplace(UnwrappedTileID{ 2, 0, 0 }); + + auto tile_1_0_0 = source.createTileData(OverscaledTileID{ 1, 0, 0 }); + tile_1_0_0->ready = true; + + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 2); + EXPECT_EQ(decltype(source.renderables)({}), source.renderables); +} + +TEST(UpdateRenderables, UseOverzoomedTileAfterMaxzoom) { + MockSource source; + source.info.maxZoom = 2; + source.idealTiles.emplace(UnwrappedTileID{ 2, 0, 0 }); + + // Add a child tile (that should never occur in practice) and make sure it's not selected. + auto tile_3_3_0_0 = source.createTileData(OverscaledTileID{ 3, 0, 0 }); + tile_3_3_0_0->ready = true; + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 2); + EXPECT_EQ(decltype(source.renderables)({}), source.renderables); + + // Only add a non-overzoomed ("parent") tile at first. + auto tile_2_2_0_0 = source.createTileData(OverscaledTileID{ 2, { 2, 0, 0 } }); + tile_2_2_0_0->ready = true; + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 3); + EXPECT_EQ(decltype(source.renderables)({ + { { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_2_2_0_0 } }, + }), + source.renderables); + + // Then add the overzoomed tile matching the zoom level we're rendering. + auto tile_3_2_0_0 = source.createTileData(OverscaledTileID{ 3, { 2, 0, 0 } }); + tile_3_2_0_0->ready = true; + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 3); + EXPECT_EQ(decltype(source.renderables)({ + { { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_3_2_0_0 } }, + }), + source.renderables); + + // Check that it's switching back to the tile that has the matching overzoom value. + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 2); + EXPECT_EQ(decltype(source.renderables)({ + { { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_2_2_0_0 } }, + }), + source.renderables); + + // Now remove the best match. + source.dataTiles.erase(OverscaledTileID{ 2, { 2, 0, 0 } }); + tile_2_2_0_0 = nullptr; + + // Use the overzoomed tile even though it doesn't match the zoom level. + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 2); + EXPECT_EQ(decltype(source.renderables)({ + { { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_3_2_0_0 } }, + }), + source.renderables); +} + +TEST(UpdateRenderables, AscendToNonOverzoomedTiles) { + MockSource source; + source.info.maxZoom = 2; + source.idealTiles.emplace(UnwrappedTileID{ 2, 0, 0 }); + + // Add a matching overzoomed tile and make sure it gets selected. + auto tile_3_2_0_0 = source.createTileData(OverscaledTileID{ 3, { 2, 0, 0 } }); + tile_3_2_0_0->ready = true; + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 3); + EXPECT_EQ(decltype(source.renderables)({ + { { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_3_2_0_0 } }, + }), + source.renderables); + + // Then, swap it with a non-overzoomed tile. + source.dataTiles.erase(OverscaledTileID{ 3, { 2, 0, 0 } }); + tile_3_2_0_0 = nullptr; + auto tile_2_2_0_0 = source.createTileData(OverscaledTileID{ 2, { 2, 0, 0 } }); + tile_2_2_0_0->ready = true; + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 3); + EXPECT_EQ(decltype(source.renderables)({ + { { 2, 0, 0 }, MockRenderable{ { 2, 0, 0 }, *tile_2_2_0_0 } }, + }), + source.renderables); + + // Then, swap it with a parent tile. + source.dataTiles.erase(OverscaledTileID{ 2, { 2, 0, 0 } }); + tile_2_2_0_0 = nullptr; + auto tile_1_1_0_0 = source.createTileData(OverscaledTileID{ 1, { 1, 0, 0 } }); + tile_1_1_0_0->ready = true; + source.renderables = algorithm::updateRenderables( + source.dataTiles, source.idealTiles, source.info, 3); + EXPECT_EQ(decltype(source.renderables)({ + { { 1, 0, 0 }, MockRenderable{ { 1, 0, 0 }, *tile_1_1_0_0 } }, + }), + source.renderables); +} -- cgit v1.2.1