Added tile tree

This commit is contained in:
r.kuznetsov 2015-03-30 11:55:10 +03:00
parent 0e6a372c05
commit 8cb22bf52d
13 changed files with 676 additions and 139 deletions

2
drape_frontend/drape_frontend.pro Normal file → Executable file
View file

@ -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 \

224
drape_frontend/frontend_renderer.cpp Normal file → Executable file
View file

@ -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<UserMarkRenderGroup> & 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<UserMarkRenderGroup> 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<UserMarkRenderGroup> emptyRenderGroup;
return emptyRenderGroup;
}
void FrontendRenderer::AcceptMessage(dp::RefPointer<Message> message)
@ -134,8 +134,8 @@ void FrontendRenderer::AcceptMessage(dp::RefPointer<Message> message)
CreateTileRenderGroup(state, bucket, key);
else
{
UserMarkRenderGroup * group = FindUserMarkRenderGroup(key, true);
ASSERT(group != nullptr, ());
unique_ptr<UserMarkRenderGroup> & group = FindUserMarkRenderGroup(key, true);
ASSERT(group.get() != nullptr, ());
group->SetRenderBucket(state, bucket.Move());
}
break;
@ -151,11 +151,15 @@ void FrontendRenderer::AcceptMessage(dp::RefPointer<Message> message)
RefreshProjection();
RefreshModelView();
ResolveTileKeys();
TTilesCollection tiles;
m_tileTree.GetRequestedTiles(tiles);
m_commutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
dp::MovePointer<Message>(new ResizeMessage(m_viewport)),
MessagePriority::Normal);
m_commutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
dp::MovePointer<Message>(new UpdateReadManagerMessage(m_view, m_tiles)),
dp::MovePointer<Message>(new UpdateReadManagerMessage(m_view, tiles)),
MessagePriority::Normal);
break;
}
@ -169,8 +173,7 @@ void FrontendRenderer::AcceptMessage(dp::RefPointer<Message> message)
InvalidateRectMessage * m = df::CastMessage<InvalidateRectMessage>(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> message)
case Message::ClearUserMarkLayer:
{
TileKey const & tileKey = df::CastMessage<ClearUserMarkLayerMessage>(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<UserMarkRenderGroup> const & g)
{
return g->GetTileKey() == tileKey;
});
if (it != m_userMarkRenderGroups.end())
{
UserMarkRenderGroup * group = *it;
ASSERT(group != nullptr, ());
unique_ptr<UserMarkRenderGroup> & group = *it;
ASSERT(group.get() != nullptr, ());
m_userMarkRenderGroups.erase(it);
delete group;
}
break;
@ -200,8 +202,8 @@ void FrontendRenderer::AcceptMessage(dp::RefPointer<Message> message)
case Message::ChangeUserMarkLayerVisibility:
{
ChangeUserMarkLayerVisibilityMessage * m = df::CastMessage<ChangeUserMarkLayerVisibilityMessage>(message);
UserMarkRenderGroup * group = FindUserMarkRenderGroup(m->GetKey(), true);
ASSERT(group != nullptr, ());
unique_ptr<UserMarkRenderGroup> & 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<dp::RenderBucket> & 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<unique_ptr<RenderGroup>> & groups,
dp::GLState const & state,
dp::MasterPointer<dp::RenderBucket> & renderBucket,
TileKey const & newTile)
{
for(auto it = keyStorage.begin(); it != keyStorage.end();)
unique_ptr<RenderGroup> 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<RenderGroup> & 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<RenderGroup> & 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<UserMarkRenderGroup> & 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<int>(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<Message>(new UpdateReadManagerMessage(m_view, m_tiles)),
dp::MovePointer<Message>(new UpdateReadManagerMessage(m_view, tiles)),
MessagePriority::Normal);
}
}
string DebugPrint(vector<unique_ptr<RenderGroup>> 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

28
drape_frontend/frontend_renderer.hpp Normal file → Executable file
View file

@ -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<UserMarkRenderGroup> & 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<dp::RenderBucket> & renderBucket,
TileKey const & newTile);
void CleanKeyStorage(TTilesCollection & keyStorage);
void AddToRenderGroup(vector<unique_ptr<RenderGroup>> & groups,
dp::GLState const & state,
dp::MasterPointer<dp::RenderBucket> & renderBucket,
TileKey const & newTile);
void OnAddDeferredTile(TileKey const & tileKey, TileStatus tileStatus);
void OnRemoveTile(TileKey const & tileKey, TileStatus tileStatus);
private:
dp::RefPointer<dp::TextureManager> m_textureManager;
dp::MasterPointer<dp::GpuProgramManager> m_gpuProgramManager;
private:
vector<RenderGroup *> m_renderGroups;
vector<UserMarkRenderGroup *> m_userMarkRenderGroups;
vector<unique_ptr<RenderGroup>> m_renderGroups;
vector<unique_ptr<RenderGroup>> m_deferredRenderGroups;
vector<unique_ptr<UserMarkRenderGroup>> m_userMarkRenderGroups;
dp::MasterPointer<gui::LayerRenderer> m_guiRenderer;
dp::MasterPointer<MyPosition> 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<unique_ptr<RenderGroup>> const & groups);
};
} // namespace df

