-
Notifications
You must be signed in to change notification settings - Fork 51
/
chunkcache.cpp
197 lines (161 loc) · 5.57 KB
/
chunkcache.cpp
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/** Copyright (c) 2013, Sean Kasun */
#include "chunkcache.h"
#include "chunkloader.h"
#if defined(__unix__) || defined(__unix) || defined(unix)
#include <unistd.h>
#elif defined(_WIN32) || defined(WIN32)
#include <windows.h>
#elif __APPLE__
#include <unistd.h>
#include <sys/sysctl.h>
#endif
ChunkCache::ChunkCache() {
const int sizeChunkMax = sizeof(Chunk) + 16 * sizeof(ChunkSection); // all sections contain Blocks
const int sizeChunkTypical = sizeof(Chunk) + 6 * sizeof(ChunkSection); // world generation is average Y=64..128
// default: 10% more than 1920x1200 blocks
int chunks = 10000;
maxcache = chunks;
// try to determine available pysical memory based on operation system we are running on
#if defined(__unix__) || defined(__unix) || defined(unix)
#ifdef _SC_AVPHYS_PAGES
auto pages = sysconf(_SC_AVPHYS_PAGES);
auto page_size = sysconf(_SC_PAGE_SIZE);
quint64 available = (pages*page_size);
chunks = available / sizeChunkMax;
maxcache = available / sizeChunkTypical; // most chunks are less filled with sections
#endif
#elif defined(_WIN32) || defined(WIN32)
MEMORYSTATUSEX status;
status.dwLength = sizeof(status);
GlobalMemoryStatusEx(&status);
DWORDLONG available = qMin(status.ullAvailPhys, status.ullAvailVirtual);
chunks = available / sizeChunkMax;
maxcache = available / sizeChunkTypical; // most chunks are less filled with sections
#elif __APPLE__
uint64_t available;
size_t len = sizeof(available);
sysctlbyname("hw.memsize", &available, &len, NULL, 0);
chunks = available / sizeChunkMax;
maxcache = available / sizeChunkTypical; // most chunks are less filled with sections
#endif
// we start the Cache based on worst case calculation
cache.setMaxCost(chunks);
// determain optimal thread pool size for "loading"
// as this contains disk access, use less than number of cores
int tmax = loaderThreadPool.maxThreadCount();
loaderThreadPool.setMaxThreadCount(tmax / 2);
qRegisterMetaType<QSharedPointer<GeneratedStructure>>("QSharedPointer<GeneratedStructure>");
}
ChunkCache::~ChunkCache() {
loaderThreadPool.waitForDone();
}
ChunkCache& ChunkCache::Instance() {
static ChunkCache singleton;
return singleton;
}
void ChunkCache::clear() {
QThreadPool::globalInstance()->waitForDone();
QMutexLocker guard(&mutex);
cache.clear();
}
void ChunkCache::setPath(QString path) {
if (this->path != path)
clear();
this->path = path;
}
QString ChunkCache::getPath() const {
return path;
}
int ChunkCache::getCacheUsage() const {
return cache.totalCost();
}
int ChunkCache::getCacheMax() const {
return cache.maxCost();
}
int ChunkCache::getMemoryMax() const {
return maxcache;
}
QSharedPointer<Chunk> ChunkCache::fetchCached(int cx, int cz) {
// try to get Chunk from Cache
ChunkID id(cx, cz);
QSharedPointer<Chunk> chunk;
getCached(id, chunk);
return chunk;
}
CacheState ChunkCache::getCached(const ChunkID &id, QSharedPointer<Chunk> &chunk_out)
{
QMutexLocker guard(&mutex);
return getCached_intern(id, chunk_out);
}
CacheState ChunkCache::getCached_intern(const ChunkID &id, QSharedPointer<Chunk> &chunk_out)
{
QSharedPointer<Chunk> * p_chunk = cache[id]; // const operation
if (!p_chunk)
{
return CacheState::uncached;
}
chunk_out = (*p_chunk);
if (!chunk_out)
return CacheState::cached; // cached - but not existing and thus empty
if (!chunk_out->loaded)
return CacheState::uncached_loading;
return CacheState::cached;
}
QSharedPointer<Chunk> ChunkCache::fetch(int cx, int cz) {
// try to get Chunk from Cache
ChunkID id(cx, cz);
QSharedPointer<Chunk> chunk;
const CacheState state = getCached(id, chunk);
if (state == CacheState::cached)
return chunk;
else if (state == CacheState::uncached_loading)
return QSharedPointer<Chunk>(); // already loading, return nullptr
// launch background process to load this chunk
QSharedPointer<Chunk> * p_chunk = new QSharedPointer<Chunk>(new Chunk());
connect(p_chunk->data(), SIGNAL(structureFound(QSharedPointer<GeneratedStructure>)),
this, SLOT (routeStructure(QSharedPointer<GeneratedStructure>)));
{
QMutexLocker guard(&mutex);
cache.insert(id, p_chunk); // non-const operation !
}
ChunkLoader *loader = new ChunkLoader(path, cx, cz);
connect(loader, SIGNAL(loaded(int, int)),
this, SLOT(gotChunk(int, int)));
loaderThreadPool.start(loader);
return QSharedPointer<Chunk>(NULL);
}
QSharedPointer<Chunk> ChunkCache::getChunkSynchronously(const ChunkID& id)
{
QSharedPointer<Chunk> chunk;
bool hasFreeSpaceInCache = false;
{
QMutexLocker guard(&mutex);
hasFreeSpaceInCache = (cache.totalCost() < cache.maxCost() * 0.9);
const CacheState state = getCached_intern(id, chunk);
if (state == CacheState::cached)
return chunk;
}
// sychronously load
chunk = QSharedPointer<Chunk>::create();
if (!ChunkLoader::loadNbt(path, id.getX(), id.getZ(), chunk))
{
return QSharedPointer<Chunk>();
}
if (hasFreeSpaceInCache && chunk->loaded) // only cache in case of lot of memory to not degrade drawing performance
{
QMutexLocker guard(&mutex);
cache.insert(id, new QSharedPointer<Chunk>(chunk));
}
return chunk;
}
void ChunkCache::gotChunk(int cx, int cz) {
emit chunkLoaded(cx, cz);
}
void ChunkCache::routeStructure(QSharedPointer<GeneratedStructure> structure) {
emit structureFound(structure);
}
void ChunkCache::setCacheMaxSize(int chunks) {
QMutexLocker guard(&mutex);
// we never decrease Cache size, and never exceed physical memory
cache.setMaxCost(std::max<int>(cache.maxCost(), std::min<int>(chunks, maxcache)));
}