1 /*
2 Minetest
3 Copyright (C) 2013, 2017 celeron55, Perttu Ahola <celeron55@gmail.com>
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14 
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 
20 #include "mesh_generator_thread.h"
21 #include "settings.h"
22 #include "profiler.h"
23 #include "client.h"
24 #include "mapblock.h"
25 #include "map.h"
26 
27 /*
28 	CachedMapBlockData
29 */
30 
~CachedMapBlockData()31 CachedMapBlockData::~CachedMapBlockData()
32 {
33 	assert(refcount_from_queue == 0);
34 
35 	delete[] data;
36 }
37 
38 /*
39 	QueuedMeshUpdate
40 */
41 
~QueuedMeshUpdate()42 QueuedMeshUpdate::~QueuedMeshUpdate()
43 {
44 	delete data;
45 }
46 
47 /*
48 	MeshUpdateQueue
49 */
50 
MeshUpdateQueue(Client * client)51 MeshUpdateQueue::MeshUpdateQueue(Client *client):
52 	m_client(client)
53 {
54 	m_cache_enable_shaders = g_settings->getBool("enable_shaders");
55 	m_cache_smooth_lighting = g_settings->getBool("smooth_lighting");
56 	m_meshgen_block_cache_size = g_settings->getS32("meshgen_block_cache_size");
57 }
58 
~MeshUpdateQueue()59 MeshUpdateQueue::~MeshUpdateQueue()
60 {
61 	MutexAutoLock lock(m_mutex);
62 
63 	for (auto &i : m_cache) {
64 		delete i.second;
65 	}
66 
67 	for (QueuedMeshUpdate *q : m_queue) {
68 		delete q;
69 	}
70 }
71 
addBlock(Map * map,v3s16 p,bool ack_block_to_server,bool urgent)72 void MeshUpdateQueue::addBlock(Map *map, v3s16 p, bool ack_block_to_server, bool urgent)
73 {
74 	MutexAutoLock lock(m_mutex);
75 
76 	cleanupCache();
77 
78 	/*
79 		Cache the block data (force-update the center block, don't update the
80 		neighbors but get them if they aren't already cached)
81 	*/
82 	std::vector<CachedMapBlockData*> cached_blocks;
83 	size_t cache_hit_counter = 0;
84 	cached_blocks.reserve(3*3*3);
85 	v3s16 dp;
86 	for (dp.X = -1; dp.X <= 1; dp.X++)
87 	for (dp.Y = -1; dp.Y <= 1; dp.Y++)
88 	for (dp.Z = -1; dp.Z <= 1; dp.Z++) {
89 		v3s16 p1 = p + dp;
90 		CachedMapBlockData *cached_block;
91 		if (dp == v3s16(0, 0, 0))
92 			cached_block = cacheBlock(map, p1, FORCE_UPDATE);
93 		else
94 			cached_block = cacheBlock(map, p1, SKIP_UPDATE_IF_ALREADY_CACHED,
95 					&cache_hit_counter);
96 		cached_blocks.push_back(cached_block);
97 	}
98 	g_profiler->avg("MeshUpdateQueue: MapBlocks from cache [%]",
99 			100.0f * cache_hit_counter / cached_blocks.size());
100 
101 	/*
102 		Mark the block as urgent if requested
103 	*/
104 	if (urgent)
105 		m_urgents.insert(p);
106 
107 	/*
108 		Find if block is already in queue.
109 		If it is, update the data and quit.
110 	*/
111 	for (QueuedMeshUpdate *q : m_queue) {
112 		if (q->p == p) {
113 			// NOTE: We are not adding a new position to the queue, thus
114 			//       refcount_from_queue stays the same.
115 			if(ack_block_to_server)
116 				q->ack_block_to_server = true;
117 			q->crack_level = m_client->getCrackLevel();
118 			q->crack_pos = m_client->getCrackPos();
119 			return;
120 		}
121 	}
122 
123 	/*
124 		Add the block
125 	*/
126 	QueuedMeshUpdate *q = new QueuedMeshUpdate;
127 	q->p = p;
128 	q->ack_block_to_server = ack_block_to_server;
129 	q->crack_level = m_client->getCrackLevel();
130 	q->crack_pos = m_client->getCrackPos();
131 	m_queue.push_back(q);
132 
133 	// This queue entry is a new reference to the cached blocks
134 	for (CachedMapBlockData *cached_block : cached_blocks) {
135 		cached_block->refcount_from_queue++;
136 	}
137 }
138 
139 // Returned pointer must be deleted
140 // Returns NULL if queue is empty
pop()141 QueuedMeshUpdate *MeshUpdateQueue::pop()
142 {
143 	MutexAutoLock lock(m_mutex);
144 
145 	bool must_be_urgent = !m_urgents.empty();
146 	for (std::vector<QueuedMeshUpdate*>::iterator i = m_queue.begin();
147 			i != m_queue.end(); ++i) {
148 		QueuedMeshUpdate *q = *i;
149 		if(must_be_urgent && m_urgents.count(q->p) == 0)
150 			continue;
151 		m_queue.erase(i);
152 		m_urgents.erase(q->p);
153 		fillDataFromMapBlockCache(q);
154 		return q;
155 	}
156 	return NULL;
157 }
158 
cacheBlock(Map * map,v3s16 p,UpdateMode mode,size_t * cache_hit_counter)159 CachedMapBlockData* MeshUpdateQueue::cacheBlock(Map *map, v3s16 p, UpdateMode mode,
160 			size_t *cache_hit_counter)
161 {
162 	CachedMapBlockData *cached_block = nullptr;
163 	std::map<v3s16, CachedMapBlockData*>::iterator it =
164 			m_cache.find(p);
165 
166 	if (it != m_cache.end()) {
167 		cached_block = it->second;
168 
169 		if (mode == SKIP_UPDATE_IF_ALREADY_CACHED) {
170 			if (cache_hit_counter)
171 				(*cache_hit_counter)++;
172 			return cached_block;
173 		}
174 	}
175 
176 	if (!cached_block) {
177 		// Not yet in cache
178 		cached_block = new CachedMapBlockData();
179 		m_cache[p] = cached_block;
180 	}
181 
182 	MapBlock *b = map->getBlockNoCreateNoEx(p);
183 	if (b) {
184 		if (!cached_block->data)
185 			cached_block->data =
186 					new MapNode[MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE];
187 		memcpy(cached_block->data, b->getData(),
188 				MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE * sizeof(MapNode));
189 	} else {
190 		delete[] cached_block->data;
191 		cached_block->data = nullptr;
192 	}
193 	return cached_block;
194 }
195 
getCachedBlock(const v3s16 & p)196 CachedMapBlockData* MeshUpdateQueue::getCachedBlock(const v3s16 &p)
197 {
198 	std::map<v3s16, CachedMapBlockData*>::iterator it = m_cache.find(p);
199 	if (it != m_cache.end()) {
200 		return it->second;
201 	}
202 	return NULL;
203 }
204 
fillDataFromMapBlockCache(QueuedMeshUpdate * q)205 void MeshUpdateQueue::fillDataFromMapBlockCache(QueuedMeshUpdate *q)
206 {
207 	MeshMakeData *data = new MeshMakeData(m_client, m_cache_enable_shaders);
208 	q->data = data;
209 
210 	data->fillBlockDataBegin(q->p);
211 
212 	std::time_t t_now = std::time(0);
213 
214 	// Collect data for 3*3*3 blocks from cache
215 	v3s16 dp;
216 	for (dp.X = -1; dp.X <= 1; dp.X++)
217 	for (dp.Y = -1; dp.Y <= 1; dp.Y++)
218 	for (dp.Z = -1; dp.Z <= 1; dp.Z++) {
219 		v3s16 p = q->p + dp;
220 		CachedMapBlockData *cached_block = getCachedBlock(p);
221 		if (cached_block) {
222 			cached_block->refcount_from_queue--;
223 			cached_block->last_used_timestamp = t_now;
224 			if (cached_block->data)
225 				data->fillBlockData(dp, cached_block->data);
226 		}
227 	}
228 
229 	data->setCrack(q->crack_level, q->crack_pos);
230 	data->setSmoothLighting(m_cache_smooth_lighting);
231 }
232 
cleanupCache()233 void MeshUpdateQueue::cleanupCache()
234 {
235 	const int mapblock_kB = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE *
236 			sizeof(MapNode) / 1000;
237 	g_profiler->avg("MeshUpdateQueue MapBlock cache size kB",
238 			mapblock_kB * m_cache.size());
239 
240 	// The cache size is kept roughly below cache_soft_max_size, not letting
241 	// anything get older than cache_seconds_max or deleted before 2 seconds.
242 	const int cache_seconds_max = 10;
243 	const int cache_soft_max_size = m_meshgen_block_cache_size * 1000 / mapblock_kB;
244 	int cache_seconds = MYMAX(2, cache_seconds_max -
245 			m_cache.size() / (cache_soft_max_size / cache_seconds_max));
246 
247 	int t_now = time(0);
248 
249 	for (std::map<v3s16, CachedMapBlockData*>::iterator it = m_cache.begin();
250 			it != m_cache.end(); ) {
251 		CachedMapBlockData *cached_block = it->second;
252 		if (cached_block->refcount_from_queue == 0 &&
253 				cached_block->last_used_timestamp < t_now - cache_seconds) {
254 			m_cache.erase(it++);
255 			delete cached_block;
256 		} else {
257 			++it;
258 		}
259 	}
260 }
261 
262 /*
263 	MeshUpdateThread
264 */
265 
MeshUpdateThread(Client * client)266 MeshUpdateThread::MeshUpdateThread(Client *client):
267 	UpdateThread("Mesh"),
268 	m_queue_in(client)
269 {
270 	m_generation_interval = g_settings->getU16("mesh_generation_interval");
271 	m_generation_interval = rangelim(m_generation_interval, 0, 50);
272 }
273 
updateBlock(Map * map,v3s16 p,bool ack_block_to_server,bool urgent)274 void MeshUpdateThread::updateBlock(Map *map, v3s16 p, bool ack_block_to_server,
275 		bool urgent)
276 {
277 	// Allow the MeshUpdateQueue to do whatever it wants
278 	m_queue_in.addBlock(map, p, ack_block_to_server, urgent);
279 	deferUpdate();
280 }
281 
doUpdate()282 void MeshUpdateThread::doUpdate()
283 {
284 	QueuedMeshUpdate *q;
285 	while ((q = m_queue_in.pop())) {
286 		if (m_generation_interval)
287 			sleep_ms(m_generation_interval);
288 		ScopeProfiler sp(g_profiler, "Client: Mesh making (sum)");
289 
290 		MapBlockMesh *mesh_new = new MapBlockMesh(q->data, m_camera_offset);
291 
292 		MeshUpdateResult r;
293 		r.p = q->p;
294 		r.mesh = mesh_new;
295 		r.ack_block_to_server = q->ack_block_to_server;
296 
297 		m_queue_out.push_back(r);
298 
299 		delete q;
300 	}
301 }
302