diff --git a/drape_frontend/drape_frontend.pro b/drape_frontend/drape_frontend.pro index 0d43d2693f..8945315c54 100644 --- a/drape_frontend/drape_frontend.pro +++ b/drape_frontend/drape_frontend.pro @@ -2,15 +2,18 @@ TARGET = drape_frontend TEMPLATE = lib CONFIG += staticlib +DEPENDENCIES = base ROOT_DIR = .. - include($$ROOT_DIR/common.pri) SOURCES += \ backend_renderer.cpp \ - engine_context.cpp + engine_context.cpp \ + memory_feature_index.cpp HEADERS += \ backend_renderer.hpp \ render_thread.hpp \ - engine_context.hpp + engine_context.hpp \ + memory_feature_index.hpp \ + tile_info.hpp diff --git a/drape_frontend/drape_frontend_tests/drape_frontend_tests.pro b/drape_frontend/drape_frontend_tests/drape_frontend_tests.pro new file mode 100644 index 0000000000..3b0ff02ce5 --- /dev/null +++ b/drape_frontend/drape_frontend_tests/drape_frontend_tests.pro @@ -0,0 +1,13 @@ + +TARGET = drape_frontend_tests +CONFIG += console warn_on +CONFIG -= app_bundle +TEMPLATE = app + +DEPENDENCIES = drape_frontend base +ROOT_DIR = ../.. +include($$ROOT_DIR/common.pri) + +SOURCES += \ + ../../testing/testingmain.cpp \ + memory_feature_index_tests.cpp diff --git a/drape_frontend/drape_frontend_tests/memory_feature_index_tests.cpp b/drape_frontend/drape_frontend_tests/memory_feature_index_tests.cpp new file mode 100644 index 0000000000..b4cc57d34e --- /dev/null +++ b/drape_frontend/drape_frontend_tests/memory_feature_index_tests.cpp @@ -0,0 +1,215 @@ +#include "../../testing/testing.hpp" + +#include "../memory_feature_index.hpp" + +#include "../../base/thread.hpp" +#include "../../base/thread_pool.hpp" +#include "../../base/condition.hpp" + +#include "../../std/vector.hpp" +#include "../../std/set.hpp" +#include "../../std/bind.hpp" + +#include +#include + +namespace +{ + void MarkNodesAsReaded(vector & features, const vector & indexes) + { + for (size_t i = 0; i < indexes.size(); ++i) + features[indexes[i]].m_isOwner = true; + } + + void ResetReadedMark(vector & features) + { + for (size_t i = 0; i < features.size(); ++i) + features[i].m_isOwner = false; + } +} + +UNIT_TEST(MemoryFeatureIndex_SimpleTest) +{ + df::TileInfo info1(1, 1, 10); + df::TileInfo info2(1, 2, 10); + + set features; + + for (size_t i = 0; i < 10; ++i) + { + info1.m_featureInfo.push_back(df::FeatureInfo(FeatureID(0, i))); + info2.m_featureInfo.push_back(df::FeatureInfo(FeatureID(0, 2 * i))); + + features.insert(info1.m_featureInfo.back().m_id); + features.insert(info2.m_featureInfo.back().m_id); + } + + df::MemoryFeatureIndex index; + + { + vector result; + index.ReadFeaturesRequest(info1.m_featureInfo, result); + MarkNodesAsReaded(info1.m_featureInfo, result); + } + + { + vector result; + index.ReadFeaturesRequest(info2.m_featureInfo, result); + MarkNodesAsReaded(info2.m_featureInfo, result); + } + + set readedFeatures; + for (size_t i = 0; i < info1.m_featureInfo.size(); ++i) + TEST_EQUAL(readedFeatures.insert(info1.m_featureInfo[i].m_id).second, true, ()); + + for (size_t i = 0; i < info2.m_featureInfo.size(); ++i) + { + if (info2.m_featureInfo[i].m_isOwner) + TEST_EQUAL(readedFeatures.insert(info2.m_featureInfo[i].m_id).second, true, ()); + } + + TEST_EQUAL(readedFeatures.size(), features.size(), ()); +} + +UNIT_TEST(MemoryFeatureIndex_Test) +{ + df::TileInfo info1(1, 1, 10); + df::TileInfo info2(1, 2, 10); + + for (size_t i = 0; i < 10; ++i) + { + FeatureID id1(0, i); + FeatureID id2(0, 2 * i); + + info1.m_featureInfo.push_back(df::FeatureInfo(FeatureID(0, i))); + info2.m_featureInfo.push_back(df::FeatureInfo(FeatureID(0, 2 * i))); + } + + df::MemoryFeatureIndex index; + + { + vector result; + index.ReadFeaturesRequest(info1.m_featureInfo, result); + TEST_EQUAL(result.size(), info1.m_featureInfo.size(), ()); + MarkNodesAsReaded(info1.m_featureInfo, result); + } + + { + vector result; + index.ReadFeaturesRequest(info2.m_featureInfo, result); + TEST_EQUAL(result.size(), 5, ()); + MarkNodesAsReaded(info2.m_featureInfo, result); + } + + index.RemoveFeatures(info1.m_featureInfo); + ResetReadedMark(info1.m_featureInfo); + + { + vector result; + index.ReadFeaturesRequest(info2.m_featureInfo, result); + TEST_EQUAL(result.size(), 5, ()); + MarkNodesAsReaded(info2.m_featureInfo, result); + } + + for (size_t i = 0; i < info2.m_featureInfo.size(); ++i) + TEST_EQUAL(info2.m_featureInfo[i].m_isOwner, true, ()); + + index.RemoveFeatures(info2.m_featureInfo); + ResetReadedMark(info2.m_featureInfo); + + { + vector result; + index.ReadFeaturesRequest(info1.m_featureInfo, result); + TEST_EQUAL(result.size(), info1.m_featureInfo.size(), ()); + MarkNodesAsReaded(info1.m_featureInfo, result); + } + + for (size_t i = 0; i < info1.m_featureInfo.size(); ++i) + TEST_EQUAL(info1.m_featureInfo[i].m_isOwner, true, ()); +} + +namespace +{ + void JoinFinishFinction(threads::IRoutine * routine, threads::Condition & condition, int & counter) + { + condition.Lock(); + counter++; + condition.Unlock(); + delete routine; + + condition.Signal(); + } + + class TestRoutine : public threads::IRoutine + { + public: + TestRoutine(vector & features, df::MemoryFeatureIndex &index) + : m_features(features) + , m_index(index) + { + } + + virtual void Do() + { + vector result; + m_index.ReadFeaturesRequest(m_features, result); + MarkNodesAsReaded(m_features, result); + } + + private: + vector & m_features; + df::MemoryFeatureIndex & m_index; + }; + + void GenerateFeatures(vector & features) + { + for (int i = 0; i < 100; ++i) + features.push_back(df::FeatureInfo(FeatureID(0, rand()))); + } +} + +UNIT_TEST(MemoryFeatureIndex_MT_Test) +{ + std::srand(std::time(0)); + const int TASK_COUNT = 10; + threads::Condition cond; + int counter = 0; + threads::ThreadPool pool(4, bind(&JoinFinishFinction, _1, ref(cond), ref(counter))); + + df::MemoryFeatureIndex index; + vector features[TASK_COUNT]; + + for (int i = 0; i < TASK_COUNT; ++i) + { + GenerateFeatures(features[i]); + pool.AddTask(new TestRoutine(features[i], index)); + } + + while(true) + { + cond.Lock(); + if (counter == TASK_COUNT) + { + cond.Unlock(); + break; + } + + cond.Wait(); + cond.Unlock(); + } + + set allFeatures; + set readedFeatures; + + for (int i = 0; i < TASK_COUNT; ++i) + { + for (size_t j = 0; j < features[i].size(); ++j) + { + allFeatures.insert(features[i][j].m_id); + if (features[i][j].m_isOwner == true) + TEST_EQUAL(readedFeatures.insert(features[i][j].m_id).second, true, ()); + } + } + + TEST_EQUAL(allFeatures.size(), readedFeatures.size(), ()); +} diff --git a/drape_frontend/memory_feature_index.cpp b/drape_frontend/memory_feature_index.cpp new file mode 100644 index 0000000000..22fcea9280 --- /dev/null +++ b/drape_frontend/memory_feature_index.cpp @@ -0,0 +1,38 @@ +#include "memory_feature_index.hpp" + +namespace df +{ + MemoryFeatureIndex::MemoryFeatureIndex() {} + + void MemoryFeatureIndex::ReadFeaturesRequest(const vector & features, vector & indexes) + { + threads::MutexGuard lock(m_mutex); + + for (size_t i = 0; i < features.size(); ++i) + { + const FeatureInfo & info = features[i]; + bool featureExists = m_features.find(info.m_id) != m_features.end(); + ASSERT(!(featureExists == false && info.m_isOwner == true), ()); + if (info.m_isOwner == false && featureExists == false) + { + m_features.insert(info.m_id); + indexes.push_back(i); + } + } + } + + void MemoryFeatureIndex::RemoveFeatures(const vector & features) + { + threads::MutexGuard lock(m_mutex); + + for (size_t i = 0; i < features.size(); ++i) + { + const FeatureInfo & info = features[i]; + if (info.m_isOwner == true) + { + ASSERT(m_features.find(info.m_id) != m_features.end(), ()); + m_features.erase(info.m_id); + } + } + } +} diff --git a/drape_frontend/memory_feature_index.hpp b/drape_frontend/memory_feature_index.hpp new file mode 100644 index 0000000000..5eea9be113 --- /dev/null +++ b/drape_frontend/memory_feature_index.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "tile_info.hpp" + +#include "../base/mutex.hpp" + +#include "../std/set.hpp" +#include "../std/utility.hpp" +#include "../std/vector.hpp" + +namespace df +{ + class MemoryFeatureIndex + { + public: + MemoryFeatureIndex(); + + void ReadFeaturesRequest(const vector & features, vector & indexes); + void RemoveFeatures(const vector & features); + + private: + threads::Mutex m_mutex; + set m_features; + }; +} diff --git a/drape_frontend/tile_info.hpp b/drape_frontend/tile_info.hpp new file mode 100644 index 0000000000..a45d85e387 --- /dev/null +++ b/drape_frontend/tile_info.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include "../indexer/feature_decl.hpp" + +#include "../std/vector.hpp" + +namespace df +{ + struct FeatureInfo + { + FeatureInfo(const FeatureID & id) + : m_id(id), m_isOwner(false) {} + + FeatureID m_id; + bool m_isOwner; + }; + + class TileInfo + { + public: + //TileInfo() : m_x(-1), m_y(-1), m_zoomLevel(-1) {} + TileInfo(int x, int y, int zoomLevel) + : m_x(x), m_y(y), m_zoomLevel(zoomLevel) {} + + bool operator < (const TileInfo & other) const + { + if (m_zoomLevel != other.m_zoomLevel) + return m_zoomLevel < other.m_zoomLevel; + if (m_y != other.m_y) + return m_y < other.m_y; + if (m_x != other.m_x) + return m_x < other.m_x; + return false; + } + + int m_x, m_y, m_zoomLevel; + vector m_featureInfo; + }; +} diff --git a/omim.pro b/omim.pro index 9323840927..4fd3bb7cf7 100644 --- a/omim.pro +++ b/omim.pro @@ -31,7 +31,7 @@ SUBDIRS = 3party \ storage storage/storage_tests \ search search/search_tests \ map map/map_tests map/benchmark_tool map/mwm_tests\ - drape_frontend \ + drape_frontend drape_frontend/drape_frontend_tests \ generator generator/generator_tests \ generator/generator_tool \ qt_tstfrm \