23
drape_frontend/read_manager.cpp Normal file → Executable file
View file

@ -17,14 +17,14 @@ namespace
struct LessCoverageCell
{
bool operator()(shared_ptr<TileInfo> const & l, TTilePair const & r) const
bool operator()(shared_ptr<TileInfo> const & l, TileKey const & r) const
{
return l->GetTileKey() < r.first;
return l->GetTileKey() < r;
}
bool operator()(TTilePair const & l, shared_ptr<TileInfo> const & r) const
bool operator()(TileKey const & l, shared_ptr<TileInfo> 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<TTilePair, 8> inputRects;
buffer_vector<TileKey, 8> 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> tileInfo(new TileInfo(EngineContext(tileKey, m_commutator)));
m_tileInfos.insert(tileInfo);

0
drape_frontend/read_manager.hpp Normal file → Executable file
View file

17
drape_frontend/render_group.cpp Normal file → Executable file
View file

@ -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<RenderGroup> const & l, unique_ptr<RenderGroup> 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();

8
drape_frontend/render_group.hpp Normal file → Executable file
View file

@ -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<RenderGroup> const & l, unique_ptr<RenderGroup> const & r);
private:
TTilesCollection const & m_activeTiles;
bool m_needGroupMergeOperation;
bool m_needBucketsMergeOperation;
bool NeedToDelete(TileKey const & key);
};
class UserMarkRenderGroup : public BaseRenderGroup

18
drape_frontend/tile_key.cpp Normal file → Executable file
View file

@ -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

8
drape_frontend/tile_key.hpp Normal file → Executable file
View file

@ -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

View file

@ -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<TNodePtr> 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<TNodePtr> 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

View file

@ -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(TileKey const &, TileStatus)>;
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<Node>;
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<TNodePtr> 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

17
drape_frontend/tile_utils.cpp Normal file → Executable file
View file

@ -28,7 +28,7 @@ int Minificate(int coord, int zoom, int targetZoom)
} // namespace
void CalcTilesCoverage(TileKey const & tileKey, int targetZoom, set<TileKey> & 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<TileKey> & t
});
}
void CalcTilesCoverage(set<TileKey> const & tileKeys, int targetZoom, set<TileKey> & tiles)
void CalcTilesCoverage(set<TileKey> 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<void(Ti
else if (tileKey.m_zoomLevel > 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

10
drape_frontend/tile_utils.hpp Normal file → Executable file
View file

@ -7,14 +7,13 @@
namespace df
{
using TTilesCollection = map<TileKey, TileStatus>;
using TTilePair = pair<TileKey, TileStatus>;
using TTilesCollection = set<TileKey>;
/// this function determines the coverage of a tile in specified zoom level
void CalcTilesCoverage(TileKey const & tileKey, int targetZoom, set<TileKey> & tiles);
void CalcTilesCoverage(TileKey const & tileKey, int targetZoom, TTilesCollection & tiles);
/// this function determines the coverage of tiles in specified zoom level
void CalcTilesCoverage(set<TileKey> const & tileKeys, int targetZoom, set<TileKey> & tiles);
void CalcTilesCoverage(set<TileKey> 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