1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
#include "Globals.h"
#include "RegionCache.h"
#include <QMutexLocker>
#include <QThreadPool>
#include "ChunkSource.h"
#include "RegionLoader.h"
#include "Region.h"
RegionCache::RegionCache(QObject * parent) :
super(parent)
{
m_Cache.setMaxCost(1024 * 1024 * 1024); // 1 GiB of memory for the cache
}
RegionPtr RegionCache::fetch(int a_RegionX, int a_RegionZ)
{
// Retrieve from the cache:
quint32 hash = getRegionHash(a_RegionX, a_RegionZ);
RegionPtr * res;
{
QMutexLocker lock(&m_Mtx);
res = m_Cache[hash];
// If succesful and region loaded, return the retrieved value:
if ((res != nullptr) && (*res)->isValid())
{
return *res;
}
}
// If the region is in cache but not valid, it means it has been already queued for rendering, do nothing now:
if (res != nullptr)
{
return RegionPtr(nullptr);
}
// There's no such item in the cache, create it now:
try
{
res = new RegionPtr(new Region);
}
catch (const std::bad_alloc &)
{
/* Allocation failed (32-bit process hit the 2 GiB barrier?)
This may happen even with the cache set to 1 GiB, because it contains shared ptrs and so they may be
held by another place in the code even when they are removed from cache.
*/
return RegionPtr(nullptr);
}
if (res == nullptr)
{
return RegionPtr(nullptr);
}
{
QMutexLocker lock(&m_Mtx);
m_Cache.insert(hash, res, sizeof(Region));
}
// Queue the region for rendering:
queueRegionRender(a_RegionX, a_RegionZ, *res);
// Return failure, the region is not yet rendered:
return RegionPtr(nullptr);
}
void RegionCache::setChunkSource(std::shared_ptr<ChunkSource> a_ChunkSource)
{
// Replace the chunk source:
m_ChunkSource = a_ChunkSource;
// Clear the cache:
QMutexLocker lock(&m_Mtx);
m_Cache.clear();
}
void RegionCache::reload()
{
assert(m_ChunkSource.get() != nullptr);
// Reload the chunk source:
m_ChunkSource->reload();
// Clear the cache:
QMutexLocker lock(&m_Mtx);
m_Cache.clear();
}
void RegionCache::gotRegion(int a_RegionX, int a_RegionZ)
{
emit regionAvailable(a_RegionX, a_RegionZ);
}
quint32 RegionCache::getRegionHash(int a_RegionX, int a_RegionZ)
{
// Simply join the two coords into a single int
// The coords will never be larger than 16-bits, so we can do this safely
return (((static_cast<quint32>(a_RegionX) & 0xffff) << 16) | (static_cast<quint32>(a_RegionZ) & 0xffff));
}
void RegionCache::queueRegionRender(int a_RegionX, int a_RegionZ, RegionPtr & a_Region)
{
// Create a new loader task:
RegionLoader * loader = new RegionLoader(a_RegionX, a_RegionZ, a_Region, m_ChunkSource);
connect(loader, SIGNAL(loaded(int, int)), this, SLOT(gotRegion(int, int)));
QThreadPool::globalInstance()->start(loader);
}
|