diff --git a/drape_frontend/drape_frontend.pro b/drape_frontend/drape_frontend.pro old mode 100644 new mode 100755 index 77191502c0..6e87fd6989 --- a/drape_frontend/drape_frontend.pro +++ b/drape_frontend/drape_frontend.pro @@ -36,6 +36,7 @@ SOURCES += \ threads_commutator.cpp \ tile_info.cpp \ tile_key.cpp \ + tile_tree.cpp \ tile_utils.cpp \ user_mark_shapes.cpp \ user_marks_provider.cpp \ @@ -76,6 +77,7 @@ HEADERS += \ threads_commutator.hpp \ tile_info.hpp \ tile_key.hpp \ + tile_tree.hpp \ tile_utils.hpp \ user_mark_shapes.hpp \ user_marks_provider.hpp \ diff --git a/drape_frontend/frontend_renderer.cpp b/drape_frontend/frontend_renderer.cpp old mode 100644 new mode 100755 index b64806c5f1..0e7c2a466d --- a/drape_frontend/frontend_renderer.cpp +++ b/drape_frontend/frontend_renderer.cpp @@ -79,8 +79,8 @@ void FrontendRenderer::AfterDrawFrame() m_tpf = accumulate(m_tpfs.begin(), m_tpfs.end(), 0.0) / m_tpfs.size(); - LOG(LINFO, ("Average Fps : ", m_fps)); - LOG(LINFO, ("Average Tpf : ", m_tpf)); + //LOG(LINFO, ("Average Fps : ", m_fps)); + //LOG(LINFO, ("Average Tpf : ", m_tpf)); #if defined(TRACK_GPU_MEM) string report = dp::GPUMemTracker::Inst().Report(); @@ -94,27 +94,27 @@ void FrontendRenderer::AfterDrawFrame() } #endif -UserMarkRenderGroup * FrontendRenderer::FindUserMarkRenderGroup(TileKey const & tileKey, bool createIfNeed) +unique_ptr & FrontendRenderer::FindUserMarkRenderGroup(TileKey const & tileKey, bool createIfNeed) { - auto it = find_if(m_userMarkRenderGroups.begin(), m_userMarkRenderGroups.end(), [&tileKey](UserMarkRenderGroup * g) + auto it = find_if(m_userMarkRenderGroups.begin(), m_userMarkRenderGroups.end(), [&tileKey](unique_ptr const & g) { return g->GetTileKey() == tileKey; }); if (it != m_userMarkRenderGroups.end()) { - ASSERT((*it) != nullptr, ()); + ASSERT((*it).get() != nullptr, ()); return *it; } if (createIfNeed) { - UserMarkRenderGroup * group = new UserMarkRenderGroup(dp::GLState(0, dp::GLState::UserMarkLayer), tileKey); - m_userMarkRenderGroups.push_back(group); - return group; + m_userMarkRenderGroups.emplace_back(new UserMarkRenderGroup(dp::GLState(0, dp::GLState::UserMarkLayer), tileKey)); + return m_userMarkRenderGroups.back(); } - return nullptr; + static unique_ptr emptyRenderGroup; + return emptyRenderGroup; } void FrontendRenderer::AcceptMessage(dp::RefPointer message) @@ -134,8 +134,8 @@ void FrontendRenderer::AcceptMessage(dp::RefPointer message) CreateTileRenderGroup(state, bucket, key); else { - UserMarkRenderGroup * group = FindUserMarkRenderGroup(key, true); - ASSERT(group != nullptr, ()); + unique_ptr & group = FindUserMarkRenderGroup(key, true); + ASSERT(group.get() != nullptr, ()); group->SetRenderBucket(state, bucket.Move()); } break; @@ -151,11 +151,15 @@ void FrontendRenderer::AcceptMessage(dp::RefPointer message) RefreshProjection(); RefreshModelView(); ResolveTileKeys(); + + TTilesCollection tiles; + m_tileTree.GetRequestedTiles(tiles); + m_commutator->PostMessage(ThreadsCommutator::ResourceUploadThread, dp::MovePointer(new ResizeMessage(m_viewport)), MessagePriority::Normal); m_commutator->PostMessage(ThreadsCommutator::ResourceUploadThread, - dp::MovePointer(new UpdateReadManagerMessage(m_view, m_tiles)), + dp::MovePointer(new UpdateReadManagerMessage(m_view, tiles)), MessagePriority::Normal); break; } @@ -169,8 +173,7 @@ void FrontendRenderer::AcceptMessage(dp::RefPointer message) InvalidateRectMessage * m = df::CastMessage(message); TTilesCollection keyStorage; - ResolveTileKeys(keyStorage, m->GetRect()); - InvalidateRenderGroups(keyStorage); + // TODO: implement invalidation Message * msgToBackend = new InvalidateReadManagerRectMessage(keyStorage); m_commutator->PostMessage(ThreadsCommutator::ResourceUploadThread, @@ -182,17 +185,16 @@ void FrontendRenderer::AcceptMessage(dp::RefPointer message) case Message::ClearUserMarkLayer: { TileKey const & tileKey = df::CastMessage(message)->GetKey(); - auto it = find_if(m_userMarkRenderGroups.begin(), m_userMarkRenderGroups.end(), [&tileKey](UserMarkRenderGroup * g) + auto it = find_if(m_userMarkRenderGroups.begin(), m_userMarkRenderGroups.end(), [&tileKey](unique_ptr const & g) { return g->GetTileKey() == tileKey; }); if (it != m_userMarkRenderGroups.end()) { - UserMarkRenderGroup * group = *it; - ASSERT(group != nullptr, ()); + unique_ptr & group = *it; + ASSERT(group.get() != nullptr, ()); m_userMarkRenderGroups.erase(it); - delete group; } break; @@ -200,8 +202,8 @@ void FrontendRenderer::AcceptMessage(dp::RefPointer message) case Message::ChangeUserMarkLayerVisibility: { ChangeUserMarkLayerVisibilityMessage * m = df::CastMessage(message); - UserMarkRenderGroup * group = FindUserMarkRenderGroup(m->GetKey(), true); - ASSERT(group != nullptr, ()); + unique_ptr & group = FindUserMarkRenderGroup(m->GetKey(), true); + ASSERT(group.get() != nullptr, ()); group->SetIsVisible(m->IsVisible()); break; } @@ -231,47 +233,90 @@ void FrontendRenderer::CreateTileRenderGroup(dp::GLState const & state, dp::MasterPointer & renderBucket, TileKey const & newTile) { - TTilesCollection & tiles = GetTileKeyStorage(); - auto const & tileIt = tiles.find(newTile); + // adding tiles to render + auto onAddTile = [&state, &renderBucket, this](TileKey const & tileKey, TileStatus tileStatus) + { + if (tileStatus == TileStatus::Rendered) + { + AddToRenderGroup(m_renderGroups, state, renderBucket, tileKey); + LOG(LINFO, ("Render tile", tileKey, tileStatus)); + } + else if (tileStatus == TileStatus::Deferred) + OnAddDeferredTile(tileKey, tileStatus); + else + ASSERT(false, ()); + }; - // skip obsolete tiles - if (tileIt == tiles.end() || df::GetTileScaleBase(m_view) != newTile.m_zoomLevel) + // deferring tiles + auto onDeferTile = [&state, &renderBucket, this](TileKey const & tileKey, TileStatus tileStatus) + { + AddToRenderGroup(m_deferredRenderGroups, state, renderBucket, tileKey); + LOG(LINFO, ("Defer tile", tileKey, tileStatus)); + }; + + // removing tiles + auto onRemoveTile = [this](TileKey const & tileKey, TileStatus tileStatus) + { + OnRemoveTile(tileKey, tileStatus); + LOG(LINFO, ("Remove tile", tileKey, tileStatus)); + }; + + bool const result = m_tileTree.ProcessTile(newTile, onAddTile, onRemoveTile, onDeferTile); + if (!result) { renderBucket.Destroy(); - return; + LOG(LINFO, ("Skip tile", newTile)); } - // clip tiles below loaded one - for (auto it = tiles.begin(); it != tiles.end(); ++it) - if (it->second == TileStatus::Rendered && IsTileBelow(newTile, it->first)) - it->second = TileStatus::Clipped; - - // clip tiles above loaded one - for (auto it = tiles.begin(); it != tiles.end(); ++it) - if (it->second == TileStatus::Rendered && IsTileAbove(newTile, it->first)) - it->second = TileStatus::Clipped; - - // create a render group for visible tiles - if (tileIt->second != TileStatus::Clipped) + LOG(LINFO, ("Deferred count = ", m_deferredRenderGroups.size())); + if (m_deferredRenderGroups.size() != 0) { - RenderGroup * group = new RenderGroup(state, newTile); - group->AddBucket(renderBucket.Move()); - m_renderGroups.push_back(group); - tileIt->second = TileStatus::Rendered; + LOG(LINFO, ("Zoom level: ", df::GetTileScaleBase(m_view))); + LOG(LINFO, ("Tile tree: ", m_tileTree)); + LOG(LINFO, ("Rendered tiles: ", m_renderGroups)); + LOG(LINFO, ("Deferred tiles: ", m_deferredRenderGroups)); } - else - renderBucket.Destroy(); - - // clean storage from clipped tiles - CleanKeyStorage(tiles); } -void FrontendRenderer::CleanKeyStorage(TTilesCollection & keyStorage) +void FrontendRenderer::AddToRenderGroup(vector> & groups, + dp::GLState const & state, + dp::MasterPointer & renderBucket, + TileKey const & newTile) { - for(auto it = keyStorage.begin(); it != keyStorage.end();) + unique_ptr group(new RenderGroup(state, newTile)); + group->AddBucket(renderBucket.Move()); + groups.emplace_back(move(group)); +} + +void FrontendRenderer::OnAddDeferredTile(TileKey const & tileKey, TileStatus tileStatus) +{ + ASSERT(tileStatus == TileStatus::Deferred, ()); + for(auto it = m_deferredRenderGroups.begin(); it != m_deferredRenderGroups.end();) { - if (it->second == TileStatus::Clipped) - it = keyStorage.erase(it); + if ((*it)->GetTileKey() == tileKey) + { + m_renderGroups.emplace_back(move(*it)); + it = m_deferredRenderGroups.erase(it); + } + else + ++it; + } + + LOG(LINFO, ("Add deferred tile", tileKey, tileStatus)); +} + +void FrontendRenderer::OnRemoveTile(TileKey const & tileKey, TileStatus tileStatus) +{ + for(auto it = m_renderGroups.begin(); it != m_renderGroups.end(); ++it) + { + if ((*it)->GetTileKey() == tileKey) + (*it)->DeleteLater(); + } + + for(auto it = m_deferredRenderGroups.begin(); it != m_deferredRenderGroups.end();) + { + if ((*it)->GetTileKey() == tileKey) + it = m_deferredRenderGroups.erase(it); else ++it; } @@ -283,20 +328,20 @@ void FrontendRenderer::RenderScene() BeforeDrawFrame(); #endif - RenderGroupComparator comparator(GetTileKeyStorage()); + RenderGroupComparator comparator; sort(m_renderGroups.begin(), m_renderGroups.end(), bind(&RenderGroupComparator::operator (), &comparator, _1, _2)); m_overlayTree.StartOverlayPlacing(m_view); size_t eraseCount = 0; for (size_t i = 0; i < m_renderGroups.size(); ++i) { - RenderGroup * group = m_renderGroups[i]; + unique_ptr & group = m_renderGroups[i]; if (group->IsEmpty()) continue; if (group->IsPendingOnDelete()) { - delete group; + group.reset(); ++eraseCount; continue; } @@ -322,7 +367,7 @@ void FrontendRenderer::RenderScene() dp::GLState::DepthLayer prevLayer = dp::GLState::GeometryLayer; for (size_t i = 0; i < m_renderGroups.size(); ++i) { - RenderGroup * group = m_renderGroups[i]; + unique_ptr & group = m_renderGroups[i]; dp::GLState const & state = group->GetState(); dp::GLState::DepthLayer layer = state.GetDepthLayer(); if (prevLayer != layer && layer == dp::GLState::OverlayLayer) @@ -345,9 +390,9 @@ void FrontendRenderer::RenderScene() GLFunctions::glClearDepth(); - for (UserMarkRenderGroup * group : m_userMarkRenderGroups) + for (unique_ptr & group : m_userMarkRenderGroups) { - ASSERT(group != nullptr, ()); + ASSERT(group.get() != nullptr, ()); if (group->IsVisible()) { dp::GLState const & state = group->GetState(); @@ -393,15 +438,15 @@ void FrontendRenderer::RefreshModelView() void FrontendRenderer::ResolveTileKeys() { - ResolveTileKeys(GetTileKeyStorage(), df::GetTileScaleBase(m_view)); + ResolveTileKeys(df::GetTileScaleBase(m_view)); } -void FrontendRenderer::ResolveTileKeys(TTilesCollection & keyStorage, m2::RectD const & rect) +void FrontendRenderer::ResolveTileKeys(m2::RectD const & rect) { - ResolveTileKeys(keyStorage, df::GetTileScaleBase(rect)); + ResolveTileKeys(df::GetTileScaleBase(rect)); } -void FrontendRenderer::ResolveTileKeys(TTilesCollection & keyStorage, int tileScale) +void FrontendRenderer::ResolveTileKeys(int tileScale) { // equal for x and y double const range = MercatorBounds::maxX - MercatorBounds::minX; @@ -415,38 +460,29 @@ void FrontendRenderer::ResolveTileKeys(TTilesCollection & keyStorage, int tileSc int const maxTileY = static_cast(ceil(clipRect.maxY() / rectSize)); // clip all tiles which are out of viewport - for (auto it = keyStorage.begin(); it != keyStorage.end(); ++it) - if (!clipRect.IsIntersect(it->first.GetGlobalRect())) - it->second = TileStatus::Clipped; + auto onRemoveTile = [this](TileKey const & tileKey, TileStatus tileStatus) + { + OnRemoveTile(tileKey, tileStatus); + LOG(LINFO, ("Clip tile", tileKey, tileStatus)); + }; + m_tileTree.ClipByRect(clipRect, bind(&FrontendRenderer::OnAddDeferredTile, this, _1, _2), + onRemoveTile); + + // request new tiles + m_tileTree.BeginRequesting(tileScale); for (int tileY = minTileY; tileY < maxTileY; ++tileY) { for (int tileX = minTileX; tileX < maxTileX; ++tileX) { - // request new tile TileKey key(tileX, tileY, tileScale); - if (clipRect.IsIntersect(key.GetGlobalRect()) && keyStorage.find(key) == keyStorage.end()) - keyStorage.insert(make_pair(key, TileStatus::Requested)); + if (clipRect.IsIntersect(key.GetGlobalRect())) + m_tileTree.RequestTile(key); } } + m_tileTree.EndRequesting(); - // clean storage from clipped tiles - CleanKeyStorage(keyStorage); -} - -void FrontendRenderer::InvalidateRenderGroups(TTilesCollection & keyStorage) -{ - for (size_t i = 0; i < m_renderGroups.size(); ++i) - { - RenderGroup * group = m_renderGroups[i]; - if (keyStorage.find(group->GetTileKey()) != keyStorage.end()) - group->DeleteLater(); - } -} - -TTilesCollection & FrontendRenderer::GetTileKeyStorage() -{ - return m_tiles; + //LOG(LINFO, (m_tileTree)); } void FrontendRenderer::StartThread() @@ -523,8 +559,10 @@ void FrontendRenderer::ReleaseResources() void FrontendRenderer::DeleteRenderData() { - DeleteRange(m_renderGroups, DeleteFunctor()); - DeleteRange(m_userMarkRenderGroups, DeleteFunctor()); + m_renderGroups.clear(); + m_deferredRenderGroups.clear(); + m_userMarkRenderGroups.clear(); + m_tileTree.Clear(); } void FrontendRenderer::SetModelView(ScreenBase const & screen) @@ -541,10 +579,24 @@ void FrontendRenderer::UpdateScene() m_view = m_newView; RefreshModelView(); ResolveTileKeys(); + + TTilesCollection tiles; + m_tileTree.GetRequestedTiles(tiles); + m_commutator->PostMessage(ThreadsCommutator::ResourceUploadThread, - dp::MovePointer(new UpdateReadManagerMessage(m_view, m_tiles)), + dp::MovePointer(new UpdateReadManagerMessage(m_view, tiles)), MessagePriority::Normal); } } +string DebugPrint(vector> const & groups) +{ + ostringstream out; + out << "\n{\n"; + for (auto it = groups.begin(); it != groups.end(); ++it) + out << DebugPrint((*it)->GetTileKey()) << "\n"; + out << "}\n"; + return out.str(); +} + } // namespace df diff --git a/drape_frontend/frontend_renderer.hpp b/drape_frontend/frontend_renderer.hpp old mode 100644 new mode 100755 index 2de1e40890..8fcea3ca4e --- a/drape_frontend/frontend_renderer.hpp +++ b/drape_frontend/frontend_renderer.hpp @@ -11,6 +11,7 @@ #include "drape_frontend/base_renderer.hpp" #include "drape_frontend/threads_commutator.hpp" #include "drape_frontend/tile_info.hpp" +#include "drape_frontend/tile_tree.hpp" #include "drape_frontend/backend_renderer.hpp" #include "drape_frontend/render_group.hpp" #include "drape_frontend/my_position.hpp" @@ -67,12 +68,10 @@ private: void RefreshModelView(); void ResolveTileKeys(); - void ResolveTileKeys(TTilesCollection & keyStorage, m2::RectD const & rect); - void ResolveTileKeys(TTilesCollection & keyStorage, int tileScale); - TTilesCollection & GetTileKeyStorage(); + void ResolveTileKeys(m2::RectD const & rect); + void ResolveTileKeys(int tileScale); - void InvalidateRenderGroups(TTilesCollection & keyStorage); - UserMarkRenderGroup * FindUserMarkRenderGroup(TileKey const & tileKey, bool createIfNeed); + unique_ptr & FindUserMarkRenderGroup(TileKey const & tileKey, bool createIfNeed); private: class Routine : public threads::IRoutine @@ -100,15 +99,22 @@ private: void CreateTileRenderGroup(dp::GLState const & state, dp::MasterPointer & renderBucket, TileKey const & newTile); - void CleanKeyStorage(TTilesCollection & keyStorage); + void AddToRenderGroup(vector> & groups, + dp::GLState const & state, + dp::MasterPointer & renderBucket, + TileKey const & newTile); + + void OnAddDeferredTile(TileKey const & tileKey, TileStatus tileStatus); + void OnRemoveTile(TileKey const & tileKey, TileStatus tileStatus); private: dp::RefPointer m_textureManager; dp::MasterPointer m_gpuProgramManager; private: - vector m_renderGroups; - vector m_userMarkRenderGroups; + vector> m_renderGroups; + vector> m_deferredRenderGroups; + vector> m_userMarkRenderGroups; dp::MasterPointer m_guiRenderer; dp::MasterPointer m_myPositionMark; @@ -116,12 +122,16 @@ private: Viewport m_viewport; ScreenBase m_view; - TTilesCollection m_tiles; + + TileTree m_tileTree; ScreenBase m_newView; mutex m_modelViewMutex; dp::OverlayTree m_overlayTree; + +private: + friend string DebugPrint(vector> const & groups); }; } // namespace df diff --git a/drape_frontend/read_manager.cpp b/drape_frontend/read_manager.cpp old mode 100644 new mode 100755 index 01cd5dc462..6955f97885 --- a/drape_frontend/read_manager.cpp +++ b/drape_frontend/read_manager.cpp @@ -17,14 +17,14 @@ namespace struct LessCoverageCell { - bool operator()(shared_ptr const & l, TTilePair const & r) const + bool operator()(shared_ptr const & l, TileKey const & r) const { - return l->GetTileKey() < r.first; + return l->GetTileKey() < r; } - bool operator()(TTilePair const & l, shared_ptr const & r) const + bool operator()(TileKey const & l, shared_ptr const & r) const { - return l.first < r->GetTileKey(); + return l < r->GetTileKey(); } }; @@ -51,18 +51,11 @@ void ReadManager::UpdateCoverage(ScreenBase const & screen, TTilesCollection con if (screen == m_currentViewport) return; - auto newTilesTask = [this, &screen](TTilePair const & tileKey) - { - int const newScale = df::GetTileScaleBase(screen); - if (newScale == tileKey.first.m_zoomLevel) - PushTaskBackForTileKey(tileKey); - }; - if (MustDropAllTiles(screen)) { for_each(m_tileInfos.begin(), m_tileInfos.end(), bind(&ReadManager::CancelTileInfo, this, _1)); m_tileInfos.clear(); - for_each(tiles.begin(), tiles.end(), newTilesTask); + for_each(tiles.begin(), tiles.end(), bind(&ReadManager::PushTaskBackForTileKey, this, _1)); } else { @@ -76,7 +69,7 @@ void ReadManager::UpdateCoverage(ScreenBase const & screen, TTilesCollection con back_inserter(outdatedTiles), LessCoverageCell()); // Find rects that go in into viewport - buffer_vector inputRects; + buffer_vector inputRects; #ifdef _MSC_VER vs_bug:: #endif @@ -86,7 +79,7 @@ void ReadManager::UpdateCoverage(ScreenBase const & screen, TTilesCollection con for_each(outdatedTiles.begin(), outdatedTiles.end(), bind(&ReadManager::ClearTileInfo, this, _1)); for_each(m_tileInfos.begin(), m_tileInfos.end(), bind(&ReadManager::PushTaskFront, this, _1)); - for_each(inputRects.begin(), inputRects.end(), newTilesTask); + for_each(inputRects.begin(), inputRects.end(), bind(&ReadManager::PushTaskBackForTileKey, this, _1)); } m_currentViewport = screen; } @@ -124,7 +117,7 @@ bool ReadManager::MustDropAllTiles(ScreenBase const & screen) const return (oldScale != newScale) || !m_currentViewport.GlobalRect().IsIntersect(screen.GlobalRect()); } -void ReadManager::PushTaskBackForTileKey(TTilePair const & tileKey) +void ReadManager::PushTaskBackForTileKey(TileKey const & tileKey) { shared_ptr tileInfo(new TileInfo(EngineContext(tileKey, m_commutator))); m_tileInfos.insert(tileInfo); diff --git a/drape_frontend/read_manager.hpp b/drape_frontend/read_manager.hpp old mode 100644 new mode 100755 diff --git a/drape_frontend/render_group.cpp b/drape_frontend/render_group.cpp old mode 100644 new mode 100755 index 061072cd66..c21b9a160a --- a/drape_frontend/render_group.cpp +++ b/drape_frontend/render_group.cpp @@ -55,9 +55,8 @@ bool RenderGroup::IsLess(RenderGroup const & other) const return m_state < other.m_state; } -RenderGroupComparator::RenderGroupComparator(TTilesCollection const & activeTiles) - : m_activeTiles(activeTiles) - , m_needGroupMergeOperation(false) +RenderGroupComparator::RenderGroupComparator() + : m_needGroupMergeOperation(false) , m_needBucketsMergeOperation(false) { } @@ -68,13 +67,7 @@ void RenderGroupComparator::ResetInternalState() m_needGroupMergeOperation = false; } -bool RenderGroupComparator::NeedToDelete(TileKey const & key) -{ - auto const & tileIt = m_activeTiles.find(key); - return tileIt == m_activeTiles.end(); -} - -bool RenderGroupComparator::operator()(RenderGroup const * l, RenderGroup const * r) +bool RenderGroupComparator::operator()(unique_ptr const & l, unique_ptr const & r) { dp::GLState const & lState = l->GetState(); dp::GLState const & rState = r->GetState(); @@ -82,10 +75,10 @@ bool RenderGroupComparator::operator()(RenderGroup const * l, RenderGroup const TileKey const & lKey = l->GetTileKey(); TileKey const & rKey = r->GetTileKey(); - if (!l->IsPendingOnDelete() && (l->IsEmpty() || NeedToDelete(lKey))) + if (!l->IsPendingOnDelete() && l->IsEmpty()) l->DeleteLater(); - if (!r->IsPendingOnDelete() && (r->IsEmpty() || NeedToDelete(rKey))) + if (!r->IsPendingOnDelete() && r->IsEmpty()) r->DeleteLater(); bool lPendingOnDelete = l->IsPendingOnDelete(); diff --git a/drape_frontend/render_group.hpp b/drape_frontend/render_group.hpp old mode 100644 new mode 100755 index 9931458495..807997ffac --- a/drape_frontend/render_group.hpp +++ b/drape_frontend/render_group.hpp @@ -8,6 +8,7 @@ #include "std/vector.hpp" #include "std/set.hpp" +#include "std/unique_ptr.hpp" class ScreenBase; namespace dp { class OverlayTree; } @@ -61,18 +62,15 @@ private: class RenderGroupComparator { public: - RenderGroupComparator(TTilesCollection const & activeTiles); + RenderGroupComparator(); void ResetInternalState(); - bool operator()(RenderGroup const * l, RenderGroup const * r); + bool operator()(unique_ptr const & l, unique_ptr const & r); private: - TTilesCollection const & m_activeTiles; bool m_needGroupMergeOperation; bool m_needBucketsMergeOperation; - - bool NeedToDelete(TileKey const & key); }; class UserMarkRenderGroup : public BaseRenderGroup diff --git a/drape_frontend/tile_key.cpp b/drape_frontend/tile_key.cpp old mode 100644 new mode 100755 index 799ccc22c2..56289c0b93 --- a/drape_frontend/tile_key.cpp +++ b/drape_frontend/tile_key.cpp @@ -51,4 +51,22 @@ string DebugPrint(TileKey const & key) return out.str(); } +string DebugPrint(TileStatus status) +{ + switch (status) + { + case TileStatus::Unknown: + return "Unknown"; + case TileStatus::Rendered: + return "Rendered"; + case TileStatus::Requested: + return "Requested"; + case TileStatus::Deferred: + return "Deferred"; + default: + ASSERT(false, ()); + } + return ""; +} + } //namespace df diff --git a/drape_frontend/tile_key.hpp b/drape_frontend/tile_key.hpp old mode 100644 new mode 100755 index b87f5bc367..6871b980a9 --- a/drape_frontend/tile_key.hpp +++ b/drape_frontend/tile_key.hpp @@ -5,12 +5,12 @@ namespace df { -enum TileStatus +enum class TileStatus { Unknown = 0, Rendered, Requested, - Clipped + Deferred }; struct TileKey @@ -29,7 +29,9 @@ struct TileKey int m_zoomLevel; private: - friend string DebugPrint(TileKey const & ); + friend string DebugPrint(TileKey const & key); }; +string DebugPrint(TileStatus status); + } // namespace df diff --git a/drape_frontend/tile_tree.cpp b/drape_frontend/tile_tree.cpp new file mode 100644 index 0000000000..1ad36eaad7 --- /dev/null +++ b/drape_frontend/tile_tree.cpp @@ -0,0 +1,372 @@ +#include "tile_tree.hpp" + +#include "../std/algorithm.hpp" +#include "../std/utility.hpp" + +namespace df +{ + +TileTree::TileTree() + : m_root(new Node()) +{ +} + +TileTree::~TileTree() +{ + m_root.reset(); +} + +void TileTree::Clear() +{ + m_root.reset(new Node()); +} + +void TileTree::BeginRequesting(int const zoomLevel) +{ + AbortRequestedTiles(m_root, zoomLevel); +} + +void TileTree::RequestTile(TileKey const & tileKey) +{ + InsertToNode(m_root, tileKey); +} + +void TileTree::EndRequesting() +{ + SimplifyTree(nullptr); +} + +void TileTree::GetRequestedTiles(TTilesCollection & tiles) +{ + FindRequestedTiles(m_root, tiles); +} + +bool TileTree::ContainsTileKey(TileKey const & tileKey) +{ + return ContainsTileKey(m_root, tileKey); +} + +void TileTree::ClipByRect(m2::RectD const & rect, TTileHandler const & addTile, + TTileHandler const & removeTile) +{ + ClipNode(m_root, rect, removeTile); + CheckDeferredTiles(m_root, addTile, removeTile); + SimplifyTree(removeTile); +} + +bool TileTree::ProcessTile(TileKey const & tileKey, TTileHandler const & addTile, + TTileHandler const & removeTile, TTileHandler const & deferTile) +{ + bool const result = ProcessNode(m_root, tileKey, addTile, removeTile, deferTile); + if (result) + SimplifyTree(removeTile); + + return result; +} + +void TileTree::InsertToNode(TNodePtr const & node, TileKey const & tileKey) +{ + // here we try to insert new tile to the node. The tree is built in such way that + // child nodes always have got the same zoom level + + // insert to empty node + if (node->m_children.empty()) + { + node->m_children.emplace_back(TNodePtr(new Node(tileKey, TileStatus::Requested))); + return; + } + + int const childrenZoomLevel = node->m_children.front()->m_tileKey.m_zoomLevel; + if (tileKey.m_zoomLevel > childrenZoomLevel) + { + // here zoom level of node's children less than new tile's zoom level + // so we are looking for node to insert new tile recursively + + // looking for parent node + auto parentNodeIt = find_if(node->m_children.begin(), node->m_children.end(), [&tileKey](TNodePtr const & n) + { + return IsTileBelow(n->m_tileKey, tileKey); + }); + + // insert to parent node + if (parentNodeIt == node->m_children.end()) + { + TileKey parentTileKey = GetParentTile(tileKey, childrenZoomLevel); + node->m_children.emplace_back(TNodePtr(new Node(parentTileKey, TileStatus::Unknown))); + InsertToNode(node->m_children.back(), tileKey); + } + else + InsertToNode(*parentNodeIt, tileKey); + } + else if (tileKey.m_zoomLevel < childrenZoomLevel) + { + // here zoom level of node's children more than new tile's zoom level + // so we paste new tile and redistribute children of current node + // between new tile and his siblings + + list newChildren; + newChildren.emplace_back(new Node(tileKey, TileStatus::Requested)); + for (auto it = node->m_children.begin(); it != node->m_children.end(); ++it) + { + // looking for parent node + TileKey parentTileKey = GetParentTile((*it)->m_tileKey, tileKey.m_zoomLevel); + auto parentNodeIt = find_if(newChildren.begin(), newChildren.end(), [&parentTileKey](TNodePtr const & n) + { + return n->m_tileKey == parentTileKey; + }); + + // insert to parent node + if (parentNodeIt == newChildren.end()) + { + newChildren.emplace_back(TNodePtr(new Node(parentTileKey, TileStatus::Unknown))); + newChildren.back()->m_children.emplace_back(move(*it)); + } + else + (*parentNodeIt)->m_children.emplace_back(move(*it)); + } + node->m_children.swap(newChildren); + } + else + { + // here zoom level of node's children equals to new tile's zoom level + // so we insert new tile if we haven't got one + + auto it = find_if(node->m_children.begin(), node->m_children.end(), [&tileKey](TNodePtr const & n) + { + return n->m_tileKey == tileKey; + }); + if (it != node->m_children.end()) + { + if ((*it)->m_tileStatus == TileStatus::Unknown) + { + (*it)->m_tileStatus = TileStatus::Requested; + (*it)->m_isRemoved = false; + } + } + else + node->m_children.emplace_back(TNodePtr(new Node(tileKey, TileStatus::Requested))); + } +} + +void TileTree::AbortRequestedTiles(TNodePtr const & node, int const zoomLevel) +{ + for (auto it = node->m_children.begin(); it != node->m_children.end(); ++it) + { + if ((*it)->m_tileStatus == TileStatus::Requested && (*it)->m_tileKey.m_zoomLevel != zoomLevel) + (*it)->m_tileStatus = TileStatus::Unknown; + + AbortRequestedTiles(*it, zoomLevel); + } +} + +void TileTree::FindRequestedTiles(TNodePtr const & node, TTilesCollection & tiles) +{ + for (auto it = node->m_children.begin(); it != node->m_children.end(); ++it) + { + if ((*it)->m_tileStatus == TileStatus::Requested) + tiles.insert((*it)->m_tileKey); + + FindRequestedTiles(*it, tiles); + } +} + +bool TileTree::ContainsTileKey(TNodePtr const & node, TileKey const & tileKey) +{ + for (auto it = node->m_children.begin(); it != node->m_children.end(); ++it) + { + if (tileKey == (*it)->m_tileKey) + return (*it)->m_tileStatus != TileStatus::Unknown; + else if (IsTileBelow((*it)->m_tileKey, tileKey)) + return ContainsTileKey(*it, tileKey); + } + return false; +} + +void TileTree::ClipNode(TNodePtr const & node, m2::RectD const & rect, TTileHandler const & removeTile) +{ + for (auto it = node->m_children.begin(); it != node->m_children.end();) + { + m2::RectD const tileRect = (*it)->m_tileKey.GetGlobalRect(); + if(rect.IsIntersect(tileRect)) + { + ClipNode(*it, rect, removeTile); + ++it; + } + else + { + RemoveTile(*it, removeTile); + ClipNode(*it, rect, removeTile); + it = node->m_children.erase(it); + } + } +} + +void TileTree::RemoveTile(TNodePtr const & node, TTileHandler const & removeTile) +{ + if (removeTile != nullptr && !node->m_isRemoved) + removeTile(node->m_tileKey, node->m_tileStatus); + + node->m_isRemoved = true; + node->m_tileStatus = TileStatus::Unknown; +} + +bool TileTree::ProcessNode(TNodePtr const & node, TileKey const & tileKey, + TTileHandler const & addTile, TTileHandler const & removeTile, + TTileHandler const & deferTile) +{ + for (auto it = node->m_children.begin(); it != node->m_children.end(); ++it) + { + if (tileKey == (*it)->m_tileKey) + { + if ((*it)->m_tileStatus == TileStatus::Unknown) + return false; + + DeleteTilesBelow(*it, removeTile); + + if (node->m_tileStatus == TileStatus::Rendered) + { + (*it)->m_tileStatus = TileStatus::Deferred; + if (deferTile != nullptr) + deferTile((*it)->m_tileKey, (*it)->m_tileStatus); + (*it)->m_isRemoved = false; + } + else + { + (*it)->m_tileStatus = TileStatus::Rendered; + if (addTile != nullptr) + addTile((*it)->m_tileKey, (*it)->m_tileStatus); + (*it)->m_isRemoved = false; + } + + DeleteTilesAbove(node, addTile, removeTile); + return true; + } + else if (IsTileBelow((*it)->m_tileKey, tileKey)) + return ProcessNode(*it, tileKey, addTile, removeTile, deferTile); + } + return false; +} + +void TileTree::DeleteTilesBelow(TNodePtr const & node, TTileHandler const & removeTile) +{ + for (auto it = node->m_children.begin(); it != node->m_children.end(); ++it) + { + RemoveTile(*it, removeTile); + DeleteTilesBelow(*it, removeTile); + } + node->m_children.clear(); +} + +void TileTree::DeleteTilesAbove(TNodePtr const & node, TTileHandler const & addTile, TTileHandler const & removeTile) +{ + if (node->m_tileStatus == TileStatus::Requested) + return; + + // check if all child tiles are ready + for (auto it = node->m_children.begin(); it != node->m_children.end(); ++it) + if ((*it)->m_tileStatus == TileStatus::Requested) + return; + + // add deferred tiles + for (auto it = node->m_children.begin(); it != node->m_children.end(); ++it) + { + if ((*it)->m_tileStatus == TileStatus::Deferred) + { + if (addTile != nullptr) + addTile((*it)->m_tileKey, (*it)->m_tileStatus); + + (*it)->m_tileStatus = TileStatus::Rendered; + (*it)->m_isRemoved = false; + } + } + + // remove current tile + if (node != m_root) + RemoveTile(node, removeTile); +} + +void TileTree::SimplifyTree(TTileHandler const & removeTile) +{ + SimplifyTree(m_root, removeTile); + ClearObsoleteTiles(m_root, removeTile); +} + +void TileTree::SimplifyTree(TNodePtr const & node, TTileHandler const & removeTile) +{ + if (HaveChildrenSameStatus(node, TileStatus::Unknown)) + { + // all children of children are rendered + for (auto it = node->m_children.begin(); it != node->m_children.end(); ++it) + if (!HaveChildrenSameStatus(*it, TileStatus::Rendered)) + return; + + // move children to grandfather + list newChildren; + for (auto it = node->m_children.begin(); it != node->m_children.end(); ++it) + { + RemoveTile(*it, removeTile); + newChildren.splice(newChildren.begin(), (*it)->m_children); + } + + node->m_children.swap(newChildren); + } + + for (auto it = node->m_children.begin(); it != node->m_children.end(); ++it) + SimplifyTree(*it, removeTile); +} + +bool TileTree::ClearObsoleteTiles(TNodePtr const & node, TTileHandler const & removeTile) +{ + bool canClear = true; + for (auto it = node->m_children.begin(); it != node->m_children.end(); ++it) + canClear &= ClearObsoleteTiles(*it, removeTile); + + if (canClear) + { + for (auto it = node->m_children.begin(); it != node->m_children.end(); ++it) + RemoveTile(*it, removeTile); + + node->m_children.clear(); + } + + return canClear && node->m_tileStatus == TileStatus::Unknown; +} + +bool TileTree::HaveChildrenSameStatus(TNodePtr const & node, TileStatus tileStatus) +{ + for (auto it = node->m_children.begin(); it != node->m_children.end(); ++it) + if ((*it)->m_tileStatus != tileStatus) + return false; + + return true; +} + +void TileTree::CheckDeferredTiles(TNodePtr const & node, TTileHandler const & addTile, TTileHandler const & removeTile) +{ + for (auto it = node->m_children.begin(); it != node->m_children.end(); ++it) + { + DeleteTilesAbove(node, addTile, removeTile); + CheckDeferredTiles(*it, addTile, removeTile); + } +} + +void DebugPrintNode(TileTree::TNodePtr const & node, ostringstream & out, string const & offset) +{ + for (auto it = node->m_children.begin(); it != node->m_children.end(); ++it) + { + out << offset << "{ " << DebugPrint((*it)->m_tileKey) << ", " + << DebugPrint((*it)->m_tileStatus) << ((*it)->m_isRemoved ? ", removed" : "") << "}\n"; + DebugPrintNode(*it, out, offset + " "); + } +} + +string DebugPrint(TileTree const & tileTree) +{ + ostringstream out; + out << "\n{\n"; + DebugPrintNode(tileTree.m_root, out, " "); + out << "}\n"; + return out.str(); +} + +} // namespace df diff --git a/drape_frontend/tile_tree.hpp b/drape_frontend/tile_tree.hpp new file mode 100644 index 0000000000..d465a2fc4e --- /dev/null +++ b/drape_frontend/tile_tree.hpp @@ -0,0 +1,88 @@ +#pragma once + +#include "tile_utils.hpp" + +#include "../geometry/rect2d.hpp" + +#include "../std/function.hpp" +#include "../std/list.hpp" +#include "../std/sstream.hpp" +#include "../std/unique_ptr.hpp" + +namespace df +{ + +class TileTree +{ +public: + TileTree(); + ~TileTree(); + + using TTileHandler = function; + + void BeginRequesting(int const zoomLevel); + void RequestTile(TileKey const & tileKey); + void EndRequesting(); + + bool ProcessTile(TileKey const & tileKey, TTileHandler const & addTile, + TTileHandler const & removeTile, TTileHandler const & deferTile); + void ClipByRect(m2::RectD const & rect, TTileHandler const & addTile, + TTileHandler const & removeTile); + void Clear(); + + bool ContainsTileKey(TileKey const & tileKey); + void GetRequestedTiles(TTilesCollection & tiles); + +private: + struct Node; + using TNodePtr = unique_ptr; + + void InsertToNode(TNodePtr const & node, TileKey const & tileKey); + void AbortRequestedTiles(TNodePtr const & node, int const zoomLevel); + + void FindRequestedTiles(TNodePtr const & node, TTilesCollection & tiles); + bool ContainsTileKey(TNodePtr const & node, TileKey const & tileKey); + + void ClipNode(TNodePtr const & node, m2::RectD const & rect, + TTileHandler const & removeTile); + + void RemoveTile(TNodePtr const & node, TTileHandler const & removeTile); + + bool ProcessNode(TNodePtr const & node, TileKey const & tileKey, + TTileHandler const & addTile, TTileHandler const & removeTile, + TTileHandler const & deferTile); + + void DeleteTilesBelow(TNodePtr const & node, TTileHandler const & removeTile); + void DeleteTilesAbove(TNodePtr const & node, TTileHandler const & addTile, + TTileHandler const & removeTile); + + void SimplifyTree(TTileHandler const & removeTile); + void SimplifyTree(TNodePtr const & node, TTileHandler const & removeTile); + bool ClearObsoleteTiles(TNodePtr const & node, TTileHandler const & removeTile); + void CheckDeferredTiles(TNodePtr const & node, TTileHandler const & addTile, + TTileHandler const & removeTile); + + bool HaveChildrenSameStatus(TNodePtr const & node, TileStatus tileStatus); + +private: + struct Node + { + TileKey m_tileKey; + list m_children; + TileStatus m_tileStatus = TileStatus::Unknown; + bool m_isRemoved = false; + + Node() = default; + Node(TileKey const & key, TileStatus status) + : m_tileKey(key), m_tileStatus(status) + {} + }; + + TNodePtr m_root; + +private: + friend void DebugPrintNode(TileTree::TNodePtr const & node, ostringstream & out, string const & offset); + friend string DebugPrint(TileTree const & tileTree); +}; + +} // namespace df diff --git a/drape_frontend/tile_utils.cpp b/drape_frontend/tile_utils.cpp old mode 100644 new mode 100755 index 867ff137f1..fa969b5cfb --- a/drape_frontend/tile_utils.cpp +++ b/drape_frontend/tile_utils.cpp @@ -28,7 +28,7 @@ int Minificate(int coord, int zoom, int targetZoom) } // namespace -void CalcTilesCoverage(TileKey const & tileKey, int targetZoom, set & tiles) +void CalcTilesCoverage(TileKey const & tileKey, int targetZoom, TTilesCollection & tiles) { CalcTilesCoverage(tileKey, targetZoom, [&tiles](TileKey const & tileKey) { @@ -36,7 +36,7 @@ void CalcTilesCoverage(TileKey const & tileKey, int targetZoom, set & t }); } -void CalcTilesCoverage(set const & tileKeys, int targetZoom, set & tiles) +void CalcTilesCoverage(set const & tileKeys, int targetZoom, TTilesCollection & tiles) { for(TileKey const & tileKey : tileKeys) CalcTilesCoverage(tileKey, targetZoom, tiles); @@ -51,9 +51,7 @@ void CalcTilesCoverage(TileKey const & tileKey, int targetZoom, function targetZoom) { // minification - processTile(TileKey(Minificate(tileKey.m_x, tileKey.m_zoomLevel, targetZoom), - Minificate(tileKey.m_y, tileKey.m_zoomLevel, targetZoom), - targetZoom)); + processTile(GetParentTile(tileKey, targetZoom)); } else { @@ -96,4 +94,13 @@ bool IsTileBelow(TileKey const & tileKey, TileKey const & targetTileKey) return targetTileKey.m_y >= startY && targetTileKey.m_y < startY + tilesInRow; } +TileKey GetParentTile(TileKey const & tileKey, int targetZoom) +{ + ASSERT(tileKey.m_zoomLevel > targetZoom, ()); + + return TileKey(Minificate(tileKey.m_x, tileKey.m_zoomLevel, targetZoom), + Minificate(tileKey.m_y, tileKey.m_zoomLevel, targetZoom), + targetZoom); +} + } // namespace df diff --git a/drape_frontend/tile_utils.hpp b/drape_frontend/tile_utils.hpp old mode 100644 new mode 100755 index 95993aca65..ced47c780b --- a/drape_frontend/tile_utils.hpp +++ b/drape_frontend/tile_utils.hpp @@ -7,14 +7,13 @@ namespace df { -using TTilesCollection = map; -using TTilePair = pair; +using TTilesCollection = set; /// this function determines the coverage of a tile in specified zoom level -void CalcTilesCoverage(TileKey const & tileKey, int targetZoom, set & tiles); +void CalcTilesCoverage(TileKey const & tileKey, int targetZoom, TTilesCollection & tiles); /// this function determines the coverage of tiles in specified zoom level -void CalcTilesCoverage(set const & tileKeys, int targetZoom, set & tiles); +void CalcTilesCoverage(set const & tileKeys, int targetZoom, TTilesCollection & tiles); /// this function determines the coverage of a tile in specified zoom level. Each new tile can be processed /// in processTile callback @@ -26,6 +25,9 @@ bool IsTileAbove(TileKey const & tileKey, TileKey const & targetTileKey); /// this function checks if targetTileKey is below tileKey bool IsTileBelow(TileKey const & tileKey, TileKey const & targetTileKey); +/// this function returns parent tile on specified zoom level +TileKey GetParentTile(TileKey const & tileKey, int targetZoom); + } // namespace df