diff --git a/src/areastore.cpp b/src/areastore.cpp index 657875d57a0f..06965e8b3c14 100644 --- a/src/areastore.cpp +++ b/src/areastore.cpp @@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "areastore.h" #include "util/serialize.h" +#include "util/container.h" #include "log.h" // TODO remove (for debugging) #if USE_SPATIAL @@ -133,6 +134,65 @@ void AreaStore::serialize(std::ostream &os) const forEach(&serialize_area, &os); } + +void AreaStore::invalidateCache() +{ + if (cache_enabled) { + m_res_cache.invalidate(); + } +} + + +void AreaStore::setCacheEnabled(bool enabled) +{ + if (!enabled && cache_enabled) { + invalidateCache(); + } +} + +inline static void other_val(s16 p, u8 r) +{ + return p >= 0 ? (p + r - 1) : (p - r + 1); +} + +void AreaStore::cacheMiss(void *data, const v3s16 &mpos, std::vector *dest) +{ + AreaStore *as = (AreaStore *)data; + u8 r = as->m_cacheblock_radius; + + // get the point at the other side of the mapblock + v3s16 mpos_other; + mpos_other.X = other_val(mpos.X, r); + mpos_other.Y = other_val(mpos.Y, r); + mpos_other.Z = other_val(mpos.Z, r); + + // extremify both points + v3s16 minedge; + v3s16 maxedge; + AST_EXTREMIFY(minedge, maxedge, mpos, mpos_other) + + as->getAreasInArea(dest, minedge, maxedge, true); +} + +void AreaStore::getAreasForPos(std::vector *result, v3s16 pos) +{ + if (cache_enabled) { + v3s16 mblock = getContainerPos(pos, m_cacheblock_radius); + std::vector &pre_list = m_res_cache.lookupCache(mblock); + + size_t s_p_l = pre_list.size(); + for (size_t i = 0; i < s_p_l; i++) { + Area *b = pre_list[i]; + if (AST_CONTAINS_PT(b, pos)) { + result->push_back(b); + } + } + } else { + return getAreasForPosImpl(result, pos); + } +} + + void VectorAreaStore::insertArea(const Area &a) { areas_map[a.id] = a; @@ -165,7 +225,7 @@ bool VectorAreaStore::removeArea(u32 id) return false; } -void VectorAreaStore::getAreasForPos(std::vector *result, v3s16 pos) +void VectorAreaStore::getAreasForPosImpl(std::vector *result, v3s16 pos) { size_t msiz = m_areas.size(); for (size_t i = 0; i < msiz; i++) { @@ -239,7 +299,7 @@ bool SpatialAreaStore::removeArea(u32 id) } } -void SpatialAreaStore::getAreasForPos(std::vector *result, v3s16 pos) +void SpatialAreaStore::getAreasForPosImpl(std::vector *result, v3s16 pos) { VectorResultVisitor visitor(result, this); m_tree->pointLocationQuery(get_spatial_point(pos), visitor); diff --git a/src/areastore.h b/src/areastore.h index 057d67af1dcb..698c7ebf17b5 100644 --- a/src/areastore.h +++ b/src/areastore.h @@ -30,13 +30,16 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef ANDROID #include "cmake_config.h" #endif - #if USE_SPATIAL - -#include -#include "util/serialize.h" + #include + #include "util/serialize.h" #endif +#define AST_EXTREMIFY(min, max, pa, pb) \ + min.X = MYMIN(pa.X, pb.X); \ + min.Y = MYMIN(pa.Y, pb.Y); \ + min.Z = MYMIN(pa.Z, pb.Z); + typedef struct Area { Area() { @@ -54,12 +57,7 @@ typedef struct Area { v3s16 nminedge; v3s16 nmaxedge; - nminedge.X = MYMIN(minedge.X, maxedge.X); - nminedge.Y = MYMIN(minedge.Y, maxedge.Y); - nminedge.Z = MYMIN(minedge.Z, maxedge.Z); - nmaxedge.X = MYMAX(minedge.X, maxedge.X); - nmaxedge.Y = MYMAX(minedge.Y, maxedge.Y); - nmaxedge.Z = MYMAX(minedge.Z, maxedge.Z); + AST_EXTREMIFY(nminedge, nmaxedge, minedge, maxedge) maxedge = nmaxedge; minedge = nminedge; @@ -78,11 +76,14 @@ class AreaStore { std::map areas_map; u16 count; PcgRandom random; + void invalidateCache(); + virtual void getAreasForPosImpl(std::vector *result, v3s16 pos) = 0; + bool cache_enabled; // don't write to this from subclasses, only read. public: virtual void insertArea(const Area &a) = 0; virtual void reserve(size_t count) {}; virtual bool removeArea(u32 id) = 0; - virtual void getAreasForPos(std::vector *result, v3s16 pos) = 0; + void getAreasForPos(std::vector *result, v3s16 pos); virtual void getAreasInArea(std::vector *result, v3s16 minedge, v3s16 maxedge, bool accept_overlap) = 0; @@ -94,20 +95,37 @@ class AreaStore { virtual ~AreaStore() {} + AreaStore() : + m_res_cache(1000, &cacheMiss, this), + cache_enabled(false), + m_cacheblock_radius(64), + { + cache_enabled = false; + m_cacheblock_radius = 64; + } + + void setCacheEnabled(bool enabled); + u32 getFreeId(v3s16 minedge, v3s16 maxedge); const Area *getArea(u32 id) const; u16 size() const; bool deserialize(std::istream &is); void serialize(std::ostream &is) const; +private: + static void cacheMiss(void *data, const K &val, V *dest); + u8 m_cacheblock_radius; // if you modify this, call invalidateCache() + LRUCache> m_res_cache; + }; class VectorAreaStore : public AreaStore { +protected: + virtual void getAreasForPosImpl(std::vector *result, v3s16 pos); public: virtual void insertArea(const Area &a); virtual void reserve(size_t count); virtual bool removeArea(u32 id); - virtual void getAreasForPos(std::vector *result, v3s16 pos); virtual void getAreasInArea(std::vector *result, v3s16 minedge, v3s16 maxedge, bool accept_overlap); virtual bool forEach(bool (*callback)(void *args, Area *a), void *args) const; @@ -120,11 +138,12 @@ class VectorAreaStore : public AreaStore { //#define SPATIAL_DLEN sizeof(u32) class SpatialAreaStore : public AreaStore { +protected: + virtual void getAreasForPosImpl(std::vector *result, v3s16 pos); public: SpatialAreaStore(); virtual void insertArea(const Area &a); virtual bool removeArea(u32 id); - virtual void getAreasForPos(std::vector *result, v3s16 pos); virtual void getAreasInArea(std::vector *result, v3s16 minedge, v3s16 maxedge, bool accept_overlap); virtual bool forEach(bool (*callback)(void *args, Area *a), void *args) const; diff --git a/src/util/container.h b/src/util/container.h index 936c46d6111b..914294cddcee 100644 --- a/src/util/container.h +++ b/src/util/container.h @@ -309,5 +309,66 @@ class MutexedQueue JSemaphore m_size; }; +template +class LRUCache +{ +public: + LRUCache(size_t limit, void (*cache_miss)(void *data, const K &key, V *dest), + void *data) + { + m_limit = limit; + m_cache_miss = cache_miss; + m_cache_miss_data = data; + } + + void invalidate() + { + m_map.clear(); + m_queue.clear(); + } + + const *V lookupCache(const K &key) + { + cache_type::iterator it = m_map.find(key); + V &ret; + if (it != m_map.end()) { + // found! + + std::pair::iterator, V> &entry = it->second; + + ret = entry.second; + + // update the usage information + m_queue.erase(entry.first); + m_queue.push_front(key); + + } else { + // cache miss -- enter into cache + std::pair::iterator, V> &entry = + m_map[key]; + ret = entry.second; + m_cache_miss(m_cache_miss_data, key, &entry.second); + + // delete old entries + if (m_queue.size() == m_limit) { + const K &id = m_queue.back(); + m_map.erase(id); + m_queue.pop_back(); + } + + entry.first = m_queue.begin(); + m_queue.push_front(key); + } + return ret; + } +private: + void (*m_cache_miss)(void *data, const K &key, V *dest); + void *m_cache_miss_data; + size_t m_limit; + typedef std::map::iterator, V> > cache_type; + cache_type m_map; + std::list m_queue; +}; + #endif