3 Copyright (C) 2013, 2017 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
20 #include "mesh_generator_thread.h"
26 #include "util/directiontables.h"
28 static class BlockPlaceholder {
30 MapNode data[MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE];
34 for (std::size_t i = 0; i < MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE; i++)
35 data[i] = MapNode(CONTENT_IGNORE);
43 QueuedMeshUpdate::~QueuedMeshUpdate()
52 MeshUpdateQueue::MeshUpdateQueue(Client *client):
55 m_cache_enable_shaders = g_settings->getBool("enable_shaders");
56 m_cache_smooth_lighting = g_settings->getBool("smooth_lighting");
57 m_meshgen_block_cache_size = g_settings->getS32("meshgen_block_cache_size");
60 MeshUpdateQueue::~MeshUpdateQueue()
62 MutexAutoLock lock(m_mutex);
64 for (QueuedMeshUpdate *q : m_queue) {
65 for (auto block : q->map_blocks)
72 bool MeshUpdateQueue::addBlock(Map *map, v3s16 p, bool ack_block_to_server, bool urgent)
74 MapBlock *main_block = map->getBlockNoCreateNoEx(p);
78 MutexAutoLock lock(m_mutex);
80 // Mesh is placed at even positions at all coordinates
81 // (every 8-th block) and will cover 8 blocks
82 v3s16 mesh_position(p.X & ~1, p.Y & ~1, p.Z & ~1);
84 Mark the block as urgent if requested
87 m_urgents.insert(mesh_position);
90 Find if block is already in queue.
91 If it is, update the data and quit.
93 for (QueuedMeshUpdate *q : m_queue) {
94 if (q->p == mesh_position) {
95 // NOTE: We are not adding a new position to the queue, thus
96 // refcount_from_queue stays the same.
97 if(ack_block_to_server)
98 q->ack_list.push_back(p);
99 q->crack_level = m_client->getCrackLevel();
100 q->crack_pos = m_client->getCrackPos();
102 for (std::size_t i = 0; i < q->map_blocks.size(); i++) {
103 if (!q->map_blocks[i]) {
104 MapBlock *block = map->getBlockNoCreateNoEx(q->p + g_64dirs[i]);
107 q->map_blocks[i] = block;
116 Make a list of blocks necessary for mesh generation and lock the blocks in memory.
118 std::vector<MapBlock *> map_blocks;
119 map_blocks.reserve(4*4*4);
120 for (v3s16 dp : g_64dirs) {
121 MapBlock *block = map->getBlockNoCreateNoEx(mesh_position + dp);
122 map_blocks.push_back(block);
130 QueuedMeshUpdate *q = new QueuedMeshUpdate;
131 q->p = mesh_position;
132 if(ack_block_to_server)
133 q->ack_list.push_back(p);
134 q->crack_level = m_client->getCrackLevel();
135 q->crack_pos = m_client->getCrackPos();
137 q->map_blocks = std::move(map_blocks);
138 m_queue.push_back(q);
143 // Returned pointer must be deleted
144 // Returns NULL if queue is empty
145 QueuedMeshUpdate *MeshUpdateQueue::pop()
147 QueuedMeshUpdate *result = NULL;
149 MutexAutoLock lock(m_mutex);
151 bool must_be_urgent = !m_urgents.empty();
152 for (std::vector<QueuedMeshUpdate*>::iterator i = m_queue.begin();
153 i != m_queue.end(); ++i) {
154 QueuedMeshUpdate *q = *i;
155 if (must_be_urgent && m_urgents.count(q->p) == 0)
157 // Make sure no two threads are processing the same mapblock, as that causes racing conditions
158 if (m_inflight_blocks.find(q->p) != m_inflight_blocks.end())
161 m_urgents.erase(q->p);
162 m_inflight_blocks.insert(q->p);
169 fillDataFromMapBlocks(result);
174 void MeshUpdateQueue::done(v3s16 pos)
176 MutexAutoLock lock(m_mutex);
177 m_inflight_blocks.erase(pos);
181 void MeshUpdateQueue::fillDataFromMapBlocks(QueuedMeshUpdate *q)
183 MeshMakeData *data = new MeshMakeData(m_client, m_cache_enable_shaders);
185 data->side_length = 2 * MAP_BLOCKSIZE;
187 data->fillBlockDataBegin(q->p);
189 for (std::size_t i = 0; i < 64; i++) {
190 MapBlock *block = q->map_blocks[i];
191 data->fillBlockData(g_64dirs[i], block ? block->getData() : block_placeholder.data);
194 data->setCrack(q->crack_level, q->crack_pos);
195 data->setSmoothLighting(m_cache_smooth_lighting);
199 MeshUpdateWorkerThread
202 MeshUpdateWorkerThread::MeshUpdateWorkerThread(MeshUpdateQueue *queue_in, MeshUpdateManager *manager, v3s16 *camera_offset) :
203 UpdateThread("Mesh"), m_queue_in(queue_in), m_manager(manager), m_camera_offset(camera_offset)
205 m_generation_interval = g_settings->getU16("mesh_generation_interval");
206 m_generation_interval = rangelim(m_generation_interval, 0, 50);
209 void MeshUpdateWorkerThread::doUpdate()
212 while ((q = m_queue_in->pop())) {
213 if (m_generation_interval)
214 sleep_ms(m_generation_interval);
215 ScopeProfiler sp(g_profiler, "Client: Mesh making (sum)");
217 MapBlockMesh *mesh_new = new MapBlockMesh(q->data, *m_camera_offset);
224 r.solid_sides = get_solid_sides(q->data);
225 r.ack_list = std::move(q->ack_list);
226 r.urgent = q->urgent;
227 r.map_blocks = q->map_blocks;
229 m_manager->putResult(r);
230 m_queue_in->done(q->p);
239 MeshUpdateManager::MeshUpdateManager(Client *client):
242 int number_of_threads = rangelim(g_settings->getS32("mesh_generation_threads"), 0, 8);
244 // Automatically use 33% of the system cores for mesh generation, max 4
245 if (number_of_threads == 0)
246 number_of_threads = MYMIN(4, Thread::getNumberOfProcessors() / 3);
248 // use at least one thread
249 number_of_threads = MYMAX(1, number_of_threads);
250 infostream << "MeshUpdateManager: using " << number_of_threads << " threads" << std::endl;
252 for (int i = 0; i < number_of_threads; i++)
253 m_workers.push_back(std::make_unique<MeshUpdateWorkerThread>(&m_queue_in, this, &m_camera_offset));
256 void MeshUpdateManager::updateBlock(Map *map, v3s16 p, bool ack_block_to_server,
257 bool urgent, bool update_neighbors)
259 static thread_local const bool many_neighbors =
260 g_settings->getBool("smooth_lighting")
261 && !g_settings->getFlag("performance_tradeoffs");
262 if (!m_queue_in.addBlock(map, p, ack_block_to_server, urgent)) {
263 warningstream << "Update requested for non-existent block at ("
264 << p.X << ", " << p.Y << ", " << p.Z << ")" << std::endl;
267 if (update_neighbors) {
268 if (many_neighbors) {
269 for (v3s16 dp : g_26dirs)
270 m_queue_in.addBlock(map, p + dp, false, urgent);
272 for (v3s16 dp : g_6dirs)
273 m_queue_in.addBlock(map, p + dp, false, urgent);
279 void MeshUpdateManager::putResult(const MeshUpdateResult &result)
282 m_queue_out_urgent.push_back(result);
284 m_queue_out.push_back(result);
287 bool MeshUpdateManager::getNextResult(MeshUpdateResult &r)
289 if (!m_queue_out_urgent.empty()) {
290 r = m_queue_out_urgent.pop_frontNoEx();
294 if (!m_queue_out.empty()) {
295 r = m_queue_out.pop_frontNoEx();
302 void MeshUpdateManager::deferUpdate()
304 for (auto &thread : m_workers)
305 thread->deferUpdate();
308 void MeshUpdateManager::start()
310 for (auto &thread: m_workers)
314 void MeshUpdateManager::stop()
316 for (auto &thread: m_workers)
320 void MeshUpdateManager::wait()
322 for (auto &thread: m_workers)
326 bool MeshUpdateManager::isRunning()
328 for (auto &thread: m_workers)
329 if (thread->isRunning())