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