diff options
author | Mikhail Pozdnyakov <mikhail.pozdnyakov@mapbox.com> | 2019-09-17 15:15:17 +0300 |
---|---|---|
committer | Mikhail Pozdnyakov <mikhail.pozdnyakov@mapbox.com> | 2019-09-19 14:51:32 +0300 |
commit | f04efb69c0d72c85618c9b609fb8f56a5f0a9aa4 (patch) | |
tree | 1e23281a8c1a5148b92547d9549e18d225e94ceb /src | |
parent | 870f306f680a26d2a4994d46cadb1fd664696396 (diff) | |
download | qtlocation-mapboxgl-f04efb69c0d72c85618c9b609fb8f56a5f0a9aa4.tar.gz |
[core] Immutable/Mutable pattern for Placement
Diffstat (limited to 'src')
-rw-r--r-- | src/mbgl/renderer/render_orchestrator.cpp | 30 | ||||
-rw-r--r-- | src/mbgl/renderer/render_orchestrator.hpp | 2 | ||||
-rw-r--r-- | src/mbgl/text/placement.cpp | 120 | ||||
-rw-r--r-- | src/mbgl/text/placement.hpp | 37 |
4 files changed, 109 insertions, 80 deletions
diff --git a/src/mbgl/renderer/render_orchestrator.cpp b/src/mbgl/renderer/render_orchestrator.cpp index e71799cc19..832fd461ab 100644 --- a/src/mbgl/renderer/render_orchestrator.cpp +++ b/src/mbgl/renderer/render_orchestrator.cpp @@ -63,7 +63,7 @@ public: LineAtlas& lineAtlas_, PatternAtlas& patternAtlas_, std::vector<std::reference_wrapper<RenderLayer>> layersNeedPlacement_, - std::shared_ptr<const Placement> placement_, + Immutable<Placement> placement_, bool updateSymbolOpacities_) : RenderTree(std::move(parameters_)), layerRenderItems(std::move(layerRenderItems_)), @@ -73,7 +73,6 @@ public: layersNeedPlacement(std::move(layersNeedPlacement_)), placement(std::move(placement_)), updateSymbolOpacities(updateSymbolOpacities_) { - assert(placement); } void prepare() override { @@ -99,7 +98,7 @@ public: std::reference_wrapper<LineAtlas> lineAtlas; std::reference_wrapper<PatternAtlas> patternAtlas; std::vector<std::reference_wrapper<RenderLayer>> layersNeedPlacement; - std::shared_ptr<const Placement> placement; + Immutable<Placement> placement; bool updateSymbolOpacities; }; @@ -117,7 +116,6 @@ RenderOrchestrator::RenderOrchestrator( , sourceImpls(makeMutable<std::vector<Immutable<style::Source::Impl>>>()) , layerImpls(makeMutable<std::vector<Immutable<style::Layer::Impl>>>()) , renderLight(makeMutable<Light::Impl>()) - , placement(std::make_unique<Placement>(TransformState{}, MapMode::Static, TransitionOptions{}, true)) , backgroundLayerAsColor(backgroundLayerAsColor_) { glyphManager->setObserver(this); imageManager->setObserver(this); @@ -386,16 +384,17 @@ std::unique_ptr<RenderTree> RenderOrchestrator::createRenderTree(const UpdatePar // We want new symbols to show up faster, however simple setting `placementChanged` to `true` would // initiate placement too often as new buckets ususally come from several rendered tiles in a row within // a short period of time. Instead, we squeeze placement update period to coalesce buckets updates from several tiles. - if (symbolBucketsAdded) placement->setMaximumUpdatePeriod(Milliseconds(30)); - renderTreeParameters->placementChanged = !placement->stillRecent(updateParameters.timePoint, updateParameters.transformState.getZoom()); + optional<Duration> maximumPlacementUpdatePeriod; + if (symbolBucketsAdded) maximumPlacementUpdatePeriod = optional<Duration>(Milliseconds(30)); + renderTreeParameters->placementChanged = !placementController.placementIsRecent(updateParameters.timePoint, updateParameters.transformState.getZoom(), maximumPlacementUpdatePeriod); symbolBucketsChanged |= renderTreeParameters->placementChanged; std::set<std::string> usedSymbolLayers; if (renderTreeParameters->placementChanged) { - placement = std::make_shared<Placement>( + Mutable<Placement> placement = makeMutable<Placement>( updateParameters.transformState, updateParameters.mode, updateParameters.transitionOptions, updateParameters.crossSourceCollisions, - std::move(placement)); + placementController.getPlacement()); for (auto it = layersNeedPlacement.rbegin(); it != layersNeedPlacement.rend(); ++it) { const RenderLayer& layer = *it; @@ -408,10 +407,11 @@ std::unique_ptr<RenderTree> RenderOrchestrator::createRenderTree(const UpdatePar for (const auto& entry : renderSources) { entry.second->updateFadingTiles(); } + placementController.setPlacement(std::move(placement)); } else { - placement->setStale(); + placementController.setPlacementStale(); } - renderTreeParameters->symbolFadeChange = placement->symbolFadeChange(updateParameters.timePoint); + renderTreeParameters->symbolFadeChange = placementController.getPlacement()->symbolFadeChange(updateParameters.timePoint); } renderTreeParameters->needsRepaint = isMapModeContinuous && hasTransitions(updateParameters.timePoint); @@ -435,7 +435,7 @@ std::unique_ptr<RenderTree> RenderOrchestrator::createRenderTree(const UpdatePar *lineAtlas, *patternAtlas, std::move(layersNeedPlacement), - placement, + placementController.getPlacement(), symbolBucketsChanged); } @@ -473,11 +473,11 @@ void RenderOrchestrator::queryRenderedSymbols(std::unordered_map<std::string, st if (crossTileSymbolIndexLayers.empty()) { return; } - - auto renderedSymbols = placement->getCollisionIndex().queryRenderedSymbols(geometry); + const Placement& placement = *placementController.getPlacement(); + auto renderedSymbols = placement.getCollisionIndex().queryRenderedSymbols(geometry); std::vector<std::reference_wrapper<const RetainedQueryData>> bucketQueryData; for (auto entry : renderedSymbols) { - bucketQueryData.emplace_back(placement->getQueryData(entry.first)); + bucketQueryData.emplace_back(placement.getQueryData(entry.first)); } // Although symbol query is global, symbol results are only sortable within a bucket // For a predictable global sort renderItems, we sort the buckets based on their corresponding tile position @@ -643,7 +643,7 @@ bool RenderOrchestrator::hasTransitions(TimePoint timePoint) const { } } - if (placement->hasTransitions(timePoint)) { + if (placementController.hasTransitions(timePoint)) { return true; } diff --git a/src/mbgl/renderer/render_orchestrator.hpp b/src/mbgl/renderer/render_orchestrator.hpp index 4592abc35c..9b63498a2a 100644 --- a/src/mbgl/renderer/render_orchestrator.hpp +++ b/src/mbgl/renderer/render_orchestrator.hpp @@ -122,7 +122,7 @@ private: RenderLight renderLight; CrossTileSymbolIndex crossTileSymbolIndex; - std::shared_ptr<Placement> placement; + PlacementController placementController; const bool backgroundLayerAsColor; bool contextLost = false; diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp index 17c3459f91..b7d89821a5 100644 --- a/src/mbgl/text/placement.cpp +++ b/src/mbgl/text/placement.cpp @@ -58,16 +58,49 @@ const CollisionGroups::CollisionGroup& CollisionGroups::get(const std::string& s } } -Placement::Placement(const TransformState& state_, MapMode mapMode_, style::TransitionOptions transitionOptions_, const bool crossSourceCollisions, std::shared_ptr<const Placement> prevPlacement_) +// PlacementController implemenation + +PlacementController::PlacementController() + : placement(makeMutable<Placement>(TransformState{}, MapMode::Static, style::TransitionOptions{}, true, nullopt)) { +} + +void PlacementController::setPlacement(Immutable<Placement> placement_) { + placement = std::move(placement_); + stale = false; +} + +bool PlacementController::placementIsRecent(TimePoint now, const float zoom, optional<Duration> maximumDuration) const { + if (!placement->transitionsEnabled()) return false; + + auto updatePeriod = placement->getUpdatePeriod(zoom); + + if (maximumDuration) { + updatePeriod = std::min(*maximumDuration, updatePeriod); + } + + return placement->getCommitTime() + updatePeriod > now; +} + + +bool PlacementController::hasTransitions(TimePoint now) const { + if (!placement->transitionsEnabled()) return false; + + if (stale) return true; + + return placement->hasTransitions(now); +} + +// Placement implementation + +Placement::Placement(const TransformState& state_, MapMode mapMode_, style::TransitionOptions transitionOptions_, const bool crossSourceCollisions, optional<Immutable<Placement>> prevPlacement_) : collisionIndex(state_) , mapMode(mapMode_) , transitionOptions(std::move(transitionOptions_)) , placementZoom(state_.getZoom()) , collisionGroups(crossSourceCollisions) - , prevPlacement(std::move(prevPlacement_)) -{ + , prevPlacement(std::move(prevPlacement_)) { if (prevPlacement) { - prevPlacement->prevPlacement.reset(); // Only hold on to one placement back + prevPlacement->get()->prevPlacement = nullopt; // Only hold on to one placement back } } @@ -188,9 +221,9 @@ void Placement::placeBucket( const CollisionFeature& textCollisionFeature = symbolInstance.textCollisionFeature; const auto updatePreviousOrientationIfNotPlaced = [&](bool isPlaced) { - if (bucket.allowVerticalPlacement && !isPlaced && prevPlacement) { - auto prevOrientation = prevPlacement->placedOrientations.find(symbolInstance.crossTileID); - if (prevOrientation != prevPlacement->placedOrientations.end()) { + if (bucket.allowVerticalPlacement && !isPlaced && getPrevPlacement()) { + auto prevOrientation = getPrevPlacement()->placedOrientations.find(symbolInstance.crossTileID); + if (prevOrientation != getPrevPlacement()->placedOrientations.end()) { placedOrientations[symbolInstance.crossTileID] = prevOrientation->second; } } @@ -251,9 +284,9 @@ void Placement::placeBucket( // If this symbol was in the last placement, shift the previously used // anchor to the front of the anchor list, only if the previous anchor // is still in the anchor list. - if (prevPlacement) { - auto prevOffset = prevPlacement->variableOffsets.find(symbolInstance.crossTileID); - if (prevOffset != prevPlacement->variableOffsets.end()) { + if (getPrevPlacement()) { + auto prevOffset = getPrevPlacement()->variableOffsets.find(symbolInstance.crossTileID); + if (prevOffset != getPrevPlacement()->variableOffsets.end()) { const auto prevAnchor = prevOffset->second.anchor; auto found = std::find(variableTextAnchors.begin(), variableTextAnchors.end(), prevAnchor); if (found != variableTextAnchors.begin() && found != variableTextAnchors.end()) { @@ -300,11 +333,11 @@ void Placement::placeBucket( // If this label was placed in the previous placement, record the anchor position // to allow us to animate the transition - if (prevPlacement) { - auto prevOffset = prevPlacement->variableOffsets.find(symbolInstance.crossTileID); - auto prevPlacements = prevPlacement->placements.find(symbolInstance.crossTileID); - if (prevOffset != prevPlacement->variableOffsets.end() && - prevPlacements != prevPlacement->placements.end() && + if (getPrevPlacement()) { + auto prevOffset = getPrevPlacement()->variableOffsets.find(symbolInstance.crossTileID); + auto prevPlacements = getPrevPlacement()->placements.find(symbolInstance.crossTileID); + if (prevOffset != getPrevPlacement()->variableOffsets.end() && + prevPlacements != getPrevPlacement()->placements.end() && prevPlacements->second.text) { // TODO: The prevAnchor seems to be unused, needs to be fixed. prevAnchor = prevOffset->second.anchor; @@ -350,9 +383,9 @@ void Placement::placeBucket( // If we didn't get placed, we still need to copy our position from the last placement for // fade animations - if (!placeText && prevPlacement) { - auto prevOffset = prevPlacement->variableOffsets.find(symbolInstance.crossTileID); - if (prevOffset != prevPlacement->variableOffsets.end()) { + if (!placeText && getPrevPlacement()) { + auto prevOffset = getPrevPlacement()->variableOffsets.find(symbolInstance.crossTileID); + if (prevOffset != getPrevPlacement()->variableOffsets.end()) { variableOffsets[symbolInstance.crossTileID] = prevOffset->second; } } @@ -459,19 +492,18 @@ void Placement::placeBucket( } void Placement::commit(TimePoint now, const double zoom) { - assert(prevPlacement); commitTime = now; bool placementChanged = false; - prevZoomAdjustment = prevPlacement->zoomAdjustment(zoom); + prevZoomAdjustment = getPrevPlacement()->zoomAdjustment(zoom); - float increment = prevPlacement->symbolFadeChange(commitTime); + float increment = getPrevPlacement()->symbolFadeChange(commitTime); // add the opacities from the current placement, and copy their current values from the previous placement for (auto& jointPlacement : placements) { - auto prevOpacity = prevPlacement->opacities.find(jointPlacement.first); - if (prevOpacity != prevPlacement->opacities.end()) { + auto prevOpacity = getPrevPlacement()->opacities.find(jointPlacement.first); + if (prevOpacity != getPrevPlacement()->opacities.end()) { opacities.emplace(jointPlacement.first, JointOpacityState(prevOpacity->second, increment, jointPlacement.second.text, jointPlacement.second.icon)); placementChanged = placementChanged || jointPlacement.second.icon != prevOpacity->second.icon.placed || @@ -483,7 +515,7 @@ void Placement::commit(TimePoint now, const double zoom) { } // copy and update values from the previous placement that aren't in the current placement but haven't finished fading - for (auto& prevOpacity : prevPlacement->opacities) { + for (auto& prevOpacity : getPrevPlacement()->opacities) { if (opacities.find(prevOpacity.first) == opacities.end()) { JointOpacityState jointOpacity(prevOpacity.second, increment, false, false); if (!jointOpacity.isHidden()) { @@ -493,7 +525,7 @@ void Placement::commit(TimePoint now, const double zoom) { } } - for (auto& prevOffset : prevPlacement->variableOffsets) { + for (auto& prevOffset : getPrevPlacement()->variableOffsets) { const uint32_t crossTileID = prevOffset.first; auto foundOffset = variableOffsets.find(crossTileID); auto foundOpacity = opacities.find(crossTileID); @@ -502,7 +534,7 @@ void Placement::commit(TimePoint now, const double zoom) { } } - for (auto& prevOrientation : prevPlacement->placedOrientations) { + for (auto& prevOrientation : getPrevPlacement()->placedOrientations) { const uint32_t crossTileID = prevOrientation.first; auto foundOrientation = placedOrientations.find(crossTileID); auto foundOpacity = opacities.find(crossTileID); @@ -511,7 +543,7 @@ void Placement::commit(TimePoint now, const double zoom) { } } - fadeStartTime = placementChanged ? commitTime : prevPlacement->fadeStartTime; + fadeStartTime = placementChanged ? commitTime : getPrevPlacement()->fadeStartTime; } void Placement::updateLayerBuckets(const RenderLayer& layer, const TransformState& state, bool updateOpacities) const { @@ -948,12 +980,11 @@ void Placement::markUsedOrientation(SymbolBucket& bucket, style::TextWritingMode } float Placement::symbolFadeChange(TimePoint now) const { - if (mapMode == MapMode::Continuous && transitionOptions.enablePlacementTransitions && + if (transitionsEnabled() && transitionOptions.duration.value_or(util::DEFAULT_TRANSITION_DURATION) > Milliseconds(0)) { return std::chrono::duration<float>(now - commitTime) / transitionOptions.duration.value_or(util::DEFAULT_TRANSITION_DURATION) + prevZoomAdjustment; - } else { - return 1.0; } + return 1.0; } float Placement::zoomAdjustment(const float zoom) const { @@ -968,33 +999,16 @@ Duration Placement::getUpdatePeriod(const float zoom) const { // Even if transitionOptions.duration is set to a value < 300ms, we still wait for this default transition duration // before attempting another placement operation. const auto fadeDuration = std::max(util::DEFAULT_TRANSITION_DURATION, transitionOptions.duration.value_or(util::DEFAULT_TRANSITION_DURATION)); - const auto adjustedDuration = std::chrono::duration_cast<Duration>(fadeDuration * (1.0 - zoomAdjustment(zoom))); - if (maximumUpdatePeriod) { - return std::min(*maximumUpdatePeriod, adjustedDuration); - } - return adjustedDuration; -} - -bool Placement::hasTransitions(TimePoint now) const { - if (mapMode == MapMode::Continuous && transitionOptions.enablePlacementTransitions) { - return stale || std::chrono::duration<float>(now - fadeStartTime) < transitionOptions.duration.value_or(util::DEFAULT_TRANSITION_DURATION); - } else { - return false; - } + return std::chrono::duration_cast<Duration>(fadeDuration * (1.0 - zoomAdjustment(zoom))); } -bool Placement::stillRecent(TimePoint now, const float zoom) const { - return mapMode == MapMode::Continuous && - transitionOptions.enablePlacementTransitions && - commitTime + getUpdatePeriod(zoom) > now; +bool Placement::transitionsEnabled() const { + return mapMode == MapMode::Continuous && transitionOptions.enablePlacementTransitions; } -void Placement::setMaximumUpdatePeriod(Duration duration) { - maximumUpdatePeriod = duration; -} - -void Placement::setStale() { - stale = true; +bool Placement::hasTransitions(TimePoint now) const { + assert(transitionsEnabled()); + return std::chrono::duration<float>(now - fadeStartTime) < transitionOptions.duration.value_or(util::DEFAULT_TRANSITION_DURATION); } const CollisionIndex& Placement::getCollisionIndex() const { diff --git a/src/mbgl/text/placement.hpp b/src/mbgl/text/placement.hpp index 917cf8bded..ef3effff3f 100644 --- a/src/mbgl/text/placement.hpp +++ b/src/mbgl/text/placement.hpp @@ -97,22 +97,39 @@ public: std::shared_ptr<FeatureIndex> featureIndex; bool showCollisionBoxes; }; + +class Placement; + +class PlacementController { +public: + PlacementController(); + void setPlacement(Immutable<Placement>); + const Immutable<Placement>& getPlacement() const { return placement; } + void setPlacementStale() { stale = true; } + bool placementIsRecent(TimePoint now, const float zoom, optional<Duration> maximumDuration = nullopt) const; + bool hasTransitions(TimePoint now) const; + +private: + Immutable<Placement> placement; + bool stale = false; +}; class Placement { public: - Placement(const TransformState&, MapMode, style::TransitionOptions, const bool crossSourceCollisions, std::shared_ptr<const Placement> prevPlacement = nullptr); + Placement(const TransformState&, MapMode, style::TransitionOptions, const bool crossSourceCollisions, optional<Immutable<Placement>> prevPlacement); void placeLayer(const RenderLayer&, const mat4&, bool showCollisionBoxes); void commit(TimePoint, const double zoom); void updateLayerBuckets(const RenderLayer&, const TransformState&, bool updateOpacities) const; float symbolFadeChange(TimePoint now) const; bool hasTransitions(TimePoint now) const; + bool transitionsEnabled() const; const CollisionIndex& getCollisionIndex() const; + TimePoint getCommitTime() const { return commitTime; } + Duration getUpdatePeriod(const float zoom) const; + + float zoomAdjustment(const float zoom) const; - bool stillRecent(TimePoint now, const float zoom) const; - void setMaximumUpdatePeriod(Duration); - void setStale(); - const RetainedQueryData& getQueryData(uint32_t bucketInstanceId) const; private: friend SymbolBucket; @@ -125,8 +142,9 @@ private: void updateBucketOpacities(SymbolBucket&, const TransformState&, std::set<uint32_t>&) const; void markUsedJustification(SymbolBucket&, style::TextVariableAnchorType, const SymbolInstance&, style::TextWritingModeType orientation) const; void markUsedOrientation(SymbolBucket&, style::TextWritingModeType, const SymbolInstance&) const; - float zoomAdjustment(const float zoom) const; - Duration getUpdatePeriod(const float zoom) const; + const Placement* getPrevPlacement() const { + return prevPlacement ? prevPlacement->get() : nullptr; + } CollisionIndex collisionIndex; @@ -143,12 +161,9 @@ private: std::unordered_map<uint32_t, VariableOffset> variableOffsets; std::unordered_map<uint32_t, style::TextWritingModeType> placedOrientations; - bool stale = false; - std::unordered_map<uint32_t, RetainedQueryData> retainedQueryData; CollisionGroups collisionGroups; - mutable std::shared_ptr<const Placement> prevPlacement; - optional<Duration> maximumUpdatePeriod; + mutable optional<Immutable<Placement>> prevPlacement; // Used for debug purposes. std::unordered_map<const CollisionFeature*, std::vector<ProjectedCollisionBox>> collisionCircles; |