From d94c7d9cb0f524269278736c93e37ca143517f58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Wed, 5 Jul 2017 15:38:50 +0200 Subject: [PATCH 1/2] [core] add algorithm for computing masks for raster tiles --- benchmark/parse/tile_mask.benchmark.cpp | 38 +++++++ cmake/benchmark-files.cmake | 1 + cmake/benchmark.cmake | 1 + cmake/core-files.cmake | 2 + cmake/test-files.cmake | 1 + src/mbgl/algorithm/update_tile_masks.hpp | 128 +++++++++++++++++++++ src/mbgl/renderer/tile_mask.hpp | 15 +++ src/mbgl/tile/tile_id_io.cpp | 5 + test/algorithm/update_tile_masks.test.cpp | 132 ++++++++++++++++++++++ 9 files changed, 323 insertions(+) create mode 100644 benchmark/parse/tile_mask.benchmark.cpp create mode 100644 src/mbgl/algorithm/update_tile_masks.hpp create mode 100644 src/mbgl/renderer/tile_mask.hpp create mode 100644 test/algorithm/update_tile_masks.test.cpp diff --git a/benchmark/parse/tile_mask.benchmark.cpp b/benchmark/parse/tile_mask.benchmark.cpp new file mode 100644 index 00000000000..79ab685c283 --- /dev/null +++ b/benchmark/parse/tile_mask.benchmark.cpp @@ -0,0 +1,38 @@ +#include + +#include + +using namespace mbgl; + +class MaskedRenderable { +public: + MaskedRenderable(const UnwrappedTileID& id_, TileMask&& mask_) + : id(id_), mask(std::move(mask_)) { + } + + UnwrappedTileID id; + TileMask mask; + bool used = true; + + void setMask(TileMask&& mask_) { + mask = std::move(mask_); + } +}; + +static void TileMaskGeneration(benchmark::State& state) { + std::vector renderables = { + MaskedRenderable{ UnwrappedTileID{ 12, 1028, 1456 }, {} }, + MaskedRenderable{ UnwrappedTileID{ 13, 2056, 2912 }, {} }, + MaskedRenderable{ UnwrappedTileID{ 13, 2056, 2913 }, {} }, + MaskedRenderable{ UnwrappedTileID{ 14, 4112, 5824 }, {} }, + MaskedRenderable{ UnwrappedTileID{ 14, 4112, 5827 }, {} }, + MaskedRenderable{ UnwrappedTileID{ 14, 4114, 5824 }, {} }, + MaskedRenderable{ UnwrappedTileID{ 14, 4114, 5825 }, {} }, + }; + + while (state.KeepRunning()) { + algorithm::updateTileMasks({ renderables.begin(), renderables.end() }); + } +} + +BENCHMARK(TileMaskGeneration); diff --git a/cmake/benchmark-files.cmake b/cmake/benchmark-files.cmake index 6fb9a80d579..d5364c3e587 100644 --- a/cmake/benchmark-files.cmake +++ b/cmake/benchmark-files.cmake @@ -10,6 +10,7 @@ set(MBGL_BENCHMARK_FILES # parse benchmark/parse/filter.benchmark.cpp + benchmark/parse/tile_mask.benchmark.cpp benchmark/parse/vector_tile.benchmark.cpp # src diff --git a/cmake/benchmark.cmake b/cmake/benchmark.cmake index f89e034c4ab..6f8a7751e48 100644 --- a/cmake/benchmark.cmake +++ b/cmake/benchmark.cmake @@ -19,6 +19,7 @@ target_link_libraries(mbgl-benchmark PRIVATE mbgl-core ) +target_add_mason_package(mbgl-benchmark PRIVATE boost) target_add_mason_package(mbgl-benchmark PRIVATE benchmark) target_add_mason_package(mbgl-benchmark PRIVATE rapidjson) target_add_mason_package(mbgl-benchmark PRIVATE protozero) diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index 94eb321ddf5..93bc8326d43 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -15,6 +15,7 @@ set(MBGL_CORE_FILES src/mbgl/algorithm/generate_clip_ids.hpp src/mbgl/algorithm/generate_clip_ids_impl.hpp src/mbgl/algorithm/update_renderables.hpp + src/mbgl/algorithm/update_tile_masks.hpp # annotation include/mbgl/annotation/annotation.hpp @@ -205,6 +206,7 @@ set(MBGL_CORE_FILES src/mbgl/renderer/renderer_observer.hpp src/mbgl/renderer/style_diff.cpp src/mbgl/renderer/style_diff.hpp + src/mbgl/renderer/tile_mask.hpp src/mbgl/renderer/tile_parameters.hpp src/mbgl/renderer/tile_pyramid.cpp src/mbgl/renderer/tile_pyramid.hpp diff --git a/cmake/test-files.cmake b/cmake/test-files.cmake index 2d2ed7d35bd..3595233f2a0 100644 --- a/cmake/test-files.cmake +++ b/cmake/test-files.cmake @@ -10,6 +10,7 @@ set(MBGL_TEST_FILES test/algorithm/generate_clip_ids.test.cpp test/algorithm/mock.hpp test/algorithm/update_renderables.test.cpp + test/algorithm/update_tile_masks.test.cpp # api test/api/annotations.test.cpp diff --git a/src/mbgl/algorithm/update_tile_masks.hpp b/src/mbgl/algorithm/update_tile_masks.hpp new file mode 100644 index 00000000000..a7840cd163e --- /dev/null +++ b/src/mbgl/algorithm/update_tile_masks.hpp @@ -0,0 +1,128 @@ +#pragma once + +#include + +#include +#include +#include + +namespace mbgl { +namespace algorithm { + +namespace { + +template +void computeTileMasks( + const CanonicalTileID& root, + const UnwrappedTileID ref, + typename std::vector>::const_iterator it, + const typename std::vector>::const_iterator end, + TileMask& mask) { + // If the reference or any of its children is found in the list, we need to recurse. + for (; it != end; ++it) { + auto& renderable = it->get(); + if (!renderable.used) { + continue; + } + if (ref == renderable.id) { + // The current tile is masked out, so we don't need to add them to the mask set. + return; + } else if (renderable.id.isChildOf(ref)) { + // There's at least one child tile that is masked out, so recursively descend. + for (const auto& child : ref.children()) { + computeTileMasks(root, child, it, end, mask); + } + return; + } + } + + // We couldn't find a child, so it's definitely a masked part. + // Compute the difference between the root tile ID and the reference tile ID, since TileMask + // elements are always relative (see below for explanation). + const uint8_t diffZ = ref.canonical.z - root.z; + mask.emplace(diffZ, ref.canonical.x - (root.x << diffZ), ref.canonical.y - (root.y << diffZ)); +} + +} // namespace + +// Updates the TileMasks for all renderables. Renderables are objects that have an UnwrappedTileID +// property indicating where they should be rendered on the screen. A TileMask describes all regions +// within that tile that are *not* covered by other Renderables. +// Example: Renderables in our list are 2/1/3, 3/3/6, and 4/5/13. The schematic for creating the +// TileMask for 2/1/3 looks like this: +// +// ┌────────┬────────┬─────────────────┐ +// │ │ │#################│ +// │ 4/4/12 │ 4/5/12 │#################│ +// │ │ │#################│ +// ├──────3/2/6──────┤#####3/3/6#######│ +// │ │########│#################│ +// │ 4/4/13 │#4/5/13#│#################│ +// │ │########│#################│ +// ├────────┴──────2/1/3───────────────┤ +// │ │ │ +// │ │ │ +// │ │ │ +// │ 3/2/7 │ 3/3/7 │ +// │ │ │ +// │ │ │ +// │ │ │ +// └─────────────────┴─────────────────┘ +// +// The TileMask for 2/1/3 thus consists of the tiles 4/4/12, 4/5/12, 4/4/13, 3/2/7, and 3/3/7, +// but it does *not* include 4/5/13, and 3/3/6, since these are other Renderables. +// A TileMask always contains TileIDs *relative* to the tile it is generated for, so 2/1/3 is +// "subtracted" from these TileIDs. The final TileMask for 2/1/3 will thus be: +// +// ┌────────┬────────┬─────────────────┐ +// │ │ │#################│ +// │ 2/0/0 │ 2/1/0 │#################│ +// │ │ │#################│ +// ├────────┼────────┤#################│ +// │ │########│#################│ +// │ 2/0/1 │########│#################│ +// │ │########│#################│ +// ├────────┴────────┼─────────────────┤ +// │ │ │ +// │ │ │ +// │ │ │ +// │ 1/0/1 │ 1/1/1 │ +// │ │ │ +// │ │ │ +// │ │ │ +// └─────────────────┴─────────────────┘ +// +// Only other Renderables that are *children* of the Renderable we are generating the mask for will +// be considered. For example, adding a Renderable with TileID 4/8/13 won't affect the TileMask for +// 2/1/3, since it is not a descendant of it. +template +void updateTileMasks(std::vector> renderables) { + std::sort(renderables.begin(), renderables.end(), + [](const Renderable& a, const Renderable& b) { return a.id < b.id; }); + + TileMask mask; + const auto end = renderables.end(); + for (auto it = renderables.begin(); it != end; it++) { + auto& renderable = it->get(); + if (!renderable.used) { + continue; + } + // Try to add all remaining ids as children. We sorted the tile list + // by z earlier, so all preceding items cannot be children of the current + // tile. We also compute the lower bound of the next wrap, because items of the next wrap + // can never be children of the current wrap. + auto child_it = std::next(it); + const auto children_end = std::lower_bound( + child_it, end, + UnwrappedTileID{ static_cast(renderable.id.wrap + 1), { 0, 0, 0 } }, + [](auto& a, auto& b) { return a.get().id < b; }); + + mask.clear(); + computeTileMasks(renderable.id.canonical, renderable.id, child_it, children_end, + mask); + renderable.setMask(std::move(mask)); + } +} + +} // namespace algorithm +} // namespace mbgl diff --git a/src/mbgl/renderer/tile_mask.hpp b/src/mbgl/renderer/tile_mask.hpp new file mode 100644 index 00000000000..5f24d63ba44 --- /dev/null +++ b/src/mbgl/renderer/tile_mask.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include + +namespace mbgl { + +// A TileMask is a set of TileIDs that describe what part of a tile should be rendered. It omits +// those parts of the tile that are covered by other/better tiles. If the entire tile should be +// rendered, it contains the { 0, 0, 0 } tile. If it's empty, no part of the tile will be rendered. +// TileMasks are typically generated with algorithm::updateTileMasks(). +using TileMask = std::set; + +} // namespace mbgl diff --git a/src/mbgl/tile/tile_id_io.cpp b/src/mbgl/tile/tile_id_io.cpp index f6adbf183f6..d8be6b93d61 100644 --- a/src/mbgl/tile/tile_id_io.cpp +++ b/src/mbgl/tile/tile_id_io.cpp @@ -6,6 +6,8 @@ namespace mbgl { ::std::ostream& operator<<(::std::ostream& os, const CanonicalTileID& rhs) { + // Uncomment this to create code instead of shorthands. + // return os << "CanonicalTileID{ " << uint32_t(rhs.z) << ", " << rhs.x << ", " << rhs.y << " }"; return os << uint32_t(rhs.z) << "/" << rhs.x << "/" << rhs.y; } @@ -26,6 +28,9 @@ std::string toString(const OverscaledTileID& rhs) { } // namespace util ::std::ostream& operator<<(::std::ostream& os, const UnwrappedTileID& rhs) { + // Uncomment this to create code instead of shorthands. + // return os << "UnwrappedTileID{ " << uint32_t(rhs.wrap) << ", { " << uint32_t(rhs.canonical.z) + // << ", " << rhs.canonical.x << ", " << rhs.canonical.y << " } }"; return os << rhs.canonical << (rhs.wrap >= 0 ? "+" : "") << rhs.wrap; } diff --git a/test/algorithm/update_tile_masks.test.cpp b/test/algorithm/update_tile_masks.test.cpp new file mode 100644 index 00000000000..3c698eb0cd9 --- /dev/null +++ b/test/algorithm/update_tile_masks.test.cpp @@ -0,0 +1,132 @@ +#include + +#include + +using namespace mbgl; + +namespace { + +class MaskedRenderable { +public: + MaskedRenderable(const UnwrappedTileID& id_, TileMask&& mask_) + : id(id_), mask(std::move(mask_)) { + } + + UnwrappedTileID id; + TileMask mask; + bool used = true; + + void setMask(TileMask&& mask_) { + mask = std::move(mask_); + } +}; + +bool operator==(const MaskedRenderable& lhs, const MaskedRenderable& rhs) { + return lhs.id == rhs.id && lhs.mask == rhs.mask; +} + +::std::ostream& operator<<(::std::ostream& os, const MaskedRenderable& rhs) { + os << "MaskedRenderable{ " << rhs.id << ", { "; + bool first = true; + for (auto& id : rhs.mask) { + if (!first) { + os << ", "; + } else { + first = false; + } + os << id; + } + return os << " } }"; +} + +} // namespace + +void validate(const std::vector expected) { + std::vector actual = expected; + std::for_each(actual.begin(), actual.end(), + [](auto& renderable) { renderable.mask.clear(); }); + algorithm::updateTileMasks({ actual.begin(), actual.end() }); + EXPECT_EQ(expected, actual); +} + +TEST(UpdateTileMasks, NoChildren) { + validate({ + MaskedRenderable{ UnwrappedTileID{ 0, 0, 0 }, { CanonicalTileID{ 0, 0, 0 } } }, + }); + + validate({ + MaskedRenderable{ UnwrappedTileID{ 4, 3, 8 }, { CanonicalTileID{ 0, 0, 0 } } }, + }); + + validate({ + MaskedRenderable{ UnwrappedTileID{ 1, 0, 0 }, { CanonicalTileID{ 0, 0, 0 } } }, + MaskedRenderable{ UnwrappedTileID{ 1, 1, 1 }, { CanonicalTileID{ 0, 0, 0 } } }, + }); + + validate({ + MaskedRenderable{ UnwrappedTileID{ 1, 0, 0 }, { CanonicalTileID{ 0, 0, 0 } } }, + MaskedRenderable{ UnwrappedTileID{ 2, 2, 3 }, { CanonicalTileID{ 0, 0, 0 } } }, + }); +} + +TEST(UpdateTileMasks, ParentAndFourChildren) { + validate({ + // Mask is empty (== not rendered!) because we have four covering children. + MaskedRenderable{ UnwrappedTileID{ 0, 0, 0 }, {} }, + // All four covering children + MaskedRenderable{ UnwrappedTileID{ 1, 0, 0 }, { CanonicalTileID{ 0, 0, 0 } } }, + MaskedRenderable{ UnwrappedTileID{ 1, 0, 1 }, { CanonicalTileID{ 0, 0, 0 } } }, + MaskedRenderable{ UnwrappedTileID{ 1, 1, 0 }, { CanonicalTileID{ 0, 0, 0 } } }, + MaskedRenderable{ UnwrappedTileID{ 1, 1, 1 }, { CanonicalTileID{ 0, 0, 0 } } }, + }); +} + +TEST(UpdateTileMasks, OneChild) { + validate({ + MaskedRenderable{ UnwrappedTileID{ 0, 0, 0 }, + // Only render the three children that aren't covering the other tile. + { CanonicalTileID{ 1, 0, 1 }, CanonicalTileID{ 1, 1, 0 }, + CanonicalTileID{ 1, 1, 1 } } }, + MaskedRenderable{ UnwrappedTileID{ 1, 0, 0 }, { CanonicalTileID{ 0, 0, 0 } } }, + }); +} + +TEST(UpdateTileMasks, Complex) { + validate({ + MaskedRenderable{ UnwrappedTileID{ 0, 0, 0 }, + { CanonicalTileID{ 1, 0, 1 }, CanonicalTileID{ 1, 1, 0 }, + CanonicalTileID{ 2, 2, 3 }, CanonicalTileID{ 2, 3, 2 }, + CanonicalTileID{ 3, 6, 7 }, CanonicalTileID{ 3, 7, 6 } } }, + MaskedRenderable{ UnwrappedTileID{ 0, { 1, 0, 0 } }, { CanonicalTileID{ 0, 0, 0 } } }, + MaskedRenderable{ UnwrappedTileID{ 0, { 2, 2, 2 } }, { CanonicalTileID{ 0, 0, 0 } } }, + MaskedRenderable{ UnwrappedTileID{ 0, { 3, 7, 7 } }, { CanonicalTileID{ 0, 0, 0 } } }, + MaskedRenderable{ UnwrappedTileID{ 0, { 3, 6, 6 } }, { CanonicalTileID{ 0, 0, 0 } } }, + }); + + validate({ + MaskedRenderable{ UnwrappedTileID{ 0, 0, 0 }, + { CanonicalTileID{ 1, 0, 1 }, CanonicalTileID{ 1, 1, 0 }, + CanonicalTileID{ 1, 1, 1 }, CanonicalTileID{ 2, 0, 0 }, + CanonicalTileID{ 2, 0, 1 }, CanonicalTileID{ 2, 1, 0 }, + CanonicalTileID{ 3, 2, 3 }, CanonicalTileID{ 3, 3, 2 }, + CanonicalTileID{ 3, 3, 3 }, CanonicalTileID{ 4, 4, 5 }, + CanonicalTileID{ 4, 5, 4 }, CanonicalTileID{ 4, 5, 5 } } }, + MaskedRenderable{ UnwrappedTileID{ 4, 4, 4 }, { CanonicalTileID{ 0, 0, 0 } } }, + }); + + validate({ + MaskedRenderable{ UnwrappedTileID{ 12, 1028, 1456 }, + { CanonicalTileID{ 1, 1, 1 }, CanonicalTileID{ 2, 3, 0 }, + CanonicalTileID{ 2, 3, 1 } } }, + MaskedRenderable{ UnwrappedTileID{ 13, 2056, 2912 }, + { CanonicalTileID{ 1, 0, 1 }, CanonicalTileID{ 1, 1, 0 }, + CanonicalTileID{ 1, 1, 1 } } }, + MaskedRenderable{ UnwrappedTileID{ 13, 2056, 2913 }, + { CanonicalTileID{ 1, 0, 0 }, CanonicalTileID{ 1, 1, 0 }, + CanonicalTileID{ 1, 1, 1 } } }, + MaskedRenderable{ UnwrappedTileID{ 14, 4112, 5824 }, { CanonicalTileID{ 0, 0, 0 } } }, + MaskedRenderable{ UnwrappedTileID{ 14, 4112, 5827 }, { CanonicalTileID{ 0, 0, 0 } } }, + MaskedRenderable{ UnwrappedTileID{ 14, 4114, 5824 }, { CanonicalTileID{ 0, 0, 0 } } }, + MaskedRenderable{ UnwrappedTileID{ 14, 4114, 5825 }, { CanonicalTileID{ 0, 0, 0 } } }, + }); +} From 244fa4727cd8fe1ba02156a266f454eedd609ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Thu, 20 Jul 2017 14:49:56 +0200 Subject: [PATCH 2/2] [core] generate masks for raster tiles to avoid painting over children --- mapbox-gl-js | 2 +- src/mbgl/gl/index_buffer.hpp | 1 + src/mbgl/gl/vertex_buffer.hpp | 1 + src/mbgl/renderer/buckets/raster_bucket.cpp | 65 ++++++- src/mbgl/renderer/buckets/raster_bucket.hpp | 4 + .../renderer/layers/render_raster_layer.cpp | 17 +- src/mbgl/renderer/render_tile.cpp | 4 + src/mbgl/renderer/render_tile.hpp | 2 + .../renderer/sources/render_raster_source.cpp | 2 + src/mbgl/tile/raster_tile.cpp | 8 +- src/mbgl/tile/raster_tile.hpp | 7 +- src/mbgl/tile/tile.hpp | 2 + test/gl/bucket.test.cpp | 159 ++++++++++++++++++ 13 files changed, 265 insertions(+), 9 deletions(-) diff --git a/mapbox-gl-js b/mapbox-gl-js index 31444a72c2d..0c225291ab7 160000 --- a/mapbox-gl-js +++ b/mapbox-gl-js @@ -1 +1 @@ -Subproject commit 31444a72c2dafdd7dba99dc3bf5428c155e07cf4 +Subproject commit 0c225291ab77c6aa5e4b4ca41168c1e501d94be1 diff --git a/src/mbgl/gl/index_buffer.hpp b/src/mbgl/gl/index_buffer.hpp index 1e57fb11f22..01b6396e000 100644 --- a/src/mbgl/gl/index_buffer.hpp +++ b/src/mbgl/gl/index_buffer.hpp @@ -26,6 +26,7 @@ class IndexVector { bool empty() const { return v.empty(); } void clear() { v.clear(); } const uint16_t* data() const { return v.data(); } + const std::vector& vector() const { return v; } private: std::vector v; diff --git a/src/mbgl/gl/vertex_buffer.hpp b/src/mbgl/gl/vertex_buffer.hpp index 4808803d00f..9f8b156b255 100644 --- a/src/mbgl/gl/vertex_buffer.hpp +++ b/src/mbgl/gl/vertex_buffer.hpp @@ -28,6 +28,7 @@ class VertexVector { bool empty() const { return v.empty(); } void clear() { v.clear(); } const Vertex* data() const { return v.data(); } + const std::vector& vector() const { return v; } private: std::vector v; diff --git a/src/mbgl/renderer/buckets/raster_bucket.cpp b/src/mbgl/renderer/buckets/raster_bucket.cpp index 3f5879262df..1a0409f456e 100644 --- a/src/mbgl/renderer/buckets/raster_bucket.cpp +++ b/src/mbgl/renderer/buckets/raster_bucket.cpp @@ -22,7 +22,7 @@ void RasterBucket::upload(gl::Context& context) { if (!texture) { texture = context.createTexture(*image); } - if (!vertices.empty()) { + if (!segments.empty()) { vertexBuffer = context.createVertexBuffer(std::move(vertices)); indexBuffer = context.createIndexBuffer(std::move(indices)); } @@ -45,6 +45,69 @@ void RasterBucket::setImage(std::shared_ptr image_) { uploaded = false; } +void RasterBucket::setMask(TileMask&& mask_) { + if (mask == mask_) { + return; + } + + mask = std::move(mask_); + clear(); + + if (mask == TileMask{ { 0, 0, 0 } }) { + // We want to render the full tile, and keeping the segments/vertices/indices empty means + // using the global shared buffers for covering the entire tile. + return; + } + + // Create a new segment so that we will upload (empty) buffers even when there is nothing to + // draw for this tile. + segments.emplace_back(0, 0); + + constexpr const uint16_t vertexLength = 4; + + // Create the vertex buffer for the specified tile mask. + for (const auto& id : mask) { + // Create a quad for every masked tile. + const int32_t vertexExtent = util::EXTENT >> id.z; + const int32_t textureExtent = 32768 >> id.z; + + const Point tlVertex = { static_cast(id.x * vertexExtent), + static_cast(id.y * vertexExtent) }; + const Point brVertex = { static_cast(tlVertex.x + vertexExtent), + static_cast(tlVertex.y + vertexExtent) }; + const Point tlTexture = { static_cast(id.x * textureExtent), + static_cast(id.y * textureExtent) }; + const Point brTexture = { static_cast(tlTexture.x + textureExtent), + static_cast(tlTexture.y + textureExtent) }; + + if (segments.back().vertexLength + vertexLength > std::numeric_limits::max()) { + // Move to a new segments because the old one can't hold the geometry. + segments.emplace_back(vertices.vertexSize(), indices.indexSize()); + } + + vertices.emplace_back( + RasterProgram::layoutVertex({ tlVertex.x, tlVertex.y }, { tlTexture.x, tlTexture.y })); + vertices.emplace_back( + RasterProgram::layoutVertex({ brVertex.x, tlVertex.y }, { brTexture.x, tlTexture.y })); + vertices.emplace_back( + RasterProgram::layoutVertex({ tlVertex.x, brVertex.y }, { tlTexture.x, brTexture.y })); + vertices.emplace_back( + RasterProgram::layoutVertex({ brVertex.x, brVertex.y }, { brTexture.x, brTexture.y })); + + auto& segment = segments.back(); + assert(segment.vertexLength <= std::numeric_limits::max()); + const uint16_t offset = segment.vertexLength; + + // 0, 1, 2 + // 1, 2, 3 + indices.emplace_back(offset, offset + 1, offset + 2); + indices.emplace_back(offset + 1, offset + 2, offset + 3); + + segment.vertexLength += vertexLength; + segment.indexLength += 6; + } +} + bool RasterBucket::hasData() const { return !!image; } diff --git a/src/mbgl/renderer/buckets/raster_bucket.hpp b/src/mbgl/renderer/buckets/raster_bucket.hpp index 0b8432eaaff..3800eadec8b 100644 --- a/src/mbgl/renderer/buckets/raster_bucket.hpp +++ b/src/mbgl/renderer/buckets/raster_bucket.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -21,8 +22,11 @@ class RasterBucket : public Bucket { void clear(); void setImage(std::shared_ptr); + void setMask(TileMask&&); + std::shared_ptr image; optional texture; + TileMask mask{ { 0, 0, 0 } }; // Bucket specific vertices are used for Image Sources only // Raster Tile Sources use the default buffers from Painter diff --git a/src/mbgl/renderer/layers/render_raster_layer.cpp b/src/mbgl/renderer/layers/render_raster_layer.cpp index 367fd91fa5d..06616d90e58 100644 --- a/src/mbgl/renderer/layers/render_raster_layer.cpp +++ b/src/mbgl/renderer/layers/render_raster_layer.cpp @@ -135,10 +135,19 @@ void RenderRasterLayer::render(PaintParameters& parameters, RenderSource* source parameters.context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear); parameters.context.bindTexture(*bucket.texture, 1, gl::TextureFilter::Linear); - draw(tile.matrix, - parameters.staticData.rasterVertexBuffer, - parameters.staticData.quadTriangleIndexBuffer, - parameters.staticData.rasterSegments); + if (bucket.vertexBuffer && bucket.indexBuffer && !bucket.segments.empty()) { + // Draw only the parts of the tile that aren't drawn by another tile in the layer. + draw(tile.matrix, + *bucket.vertexBuffer, + *bucket.indexBuffer, + bucket.segments); + } else { + // Draw the full tile. + draw(tile.matrix, + parameters.staticData.rasterVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.rasterSegments); + } } } } diff --git a/src/mbgl/renderer/render_tile.cpp b/src/mbgl/renderer/render_tile.cpp index 4e924331cc6..c9912f0563e 100644 --- a/src/mbgl/renderer/render_tile.cpp +++ b/src/mbgl/renderer/render_tile.cpp @@ -52,6 +52,10 @@ mat4 RenderTile::translatedClipMatrix(const std::array& translation, return translateVtxMatrix(nearClippedMatrix, translation, anchor, state, false); } +void RenderTile::setMask(TileMask&& mask) { + tile.setMask(std::move(mask)); +} + void RenderTile::startRender(PaintParameters& parameters) { tile.upload(parameters.context); diff --git a/src/mbgl/renderer/render_tile.hpp b/src/mbgl/renderer/render_tile.hpp index d5ed4f00183..b498972f5ce 100644 --- a/src/mbgl/renderer/render_tile.hpp +++ b/src/mbgl/renderer/render_tile.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -36,6 +37,7 @@ class RenderTile final { style::TranslateAnchorType anchor, const TransformState&) const; + void setMask(TileMask&&); void startRender(PaintParameters&); void finishRender(PaintParameters&); diff --git a/src/mbgl/renderer/sources/render_raster_source.cpp b/src/mbgl/renderer/sources/render_raster_source.cpp index 910a812f052..cbd874f647e 100644 --- a/src/mbgl/renderer/sources/render_raster_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_source.cpp @@ -1,6 +1,7 @@ #include #include #include +#include namespace mbgl { @@ -57,6 +58,7 @@ void RenderRasterSource::update(Immutable baseImpl_, } void RenderRasterSource::startRender(PaintParameters& parameters) { + algorithm::updateTileMasks(tilePyramid.getRenderTiles()); tilePyramid.startRender(parameters); } diff --git a/src/mbgl/tile/raster_tile.cpp b/src/mbgl/tile/raster_tile.cpp index 8a92c40e4a9..1260fd1edd0 100644 --- a/src/mbgl/tile/raster_tile.cpp +++ b/src/mbgl/tile/raster_tile.cpp @@ -41,7 +41,7 @@ void RasterTile::setData(std::shared_ptr data, worker.invoke(&RasterTileWorker::parse, data); } -void RasterTile::onParsed(std::unique_ptr result) { +void RasterTile::onParsed(std::unique_ptr result) { bucket = std::move(result); loaded = true; renderable = bucket ? true : false; @@ -65,6 +65,12 @@ Bucket* RasterTile::getBucket(const style::Layer::Impl&) const { return bucket.get(); } +void RasterTile::setMask(TileMask&& mask) { + if (bucket) { + bucket->setMask(std::move(mask)); + } +} + void RasterTile::setNecessity(Necessity necessity) { loader.setNecessity(necessity); } diff --git a/src/mbgl/tile/raster_tile.hpp b/src/mbgl/tile/raster_tile.hpp index 51075a2dbcb..28a27b2b37f 100644 --- a/src/mbgl/tile/raster_tile.hpp +++ b/src/mbgl/tile/raster_tile.hpp @@ -9,6 +9,7 @@ namespace mbgl { class Tileset; class TileParameters; +class RasterBucket; namespace style { class Layer; @@ -33,7 +34,9 @@ class RasterTile : public Tile { void upload(gl::Context&) override; Bucket* getBucket(const style::Layer::Impl&) const override; - void onParsed(std::unique_ptr result); + void setMask(TileMask&&) override; + + void onParsed(std::unique_ptr result); void onError(std::exception_ptr); private: @@ -44,7 +47,7 @@ class RasterTile : public Tile { // Contains the Bucket object for the tile. Buckets are render // objects and they get added by tile parsing operations. - std::unique_ptr bucket; + std::unique_ptr bucket; }; } // namespace mbgl diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp index 1898f768499..a1ab6a84b7d 100644 --- a/src/mbgl/tile/tile.hpp +++ b/src/mbgl/tile/tile.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,7 @@ class Tile : private util::noncopyable { virtual void setPlacementConfig(const PlacementConfig&) {} virtual void setLayers(const std::vector>&) {} + virtual void setMask(TileMask&&) {} virtual void queryRenderedFeatures( std::unordered_map>& result, diff --git a/test/gl/bucket.test.cpp b/test/gl/bucket.test.cpp index 7ff67f40d05..afdbd2a29b6 100644 --- a/test/gl/bucket.test.cpp +++ b/test/gl/bucket.test.cpp @@ -12,6 +12,26 @@ #include +namespace mbgl { + +template +bool operator==(const Segment& lhs, const Segment& rhs) { + return std::tie(lhs.vertexOffset, lhs.indexOffset, lhs.vertexLength, lhs.indexLength) == + std::tie(rhs.vertexOffset, rhs.indexOffset, rhs.vertexLength, rhs.indexLength); +} + +namespace gl { +namespace detail { + +template +bool operator==(const Vertex& lhs, const Vertex& rhs) { + return std::tie(lhs.a1, lhs.a2) == std::tie(rhs.a1, rhs.a2); +} + +} // namespace detail +} // namespace gl +} // namespace mbgl + using namespace mbgl; namespace { @@ -114,3 +134,142 @@ TEST(Buckets, RasterBucket) { bucket.clear(); ASSERT_TRUE(bucket.needsUpload()); } + +TEST(Buckets, RasterBucketMaskEmpty) { + RasterBucket bucket{ nullptr }; + bucket.setMask({}); + EXPECT_EQ((std::vector{}), bucket.vertices.vector()); + EXPECT_EQ((std::vector{}), bucket.indices.vector()); + SegmentVector expectedSegments; + expectedSegments.emplace_back(0, 0, 0, 0); + EXPECT_EQ(expectedSegments, bucket.segments); +} + +TEST(Buckets, RasterBucketMaskNoChildren) { + RasterBucket bucket{ nullptr }; + bucket.setMask({ CanonicalTileID{ 0, 0, 0 } }); + + // A mask of 0/0/0 doesn't produce buffers since we're instead using the global shared buffers. + EXPECT_EQ((std::vector{}), bucket.vertices.vector()); + EXPECT_EQ((std::vector{}), bucket.indices.vector()); + EXPECT_EQ((SegmentVector{}), bucket.segments); +} + + TEST(Buckets, RasterBucketMaskTwoChildren) { + RasterBucket bucket{ nullptr }; + bucket.setMask( + { CanonicalTileID{ 1, 0, 0 }, CanonicalTileID{ 1, 1, 1 } }); + + EXPECT_EQ( + (std::vector{ + // 1/0/1 + RasterProgram::layoutVertex({ 0, 0 }, { 0, 0 }), + RasterProgram::layoutVertex({ 4096, 0 }, { 16384, 0 }), + RasterProgram::layoutVertex({ 0, 4096 }, { 0, 16384 }), + RasterProgram::layoutVertex({ 4096, 4096 }, { 16384, 16384 }), + + // 1/1/1 + RasterProgram::layoutVertex({ 4096, 4096 }, { 16384, 16384 }), + RasterProgram::layoutVertex({ 8192, 4096 }, { 32768, 16384 }), + RasterProgram::layoutVertex({ 4096, 8192 }, { 16384, 32768 }), + RasterProgram::layoutVertex({ 8192, 8192 }, { 32768, 32768 }), + }), + bucket.vertices.vector()); + + EXPECT_EQ( + (std::vector{ + // 1/0/1 + 0, 1, 2, + 1, 2, 3, + + // 1/1/1 + 4, 5, 6, + 5, 6, 7, + }), + bucket.indices.vector()); + + + SegmentVector expectedSegments; + expectedSegments.emplace_back(0, 0, 8, 12); + EXPECT_EQ(expectedSegments, bucket.segments); + } + + TEST(Buckets, RasterBucketMaskComplex) { + RasterBucket bucket{ nullptr }; + bucket.setMask( + { CanonicalTileID{ 1, 0, 1 }, CanonicalTileID{ 1, 1, 0 }, CanonicalTileID{ 2, 2, 3 }, + CanonicalTileID{ 2, 3, 2 }, CanonicalTileID{ 3, 6, 7 }, CanonicalTileID{ 3, 7, 6 } }); + + EXPECT_EQ( + (std::vector{ + // 1/0/1 + RasterProgram::layoutVertex({ 0, 4096 }, { 0, 16384 }), + RasterProgram::layoutVertex({ 4096, 4096 }, { 16384, 16384 }), + RasterProgram::layoutVertex({ 0, 8192 }, { 0, 32768 }), + RasterProgram::layoutVertex({ 4096, 8192 }, { 16384, 32768 }), + + // 1/1/0 + RasterProgram::layoutVertex({ 4096, 0 }, { 16384, 0 }), + RasterProgram::layoutVertex({ 8192, 0 }, { 32768, 0 }), + RasterProgram::layoutVertex({ 4096, 4096 }, { 16384, 16384 }), + RasterProgram::layoutVertex({ 8192, 4096 }, { 32768, 16384 }), + + // 2/2/3 + RasterProgram::layoutVertex({ 4096, 6144 }, { 16384, 24576 }), + RasterProgram::layoutVertex({ 6144, 6144 }, { 24576, 24576 }), + RasterProgram::layoutVertex({ 4096, 8192 }, { 16384, 32768 }), + RasterProgram::layoutVertex({ 6144, 8192 }, { 24576, 32768 }), + + // 2/3/2 + RasterProgram::layoutVertex({ 6144, 4096 }, { 24576, 16384 }), + RasterProgram::layoutVertex({ 8192, 4096 }, { 32768, 16384 }), + RasterProgram::layoutVertex({ 6144, 6144 }, { 24576, 24576 }), + RasterProgram::layoutVertex({ 8192, 6144 }, { 32768, 24576 }), + + // 3/6/7 + RasterProgram::layoutVertex({ 6144, 7168 }, { 24576, 28672 }), + RasterProgram::layoutVertex({ 7168, 7168 }, { 28672, 28672 }), + RasterProgram::layoutVertex({ 6144, 8192 }, { 24576, 32768 }), + RasterProgram::layoutVertex({ 7168, 8192 }, { 28672, 32768 }), + + // 3/7/6 + RasterProgram::layoutVertex({ 7168, 6144 }, { 28672, 24576 }), + RasterProgram::layoutVertex({ 8192, 6144 }, { 32768, 24576 }), + RasterProgram::layoutVertex({ 7168, 7168 }, { 28672, 28672 }), + RasterProgram::layoutVertex({ 8192, 7168 }, { 32768, 28672 }), + }), + bucket.vertices.vector()); + + EXPECT_EQ( + (std::vector{ + // 1/0/1 + 0, 1, 2, + 1, 2, 3, + + // 1/1/0 + 4, 5, 6, + 5, 6, 7, + + // 2/2/3 + 8, 9, 10, + 9, 10, 11, + + // 2/3/2 + 12, 13, 14, + 13, 14, 15, + + // 3/6/7 + 16, 17, 18, + 17, 18, 19, + + // 3/7/6 + 20, 21, 22, + 21, 22, 23, + }), + bucket.indices.vector()); + + + SegmentVector expectedSegments; + expectedSegments.emplace_back(0, 0, 24, 36); + EXPECT_EQ(expectedSegments, bucket.segments); + }