From 3f0c89d633a5056006557ad5f4b9e446807d00ee Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Thu, 20 Apr 2017 17:11:50 -0700 Subject: [PATCH] [core] Refactor Source::*Impls into RenderSources and TilePyramid --- cmake/core-files.cmake | 15 + include/mbgl/style/source.hpp | 1 - include/mbgl/style/sources/geojson_source.hpp | 19 +- .../default/mbgl/storage/offline_download.cpp | 4 +- src/mbgl/annotation/annotation_source.cpp | 11 +- src/mbgl/annotation/annotation_source.hpp | 8 +- .../annotation/render_annotation_source.cpp | 79 +++++ .../annotation/render_annotation_source.hpp | 53 ++++ src/mbgl/map/map.cpp | 7 +- src/mbgl/renderer/painter.cpp | 8 +- src/mbgl/renderer/painter.hpp | 1 + src/mbgl/renderer/render_item.hpp | 4 +- src/mbgl/renderer/render_source.cpp | 26 ++ src/mbgl/renderer/render_source.hpp | 87 ++++++ src/mbgl/renderer/render_source_observer.hpp | 18 ++ .../sources/render_geojson_source.cpp | 95 ++++++ .../sources/render_geojson_source.hpp | 59 ++++ .../renderer/sources/render_raster_source.cpp | 87 ++++++ .../renderer/sources/render_raster_source.hpp | 55 ++++ .../renderer/sources/render_vector_source.cpp | 91 ++++++ .../renderer/sources/render_vector_source.hpp | 55 ++++ src/mbgl/renderer/tile_pyramid.cpp | 263 ++++++++++++++++ src/mbgl/renderer/tile_pyramid.hpp | 87 ++++++ src/mbgl/style/source.cpp | 4 - src/mbgl/style/source_impl.cpp | 281 +----------------- src/mbgl/style/source_impl.hpp | 96 +----- src/mbgl/style/source_observer.hpp | 8 - src/mbgl/style/sources/geojson_source.cpp | 2 +- .../style/sources/geojson_source_impl.cpp | 89 +++--- .../style/sources/geojson_source_impl.hpp | 21 +- src/mbgl/style/sources/raster_source_impl.cpp | 7 +- src/mbgl/style/sources/raster_source_impl.hpp | 3 +- src/mbgl/style/sources/vector_source_impl.cpp | 8 +- src/mbgl/style/sources/vector_source_impl.hpp | 3 +- src/mbgl/style/style.cpp | 127 ++++---- src/mbgl/style/style.hpp | 11 +- src/mbgl/style/tile_source_impl.cpp | 25 +- src/mbgl/style/tile_source_impl.hpp | 4 +- src/mbgl/tile/geojson_tile.cpp | 8 +- src/mbgl/tile/geojson_tile.hpp | 5 +- .../mbgl/test/stub_render_source_observer.hpp | 23 ++ test/src/mbgl/test/stub_style_observer.hpp | 11 - test/style/source.test.cpp | 226 +++++++------- test/tile/geojson_tile.test.cpp | 16 +- 44 files changed, 1401 insertions(+), 710 deletions(-) create mode 100644 src/mbgl/annotation/render_annotation_source.cpp create mode 100644 src/mbgl/annotation/render_annotation_source.hpp create mode 100644 src/mbgl/renderer/render_source.cpp create mode 100644 src/mbgl/renderer/render_source.hpp create mode 100644 src/mbgl/renderer/render_source_observer.hpp create mode 100644 src/mbgl/renderer/sources/render_geojson_source.cpp create mode 100644 src/mbgl/renderer/sources/render_geojson_source.hpp create mode 100644 src/mbgl/renderer/sources/render_raster_source.cpp create mode 100644 src/mbgl/renderer/sources/render_raster_source.hpp create mode 100644 src/mbgl/renderer/sources/render_vector_source.cpp create mode 100644 src/mbgl/renderer/sources/render_vector_source.hpp create mode 100644 src/mbgl/renderer/tile_pyramid.cpp create mode 100644 src/mbgl/renderer/tile_pyramid.hpp create mode 100644 test/src/mbgl/test/stub_render_source_observer.hpp diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index d5f7ee0cc2f..3594b55be6d 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -28,6 +28,8 @@ set(MBGL_CORE_FILES src/mbgl/annotation/fill_annotation_impl.hpp src/mbgl/annotation/line_annotation_impl.cpp src/mbgl/annotation/line_annotation_impl.hpp + src/mbgl/annotation/render_annotation_source.cpp + src/mbgl/annotation/render_annotation_source.hpp src/mbgl/annotation/shape_annotation_impl.cpp src/mbgl/annotation/shape_annotation_impl.hpp src/mbgl/annotation/style_sourced_annotation_impl.cpp @@ -208,10 +210,23 @@ set(MBGL_CORE_FILES src/mbgl/renderer/render_raster_layer.hpp src/mbgl/renderer/render_symbol_layer.cpp src/mbgl/renderer/render_symbol_layer.hpp + src/mbgl/renderer/render_source.cpp + src/mbgl/renderer/render_source.hpp + src/mbgl/renderer/render_source_observer.hpp src/mbgl/renderer/render_tile.cpp src/mbgl/renderer/render_tile.hpp src/mbgl/renderer/symbol_bucket.cpp src/mbgl/renderer/symbol_bucket.hpp + src/mbgl/renderer/tile_pyramid.cpp + src/mbgl/renderer/tile_pyramid.hpp + + # renderer/sources + src/mbgl/renderer/sources/render_geojson_source.cpp + src/mbgl/renderer/sources/render_geojson_source.hpp + src/mbgl/renderer/sources/render_raster_source.cpp + src/mbgl/renderer/sources/render_raster_source.hpp + src/mbgl/renderer/sources/render_vector_source.cpp + src/mbgl/renderer/sources/render_vector_source.hpp # shaders src/mbgl/shaders/circle.cpp diff --git a/include/mbgl/style/source.hpp b/include/mbgl/style/source.hpp index e49f1fc2735..66fc2afd257 100644 --- a/include/mbgl/style/source.hpp +++ b/include/mbgl/style/source.hpp @@ -56,7 +56,6 @@ class Source : public mbgl::util::noncopyable { std::unique_ptr copy(const std::string& id) const; optional getAttribution() const; - optional> getZoomRange() const; // Private implementation class Impl; diff --git a/include/mbgl/style/sources/geojson_source.hpp b/include/mbgl/style/sources/geojson_source.hpp index ede0301725c..5b39d7821ba 100644 --- a/include/mbgl/style/sources/geojson_source.hpp +++ b/include/mbgl/style/sources/geojson_source.hpp @@ -4,26 +4,9 @@ #include #include -#include - -namespace mapbox { - -namespace geojsonvt { -class GeoJSONVT; -} // namespace geojsonvt - -namespace supercluster { -class Supercluster; -} // namespace supercluster - -} // namespace mapbox - namespace mbgl { namespace style { -using GeoJSONVTPointer = std::unique_ptr; -using SuperclusterPointer = std::unique_ptr; - struct GeoJSONOptions { // GeoJSON-VT options uint8_t maxzoom = 18; @@ -38,7 +21,7 @@ struct GeoJSONOptions { class GeoJSONSource : public Source { public: - GeoJSONSource(const std::string& id, const GeoJSONOptions options_ = GeoJSONOptions()); + GeoJSONSource(const std::string& id, const GeoJSONOptions& = {}); void setURL(const std::string& url); void setGeoJSON(const GeoJSON&); diff --git a/platform/default/mbgl/storage/offline_download.cpp b/platform/default/mbgl/storage/offline_download.cpp index c8aa98d8742..cba2f70df80 100644 --- a/platform/default/mbgl/storage/offline_download.cpp +++ b/platform/default/mbgl/storage/offline_download.cpp @@ -98,9 +98,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const { } case SourceType::GeoJSON: { - style::GeoJSONSource::Impl* geojsonSource = - static_cast(source->baseImpl.get()); - + style::GeoJSONSource* geojsonSource = source->as(); if (geojsonSource->getURL()) { result.requiredResourceCount += 1; } diff --git a/src/mbgl/annotation/annotation_source.cpp b/src/mbgl/annotation/annotation_source.cpp index c52836c5003..99561401791 100644 --- a/src/mbgl/annotation/annotation_source.cpp +++ b/src/mbgl/annotation/annotation_source.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include namespace mbgl { @@ -14,17 +14,12 @@ AnnotationSource::Impl::Impl(Source& base_) : Source::Impl(SourceType::Annotations, AnnotationManager::SourceID, base_) { } -optional> AnnotationSource::Impl::getZoomRange() const { - return { { 0, 22 } }; -} - void AnnotationSource::Impl::loadDescription(FileSource&) { loaded = true; } -std::unique_ptr AnnotationSource::Impl::createTile(const OverscaledTileID& tileID, - const style::UpdateParameters& parameters) { - return std::make_unique(tileID, parameters); +std::unique_ptr AnnotationSource::Impl::createRenderSource() const { + return std::make_unique(*this); } } // namespace mbgl diff --git a/src/mbgl/annotation/annotation_source.hpp b/src/mbgl/annotation/annotation_source.hpp index 07e00dc52df..46c9564443d 100644 --- a/src/mbgl/annotation/annotation_source.hpp +++ b/src/mbgl/annotation/annotation_source.hpp @@ -17,13 +17,7 @@ class AnnotationSource::Impl : public style::Source::Impl { Impl(Source&); void loadDescription(FileSource&) final; - - optional> getZoomRange() const final; - -private: - uint16_t getTileSize() const final { return util::tileSize; } - - std::unique_ptr createTile(const OverscaledTileID&, const style::UpdateParameters&) final; + std::unique_ptr createRenderSource() const final; }; } // namespace mbgl diff --git a/src/mbgl/annotation/render_annotation_source.cpp b/src/mbgl/annotation/render_annotation_source.cpp new file mode 100644 index 00000000000..f926b6ce9a1 --- /dev/null +++ b/src/mbgl/annotation/render_annotation_source.cpp @@ -0,0 +1,79 @@ +#include +#include +#include + +#include +#include + +namespace mbgl { + +using namespace style; + +RenderAnnotationSource::RenderAnnotationSource(const AnnotationSource::Impl& impl_) + : RenderSource(impl_) { + tilePyramid.setObserver(this); +} + +bool RenderAnnotationSource::isLoaded() const { + return tilePyramid.isLoaded(); +} + +void RenderAnnotationSource::invalidateTiles() { + tilePyramid.invalidateTiles(); +} + +void RenderAnnotationSource::startRender(algorithm::ClipIDGenerator& generator, const mat4& projMatrix, const mat4& clipMatrix, const TransformState& transform) { + generator.update(tilePyramid.getRenderTiles()); + tilePyramid.startRender(projMatrix, clipMatrix, transform); +} + +void RenderAnnotationSource::finishRender(Painter& painter) { + tilePyramid.finishRender(painter); +} + +std::map& RenderAnnotationSource::getRenderTiles() { + return tilePyramid.getRenderTiles(); +} + +void RenderAnnotationSource::updateTiles(const UpdateParameters& parameters) { + tilePyramid.updateTiles(parameters, + SourceType::Annotations, + util::tileSize, + { 0, 22 }, + [&] (const OverscaledTileID& tileID) { + return std::make_unique(tileID, parameters); + }); +} + +void RenderAnnotationSource::removeTiles() { + tilePyramid.removeTiles(); +} + +void RenderAnnotationSource::reloadTiles() { + tilePyramid.reloadTiles(); +} + +std::unordered_map> +RenderAnnotationSource::queryRenderedFeatures(const ScreenLineString& geometry, + const TransformState& transformState, + const RenderedQueryOptions& options) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, options); +} + +std::vector RenderAnnotationSource::querySourceFeatures(const SourceQueryOptions&) const { + return {}; +} + +void RenderAnnotationSource::setCacheSize(size_t size) { + tilePyramid.setCacheSize(size); +} + +void RenderAnnotationSource::onLowMemory() { + tilePyramid.onLowMemory(); +} + +void RenderAnnotationSource::dumpDebugLogs() const { + tilePyramid.dumpDebugLogs(); +} + +} // namespace mbgl diff --git a/src/mbgl/annotation/render_annotation_source.hpp b/src/mbgl/annotation/render_annotation_source.hpp new file mode 100644 index 00000000000..394acc51084 --- /dev/null +++ b/src/mbgl/annotation/render_annotation_source.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { + +class RenderAnnotationSource : public RenderSource { +public: + RenderAnnotationSource(const AnnotationSource::Impl&); + + bool isLoaded() const final; + + // Called when the camera has changed. May load new tiles, unload obsolete tiles, or + // trigger re-placement of existing complete tiles. + void updateTiles(const style::UpdateParameters&) final; + + // Removes all tiles (by putting them into the cache). + void removeTiles() final; + + // Remove all tiles and clear the cache. + void invalidateTiles() final; + + // Request that all loaded tiles re-run the layout operation on the existing source + // data with fresh style information. + void reloadTiles() final; + + void startRender(algorithm::ClipIDGenerator&, + const mat4& projMatrix, + const mat4& clipMatrix, + const TransformState&) final; + void finishRender(Painter&) final; + + std::map& getRenderTiles() final; + + std::unordered_map> + queryRenderedFeatures(const ScreenLineString& geometry, + const TransformState& transformState, + const RenderedQueryOptions& options) const final; + + std::vector + querySourceFeatures(const SourceQueryOptions&) const final; + + void setCacheSize(size_t) final; + void onLowMemory() final; + void dumpDebugLogs() const final; + +private: + TilePyramid tilePyramid; +}; + +} // namespace mbgl diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 696dca18722..a1f3bdbbde5 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -7,13 +7,14 @@ #include #include #include -#include +#include #include #include #include #include #include #include +#include #include #include #include @@ -871,10 +872,10 @@ std::vector Map::queryRenderedFeatures(const ScreenBox& box, const Rend std::vector Map::querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options) { if (!impl->style) return {}; - const style::Source* source = impl->style->getSource(sourceID); + const RenderSource* source = impl->style->getRenderSource(sourceID); if (!source) return {}; - return source->baseImpl->querySourceFeatures(options); + return source->querySourceFeatures(options); } AnnotationIDs Map::queryPointAnnotations(const ScreenBox& box) { diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp index 36dd4a793f7..01df326d95f 100644 --- a/src/mbgl/renderer/painter.cpp +++ b/src/mbgl/renderer/painter.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -13,6 +14,7 @@ #include #include +#include #include #include #include @@ -148,7 +150,7 @@ void Painter::render(const Style& style, const FrameData& frame_, View& view, Sp RenderData renderData = style.getRenderData(frame.debugOptions, state.getAngle()); const std::vector& order = renderData.order; - const std::unordered_set& sources = renderData.sources; + const std::unordered_set& sources = renderData.sources; // Update the default matrices to the current viewport dimensions. state.getProjMatrix(projMatrix); @@ -209,7 +211,7 @@ void Painter::render(const Style& style, const FrameData& frame_, View& view, Sp // Update all clipping IDs. algorithm::ClipIDGenerator generator; for (const auto& source : sources) { - source->baseImpl->startRender(generator, projMatrix, nearClippedProjMatrix, state); + source->startRender(generator, projMatrix, nearClippedProjMatrix, state); } MBGL_DEBUG_GROUP(context, "clipping masks"); @@ -258,7 +260,7 @@ void Painter::render(const Style& style, const FrameData& frame_, View& view, Sp // When only rendering layers via the stylesheet, it's possible that we don't // ever visit a tile during rendering. for (const auto& source : sources) { - source->baseImpl->finishRender(*this); + source->finishRender(*this); } } diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp index 7706d2d4512..c7c8173d0d2 100644 --- a/src/mbgl/renderer/painter.hpp +++ b/src/mbgl/renderer/painter.hpp @@ -54,6 +54,7 @@ class RenderBackgroundLayer; class Programs; class PaintParameters; +class TilePyramid; struct ClipID; diff --git a/src/mbgl/renderer/render_item.hpp b/src/mbgl/renderer/render_item.hpp index 01bb263d1e8..787211c30a8 100644 --- a/src/mbgl/renderer/render_item.hpp +++ b/src/mbgl/renderer/render_item.hpp @@ -8,11 +8,11 @@ namespace mbgl { class RenderLayer; +class RenderSource; class RenderTile; class Bucket; namespace style { -class Source; } // namespace style class RenderItem { @@ -29,7 +29,7 @@ class RenderItem { class RenderData { public: Color backgroundColor; - std::unordered_set sources; + std::unordered_set sources; std::vector order; }; diff --git a/src/mbgl/renderer/render_source.cpp b/src/mbgl/renderer/render_source.cpp new file mode 100644 index 00000000000..643d92fe81a --- /dev/null +++ b/src/mbgl/renderer/render_source.cpp @@ -0,0 +1,26 @@ +#include +#include +#include + +namespace mbgl { + +static RenderSourceObserver nullObserver; + +RenderSource::RenderSource(const style::Source::Impl& impl) + : baseImpl(impl), + observer(&nullObserver) { +} + +void RenderSource::setObserver(RenderSourceObserver* observer_) { + observer = observer_; +} + +void RenderSource::onTileChanged(Tile& tile) { + observer->onTileChanged(*this, tile.id); +} + +void RenderSource::onTileError(Tile& tile, std::exception_ptr error) { + observer->onTileError(*this, tile.id, error); +} + +} diff --git a/src/mbgl/renderer/render_source.hpp b/src/mbgl/renderer/render_source.hpp new file mode 100644 index 00000000000..5d93ae49d6d --- /dev/null +++ b/src/mbgl/renderer/render_source.hpp @@ -0,0 +1,87 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace mbgl { + +class Painter; +class TransformState; +class RenderTile; +class RenderedQueryOptions; +class SourceQueryOptions; +class Tile; +class RenderSourceObserver; + +namespace algorithm { +class ClipIDGenerator; +} // namespace algorithm + +namespace style { +class UpdateParameters; +} // namespace style + +class RenderSource : protected TileObserver { +public: + RenderSource(const style::Source::Impl&); + virtual ~RenderSource() = default; + + virtual bool isLoaded() const = 0; + + // Called when the camera has changed. May load new tiles, unload obsolete tiles, or + // trigger re-placement of existing complete tiles. + virtual void updateTiles(const style::UpdateParameters&) = 0; + + // Removes all tiles (by putting them into the cache). + virtual void removeTiles() = 0; + + // Remove all tiles and clear the cache. + virtual void invalidateTiles() = 0; + + // Request that all loaded tiles re-run the layout operation on the existing source + // data with fresh style information. + virtual void reloadTiles() = 0; + + virtual void startRender(algorithm::ClipIDGenerator&, + const mat4& projMatrix, + const mat4& clipMatrix, + const TransformState&) = 0; + virtual void finishRender(Painter&) = 0; + + virtual std::map& getRenderTiles() = 0; + + virtual std::unordered_map> + queryRenderedFeatures(const ScreenLineString& geometry, + const TransformState& transformState, + const RenderedQueryOptions& options) const = 0; + + virtual std::vector + querySourceFeatures(const SourceQueryOptions&) const = 0; + + virtual void setCacheSize(size_t) = 0; + virtual void onLowMemory() = 0; + + virtual void dumpDebugLogs() const = 0; + + void setObserver(RenderSourceObserver*); + + const style::Source::Impl& baseImpl; + bool enabled = false; + +protected: + RenderSourceObserver* observer; + + void onTileChanged(Tile&) final; + void onTileError(Tile&, std::exception_ptr) final; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_source_observer.hpp b/src/mbgl/renderer/render_source_observer.hpp new file mode 100644 index 00000000000..792a18db2b1 --- /dev/null +++ b/src/mbgl/renderer/render_source_observer.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace mbgl { + +class RenderSource; +class OverscaledTileID; + +class RenderSourceObserver { +public: + virtual ~RenderSourceObserver() = default; + + virtual void onTileChanged(RenderSource&, const OverscaledTileID&) {} + virtual void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) {} +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_geojson_source.cpp b/src/mbgl/renderer/sources/render_geojson_source.cpp new file mode 100644 index 00000000000..7e36b44ea42 --- /dev/null +++ b/src/mbgl/renderer/sources/render_geojson_source.cpp @@ -0,0 +1,95 @@ +#include +#include +#include + +#include +#include + +namespace mbgl { + +using namespace style; + +RenderGeoJSONSource::RenderGeoJSONSource(const style::GeoJSONSource::Impl& impl_) + : RenderSource(impl_), + impl(impl_) { + tilePyramid.setObserver(this); +} + +bool RenderGeoJSONSource::isLoaded() const { + return tilePyramid.isLoaded(); +} + +void RenderGeoJSONSource::invalidateTiles() { + tilePyramid.invalidateTiles(); +} + +void RenderGeoJSONSource::startRender(algorithm::ClipIDGenerator& generator, const mat4& projMatrix, const mat4& clipMatrix, const TransformState& transform) { + generator.update(tilePyramid.getRenderTiles()); + tilePyramid.startRender(projMatrix, clipMatrix, transform); +} + +void RenderGeoJSONSource::finishRender(Painter& painter) { + tilePyramid.finishRender(painter); +} + +std::map& RenderGeoJSONSource::getRenderTiles() { + return tilePyramid.getRenderTiles(); +} + +void RenderGeoJSONSource::updateTiles(const UpdateParameters& parameters) { + GeoJSONData* data_ = impl.getData(); + + if (!data_) { + return; + } + + if (data_ != data) { + data = data_; + tilePyramid.cache.clear(); + + for (auto const& item : tilePyramid.tiles) { + static_cast(item.second.get())->updateData(data->getTile(item.first.canonical)); + } + } + + tilePyramid.updateTiles(parameters, + SourceType::GeoJSON, + util::tileSize, + impl.getZoomRange(), + [&] (const OverscaledTileID& tileID) { + return std::make_unique(tileID, impl.id, parameters, data->getTile(tileID.canonical)); + }); +} + +void RenderGeoJSONSource::removeTiles() { + tilePyramid.removeTiles(); +} + +void RenderGeoJSONSource::reloadTiles() { + tilePyramid.reloadTiles(); +} + +std::unordered_map> +RenderGeoJSONSource::queryRenderedFeatures(const ScreenLineString& geometry, + const TransformState& transformState, + const RenderedQueryOptions& options) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, options); +} + +std::vector RenderGeoJSONSource::querySourceFeatures(const SourceQueryOptions& options) const { + return tilePyramid.querySourceFeatures(options); +} + +void RenderGeoJSONSource::setCacheSize(size_t size) { + tilePyramid.setCacheSize(size); +} + +void RenderGeoJSONSource::onLowMemory() { + tilePyramid.onLowMemory(); +} + +void RenderGeoJSONSource::dumpDebugLogs() const { + tilePyramid.dumpDebugLogs(); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_geojson_source.hpp b/src/mbgl/renderer/sources/render_geojson_source.hpp new file mode 100644 index 00000000000..3476b63afde --- /dev/null +++ b/src/mbgl/renderer/sources/render_geojson_source.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { + +namespace style { +class GeoJSONData; +} // namespace style + +class RenderGeoJSONSource : public RenderSource { +public: + RenderGeoJSONSource(const style::GeoJSONSource::Impl&); + + bool isLoaded() const final; + + // Called when the camera has changed. May load new tiles, unload obsolete tiles, or + // trigger re-placement of existing complete tiles. + void updateTiles(const style::UpdateParameters&) final; + + // Removes all tiles (by putting them into the cache). + void removeTiles() final; + + // Remove all tiles and clear the cache. + void invalidateTiles() final; + + // Request that all loaded tiles re-run the layout operation on the existing source + // data with fresh style information. + void reloadTiles() final; + + void startRender(algorithm::ClipIDGenerator&, + const mat4& projMatrix, + const mat4& clipMatrix, + const TransformState&) final; + void finishRender(Painter&) final; + + std::map& getRenderTiles() final; + + std::unordered_map> + queryRenderedFeatures(const ScreenLineString& geometry, + const TransformState& transformState, + const RenderedQueryOptions& options) const final; + + std::vector + querySourceFeatures(const SourceQueryOptions&) const final; + + void setCacheSize(size_t) final; + void onLowMemory() final; + void dumpDebugLogs() const final; + +private: + const style::GeoJSONSource::Impl& impl; + TilePyramid tilePyramid; + style::GeoJSONData* data; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_raster_source.cpp b/src/mbgl/renderer/sources/render_raster_source.cpp new file mode 100644 index 00000000000..75a21890539 --- /dev/null +++ b/src/mbgl/renderer/sources/render_raster_source.cpp @@ -0,0 +1,87 @@ +#include +#include +#include + +namespace mbgl { + +using namespace style; + +RenderRasterSource::RenderRasterSource(const style::RasterSource::Impl& impl_) + : RenderSource(impl_), + impl(impl_) { + tilePyramid.setObserver(this); +} + +bool RenderRasterSource::isLoaded() const { + return tilePyramid.isLoaded(); +} + +void RenderRasterSource::invalidateTiles() { + tilePyramid.invalidateTiles(); +} + +void RenderRasterSource::startRender(algorithm::ClipIDGenerator&, const mat4& projMatrix, const mat4& clipMatrix, const TransformState& transform) { + tilePyramid.startRender(projMatrix, clipMatrix, transform); +} + +void RenderRasterSource::finishRender(Painter& painter) { + tilePyramid.finishRender(painter); +} + +std::map& RenderRasterSource::getRenderTiles() { + return tilePyramid.getRenderTiles(); +} + +void RenderRasterSource::updateTiles(const UpdateParameters& parameters) { + optional tileset = impl.getTileset(); + + if (!tileset) { + return; + } + + if (tileURLTemplates != tileset->tiles) { + tileURLTemplates = tileset->tiles; + tilePyramid.invalidateTiles(); + } + + tilePyramid.updateTiles(parameters, + SourceType::Raster, + impl.getTileSize(), + tileset->zoomRange, + [&] (const OverscaledTileID& tileID) { + return std::make_unique(tileID, parameters, *tileset); + }); +} + +void RenderRasterSource::removeTiles() { + tilePyramid.removeTiles(); +} + +void RenderRasterSource::reloadTiles() { + tilePyramid.reloadTiles(); +} + +std::unordered_map> +RenderRasterSource::queryRenderedFeatures(const ScreenLineString&, + const TransformState&, + const RenderedQueryOptions&) const { + return {}; +} + +std::vector RenderRasterSource::querySourceFeatures(const SourceQueryOptions&) const { + return {}; +} + +void RenderRasterSource::setCacheSize(size_t size) { + tilePyramid.setCacheSize(size); +} + +void RenderRasterSource::onLowMemory() { + tilePyramid.onLowMemory(); +} + +void RenderRasterSource::dumpDebugLogs() const { + tilePyramid.dumpDebugLogs(); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_raster_source.hpp b/src/mbgl/renderer/sources/render_raster_source.hpp new file mode 100644 index 00000000000..6b95be363f1 --- /dev/null +++ b/src/mbgl/renderer/sources/render_raster_source.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { + +class RenderRasterSource : public RenderSource { +public: + RenderRasterSource(const style::RasterSource::Impl&); + + bool isLoaded() const final; + + // Called when the camera has changed. May load new tiles, unload obsolete tiles, or + // trigger re-placement of existing complete tiles. + void updateTiles(const style::UpdateParameters&) final; + + // Removes all tiles (by putting them into the cache). + void removeTiles() final; + + // Remove all tiles and clear the cache. + void invalidateTiles() final; + + // Request that all loaded tiles re-run the layout operation on the existing source + // data with fresh style information. + void reloadTiles() final; + + void startRender(algorithm::ClipIDGenerator&, + const mat4& projMatrix, + const mat4& clipMatrix, + const TransformState&) final; + void finishRender(Painter&) final; + + std::map& getRenderTiles() final; + + std::unordered_map> + queryRenderedFeatures(const ScreenLineString& geometry, + const TransformState& transformState, + const RenderedQueryOptions& options) const final; + + std::vector + querySourceFeatures(const SourceQueryOptions&) const final; + + void setCacheSize(size_t) final; + void onLowMemory() final; + void dumpDebugLogs() const final; + +private: + const style::RasterSource::Impl& impl; + TilePyramid tilePyramid; + optional> tileURLTemplates; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_vector_source.cpp b/src/mbgl/renderer/sources/render_vector_source.cpp new file mode 100644 index 00000000000..3d7e09fd2ab --- /dev/null +++ b/src/mbgl/renderer/sources/render_vector_source.cpp @@ -0,0 +1,91 @@ +#include +#include +#include + +#include +#include + +namespace mbgl { + +using namespace style; + +RenderVectorSource::RenderVectorSource(const style::VectorSource::Impl& impl_) + : RenderSource(impl_), + impl(impl_) { + tilePyramid.setObserver(this); +} + +bool RenderVectorSource::isLoaded() const { + return tilePyramid.isLoaded(); +} + +void RenderVectorSource::invalidateTiles() { + tilePyramid.invalidateTiles(); +} + +void RenderVectorSource::startRender(algorithm::ClipIDGenerator& generator, const mat4& projMatrix, const mat4& clipMatrix, const TransformState& transform) { + generator.update(tilePyramid.getRenderTiles()); + tilePyramid.startRender(projMatrix, clipMatrix, transform); +} + +void RenderVectorSource::finishRender(Painter& painter) { + tilePyramid.finishRender(painter); +} + +std::map& RenderVectorSource::getRenderTiles() { + return tilePyramid.getRenderTiles(); +} + +void RenderVectorSource::updateTiles(const UpdateParameters& parameters) { + optional tileset = impl.getTileset(); + + if (!tileset) { + return; + } + + if (tileURLTemplates != tileset->tiles) { + tileURLTemplates = tileset->tiles; + tilePyramid.invalidateTiles(); + } + + tilePyramid.updateTiles(parameters, + SourceType::Vector, + util::tileSize, + tileset->zoomRange, + [&] (const OverscaledTileID& tileID) { + return std::make_unique(tileID, impl.id, parameters, *tileset); + }); +} + +void RenderVectorSource::removeTiles() { + tilePyramid.removeTiles(); +} + +void RenderVectorSource::reloadTiles() { + tilePyramid.reloadTiles(); +} + +std::unordered_map> +RenderVectorSource::queryRenderedFeatures(const ScreenLineString& geometry, + const TransformState& transformState, + const RenderedQueryOptions& options) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, options); +} + +std::vector RenderVectorSource::querySourceFeatures(const SourceQueryOptions& options) const { + return tilePyramid.querySourceFeatures(options); +} + +void RenderVectorSource::setCacheSize(size_t size) { + tilePyramid.setCacheSize(size); +} + +void RenderVectorSource::onLowMemory() { + tilePyramid.onLowMemory(); +} + +void RenderVectorSource::dumpDebugLogs() const { + tilePyramid.dumpDebugLogs(); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_vector_source.hpp b/src/mbgl/renderer/sources/render_vector_source.hpp new file mode 100644 index 00000000000..0f40c14cf52 --- /dev/null +++ b/src/mbgl/renderer/sources/render_vector_source.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { + +class RenderVectorSource : public RenderSource { +public: + RenderVectorSource(const style::VectorSource::Impl&); + + bool isLoaded() const final; + + // Called when the camera has changed. May load new tiles, unload obsolete tiles, or + // trigger re-placement of existing complete tiles. + void updateTiles(const style::UpdateParameters&) final; + + // Removes all tiles (by putting them into the cache). + void removeTiles() final; + + // Remove all tiles and clear the cache. + void invalidateTiles() final; + + // Request that all loaded tiles re-run the layout operation on the existing source + // data with fresh style information. + void reloadTiles() final; + + void startRender(algorithm::ClipIDGenerator&, + const mat4& projMatrix, + const mat4& clipMatrix, + const TransformState&) final; + void finishRender(Painter&) final; + + std::map& getRenderTiles() final; + + std::unordered_map> + queryRenderedFeatures(const ScreenLineString& geometry, + const TransformState& transformState, + const RenderedQueryOptions& options) const final; + + std::vector + querySourceFeatures(const SourceQueryOptions&) const final; + + void setCacheSize(size_t) final; + void onLowMemory() final; + void dumpDebugLogs() const final; + +private: + const style::VectorSource::Impl& impl; + TilePyramid tilePyramid; + optional> tileURLTemplates; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp new file mode 100644 index 00000000000..130b9dc5b6e --- /dev/null +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -0,0 +1,263 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +namespace mbgl { + +using namespace style; + +static TileObserver nullObserver; + +TilePyramid::TilePyramid() + : observer(&nullObserver) { +} + +TilePyramid::~TilePyramid() = default; + +bool TilePyramid::isLoaded() const { + for (const auto& pair : tiles) { + if (!pair.second->isComplete()) { + return false; + } + } + + return true; +} + +void TilePyramid::invalidateTiles() { + tiles.clear(); + renderTiles.clear(); + cache.clear(); +} + +void TilePyramid::startRender(const mat4& projMatrix, + const mat4& clipMatrix, + const TransformState& transform) { + for (auto& pair : renderTiles) { + auto& tile = pair.second; + tile.calculateMatrices(projMatrix, clipMatrix, transform); + } +} + +void TilePyramid::finishRender(Painter& painter) { + for (auto& pair : renderTiles) { + auto& tile = pair.second; + if (tile.used) { + painter.renderTileDebug(tile); + } + } +} + +std::map& TilePyramid::getRenderTiles() { + return renderTiles; +} + +void TilePyramid::updateTiles(const UpdateParameters& parameters, + const SourceType type, + const uint16_t tileSize, + const Range zoomRange, + std::function (const OverscaledTileID&)> createTile) { + // Determine the overzooming/underzooming amounts and required tiles. + int32_t overscaledZoom = util::coveringZoomLevel(parameters.transformState.getZoom(), type, tileSize); + int32_t tileZoom = overscaledZoom; + + std::vector idealTiles; + if (overscaledZoom >= zoomRange.min) { + int32_t idealZoom = std::min(zoomRange.max, overscaledZoom); + + // Make sure we're not reparsing overzoomed raster tiles. + if (type == SourceType::Raster) { + tileZoom = idealZoom; + } + + idealTiles = util::tileCover(parameters.transformState, idealZoom); + } + + // Stores a list of all the tiles that we're definitely going to retain. There are two + // kinds of tiles we need: the ideal tiles determined by the tile cover. They may not yet be in + // use because they're still loading. In addition to that, we also need to retain all tiles that + // we're actively using, e.g. as a replacement for tile that aren't loaded yet. + std::set retain; + + auto retainTileFn = [&](Tile& tile, Resource::Necessity necessity) -> void { + retain.emplace(tile.id); + tile.setNecessity(necessity); + }; + auto getTileFn = [&](const OverscaledTileID& tileID) -> Tile* { + auto it = tiles.find(tileID); + return it == tiles.end() ? nullptr : it->second.get(); + }; + auto createTileFn = [&](const OverscaledTileID& tileID) -> Tile* { + std::unique_ptr tile = cache.get(tileID); + if (!tile) { + tile = createTile(tileID); + if (tile) { + tile->setObserver(observer); + } + } + if (!tile) { + return nullptr; + } + return tiles.emplace(tileID, std::move(tile)).first->second.get(); + }; + auto renderTileFn = [&](const UnwrappedTileID& tileID, Tile& tile) { + renderTiles.emplace(tileID, RenderTile{ tileID, tile }); + }; + + renderTiles.clear(); + algorithm::updateRenderables(getTileFn, createTileFn, retainTileFn, renderTileFn, + idealTiles, zoomRange, tileZoom); + + if (type != SourceType::Annotations) { + size_t conservativeCacheSize = + std::max((float)parameters.transformState.getSize().width / tileSize, 1.0f) * + std::max((float)parameters.transformState.getSize().height / tileSize, 1.0f) * + (parameters.transformState.getMaxZoom() - parameters.transformState.getMinZoom() + 1) * + 0.5; + cache.setSize(conservativeCacheSize); + } + + removeStaleTiles(retain); + + const PlacementConfig config { parameters.transformState.getAngle(), + parameters.transformState.getPitch(), + parameters.debugOptions & MapDebugOptions::Collision }; + + for (auto& pair : tiles) { + pair.second->setPlacementConfig(config); + } +} + +// Moves all tiles to the cache except for those specified in the retain set. +void TilePyramid::removeStaleTiles(const std::set& retain) { + // Remove stale tiles. This goes through the (sorted!) tiles map and retain set in lockstep + // and removes items from tiles that don't have the corresponding key in the retain set. + auto tilesIt = tiles.begin(); + auto retainIt = retain.begin(); + while (tilesIt != tiles.end()) { + if (retainIt == retain.end() || tilesIt->first < *retainIt) { + tilesIt->second->setNecessity(Tile::Necessity::Optional); + cache.add(tilesIt->first, std::move(tilesIt->second)); + tiles.erase(tilesIt++); + } else { + if (!(*retainIt < tilesIt->first)) { + ++tilesIt; + } + ++retainIt; + } + } +} + +void TilePyramid::removeTiles() { + renderTiles.clear(); + if (!tiles.empty()) { + removeStaleTiles({}); + } +} + +void TilePyramid::reloadTiles() { + cache.clear(); + + for (auto& pair : tiles) { + pair.second->redoLayout(); + } +} + +std::unordered_map> TilePyramid::queryRenderedFeatures(const ScreenLineString& geometry, + const TransformState& transformState, + const RenderedQueryOptions& options) const { + std::unordered_map> result; + if (renderTiles.empty() || geometry.empty()) { + return result; + } + + LineString queryGeometry; + + for (const auto& p : geometry) { + queryGeometry.push_back(TileCoordinate::fromScreenCoordinate( + transformState, 0, { p.x, transformState.getSize().height - p.y }).p); + } + + mapbox::geometry::box box = mapbox::geometry::envelope(queryGeometry); + + + auto sortRenderTiles = [](const RenderTile& a, const RenderTile& b) { + return std::tie(a.id.canonical.z, a.id.canonical.y, a.id.wrap, a.id.canonical.x) < + std::tie(b.id.canonical.z, b.id.canonical.y, b.id.wrap, b.id.canonical.x); + }; + std::vector> sortedTiles; + std::transform(renderTiles.cbegin(), renderTiles.cend(), std::back_inserter(sortedTiles), + [](const auto& pair) { return std::ref(pair.second); }); + std::sort(sortedTiles.begin(), sortedTiles.end(), sortRenderTiles); + + for (const auto& renderTileRef : sortedTiles) { + const RenderTile& renderTile = renderTileRef.get(); + GeometryCoordinate tileSpaceBoundsMin = TileCoordinate::toGeometryCoordinate(renderTile.id, box.min); + if (tileSpaceBoundsMin.x >= util::EXTENT || tileSpaceBoundsMin.y >= util::EXTENT) { + continue; + } + + GeometryCoordinate tileSpaceBoundsMax = TileCoordinate::toGeometryCoordinate(renderTile.id, box.max); + if (tileSpaceBoundsMax.x < 0 || tileSpaceBoundsMax.y < 0) { + continue; + } + + GeometryCoordinates tileSpaceQueryGeometry; + tileSpaceQueryGeometry.reserve(queryGeometry.size()); + for (const auto& c : queryGeometry) { + tileSpaceQueryGeometry.push_back(TileCoordinate::toGeometryCoordinate(renderTile.id, c)); + } + + renderTile.tile.queryRenderedFeatures(result, + tileSpaceQueryGeometry, + transformState, + options); + } + + return result; +} + +std::vector TilePyramid::querySourceFeatures(const SourceQueryOptions& options) const { + std::vector result; + + for (const auto& pair : tiles) { + pair.second->querySourceFeatures(result, options); + } + + return result; +} + +void TilePyramid::setCacheSize(size_t size) { + cache.setSize(size); +} + +void TilePyramid::onLowMemory() { + cache.clear(); +} + +void TilePyramid::setObserver(TileObserver* observer_) { + observer = observer_; +} + +void TilePyramid::dumpDebugLogs() const { + for (const auto& pair : tiles) { + pair.second->dumpDebugLogs(); + } +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp new file mode 100644 index 00000000000..a38a21dd880 --- /dev/null +++ b/src/mbgl/renderer/tile_pyramid.hpp @@ -0,0 +1,87 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace mbgl { + +class Painter; +class TransformState; +class RenderTile; +class RenderedQueryOptions; +class SourceQueryOptions; + +namespace style { +class UpdateParameters; +} // namespace style + +class TilePyramid { +public: + TilePyramid(); + ~TilePyramid(); + + bool isLoaded() const; + + // Called when the camera has changed. May load new tiles, unload obsolete tiles, or + // trigger re-placement of existing complete tiles. + void updateTiles(const style::UpdateParameters&, + SourceType type, + uint16_t tileSize, + Range zoomRange, + std::function (const OverscaledTileID&)> createTile); + + // Removes all tiles (by putting them into the cache). + void removeTiles(); + + // Remove all tiles and clear the cache. + void invalidateTiles(); + + // Request that all loaded tiles re-run the layout operation on the existing source + // data with fresh style information. + void reloadTiles(); + + void startRender(const mat4& projMatrix, + const mat4& clipMatrix, + const TransformState&); + void finishRender(Painter&); + + std::map& getRenderTiles(); + + std::unordered_map> + queryRenderedFeatures(const ScreenLineString& geometry, + const TransformState& transformState, + const RenderedQueryOptions& options) const; + + std::vector querySourceFeatures(const SourceQueryOptions&) const; + + void setCacheSize(size_t); + void onLowMemory(); + + void setObserver(TileObserver*); + void dumpDebugLogs() const; + + bool enabled = false; + + void removeStaleTiles(const std::set&); + + std::map> tiles; + TileCache cache; + + std::map renderTiles; + + TileObserver* observer = nullptr; +}; + +} // namespace mbgl diff --git a/src/mbgl/style/source.cpp b/src/mbgl/style/source.cpp index 51863f8eba8..cfb268006bb 100644 --- a/src/mbgl/style/source.cpp +++ b/src/mbgl/style/source.cpp @@ -18,9 +18,5 @@ optional Source::getAttribution() const { return baseImpl->getAttribution(); } -optional> Source::getZoomRange() const { - return baseImpl->getZoomRange(); -} - } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/source_impl.cpp b/src/mbgl/style/source_impl.cpp index 19138bd7816..8fdbd038de8 100644 --- a/src/mbgl/style/source_impl.cpp +++ b/src/mbgl/style/source_impl.cpp @@ -1,23 +1,6 @@ #include #include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include namespace mbgl { namespace style { @@ -31,271 +14,13 @@ Source::Impl::Impl(SourceType type_, std::string id_, Source& base_) observer(&nullObserver) { } -Source::Impl::~Impl() = default; - -bool Source::Impl::isLoaded() const { - if (!loaded) return false; - - for (const auto& pair : tiles) { - if (!pair.second->isComplete()) { - return false; - } - } - - return true; -} - -void Source::Impl::detach() { - invalidateTiles(); -} - -void Source::Impl::invalidateTiles() { - tiles.clear(); - renderTiles.clear(); - cache.clear(); -} - -void Source::Impl::startRender(algorithm::ClipIDGenerator& generator, - const mat4& projMatrix, - const mat4& clipMatrix, - const TransformState& transform) { - if (type == SourceType::Vector || - type == SourceType::GeoJSON || - type == SourceType::Annotations) { - generator.update(renderTiles); - } - - for (auto& pair : renderTiles) { - auto& tile = pair.second; - tile.calculateMatrices(projMatrix, clipMatrix, transform); - } -} - -void Source::Impl::finishRender(Painter& painter) { - for (auto& pair : renderTiles) { - auto& tile = pair.second; - if (tile.used) { - painter.renderTileDebug(tile); - } - } -} - -std::map& Source::Impl::getRenderTiles() { - return renderTiles; -} - -void Source::Impl::updateTiles(const UpdateParameters& parameters) { - if (!loaded) { - return; - } - - const uint16_t tileSize = getTileSize(); - const optional> zoomRange = getZoomRange(); - - // Determine the overzooming/underzooming amounts and required tiles. - int32_t overscaledZoom = util::coveringZoomLevel(parameters.transformState.getZoom(), type, tileSize); - int32_t tileZoom = overscaledZoom; - - std::vector idealTiles; - if (overscaledZoom >= zoomRange->min) { - int32_t idealZoom = std::min(zoomRange->max, overscaledZoom); - - // Make sure we're not reparsing overzoomed raster tiles. - if (type == SourceType::Raster) { - tileZoom = idealZoom; - } - - idealTiles = util::tileCover(parameters.transformState, idealZoom); - } - - // Stores a list of all the tiles that we're definitely going to retain. There are two - // kinds of tiles we need: the ideal tiles determined by the tile cover. They may not yet be in - // use because they're still loading. In addition to that, we also need to retain all tiles that - // we're actively using, e.g. as a replacement for tile that aren't loaded yet. - std::set retain; - - auto retainTileFn = [&retain](Tile& tile, Resource::Necessity necessity) -> void { - retain.emplace(tile.id); - tile.setNecessity(necessity); - }; - auto getTileFn = [this](const OverscaledTileID& tileID) -> Tile* { - auto it = tiles.find(tileID); - return it == tiles.end() ? nullptr : it->second.get(); - }; - auto createTileFn = [this, ¶meters](const OverscaledTileID& tileID) -> Tile* { - std::unique_ptr tile = cache.get(tileID); - if (!tile) { - tile = createTile(tileID, parameters); - if (tile) { - tile->setObserver(this); - } - } - if (!tile) { - return nullptr; - } - return tiles.emplace(tileID, std::move(tile)).first->second.get(); - }; - auto renderTileFn = [this](const UnwrappedTileID& tileID, Tile& tile) { - renderTiles.emplace(tileID, RenderTile{ tileID, tile }); - }; - - renderTiles.clear(); - algorithm::updateRenderables(getTileFn, createTileFn, retainTileFn, renderTileFn, - idealTiles, *zoomRange, tileZoom); - - if (type != SourceType::Annotations) { - size_t conservativeCacheSize = - std::max((float)parameters.transformState.getSize().width / tileSize, 1.0f) * - std::max((float)parameters.transformState.getSize().height / tileSize, 1.0f) * - (parameters.transformState.getMaxZoom() - parameters.transformState.getMinZoom() + 1) * - 0.5; - cache.setSize(conservativeCacheSize); - } - - removeStaleTiles(retain); - - const PlacementConfig config { parameters.transformState.getAngle(), - parameters.transformState.getPitch(), - parameters.debugOptions & MapDebugOptions::Collision }; - - for (auto& pair : tiles) { - pair.second->setPlacementConfig(config); - } -} - -// Moves all tiles to the cache except for those specified in the retain set. -void Source::Impl::removeStaleTiles(const std::set& retain) { - // Remove stale tiles. This goes through the (sorted!) tiles map and retain set in lockstep - // and removes items from tiles that don't have the corresponding key in the retain set. - auto tilesIt = tiles.begin(); - auto retainIt = retain.begin(); - while (tilesIt != tiles.end()) { - if (retainIt == retain.end() || tilesIt->first < *retainIt) { - tilesIt->second->setNecessity(Tile::Necessity::Optional); - cache.add(tilesIt->first, std::move(tilesIt->second)); - tiles.erase(tilesIt++); - } else { - if (!(*retainIt < tilesIt->first)) { - ++tilesIt; - } - ++retainIt; - } - } -} - -void Source::Impl::removeTiles() { - renderTiles.clear(); - if (!tiles.empty()) { - removeStaleTiles({}); - } -} - -void Source::Impl::reloadTiles() { - cache.clear(); - - for (auto& pair : tiles) { - pair.second->redoLayout(); - } -} - -std::unordered_map> Source::Impl::queryRenderedFeatures(const ScreenLineString& geometry, - const TransformState& transformState, - const RenderedQueryOptions& options) const { - std::unordered_map> result; - if (renderTiles.empty() || geometry.empty()) { - return result; - } - - LineString queryGeometry; - - for (const auto& p : geometry) { - queryGeometry.push_back(TileCoordinate::fromScreenCoordinate( - transformState, 0, { p.x, transformState.getSize().height - p.y }).p); - } - - mapbox::geometry::box box = mapbox::geometry::envelope(queryGeometry); - - - auto sortRenderTiles = [](const RenderTile& a, const RenderTile& b) { - return std::tie(a.id.canonical.z, a.id.canonical.y, a.id.wrap, a.id.canonical.x) < - std::tie(b.id.canonical.z, b.id.canonical.y, b.id.wrap, b.id.canonical.x); - }; - std::vector> sortedTiles; - std::transform(renderTiles.cbegin(), renderTiles.cend(), std::back_inserter(sortedTiles), - [](const auto& pair) { return std::ref(pair.second); }); - std::sort(sortedTiles.begin(), sortedTiles.end(), sortRenderTiles); - - for (const auto& renderTileRef : sortedTiles) { - const RenderTile& renderTile = renderTileRef.get(); - GeometryCoordinate tileSpaceBoundsMin = TileCoordinate::toGeometryCoordinate(renderTile.id, box.min); - if (tileSpaceBoundsMin.x >= util::EXTENT || tileSpaceBoundsMin.y >= util::EXTENT) { - continue; - } - - GeometryCoordinate tileSpaceBoundsMax = TileCoordinate::toGeometryCoordinate(renderTile.id, box.max); - if (tileSpaceBoundsMax.x < 0 || tileSpaceBoundsMax.y < 0) { - continue; - } - - GeometryCoordinates tileSpaceQueryGeometry; - tileSpaceQueryGeometry.reserve(queryGeometry.size()); - for (const auto& c : queryGeometry) { - tileSpaceQueryGeometry.push_back(TileCoordinate::toGeometryCoordinate(renderTile.id, c)); - } - - renderTile.tile.queryRenderedFeatures(result, - tileSpaceQueryGeometry, - transformState, - options); - } - - return result; -} - -std::vector Source::Impl::querySourceFeatures(const SourceQueryOptions& options) { - - // Only VectorSource and GeoJSON source supported - if (type != SourceType::GeoJSON && type != SourceType::Vector) { - Log::Warning(Event::General, "Source type not supported"); - return {}; - } - - std::vector result; - - for (const auto& pair : tiles) { - pair.second->querySourceFeatures(result, options); - } - - return result; -} - -void Source::Impl::setCacheSize(size_t size) { - cache.setSize(size); -} - -void Source::Impl::onLowMemory() { - cache.clear(); -} - -void Source::Impl::setObserver(SourceObserver* observer_) { - observer = observer_; -} - -void Source::Impl::onTileChanged(Tile& tile) { - observer->onTileChanged(base, tile.id); -} - -void Source::Impl::onTileError(Tile& tile, std::exception_ptr error) { - observer->onTileError(base, tile.id, error); -} - void Source::Impl::dumpDebugLogs() const { Log::Info(Event::General, "Source::id: %s", base.getID().c_str()); Log::Info(Event::General, "Source::loaded: %d", loaded); +} - for (const auto& pair : tiles) { - pair.second->dumpDebugLogs(); - } +void Source::Impl::setObserver(SourceObserver* observer_) { + observer = observer_; } } // namespace style diff --git a/src/mbgl/style/source_impl.hpp b/src/mbgl/style/source_impl.hpp index 132d1b97f8d..2514ec5120b 100644 --- a/src/mbgl/style/source_impl.hpp +++ b/src/mbgl/style/source_impl.hpp @@ -1,117 +1,37 @@ #pragma once #include - -#include -#include -#include -#include -#include -#include - #include -#include -#include -#include - -#include -#include -#include -#include namespace mbgl { -class Painter; class FileSource; -class TransformState; -class RenderTile; -class RenderedQueryOptions; - -namespace algorithm { -class ClipIDGenerator; -} // namespace algorithm +class RenderSource; namespace style { -class UpdateParameters; class SourceObserver; -class Source::Impl : public TileObserver, private util::noncopyable { +class Source::Impl : private util::noncopyable { public: Impl(SourceType, std::string id, Source&); - ~Impl() override; + virtual ~Impl() = default; virtual void loadDescription(FileSource&) = 0; - bool isLoaded() const; - - // Called when the camera has changed. May load new tiles, unload obsolete tiles, or - // trigger re-placement of existing complete tiles. - void updateTiles(const UpdateParameters&); - - // Removes all tiles (by putting them into the cache). - void removeTiles(); - - // Remove all tiles and clear the cache. - void invalidateTiles(); - - // Request that all loaded tiles re-run the layout operation on the existing source - // data with fresh style information. - void reloadTiles(); - - void startRender(algorithm::ClipIDGenerator&, - const mat4& projMatrix, - const mat4& clipMatrix, - const TransformState&); - void finishRender(Painter&); - - std::map& getRenderTiles(); - - std::unordered_map> - queryRenderedFeatures(const ScreenLineString& geometry, - const TransformState& transformState, - const RenderedQueryOptions& options) const; - - std::vector querySourceFeatures(const SourceQueryOptions&); - - void setCacheSize(size_t); - void onLowMemory(); + virtual std::unique_ptr createRenderSource() const = 0; - void setObserver(SourceObserver*); - void dumpDebugLogs() const; + virtual optional getAttribution() const { return {}; }; const SourceType type; const std::string id; - virtual optional getAttribution() const { return {}; }; - virtual optional> getZoomRange() const = 0; - bool loaded = false; - - // Tracks whether the source is used by any layers visible at the current zoom level. Must - // be initialized to true so that Style::isLoaded() does not produce false positives if - // called before Style::recalculate(). - bool enabled = true; - - // Detaches from the style - void detach(); - -protected: Source& base; - SourceObserver* observer = nullptr; - std::map> tiles; - TileCache cache; - -private: - void removeStaleTiles(const std::set&); - - // TileObserver implementation. - void onTileChanged(Tile&) override; - void onTileError(Tile&, std::exception_ptr) override; - virtual uint16_t getTileSize() const = 0; - virtual std::unique_ptr createTile(const OverscaledTileID&, const UpdateParameters&) = 0; + void setObserver(SourceObserver*); + SourceObserver* observer = nullptr; - std::map renderTiles; + void dumpDebugLogs() const; }; } // namespace style diff --git a/src/mbgl/style/source_observer.hpp b/src/mbgl/style/source_observer.hpp index c97ba19e84a..c99eda955e6 100644 --- a/src/mbgl/style/source_observer.hpp +++ b/src/mbgl/style/source_observer.hpp @@ -1,13 +1,8 @@ #pragma once -#include - #include namespace mbgl { - -class OverscaledTileID; - namespace style { class Source; @@ -22,9 +17,6 @@ class SourceObserver { // Source description needs to be reloaded virtual void onSourceDescriptionChanged(Source&) {} - - virtual void onTileChanged(Source&, const OverscaledTileID&) {} - virtual void onTileError(Source&, const OverscaledTileID&, std::exception_ptr) {} }; } // namespace style diff --git a/src/mbgl/style/sources/geojson_source.cpp b/src/mbgl/style/sources/geojson_source.cpp index 62726cd127b..110c1cd63c2 100644 --- a/src/mbgl/style/sources/geojson_source.cpp +++ b/src/mbgl/style/sources/geojson_source.cpp @@ -4,7 +4,7 @@ namespace mbgl { namespace style { -GeoJSONSource::GeoJSONSource(const std::string& id, const GeoJSONOptions options) +GeoJSONSource::GeoJSONSource(const std::string& id, const GeoJSONOptions& options) : Source(SourceType::GeoJSON, std::make_unique(std::move(id), *this, options)), impl(static_cast(baseImpl.get())) { diff --git a/src/mbgl/style/sources/geojson_source_impl.cpp b/src/mbgl/style/sources/geojson_source_impl.cpp index 6431d5faa46..08ed95ea3f9 100644 --- a/src/mbgl/style/sources/geojson_source_impl.cpp +++ b/src/mbgl/style/sources/geojson_source_impl.cpp @@ -1,10 +1,12 @@ -#include -#include +#include #include #include -#include -#include +#include +#include +#include #include +#include +#include #include #include @@ -31,6 +33,34 @@ optional convertGeoJSON(const JSValue& value, Error& error) { } } // namespace conversion +class GeoJSONVTData : public GeoJSONData { +public: + GeoJSONVTData(const GeoJSON& geoJSON, + const mapbox::geojsonvt::Options& options) + : impl(geoJSON, options) {} + + mapbox::geometry::feature_collection getTile(const CanonicalTileID& tileID) final { + return impl.getTile(tileID.z, tileID.x, tileID.y).features; + } + +private: + mapbox::geojsonvt::GeoJSONVT impl; +}; + +class SuperclusterData : public GeoJSONData { +public: + SuperclusterData(const mapbox::geometry::feature_collection& features, + const mapbox::supercluster::Options& options) + : impl(features, options) {} + + mapbox::geometry::feature_collection getTile(const CanonicalTileID& tileID) final { + return impl.getTile(tileID.z, tileID.x, tileID.y); + } + +private: + mapbox::supercluster::Supercluster impl; +}; + GeoJSONSource::Impl::Impl(std::string id_, Source& base_, const GeoJSONOptions options_) : Source::Impl(SourceType::GeoJSON, std::move(id_), base_), options(options_) { } @@ -52,18 +82,14 @@ optional GeoJSONSource::Impl::getURL() const { return url; } - void GeoJSONSource::Impl::setGeoJSON(const GeoJSON& geoJSON) { req.reset(); _setGeoJSON(geoJSON); } -// Private implementation void GeoJSONSource::Impl::_setGeoJSON(const GeoJSON& geoJSON) { double scale = util::EXTENT / util::tileSize; - cache.clear(); - if (options.cluster && geoJSON.is>() && !geoJSON.get>().empty()) { @@ -71,35 +97,15 @@ void GeoJSONSource::Impl::_setGeoJSON(const GeoJSON& geoJSON) { clusterOptions.maxZoom = options.clusterMaxZoom; clusterOptions.extent = util::EXTENT; clusterOptions.radius = std::round(scale * options.clusterRadius); - - const auto& features = geoJSON.get>(); - geoJSONOrSupercluster = - std::make_unique(features, clusterOptions); + data = std::make_unique( + geoJSON.get>(), clusterOptions); } else { mapbox::geojsonvt::Options vtOptions; vtOptions.maxZoom = options.maxzoom; vtOptions.extent = util::EXTENT; vtOptions.buffer = std::round(scale * options.buffer); vtOptions.tolerance = scale * options.tolerance; - geoJSONOrSupercluster = std::make_unique(geoJSON, vtOptions); - } - - for (auto const &item : tiles) { - GeoJSONTile* geoJSONTile = static_cast(item.second.get()); - setTileData(*geoJSONTile, geoJSONTile->id); - } -} - -void GeoJSONSource::Impl::setTileData(GeoJSONTile& tile, const OverscaledTileID& tileID) { - if (geoJSONOrSupercluster.is()) { - tile.updateData(geoJSONOrSupercluster.get()->getTile(tileID.canonical.z, - tileID.canonical.x, - tileID.canonical.y).features); - } else { - assert(geoJSONOrSupercluster.is()); - tile.updateData(geoJSONOrSupercluster.get()->getTile(tileID.canonical.z, - tileID.canonical.x, - tileID.canonical.y)); + data = std::make_unique(geoJSON, vtOptions); } } @@ -135,8 +141,6 @@ void GeoJSONSource::Impl::loadDescription(FileSource& fileSource) { return; } - invalidateTiles(); - conversion::Error error; optional geoJSON = conversion::convertGeoJSON(d, error); if (!geoJSON) { @@ -155,19 +159,16 @@ void GeoJSONSource::Impl::loadDescription(FileSource& fileSource) { }); } -optional> GeoJSONSource::Impl::getZoomRange() const { - if (loaded) { - return { { 0, options.maxzoom }}; - } - return {}; +std::unique_ptr GeoJSONSource::Impl::createRenderSource() const { + return std::make_unique(*this); +} + +Range GeoJSONSource::Impl::getZoomRange() const { + return { 0, options.maxzoom }; } -std::unique_ptr GeoJSONSource::Impl::createTile(const OverscaledTileID& tileID, - const UpdateParameters& parameters) { - assert(loaded); - auto tilePointer = std::make_unique(tileID, base.getID(), parameters); - setTileData(*tilePointer.get(), tileID); - return std::move(tilePointer); +GeoJSONData* GeoJSONSource::Impl::getData() const { + return data.get(); } } // namespace style diff --git a/src/mbgl/style/sources/geojson_source_impl.hpp b/src/mbgl/style/sources/geojson_source_impl.hpp index b827a0b26c1..e8b881d05ef 100644 --- a/src/mbgl/style/sources/geojson_source_impl.hpp +++ b/src/mbgl/style/sources/geojson_source_impl.hpp @@ -3,14 +3,19 @@ #include #include #include -#include namespace mbgl { class AsyncRequest; +class CanonicalTileID; namespace style { +class GeoJSONData { +public: + virtual mapbox::geometry::feature_collection getTile(const CanonicalTileID&) = 0; +}; + class GeoJSONSource::Impl : public Source::Impl { public: Impl(std::string id, Source&, const GeoJSONOptions); @@ -18,27 +23,21 @@ class GeoJSONSource::Impl : public Source::Impl { void setURL(std::string); optional getURL() const; + Range getZoomRange() const; void setGeoJSON(const GeoJSON&); - void setTileData(GeoJSONTile&, const OverscaledTileID& tileID); + GeoJSONData* getData() const; void loadDescription(FileSource&) final; - - uint16_t getTileSize() const final { - return util::tileSize; - } - - optional> getZoomRange() const final; + std::unique_ptr createRenderSource() const final; private: void _setGeoJSON(const GeoJSON&); - std::unique_ptr createTile(const OverscaledTileID&, const UpdateParameters&) final; - GeoJSONOptions options; optional url; std::unique_ptr req; - variant geoJSONOrSupercluster; + std::unique_ptr data; }; } // namespace style diff --git a/src/mbgl/style/sources/raster_source_impl.cpp b/src/mbgl/style/sources/raster_source_impl.cpp index b7276512608..b85d221f2eb 100644 --- a/src/mbgl/style/sources/raster_source_impl.cpp +++ b/src/mbgl/style/sources/raster_source_impl.cpp @@ -1,5 +1,5 @@ #include -#include +#include namespace mbgl { namespace style { @@ -10,9 +10,8 @@ RasterSource::Impl::Impl(std::string id_, Source& base_, : TileSourceImpl(SourceType::Raster, std::move(id_), base_, std::move(urlOrTileset_), tileSize_) { } -std::unique_ptr RasterSource::Impl::createTile(const OverscaledTileID& tileID, - const UpdateParameters& parameters) { - return std::make_unique(tileID, parameters, tileset); +std::unique_ptr RasterSource::Impl::createRenderSource() const { + return std::make_unique(*this); } } // namespace style diff --git a/src/mbgl/style/sources/raster_source_impl.hpp b/src/mbgl/style/sources/raster_source_impl.hpp index 6f34a050bbe..4bc76560f8d 100644 --- a/src/mbgl/style/sources/raster_source_impl.hpp +++ b/src/mbgl/style/sources/raster_source_impl.hpp @@ -10,8 +10,7 @@ class RasterSource::Impl : public TileSourceImpl { public: Impl(std::string id, Source&, variant, uint16_t tileSize); -private: - std::unique_ptr createTile(const OverscaledTileID&, const UpdateParameters&) final; + std::unique_ptr createRenderSource() const final; }; } // namespace style diff --git a/src/mbgl/style/sources/vector_source_impl.cpp b/src/mbgl/style/sources/vector_source_impl.cpp index efe8afbbeaf..158abf8575d 100644 --- a/src/mbgl/style/sources/vector_source_impl.cpp +++ b/src/mbgl/style/sources/vector_source_impl.cpp @@ -1,5 +1,6 @@ #include -#include +#include +#include namespace mbgl { namespace style { @@ -8,9 +9,8 @@ VectorSource::Impl::Impl(std::string id_, Source& base_, variant VectorSource::Impl::createTile(const OverscaledTileID& tileID, - const UpdateParameters& parameters) { - return std::make_unique(tileID, base.getID(), parameters, tileset); +std::unique_ptr VectorSource::Impl::createRenderSource() const { + return std::make_unique(*this); } } // namespace style diff --git a/src/mbgl/style/sources/vector_source_impl.hpp b/src/mbgl/style/sources/vector_source_impl.hpp index 6726fa69559..844739948c2 100644 --- a/src/mbgl/style/sources/vector_source_impl.hpp +++ b/src/mbgl/style/sources/vector_source_impl.hpp @@ -10,8 +10,7 @@ class VectorSource::Impl : public TileSourceImpl { public: Impl(std::string id, Source&, variant); -private: - std::unique_ptr createTile(const OverscaledTileID&, const UpdateParameters&) final; + std::unique_ptr createRenderSource() const final; }; } // namespace style diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp index 0bc18415663..5fc9c40ebad 100644 --- a/src/mbgl/style/style.cpp +++ b/src/mbgl/style/style.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -32,12 +33,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -112,6 +115,7 @@ TransitionOptions Style::getTransitionOptions() const { void Style::setJSON(const std::string& json) { sources.clear(); + renderSources.clear(); layers.clear(); renderLayers.clear(); classes.clear(); @@ -166,6 +170,10 @@ void Style::addSource(std::unique_ptr source) { source->baseImpl->setObserver(this); source->baseImpl->loadDescription(fileSource); + std::unique_ptr renderSource = source->baseImpl->createRenderSource(); + renderSource->setObserver(this); + renderSources.emplace_back(std::move(renderSource)); + sources.emplace_back(std::move(source)); } @@ -178,11 +186,14 @@ std::unique_ptr Style::removeSource(const std::string& id) { return nullptr; } + util::erase_if(renderSources, [&](const auto& source) { + return source->baseImpl.id == id; + }); + auto source = std::move(*it); sources.erase(it); updateBatch.sourceIDs.erase(id); - source->baseImpl->detach(); return source; } @@ -317,20 +328,20 @@ double Style::getDefaultPitch() const { } void Style::updateTiles(const UpdateParameters& parameters) { - for (const auto& source : sources) { - if (source->baseImpl->enabled) { - source->baseImpl->updateTiles(parameters); + for (const auto& renderSource : renderSources) { + if (renderSource->enabled) { + renderSource->updateTiles(parameters); } } } void Style::relayout() { for (const auto& sourceID : updateBatch.sourceIDs) { - Source* source = getSource(sourceID); - if (source && source->baseImpl->enabled) { - source->baseImpl->reloadTiles(); - } else if (source) { - source->baseImpl->invalidateTiles(); + RenderSource* renderSource = getRenderSource(sourceID); + if (renderSource && renderSource->enabled) { + renderSource->reloadTiles(); + } else if (renderSource) { + renderSource->invalidateTiles(); } } updateBatch.sourceIDs.clear(); @@ -363,8 +374,8 @@ void Style::cascade(const TimePoint& timePoint, MapMode mode) { void Style::recalculate(float z, const TimePoint& timePoint, MapMode mode) { // Disable all sources first. If we find an enabled layer that uses this source, we will // re-enable it later. - for (const auto& source : sources) { - source->baseImpl->enabled = false; + for (const auto& renderSource : renderSources) { + renderSource->enabled = false; } zoomHistory.update(z, timePoint); @@ -380,24 +391,19 @@ void Style::recalculate(float z, const TimePoint& timePoint, MapMode mode) { for (const auto& layer : renderLayers) { hasPendingTransitions |= layer->evaluate(parameters); - // Disable this layer if it doesn't need to be rendered. - const bool needsRendering = layer->needsRendering(zoomHistory.lastZoom); - if (!needsRendering) { - continue; - } - - // If this layer has a source, make sure that it gets loaded. - if (Source* source = getSource(layer->baseImpl.source)) { - source->baseImpl->enabled = true; + if (layer->needsRendering(zoomHistory.lastZoom)) { + if (RenderSource* renderSource = getRenderSource(layer->baseImpl.source)) { + renderSource->enabled = true; + } } } evaluatedLight = EvaluatedLight(transitioningLight, parameters); // Remove the existing tiles if we didn't end up re-enabling the source. - for (const auto& source : sources) { - if (!source->baseImpl->enabled) { - source->baseImpl->removeTiles(); + for (const auto& renderSource : renderSources) { + if (!renderSource->enabled) { + renderSource->removeTiles(); } } } @@ -428,6 +434,14 @@ Source* Style::getSource(const std::string& id) const { return it != sources.end() ? it->get() : nullptr; } +RenderSource* Style::getRenderSource(const std::string& id) const { + const auto it = std::find_if(renderSources.begin(), renderSources.end(), [&](const auto& source) { + return source->baseImpl.id == id; + }); + + return it != renderSources.end() ? it->get() : nullptr; +} + bool Style::hasTransitions() const { return hasPendingTransitions; } @@ -438,7 +452,13 @@ bool Style::isLoaded() const { } for (const auto& source: sources) { - if (source->baseImpl->enabled && !source->baseImpl->isLoaded()) { + if (!source->baseImpl->loaded) { + return false; + } + } + + for (const auto& renderSource: renderSources) { + if (!renderSource->isLoaded()) { return false; } } @@ -453,9 +473,9 @@ bool Style::isLoaded() const { RenderData Style::getRenderData(MapDebugOptions debugOptions, float angle) const { RenderData result; - for (const auto& source : sources) { - if (source->baseImpl->enabled) { - result.sources.insert(source.get()); + for (const auto& renderSource: renderSources) { + if (renderSource->enabled) { + result.sources.insert(renderSource.get()); } } @@ -486,13 +506,13 @@ RenderData Style::getRenderData(MapDebugOptions debugOptions, float angle) const continue; } - Source* source = getSource(layer->baseImpl.source); + RenderSource* source = getRenderSource(layer->baseImpl.source); if (!source) { Log::Warning(Event::Render, "can't find source for layer '%s'", layer->baseImpl.id.c_str()); continue; } - auto& renderTiles = source->baseImpl->getRenderTiles(); + auto& renderTiles = source->getRenderTiles(); const bool symbolLayer = layer->is(); // Sort symbol tiles in opposite y position, so tiles with overlapping @@ -557,26 +577,29 @@ RenderData Style::getRenderData(MapDebugOptions debugOptions, float angle) const std::vector Style::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const RenderedQueryOptions& options) const { - std::unordered_set sourceFilter; + std::unordered_map> resultsByLayer; if (options.layerIDs) { + std::unordered_set sourceIDs; for (const auto& layerID : *options.layerIDs) { - auto layer = getLayer(layerID); - if (layer) sourceFilter.emplace(layer->baseImpl->source); + if (Layer* layer = getLayer(layerID)) { + sourceIDs.emplace(layer->baseImpl->source); + } + } + for (const auto& sourceID : sourceIDs) { + if (RenderSource* renderSource = getRenderSource(sourceID)) { + auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, options); + std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin())); + } + } + } else { + for (const auto& renderSource : renderSources) { + auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, options); + std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin())); } } std::vector result; - std::unordered_map> resultsByLayer; - - for (const auto& source : sources) { - if (!sourceFilter.empty() && sourceFilter.find(source->getID()) == sourceFilter.end()) { - continue; - } - - auto sourceResults = source->baseImpl->queryRenderedFeatures(geometry, transformState, options); - std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin())); - } if (resultsByLayer.empty()) { return result; @@ -597,14 +620,14 @@ std::vector Style::queryRenderedFeatures(const ScreenLineString& geomet } void Style::setSourceTileCacheSize(size_t size) { - for (const auto& source : sources) { - source->baseImpl->setCacheSize(size); + for (const auto& renderSource : renderSources) { + renderSource->setCacheSize(size); } } void Style::onLowMemory() { - for (const auto& source : sources) { - source->baseImpl->onLowMemory(); + for (const auto& renderSource : renderSources) { + renderSource->onLowMemory(); } } @@ -648,16 +671,14 @@ void Style::onSourceDescriptionChanged(Source& source) { } } -void Style::onTileChanged(Source& source, const OverscaledTileID& tileID) { - observer->onTileChanged(source, tileID); +void Style::onTileChanged(RenderSource&, const OverscaledTileID&) { observer->onUpdate(Update::Repaint); } -void Style::onTileError(Source& source, const OverscaledTileID& tileID, std::exception_ptr error) { +void Style::onTileError(RenderSource& source, const OverscaledTileID& tileID, std::exception_ptr error) { lastError = error; Log::Error(Event::Style, "Failed to load tile %s for source %s: %s", - util::toString(tileID).c_str(), source.getID().c_str(), util::toString(error).c_str()); - observer->onTileError(source, tileID, error); + util::toString(tileID).c_str(), source.baseImpl.id.c_str(), util::toString(error).c_str()); observer->onResourceError(error); } @@ -725,6 +746,10 @@ void Style::dumpDebugLogs() const { source->baseImpl->dumpDebugLogs(); } + for (const auto& renderSource : renderSources) { + renderSource->dumpDebugLogs(); + } + spriteAtlas->dumpDebugLogs(); } diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp index 26959c9fbf8..6c80a2ef59c 100644 --- a/src/mbgl/style/style.hpp +++ b/src/mbgl/style/style.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ class TransformState; class RenderedQueryOptions; class Scheduler; class RenderLayer; +class RenderSource; namespace style { @@ -44,6 +46,7 @@ class QueryParameters; class Style : public GlyphAtlasObserver, public SpriteAtlasObserver, public SourceObserver, + public RenderSourceObserver, public LayerObserver, public util::noncopyable { public: @@ -125,8 +128,12 @@ class Style : public GlyphAtlasObserver, TransitioningLight transitioningLight; EvaluatedLight evaluatedLight; + RenderSource* getRenderSource(const std::string& id) const; + private: std::vector> sources; + std::vector> renderSources; + std::vector> layers; std::vector> renderLayers; std::vector classes; @@ -156,8 +163,8 @@ class Style : public GlyphAtlasObserver, void onSourceChanged(Source&) override; void onSourceError(Source&, std::exception_ptr) override; void onSourceDescriptionChanged(Source&) override; - void onTileChanged(Source&, const OverscaledTileID&) override; - void onTileError(Source&, const OverscaledTileID&, std::exception_ptr) override; + void onTileChanged(RenderSource&, const OverscaledTileID&) override; + void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override; // LayerObserver implementation. void onLayerFilterChanged(Layer&) override; diff --git a/src/mbgl/style/tile_source_impl.cpp b/src/mbgl/style/tile_source_impl.cpp index 9ea596d38b1..46237147944 100644 --- a/src/mbgl/style/tile_source_impl.cpp +++ b/src/mbgl/style/tile_source_impl.cpp @@ -82,26 +82,7 @@ void TileSourceImpl::loadDescription(FileSource& fileSource) { return; } - // Check whether previous information specifies different tile - bool attributionChanged = false; - if (tileset.tiles != newTileset.tiles) { - // Tile URLs changed: force tiles to be reloaded. - invalidateTiles(); - - // Tile size changed: We need to recalculate the tiles we need to load because we - // might have to load tiles for a different zoom level - // This is done automatically when we trigger the onSourceLoaded observer below. - - // Min/Max zoom changed: We need to recalculate what tiles to load, if we have tiles - // loaded that are outside the new zoom range - // This is done automatically when we trigger the onSourceLoaded observer below. - - // Attribution changed: We need to notify the embedding application that this - // changed. - attributionChanged = true; - - // Center/bounds changed: We're not using these values currently - } + bool attributionChanged = tileset.attribution != newTileset.attribution; tileset = newTileset; loaded = true; @@ -114,9 +95,9 @@ void TileSourceImpl::loadDescription(FileSource& fileSource) { }); } -optional> TileSourceImpl::getZoomRange() const { +optional TileSourceImpl::getTileset() const { if (loaded) { - return tileset.zoomRange; + return tileset; } return {}; } diff --git a/src/mbgl/style/tile_source_impl.hpp b/src/mbgl/style/tile_source_impl.hpp index 2993caf20a5..405408ff54b 100644 --- a/src/mbgl/style/tile_source_impl.hpp +++ b/src/mbgl/style/tile_source_impl.hpp @@ -26,7 +26,7 @@ class TileSourceImpl : public Source::Impl { void loadDescription(FileSource&) final; - uint16_t getTileSize() const final { + uint16_t getTileSize() const { return tileSize; } @@ -35,7 +35,7 @@ class TileSourceImpl : public Source::Impl { } optional getAttribution() const override; - optional> getZoomRange() const final; + optional getTileset() const; protected: const variant urlOrTileset; diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp index f989bc2ce6f..4e0623c18be 100644 --- a/src/mbgl/tile/geojson_tile.cpp +++ b/src/mbgl/tile/geojson_tile.cpp @@ -84,14 +84,16 @@ class GeoJSONTileData : public GeometryTileData, GeoJSONTile::GeoJSONTile(const OverscaledTileID& overscaledTileID, std::string sourceID_, - const style::UpdateParameters& parameters) + const style::UpdateParameters& parameters, + mapbox::geometry::feature_collection features) : GeometryTile(overscaledTileID, sourceID_, parameters, *parameters.style.glyphAtlas, *parameters.style.spriteAtlas) { + updateData(std::move(features)); } -void GeoJSONTile::updateData(const mapbox::geometry::feature_collection& features) { - setData(std::make_unique(features)); +void GeoJSONTile::updateData(mapbox::geometry::feature_collection features) { + setData(std::make_unique(std::move(features))); } void GeoJSONTile::setNecessity(Necessity) {} diff --git a/src/mbgl/tile/geojson_tile.hpp b/src/mbgl/tile/geojson_tile.hpp index f8cde751062..0bafe14bdc9 100644 --- a/src/mbgl/tile/geojson_tile.hpp +++ b/src/mbgl/tile/geojson_tile.hpp @@ -13,9 +13,10 @@ class GeoJSONTile : public GeometryTile { public: GeoJSONTile(const OverscaledTileID&, std::string sourceID, - const style::UpdateParameters&); + const style::UpdateParameters&, + mapbox::geometry::feature_collection); - void updateData(const mapbox::geometry::feature_collection&); + void updateData(mapbox::geometry::feature_collection); void setNecessity(Necessity) final; diff --git a/test/src/mbgl/test/stub_render_source_observer.hpp b/test/src/mbgl/test/stub_render_source_observer.hpp new file mode 100644 index 00000000000..ff20fff80c4 --- /dev/null +++ b/test/src/mbgl/test/stub_render_source_observer.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +using namespace mbgl; + +/** + * An implementation of style::Observer that forwards all methods to dynamically-settable lambdas. + */ +class StubRenderSourceObserver : public RenderSourceObserver { +public: + void onTileChanged(RenderSource& source, const OverscaledTileID& tileID) override { + if (tileChanged) tileChanged(source, tileID); + }; + + void + onTileError(RenderSource& source, const OverscaledTileID& tileID, std::exception_ptr error) override { + if (tileError) tileError(source, tileID, error); + } + + std::function tileChanged; + std::function tileError; +}; diff --git a/test/src/mbgl/test/stub_style_observer.hpp b/test/src/mbgl/test/stub_style_observer.hpp index 9312cfa5501..7e22c688233 100644 --- a/test/src/mbgl/test/stub_style_observer.hpp +++ b/test/src/mbgl/test/stub_style_observer.hpp @@ -42,15 +42,6 @@ class StubStyleObserver : public style::Observer { if (sourceDescriptionChanged) sourceDescriptionChanged(source); } - void onTileChanged(Source& source, const OverscaledTileID& tileID) override { - if (tileChanged) tileChanged(source, tileID); - }; - - void - onTileError(Source& source, const OverscaledTileID& tileID, std::exception_ptr error) override { - if (tileError) tileError(source, tileID, error); - } - void onResourceError(std::exception_ptr error) override { if (resourceError) resourceError(error); }; @@ -63,7 +54,5 @@ class StubStyleObserver : public style::Observer { std::function sourceChanged; std::function sourceError; std::function sourceDescriptionChanged; - std::function tileChanged; - std::function tileError; std::function resourceError; }; diff --git a/test/style/source.test.cpp b/test/style/source.test.cpp index 24ddba8aaf2..6a336a8c23e 100644 --- a/test/style/source.test.cpp +++ b/test/style/source.test.cpp @@ -1,12 +1,17 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include + #include #include #include @@ -33,7 +38,8 @@ class SourceTest { public: util::RunLoop loop; StubFileSource fileSource; - StubStyleObserver observer; + StubStyleObserver styleObserver; + StubRenderSourceObserver renderSourceObserver; Transform transform; TransformState transformState; ThreadPool threadPool { 1 }; @@ -70,32 +76,6 @@ class SourceTest { } }; -TEST(Source, DefaultZoomRange) { - VectorSource vectorSource("vectorSource", "url"); - EXPECT_FALSE(vectorSource.getZoomRange()); - vectorSource.baseImpl->loaded = true; - EXPECT_EQ(vectorSource.getZoomRange()->min, 0u); - EXPECT_EQ(vectorSource.getZoomRange()->max, 22u); - - GeoJSONSource geojsonSource("source"); - EXPECT_FALSE(geojsonSource.getZoomRange()); - geojsonSource.baseImpl->loaded = true; - EXPECT_EQ(geojsonSource.getZoomRange()->min, 0u); - EXPECT_EQ(geojsonSource.getZoomRange()->max, 18u); - - Tileset tileset; - RasterSource rasterSource("source", tileset, 512); - EXPECT_FALSE(rasterSource.getZoomRange()); - rasterSource.baseImpl->loaded = true; - EXPECT_EQ(rasterSource.getZoomRange()->min, 0u); - EXPECT_EQ(rasterSource.getZoomRange()->max, 22u); - EXPECT_EQ(*rasterSource.getZoomRange(), tileset.zoomRange); - - AnnotationSource annotationSource; - EXPECT_EQ(annotationSource.getZoomRange()->min, 0u); - EXPECT_EQ(annotationSource.getZoomRange()->max, 22u); -} - TEST(Source, LoadingFail) { SourceTest test; @@ -108,14 +88,14 @@ TEST(Source, LoadingFail) { return response; }; - test.observer.sourceError = [&] (Source& source, std::exception_ptr error) { + test.styleObserver.sourceError = [&] (Source& source, std::exception_ptr error) { EXPECT_EQ("source", source.getID()); EXPECT_EQ("Failed by the test case", util::toString(error)); test.end(); }; VectorSource source("source", "url"); - source.baseImpl->setObserver(&test.observer); + source.baseImpl->setObserver(&test.styleObserver); source.baseImpl->loadDescription(test.fileSource); test.run(); @@ -131,14 +111,14 @@ TEST(Source, LoadingCorrupt) { return response; }; - test.observer.sourceError = [&] (Source& source, std::exception_ptr error) { + test.styleObserver.sourceError = [&] (Source& source, std::exception_ptr error) { EXPECT_EQ("source", source.getID()); EXPECT_EQ("0 - Invalid value.", util::toString(error)); test.end(); }; VectorSource source("source", "url"); - source.baseImpl->setObserver(&test.observer); + source.baseImpl->setObserver(&test.styleObserver); source.baseImpl->loadDescription(test.fileSource); test.run(); @@ -153,22 +133,24 @@ TEST(Source, RasterTileEmpty) { return response; }; - test.observer.tileChanged = [&] (Source& source, const OverscaledTileID&) { - EXPECT_EQ("source", source.getID()); + Tileset tileset; + tileset.tiles = { "tiles" }; + + RasterSource source("source", tileset, 512); + source.baseImpl->loadDescription(test.fileSource); + + test.renderSourceObserver.tileChanged = [&] (RenderSource& source_, const OverscaledTileID&) { + EXPECT_EQ("source", source_.baseImpl.id); test.end(); }; - test.observer.tileError = [&] (Source&, const OverscaledTileID&, std::exception_ptr) { + test.renderSourceObserver.tileError = [&] (RenderSource&, const OverscaledTileID&, std::exception_ptr) { FAIL() << "Should never be called"; }; - Tileset tileset; - tileset.tiles = { "tiles" }; - - RasterSource source("source", tileset, 512); - source.baseImpl->setObserver(&test.observer); - source.baseImpl->loadDescription(test.fileSource); - source.baseImpl->updateTiles(test.updateParameters); + RenderRasterSource renderSource(*source.impl); + renderSource.setObserver(&test.renderSourceObserver); + renderSource.updateTiles(test.updateParameters); test.run(); } @@ -182,22 +164,24 @@ TEST(Source, VectorTileEmpty) { return response; }; - test.observer.tileChanged = [&] (Source& source, const OverscaledTileID&) { - EXPECT_EQ("source", source.getID()); + Tileset tileset; + tileset.tiles = { "tiles" }; + + VectorSource source("source", tileset); + source.baseImpl->loadDescription(test.fileSource); + + test.renderSourceObserver.tileChanged = [&] (RenderSource& source_, const OverscaledTileID&) { + EXPECT_EQ("source", source_.baseImpl.id); test.end(); }; - test.observer.tileError = [&] (Source&, const OverscaledTileID&, std::exception_ptr) { + test.renderSourceObserver.tileError = [&] (RenderSource&, const OverscaledTileID&, std::exception_ptr) { FAIL() << "Should never be called"; }; - Tileset tileset; - tileset.tiles = { "tiles" }; - - VectorSource source("source", tileset); - source.baseImpl->setObserver(&test.observer); - source.baseImpl->loadDescription(test.fileSource); - source.baseImpl->updateTiles(test.updateParameters); + RenderVectorSource renderSource(*source.impl); + renderSource.setObserver(&test.renderSourceObserver); + renderSource.updateTiles(test.updateParameters); test.run(); } @@ -213,20 +197,22 @@ TEST(Source, RasterTileFail) { return response; }; - test.observer.tileError = [&] (Source& source, const OverscaledTileID& tileID, std::exception_ptr error) { - EXPECT_EQ(SourceType::Raster, source.baseImpl->type); - EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID); - EXPECT_EQ("Failed by the test case", util::toString(error)); - test.end(); - }; - Tileset tileset; tileset.tiles = { "tiles" }; RasterSource source("source", tileset, 512); - source.baseImpl->setObserver(&test.observer); source.baseImpl->loadDescription(test.fileSource); - source.baseImpl->updateTiles(test.updateParameters); + + test.renderSourceObserver.tileError = [&] (RenderSource& source_, const OverscaledTileID& tileID, std::exception_ptr error) { + EXPECT_EQ(SourceType::Raster, source_.baseImpl.type); + EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID); + EXPECT_EQ("Failed by the test case", util::toString(error)); + test.end(); + }; + + RenderRasterSource renderSource(*source.impl); + renderSource.setObserver(&test.renderSourceObserver); + renderSource.updateTiles(test.updateParameters); test.run(); } @@ -242,20 +228,22 @@ TEST(Source, VectorTileFail) { return response; }; - test.observer.tileError = [&] (Source& source, const OverscaledTileID& tileID, std::exception_ptr error) { - EXPECT_EQ(SourceType::Vector, source.baseImpl->type); - EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID); - EXPECT_EQ("Failed by the test case", util::toString(error)); - test.end(); - }; - Tileset tileset; tileset.tiles = { "tiles" }; VectorSource source("source", tileset); - source.baseImpl->setObserver(&test.observer); source.baseImpl->loadDescription(test.fileSource); - source.baseImpl->updateTiles(test.updateParameters); + + test.renderSourceObserver.tileError = [&] (RenderSource& source_, const OverscaledTileID& tileID, std::exception_ptr error) { + EXPECT_EQ(SourceType::Vector, source_.baseImpl.type); + EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID); + EXPECT_EQ("Failed by the test case", util::toString(error)); + test.end(); + }; + + RenderVectorSource renderSource(*source.impl); + renderSource.setObserver(&test.renderSourceObserver); + renderSource.updateTiles(test.updateParameters); test.run(); } @@ -269,21 +257,23 @@ TEST(Source, RasterTileCorrupt) { return response; }; - test.observer.tileError = [&] (Source& source, const OverscaledTileID& tileID, std::exception_ptr error) { - EXPECT_EQ(source.baseImpl->type, SourceType::Raster); + Tileset tileset; + tileset.tiles = { "tiles" }; + + RasterSource source("source", tileset, 512); + source.baseImpl->loadDescription(test.fileSource); + + test.renderSourceObserver.tileError = [&] (RenderSource& source_, const OverscaledTileID& tileID, std::exception_ptr error) { + EXPECT_EQ(source_.baseImpl.type, SourceType::Raster); EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID); EXPECT_TRUE(bool(error)); // Not asserting on platform-specific error text. test.end(); }; - Tileset tileset; - tileset.tiles = { "tiles" }; - - RasterSource source("source", tileset, 512); - source.baseImpl->setObserver(&test.observer); - source.baseImpl->loadDescription(test.fileSource); - source.baseImpl->updateTiles(test.updateParameters); + RenderRasterSource renderSource(*source.impl); + renderSource.setObserver(&test.renderSourceObserver); + renderSource.updateTiles(test.updateParameters); test.run(); } @@ -297,13 +287,6 @@ TEST(Source, VectorTileCorrupt) { return response; }; - test.observer.tileError = [&] (Source& source, const OverscaledTileID& tileID, std::exception_ptr error) { - EXPECT_EQ(source.baseImpl->type, SourceType::Vector); - EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID); - EXPECT_EQ(util::toString(error), "unknown pbf field type exception"); - test.end(); - }; - // Need to have at least one layer that uses the source. auto layer = std::make_unique("id", "source"); layer->setSourceLayer("water"); @@ -313,9 +296,18 @@ TEST(Source, VectorTileCorrupt) { tileset.tiles = { "tiles" }; VectorSource source("source", tileset); - source.baseImpl->setObserver(&test.observer); source.baseImpl->loadDescription(test.fileSource); - source.baseImpl->updateTiles(test.updateParameters); + + test.renderSourceObserver.tileError = [&] (RenderSource& source_, const OverscaledTileID& tileID, std::exception_ptr error) { + EXPECT_EQ(source_.baseImpl.type, SourceType::Vector); + EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID); + EXPECT_EQ(util::toString(error), "unknown pbf field type exception"); + test.end(); + }; + + RenderVectorSource renderSource(*source.impl); + renderSource.setObserver(&test.renderSourceObserver); + renderSource.updateTiles(test.updateParameters); test.run(); } @@ -328,21 +320,23 @@ TEST(Source, RasterTileCancel) { return optional(); }; - test.observer.tileChanged = [&] (Source&, const OverscaledTileID&) { + Tileset tileset; + tileset.tiles = { "tiles" }; + + RasterSource source("source", tileset, 512); + source.baseImpl->loadDescription(test.fileSource); + + test.renderSourceObserver.tileChanged = [&] (RenderSource&, const OverscaledTileID&) { FAIL() << "Should never be called"; }; - test.observer.tileError = [&] (Source&, const OverscaledTileID&, std::exception_ptr) { + test.renderSourceObserver.tileError = [&] (RenderSource&, const OverscaledTileID&, std::exception_ptr) { FAIL() << "Should never be called"; }; - Tileset tileset; - tileset.tiles = { "tiles" }; - - RasterSource source("source", tileset, 512); - source.baseImpl->setObserver(&test.observer); - source.baseImpl->loadDescription(test.fileSource); - source.baseImpl->updateTiles(test.updateParameters); + RenderRasterSource renderSource(*source.impl); + renderSource.setObserver(&test.renderSourceObserver); + renderSource.updateTiles(test.updateParameters); test.run(); } @@ -355,21 +349,23 @@ TEST(Source, VectorTileCancel) { return optional(); }; - test.observer.tileChanged = [&] (Source&, const OverscaledTileID&) { + Tileset tileset; + tileset.tiles = { "tiles" }; + + VectorSource source("source", tileset); + source.baseImpl->loadDescription(test.fileSource); + + test.renderSourceObserver.tileChanged = [&] (RenderSource&, const OverscaledTileID&) { FAIL() << "Should never be called"; }; - test.observer.tileError = [&] (Source&, const OverscaledTileID&, std::exception_ptr) { + test.renderSourceObserver.tileError = [&] (RenderSource&, const OverscaledTileID&, std::exception_ptr) { FAIL() << "Should never be called"; }; - Tileset tileset; - tileset.tiles = { "tiles" }; - - VectorSource source("source", tileset); - source.baseImpl->setObserver(&test.observer); - source.baseImpl->loadDescription(test.fileSource); - source.baseImpl->updateTiles(test.updateParameters); + RenderVectorSource renderSource(*source.impl); + renderSource.setObserver(&test.renderSourceObserver); + renderSource.updateTiles(test.updateParameters); test.run(); } @@ -395,20 +391,18 @@ TEST(Source, RasterTileAttribution) { return response; }; - test.observer.sourceChanged = [&] (Source& source) { + test.styleObserver.sourceChanged = [&] (Source& source) { EXPECT_EQ(mapboxOSM, source.getAttribution()); EXPECT_FALSE(mapboxOSM.find("©️ OpenStreetMap") == std::string::npos); test.end(); }; - test.observer.tileError = [&] (Source&, const OverscaledTileID&, std::exception_ptr) { - FAIL() << "Should never be called"; - }; - RasterSource source("source", "url", 512); - source.baseImpl->setObserver(&test.observer); + source.baseImpl->setObserver(&test.styleObserver); source.baseImpl->loadDescription(test.fileSource); - source.baseImpl->updateTiles(test.updateParameters); + + RenderRasterSource renderSource(*source.impl); + renderSource.updateTiles(test.updateParameters); test.run(); } @@ -423,17 +417,13 @@ TEST(Source, GeoJSonSourceUrlUpdate) { return response; }; - test.observer.sourceDescriptionChanged = [&] (Source&) { + test.styleObserver.sourceDescriptionChanged = [&] (Source&) { // Should be called (test will hang if it doesn't) test.end(); }; - test.observer.tileError = [&] (Source&, const OverscaledTileID&, std::exception_ptr) { - FAIL() << "Should never be called"; - }; - GeoJSONSource source("source"); - source.baseImpl->setObserver(&test.observer); + source.baseImpl->setObserver(&test.styleObserver); // Load initial, so the source state will be loaded=true source.baseImpl->loadDescription(test.fileSource); diff --git a/test/tile/geojson_tile.test.cpp b/test/tile/geojson_tile.test.cpp index 46cbe209b69..f39ff0a4cda 100644 --- a/test/tile/geojson_tile.test.cpp +++ b/test/tile/geojson_tile.test.cpp @@ -41,26 +41,26 @@ class GeoJSONTileTest { TEST(GeoJSONTile, Issue7648) { GeoJSONTileTest test; - GeoJSONTile tile(OverscaledTileID(0, 0, 0), "source", test.updateParameters); test.style.addLayer(std::make_unique("circle", "source")); + mapbox::geometry::feature_collection features; + features.push_back(mapbox::geometry::feature { + mapbox::geometry::point(0, 0) + }); + + GeoJSONTile tile(OverscaledTileID(0, 0, 0), "source", test.updateParameters, features); + StubTileObserver observer; observer.tileChanged = [&] (const Tile&) { // Once present, the bucket should never "disappear", which would cause // flickering. ASSERT_NE(nullptr, tile.getBucket(*test.style.getRenderLayer("circle"))); }; - tile.setObserver(&observer); + tile.setObserver(&observer); tile.setPlacementConfig({}); - mapbox::geometry::feature_collection features; - features.push_back(mapbox::geometry::feature { - mapbox::geometry::point(0, 0) - }); - - tile.updateData(features); while (!tile.isComplete()) { test.loop.runOnce(); }