diff options
Diffstat (limited to '')
-rw-r--r-- | BiomeVisualiser/BiomeCache.cpp | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/BiomeVisualiser/BiomeCache.cpp b/BiomeVisualiser/BiomeCache.cpp new file mode 100644 index 000000000..962f4a3f0 --- /dev/null +++ b/BiomeVisualiser/BiomeCache.cpp @@ -0,0 +1,333 @@ +
+// BiomeCache.cpp
+
+// Implements the cBiomeCache class representing a biome source that caches data from the underlying biome source
+
+#include "Globals.h"
+#include "BiomeCache.h"
+#include "Timer.h"
+
+
+
+
+
+static int GetNumCores(void)
+{
+ // Get number of cores by querying the system process affinity mask
+ DWORD Affinity, ProcAffinity;
+ GetProcessAffinityMask(GetCurrentProcess(), &ProcAffinity, &Affinity);
+ int NumCores = 0;
+ while (Affinity > 0)
+ {
+ if ((Affinity & 1) == 1)
+ {
+ NumCores++;
+ }
+ Affinity >>= 1;
+ } // while (Affinity > 0)
+ return NumCores;
+}
+
+
+
+
+
+cBiomeCache::cBiomeCache(void) :
+ m_Source(NULL),
+ m_BaseX(MAXINT),
+ m_BaseZ(MAXINT),
+ m_Available(NULL),
+ m_IsTerminatingThreads(false)
+{
+ int NumThreads = GetNumCores();
+ NumThreads--; // One core should be left for the system to run on ;)
+ for (int i = NumThreads; i > 0; i--)
+ {
+ cThread * Thread = new cThread(*this);
+ m_Threads.push_back(Thread);
+ Thread->Start();
+ }
+}
+
+
+
+
+cBiomeCache::~cBiomeCache()
+{
+ m_IsTerminatingThreads = true;
+ for (cThreads::iterator itr = m_Threads.begin(), end = m_Threads.end(); itr != end; ++itr)
+ {
+ m_evtQueued.Set();
+ }
+ for (cThreads::iterator itr = m_Threads.begin(), end = m_Threads.end(); itr != end; ++itr)
+ {
+ delete *itr;
+ }
+ m_Threads.clear();
+
+ SetSource(NULL);
+}
+
+
+
+
+
+cBiomeSource::eAvailability cBiomeCache::GetBiome(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_Biomes)
+{
+ if (m_Source == NULL)
+ {
+ return baNever;
+ }
+
+ // Look up using the cache:
+ int x = a_ChunkX - m_BaseX;
+ int z = a_ChunkZ - m_BaseZ;
+ if ((x < 0) || (x >= m_Width) || (z < 0) || (z >= m_Height))
+ {
+ // Outside the cached region
+ return baNever;
+ }
+
+ cCSLock Lock(m_CS);
+ cItem * Item = m_Available[x + m_Width * z];
+ if (Item == NULL)
+ {
+ // Item hasn't been processed yet
+ return baLater;
+ }
+ if (Item->m_IsValid)
+ {
+ memcpy(a_Biomes, Item->m_Biomes, sizeof(a_Biomes));
+ return baNow;
+ }
+
+ // Item has been processed, but the underlying source refused to give the data to us
+ return baNever;
+}
+
+
+
+
+
+void cBiomeCache::HintViewArea(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ)
+{
+ cTimer Timer("Cache: HintViewArea");
+
+ if (
+ (a_MinChunkX == m_BaseX) &&
+ (a_MaxChunkX == m_BaseX + m_Width - 1) &&
+ (a_MinChunkZ == m_BaseZ) &&
+ (a_MaxChunkZ == m_BaseZ + m_Height - 1)
+ )
+ {
+ // The same set of parameters, bail out
+ return;
+ }
+
+ if (m_Source != NULL)
+ {
+ m_Source->HintViewArea(a_MinChunkX, a_MaxChunkX, a_MinChunkZ, a_MaxChunkZ);
+ }
+
+ int NewWidth = a_MaxChunkX - a_MinChunkX + 1;
+ int NewHeight = a_MaxChunkZ - a_MinChunkZ + 1;
+
+ // Make a new empty cache table:
+ pItem * NewAvailable = new pItem[NewWidth * NewHeight];
+ for (int i = NewWidth * NewHeight - 1; i >= 0; --i)
+ {
+ NewAvailable[i] = NULL;
+ }
+
+ // Move the common contents of the old table into the new table:
+ cCSLock Lock(m_CS);
+ for (int z = 0; z < NewHeight; z++)
+ {
+ int OldZ = z + a_MinChunkZ - m_BaseZ;
+ if ((OldZ < 0) || (OldZ >= m_Height))
+ {
+ continue;
+ }
+ for (int x = 0; x < NewWidth; x++)
+ {
+ int OldX = x + a_MinChunkX - m_BaseX;
+ if ((OldX < 0) || (OldX >= m_Width))
+ {
+ continue;
+ }
+ NewAvailable[x + NewWidth * z] = m_Available[OldX + m_Width * OldZ];
+ m_Available[OldX + m_Width * OldZ] = NULL;
+ } // for x
+ } // for z
+
+ // All items that aren't common go into the pool:
+ for (int idx = 0, z = 0; z < m_Height; z++)
+ {
+ for (int x = 0; x < m_Width; ++x, ++idx)
+ {
+ if (m_Available[idx] != NULL)
+ {
+ m_Pool.push_back(m_Available[idx]);
+ m_Available[idx] = NULL;
+ }
+ }
+ }
+
+ // Replace the cache table:
+ delete m_Available;
+ m_Available = NewAvailable;
+ m_Width = NewWidth;
+ m_Height = NewHeight;
+ m_BaseX = a_MinChunkX;
+ m_BaseZ = a_MinChunkZ;
+
+ // Remove all items outside the coords:
+ FilterOutItems(m_Queue, a_MinChunkX, a_MaxChunkX, a_MinChunkZ, a_MaxChunkZ);
+
+ // Queue all items from inside the coords into m_Queue:
+ for (int z = 0; z < NewHeight; z++)
+ {
+ for (int x = 0; x < NewWidth; x++)
+ {
+ if (m_Available[x + m_Width * z] != NULL)
+ {
+ // Already calculated, skip
+ continue;
+ }
+
+ if (m_Pool.empty())
+ {
+ m_Pool.push_back(new cItem(x + a_MinChunkX, z + a_MinChunkZ));
+ }
+ ASSERT(!m_Pool.empty());
+ m_Pool.back()->m_ChunkX = x + a_MinChunkX;
+ m_Pool.back()->m_ChunkZ = z + a_MinChunkZ;
+ m_Queue.push_back(m_Pool.back());
+ m_Pool.pop_back();
+ m_evtQueued.Set();
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cBiomeCache::SetSource(cBiomeSource * a_Source)
+{
+ // TODO: Stop all threads, so that they don't use the source anymore!
+
+ delete m_Source;
+ m_Source = a_Source;
+
+ // Invalidate cache contents:
+ cCSLock Lock(m_CS);
+ m_BaseX = MAXINT;
+ m_BaseZ = MAXINT;
+ m_Pool.splice(m_Pool.end(), m_Queue);
+}
+
+
+
+
+
+void cBiomeCache::FilterOutItems(cItems & a_Items, int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ)
+{
+ for (cItems::iterator itr = a_Items.begin(); itr != a_Items.end();)
+ {
+ if (
+ ((*itr)->m_ChunkX < a_MinChunkX) ||
+ ((*itr)->m_ChunkX > a_MaxChunkX) ||
+ ((*itr)->m_ChunkX < a_MinChunkX) ||
+ ((*itr)->m_ChunkX > a_MaxChunkX)
+ )
+ {
+ m_Pool.push_back(*itr);
+ itr = a_Items.erase(itr);
+ }
+ else
+ {
+ ++itr;
+ }
+ }
+}
+
+
+
+
+
+void cBiomeCache::thrProcessQueueItem(void)
+{
+ cItem * Item = NULL;
+ {
+ cCSLock Lock(m_CS);
+ if (m_Queue.empty())
+ {
+ cCSUnlock Unlock(Lock);
+ m_evtQueued.Wait();
+ }
+ if (m_IsTerminatingThreads || m_Queue.empty())
+ {
+ // We've been woken up only to die / spurious wakeup
+ return;
+ }
+ Item = m_Queue.back();
+ m_Queue.pop_back();
+ }
+
+ // Process the item:
+ Item->m_IsValid = (m_Source->GetBiome(Item->m_ChunkX, Item->m_ChunkZ, Item->m_Biomes) == baNow);
+
+ // Store result:
+ cCSLock Lock(m_CS);
+ int x = Item->m_ChunkX - m_BaseX;
+ int z = Item->m_ChunkZ - m_BaseZ;
+ if ((x < 0) || (x >= m_Width) || (z < 0) || (z >= m_Height))
+ {
+ // The cache rectangle has changed under our fingers, drop this chunk
+ return;
+ }
+ m_Available[x + m_Width * z] = Item;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBiomeCache::cItem:
+
+cBiomeCache::cItem::cItem(int a_ChunkX, int a_ChunkZ) :
+ m_ChunkX(a_ChunkX),
+ m_ChunkZ(a_ChunkZ)
+{
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBiomeCache::cThread:
+
+cBiomeCache::cThread::cThread(cBiomeCache & a_Parent) :
+ super("Biome cache thread"),
+ m_Parent(a_Parent)
+{
+}
+
+
+
+
+
+void cBiomeCache::cThread::Execute(void)
+{
+ while (!m_ShouldTerminate && !m_Parent.m_IsTerminatingThreads)
+ {
+ m_Parent.thrProcessQueueItem();
+ }
+}
+
+
+
+
|