summaryrefslogtreecommitdiff
path: root/src/mbgl/renderer/render_style.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mbgl/renderer/render_style.cpp')
-rw-r--r--src/mbgl/renderer/render_style.cpp445
1 files changed, 445 insertions, 0 deletions
diff --git a/src/mbgl/renderer/render_style.cpp b/src/mbgl/renderer/render_style.cpp
new file mode 100644
index 0000000000..a3a7d57527
--- /dev/null
+++ b/src/mbgl/renderer/render_style.cpp
@@ -0,0 +1,445 @@
+#include <mbgl/renderer/render_style.hpp>
+#include <mbgl/renderer/render_style_observer.hpp>
+#include <mbgl/renderer/update_parameters.hpp>
+#include <mbgl/renderer/transition_parameters.hpp>
+#include <mbgl/renderer/property_evaluation_parameters.hpp>
+#include <mbgl/renderer/tile_parameters.hpp>
+#include <mbgl/renderer/render_source.hpp>
+#include <mbgl/renderer/render_item.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/layers/render_background_layer.hpp>
+#include <mbgl/renderer/layers/render_circle_layer.hpp>
+#include <mbgl/renderer/layers/render_custom_layer.hpp>
+#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
+#include <mbgl/renderer/layers/render_fill_layer.hpp>
+#include <mbgl/renderer/layers/render_line_layer.hpp>
+#include <mbgl/renderer/layers/render_raster_layer.hpp>
+#include <mbgl/renderer/layers/render_symbol_layer.hpp>
+#include <mbgl/renderer/style_diff.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/style/source_impl.hpp>
+#include <mbgl/style/transition_options.hpp>
+#include <mbgl/sprite/sprite_atlas.hpp>
+#include <mbgl/sprite/sprite_loader.hpp>
+#include <mbgl/text/glyph_atlas.hpp>
+#include <mbgl/geometry/line_atlas.hpp>
+#include <mbgl/sprite/sprite_atlas.hpp>
+#include <mbgl/map/query.hpp>
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/util/math.hpp>
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+RenderStyleObserver nullObserver;
+
+RenderStyle::RenderStyle(Scheduler& scheduler_, FileSource& fileSource_)
+ : scheduler(scheduler_),
+ fileSource(fileSource_),
+ glyphAtlas(std::make_unique<GlyphAtlas>(Size{ 2048, 2048 }, fileSource)),
+ spriteAtlas(std::make_unique<SpriteAtlas>()),
+ lineAtlas(std::make_unique<LineAtlas>(Size{ 256, 512 })),
+ renderLight(makeMutable<Light::Impl>()),
+ observer(&nullObserver) {
+ glyphAtlas->setObserver(this);
+}
+
+RenderStyle::~RenderStyle() = default;
+
+void RenderStyle::setObserver(RenderStyleObserver* observer_) {
+ observer = observer_;
+}
+
+std::vector<const RenderLayer*> RenderStyle::getRenderLayers() const {
+ std::vector<const RenderLayer*> result;
+ result.reserve(renderLayers.size());
+ for (const auto& layer : layerImpls) {
+ result.push_back(getRenderLayer(layer->id));
+ }
+ return result;
+}
+
+RenderLayer* RenderStyle::getRenderLayer(const std::string& id) {
+ auto it = renderLayers.find(id);
+ return it != renderLayers.end() ? it->second.get() : nullptr;
+}
+
+const RenderLayer* RenderStyle::getRenderLayer(const std::string& id) const {
+ auto it = renderLayers.find(id);
+ return it != renderLayers.end() ? it->second.get() : nullptr;
+}
+
+const RenderLight& RenderStyle::getRenderLight() const {
+ return renderLight;
+}
+
+void RenderStyle::update(const UpdateParameters& parameters) {
+ const bool zoomChanged = zoomHistory.update(parameters.transformState.getZoom(), parameters.timePoint);
+
+ const TransitionParameters transitionParameters {
+ parameters.mode == MapMode::Continuous ? parameters.timePoint : Clock::time_point::max(),
+ parameters.mode == MapMode::Continuous ? parameters.transitionOptions : TransitionOptions()
+ };
+
+ const PropertyEvaluationParameters evaluationParameters {
+ zoomHistory,
+ parameters.mode == MapMode::Continuous ? parameters.timePoint : Clock::time_point::max(),
+ parameters.mode == MapMode::Continuous ? util::DEFAULT_FADE_DURATION : Duration::zero()
+ };
+
+ const TileParameters tileParameters {
+ parameters.pixelRatio,
+ parameters.debugOptions,
+ parameters.transformState,
+ parameters.scheduler,
+ parameters.fileSource,
+ parameters.mode,
+ parameters.annotationManager,
+ *spriteAtlas,
+ *glyphAtlas
+ };
+
+ glyphAtlas->setURL(parameters.glyphURL);
+
+ // Update light.
+ const bool lightChanged = renderLight.impl != parameters.light;
+
+ if (lightChanged) {
+ renderLight.impl = parameters.light;
+ renderLight.transition(transitionParameters);
+ }
+
+ if (lightChanged || zoomChanged || renderLight.hasTransition()) {
+ renderLight.evaluate(evaluationParameters);
+ }
+
+
+ const ImageDifference imageDiff = diffImages(imageImpls, parameters.images);
+ imageImpls = parameters.images;
+
+ // Remove removed images from sprite atlas.
+ for (const auto& entry : imageDiff.removed) {
+ spriteAtlas->removeImage(entry.first);
+ }
+
+ // Add added images to sprite atlas.
+ for (const auto& entry : imageDiff.added) {
+ spriteAtlas->addImage(entry.second);
+ }
+
+ // Update changed images.
+ for (const auto& entry : imageDiff.changed) {
+ spriteAtlas->updateImage(entry.second[1]);
+ }
+
+ if (parameters.spriteLoaded && !spriteAtlas->isLoaded()) {
+ spriteAtlas->onSpriteLoaded();
+ }
+
+
+ const LayerDifference layerDiff = diffLayers(layerImpls, parameters.layers);
+ layerImpls = parameters.layers;
+
+ // Remove render layers for removed layers.
+ for (const auto& entry : layerDiff.removed) {
+ renderLayers.erase(entry.first);
+ }
+
+ // Create render layers for newly added layers.
+ for (const auto& entry : layerDiff.added) {
+ renderLayers.emplace(entry.first, RenderLayer::create(entry.second));
+ }
+
+ // Update render layers for changed layers.
+ for (const auto& entry : layerDiff.changed) {
+ renderLayers.at(entry.first)->setImpl(entry.second[1]);
+ }
+
+ // Update layers for class and zoom changes.
+ for (const auto& entry : renderLayers) {
+ RenderLayer& layer = *entry.second;
+ const bool layerAdded = layerDiff.added.count(entry.first);
+ const bool layerChanged = layerDiff.changed.count(entry.first);
+
+ if (layerAdded || layerChanged) {
+ layer.transition(transitionParameters);
+ }
+
+ if (layerAdded || layerChanged || zoomChanged || layer.hasTransition()) {
+ layer.evaluate(evaluationParameters);
+ }
+ }
+
+
+ const SourceDifference sourceDiff = diffSources(sourceImpls, parameters.sources);
+ sourceImpls = parameters.sources;
+
+ // Remove render layers for removed sources.
+ for (const auto& entry : sourceDiff.removed) {
+ renderSources.erase(entry.first);
+ }
+
+ // Create render sources for newly added sources.
+ for (const auto& entry : sourceDiff.added) {
+ std::unique_ptr<RenderSource> renderSource = RenderSource::create(entry.second);
+ renderSource->setObserver(this);
+ renderSources.emplace(entry.first, std::move(renderSource));
+ }
+
+ // Update all sources.
+ for (const auto& source : sourceImpls) {
+ std::vector<Immutable<Layer::Impl>> filteredLayers;
+ bool needsRendering = false;
+ bool needsRelayout = false;
+
+ for (const auto& layer : layerImpls) {
+ if (layer->type == LayerType::Background ||
+ layer->type == LayerType::Custom ||
+ layer->source != source->id) {
+ continue;
+ }
+
+ if (getRenderLayer(layer->id)->needsRendering(zoomHistory.lastZoom)) {
+ needsRendering = true;
+ }
+
+ if (hasLayoutDifference(layerDiff, layer->id)) {
+ needsRelayout = true;
+ }
+
+ filteredLayers.push_back(layer);
+ }
+
+ renderSources.at(source->id)->update(source,
+ filteredLayers,
+ needsRendering,
+ needsRelayout,
+ tileParameters);
+ }
+}
+
+RenderSource* RenderStyle::getRenderSource(const std::string& id) const {
+ auto it = renderSources.find(id);
+ return it != renderSources.end() ? it->second.get() : nullptr;
+}
+
+bool RenderStyle::hasTransitions() const {
+ if (renderLight.hasTransition()) {
+ return true;
+ }
+
+ for (const auto& entry : renderLayers) {
+ if (entry.second->hasTransition()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool RenderStyle::isLoaded() const {
+ for (const auto& entry: renderSources) {
+ if (!entry.second->isLoaded()) {
+ return false;
+ }
+ }
+
+ if (!spriteAtlas->isLoaded()) {
+ return false;
+ }
+
+ return true;
+}
+
+RenderData RenderStyle::getRenderData(MapDebugOptions debugOptions, float angle) {
+ RenderData result;
+
+ for (const auto& entry : renderSources) {
+ if (entry.second->isEnabled()) {
+ result.sources.insert(entry.second.get());
+ }
+ }
+
+ for (auto& layerImpl : layerImpls) {
+ RenderLayer* layer = getRenderLayer(layerImpl->id);
+ assert(layer);
+
+ if (!layer->needsRendering(zoomHistory.lastZoom)) {
+ continue;
+ }
+
+ if (const RenderBackgroundLayer* background = layer->as<RenderBackgroundLayer>()) {
+ if (debugOptions & MapDebugOptions::Overdraw) {
+ // We want to skip glClear optimization in overdraw mode.
+ result.order.emplace_back(*layer, nullptr);
+ continue;
+ }
+ const BackgroundPaintProperties::PossiblyEvaluated& paint = background->evaluated;
+ if (layerImpl.get() == layerImpls[0].get() && paint.get<BackgroundPattern>().from.empty()) {
+ // This is a solid background. We can use glClear().
+ result.backgroundColor = paint.get<BackgroundColor>() * paint.get<BackgroundOpacity>();
+ } else {
+ // This is a textured background, or not the bottommost layer. We need to render it with a quad.
+ result.order.emplace_back(*layer, nullptr);
+ }
+ continue;
+ }
+
+ if (layer->is<RenderCustomLayer>()) {
+ result.order.emplace_back(*layer, nullptr);
+ continue;
+ }
+
+ RenderSource* source = getRenderSource(layer->baseImpl->source);
+ if (!source) {
+ Log::Warning(Event::Render, "can't find source for layer '%s'", layer->getID().c_str());
+ continue;
+ }
+
+ auto& renderTiles = source->getRenderTiles();
+ const bool symbolLayer = layer->is<RenderSymbolLayer>();
+
+ // Sort symbol tiles in opposite y position, so tiles with overlapping
+ // symbols are drawn on top of each other, with lower symbols being
+ // drawn on top of higher symbols.
+ std::vector<std::reference_wrapper<RenderTile>> sortedTiles;
+ std::transform(renderTiles.begin(), renderTiles.end(), std::back_inserter(sortedTiles),
+ [](auto& pair) { return std::ref(pair.second); });
+ if (symbolLayer) {
+ std::sort(sortedTiles.begin(), sortedTiles.end(),
+ [angle](const RenderTile& a, const RenderTile& b) {
+ Point<float> pa(a.id.canonical.x, a.id.canonical.y);
+ Point<float> pb(b.id.canonical.x, b.id.canonical.y);
+
+ auto par = util::rotate(pa, angle);
+ auto pbr = util::rotate(pb, angle);
+
+ return std::tie(par.y, par.x) < std::tie(pbr.y, pbr.x);
+ });
+ }
+
+ std::vector<std::reference_wrapper<RenderTile>> sortedTilesForInsertion;
+ for (auto& sortedTile : sortedTiles) {
+ auto& tile = sortedTile.get();
+ if (!tile.tile.isRenderable()) {
+ continue;
+ }
+
+ // We're not clipping symbol layers, so when we have both parents and children of symbol
+ // layers, we drop all children in favor of their parent to avoid duplicate labels.
+ // See https://github.com/mapbox/mapbox-gl-native/issues/2482
+ if (symbolLayer) {
+ bool skip = false;
+ // Look back through the buckets we decided to render to find out whether there is
+ // already a bucket from this layer that is a parent of this tile. Tiles are ordered
+ // by zoom level when we obtain them from getTiles().
+ for (auto it = sortedTilesForInsertion.rbegin();
+ it != sortedTilesForInsertion.rend(); ++it) {
+ if (tile.tile.id.isChildOf(it->get().tile.id)) {
+ skip = true;
+ break;
+ }
+ }
+ if (skip) {
+ continue;
+ }
+ }
+
+ auto bucket = tile.tile.getBucket(*layer->baseImpl);
+ if (bucket) {
+ sortedTilesForInsertion.emplace_back(tile);
+ tile.used = true;
+ }
+ }
+ layer->setRenderTiles(std::move(sortedTilesForInsertion));
+ result.order.emplace_back(*layer, source);
+ }
+
+ return result;
+}
+
+std::vector<Feature> RenderStyle::queryRenderedFeatures(const ScreenLineString& geometry,
+ const TransformState& transformState,
+ const RenderedQueryOptions& options) const {
+ std::unordered_map<std::string, std::vector<Feature>> resultsByLayer;
+
+ if (options.layerIDs) {
+ std::unordered_set<std::string> sourceIDs;
+ for (const auto& layerID : *options.layerIDs) {
+ if (const RenderLayer* layer = getRenderLayer(layerID)) {
+ sourceIDs.emplace(layer->baseImpl->source);
+ }
+ }
+ for (const auto& sourceID : sourceIDs) {
+ if (RenderSource* renderSource = getRenderSource(sourceID)) {
+ auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, *this, options);
+ std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin()));
+ }
+ }
+ } else {
+ for (const auto& entry : renderSources) {
+ auto sourceResults = entry.second->queryRenderedFeatures(geometry, transformState, *this, options);
+ std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin()));
+ }
+ }
+
+ std::vector<Feature> result;
+
+ if (resultsByLayer.empty()) {
+ return result;
+ }
+
+ // Combine all results based on the style layer order.
+ for (const auto& layerImpl : layerImpls) {
+ const RenderLayer* layer = getRenderLayer(layerImpl->id);
+ if (!layer->needsRendering(zoomHistory.lastZoom)) {
+ continue;
+ }
+ auto it = resultsByLayer.find(layer->baseImpl->id);
+ if (it != resultsByLayer.end()) {
+ std::move(it->second.begin(), it->second.end(), std::back_inserter(result));
+ }
+ }
+
+ return result;
+}
+
+void RenderStyle::setSourceTileCacheSize(size_t size) {
+ for (const auto& entry : renderSources) {
+ entry.second->setCacheSize(size);
+ }
+}
+
+void RenderStyle::onLowMemory() {
+ for (const auto& entry : renderSources) {
+ entry.second->onLowMemory();
+ }
+}
+
+void RenderStyle::onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) {
+ Log::Error(Event::Style, "Failed to load glyph range %d-%d for font stack %s: %s",
+ glyphRange.first, glyphRange.second, fontStackToString(fontStack).c_str(), util::toString(error).c_str());
+ observer->onResourceError(error);
+}
+
+void RenderStyle::onTileError(RenderSource& source, const OverscaledTileID& tileID, std::exception_ptr error) {
+ Log::Error(Event::Style, "Failed to load tile %s for source %s: %s",
+ util::toString(tileID).c_str(), source.baseImpl->id.c_str(), util::toString(error).c_str());
+ observer->onResourceError(error);
+}
+
+void RenderStyle::onTileChanged(RenderSource&, const OverscaledTileID&) {
+ observer->onInvalidate();
+}
+
+void RenderStyle::dumpDebugLogs() const {
+ for (const auto& entry : renderSources) {
+ entry.second->dumpDebugLogs();
+ }
+
+ spriteAtlas->dumpDebugLogs();
+}
+
+} // namespace mbgl