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"
32 QueuedMeshUpdate::~QueuedMeshUpdate()
41 MeshUpdateQueue::MeshUpdateQueue(Client *client):
44 m_cache_enable_shaders = g_settings->getBool("enable_shaders");
45 m_cache_smooth_lighting = g_settings->getBool("smooth_lighting");
46 m_meshgen_block_cache_size = g_settings->getS32("meshgen_block_cache_size");
49 MeshUpdateQueue::~MeshUpdateQueue()
51 MutexAutoLock lock(m_mutex);
53 for (QueuedMeshUpdate *q : m_queue) {
54 for (auto block : q->map_blocks)
61 bool MeshUpdateQueue::addBlock(Map *map, v3s16 p, bool ack_block_to_server, bool urgent)
63 MapBlock *main_block = map->getBlockNoCreateNoEx(p);
67 MutexAutoLock lock(m_mutex);
70 Mark the block as urgent if requested
76 Find if block is already in queue.
77 If it is, update the data and quit.
79 for (QueuedMeshUpdate *q : m_queue) {
81 // NOTE: We are not adding a new position to the queue, thus
82 // refcount_from_queue stays the same.
83 if(ack_block_to_server)
84 q->ack_block_to_server = true;
85 q->crack_level = m_client->getCrackLevel();
86 q->crack_pos = m_client->getCrackPos();
88 for (std::size_t i = 1; i < q->map_blocks.size(); i++) {
89 if (!q->map_blocks[i]) {
90 MapBlock *block = map->getBlockNoCreateNoEx(q->p + g_26dirs[i - 1]);
93 q->map_blocks[i] = block;
102 Cache the block data (force-update the center block, don't update the
103 neighbors but get them if they aren't already cached)
105 std::vector<MapBlock *> cached_blocks;
106 cached_blocks.reserve(3*3*3);
107 cached_blocks.push_back(main_block);
108 main_block->refGrab();
109 for (v3s16 dp : g_26dirs) {
110 MapBlock *block = map->getBlockNoCreateNoEx(p + dp);
111 cached_blocks.push_back(block);
119 QueuedMeshUpdate *q = new QueuedMeshUpdate;
121 q->ack_block_to_server = ack_block_to_server;
122 q->crack_level = m_client->getCrackLevel();
123 q->crack_pos = m_client->getCrackPos();
125 q->map_blocks = std::move(cached_blocks);
126 m_queue.push_back(q);
131 // Returned pointer must be deleted
132 // Returns NULL if queue is empty
133 QueuedMeshUpdate *MeshUpdateQueue::pop()
135 QueuedMeshUpdate *result = NULL;
137 MutexAutoLock lock(m_mutex);
139 bool must_be_urgent = !m_urgents.empty();
140 for (std::vector<QueuedMeshUpdate*>::iterator i = m_queue.begin();
141 i != m_queue.end(); ++i) {
142 QueuedMeshUpdate *q = *i;
143 if (must_be_urgent && m_urgents.count(q->p) == 0)
145 // Make sure no two threads are processing the same mapblock, as that causes racing conditions
146 if (m_inflight_blocks.find(q->p) != m_inflight_blocks.end())
149 m_urgents.erase(q->p);
150 m_inflight_blocks.insert(q->p);
157 fillDataFromMapBlocks(result);
162 void MeshUpdateQueue::done(v3s16 pos)
164 MutexAutoLock lock(m_mutex);
165 m_inflight_blocks.erase(pos);
169 void MeshUpdateQueue::fillDataFromMapBlocks(QueuedMeshUpdate *q)
171 MeshMakeData *data = new MeshMakeData(m_client, m_cache_enable_shaders);
174 data->fillBlockDataBegin(q->p);
176 for (MapBlock *block : q->map_blocks)
178 data->fillBlockData(block->getPos() - q->p, block->getData());
180 data->setCrack(q->crack_level, q->crack_pos);
181 data->setSmoothLighting(m_cache_smooth_lighting);
185 MeshUpdateWorkerThread
188 MeshUpdateWorkerThread::MeshUpdateWorkerThread(MeshUpdateQueue *queue_in, MeshUpdateManager *manager, v3s16 *camera_offset) :
189 UpdateThread("Mesh"), m_queue_in(queue_in), m_manager(manager), m_camera_offset(camera_offset)
191 m_generation_interval = g_settings->getU16("mesh_generation_interval");
192 m_generation_interval = rangelim(m_generation_interval, 0, 50);
195 void MeshUpdateWorkerThread::doUpdate()
198 while ((q = m_queue_in->pop())) {
199 if (m_generation_interval)
200 sleep_ms(m_generation_interval);
201 ScopeProfiler sp(g_profiler, "Client: Mesh making (sum)");
203 MapBlockMesh *mesh_new = new MapBlockMesh(q->data, *m_camera_offset);
210 r.solid_sides = get_solid_sides(q->data);
211 r.ack_block_to_server = q->ack_block_to_server;
212 r.urgent = q->urgent;
213 r.map_blocks = q->map_blocks;
215 m_manager->putResult(r);
216 m_queue_in->done(q->p);
225 MeshUpdateManager::MeshUpdateManager(Client *client):
228 int number_of_threads = rangelim(g_settings->getS32("mesh_generation_threads"), 0, 8);
230 // Automatically use 33% of the system cores for mesh generation, max 4
231 if (number_of_threads == 0)
232 number_of_threads = MYMIN(4, Thread::getNumberOfProcessors() / 3);
234 // use at least one thread
235 number_of_threads = MYMAX(1, number_of_threads);
236 infostream << "MeshUpdateManager: using " << number_of_threads << " threads" << std::endl;
238 for (int i = 0; i < number_of_threads; i++)
239 m_workers.push_back(std::make_unique<MeshUpdateWorkerThread>(&m_queue_in, this, &m_camera_offset));
242 void MeshUpdateManager::updateBlock(Map *map, v3s16 p, bool ack_block_to_server,
243 bool urgent, bool update_neighbors)
245 static thread_local const bool many_neighbors =
246 g_settings->getBool("smooth_lighting")
247 && !g_settings->getFlag("performance_tradeoffs");
248 if (!m_queue_in.addBlock(map, p, ack_block_to_server, urgent)) {
249 warningstream << "Update requested for non-existent block at ("
250 << p.X << ", " << p.Y << ", " << p.Z << ")" << std::endl;
253 if (update_neighbors) {
254 if (many_neighbors) {
255 for (v3s16 dp : g_26dirs)
256 m_queue_in.addBlock(map, p + dp, false, urgent);
258 for (v3s16 dp : g_6dirs)
259 m_queue_in.addBlock(map, p + dp, false, urgent);
265 void MeshUpdateManager::putResult(const MeshUpdateResult &result)
268 m_queue_out_urgent.push_back(result);
270 m_queue_out.push_back(result);
273 bool MeshUpdateManager::getNextResult(MeshUpdateResult &r)
275 if (!m_queue_out_urgent.empty()) {
276 r = m_queue_out_urgent.pop_frontNoEx();
280 if (!m_queue_out.empty()) {
281 r = m_queue_out.pop_frontNoEx();
288 void MeshUpdateManager::deferUpdate()
290 for (auto &thread : m_workers)
291 thread->deferUpdate();
294 void MeshUpdateManager::start()
296 for (auto &thread: m_workers)
300 void MeshUpdateManager::stop()
302 for (auto &thread: m_workers)
306 void MeshUpdateManager::wait()
308 for (auto &thread: m_workers)
312 bool MeshUpdateManager::isRunning()
314 for (auto &thread: m_workers)
315 if (thread->isRunning())