]> git.lizzy.rs Git - minetest.git/blobdiff - src/client/mesh_generator_thread.cpp
8x block meshes (#13133)
[minetest.git] / src / client / mesh_generator_thread.cpp
index 345f44eb9f9eb48d93c8b322d438373a6bbf84e6..6d1fe2bda1a588b74f8e995f561b3f905b99608b 100644 (file)
@@ -25,17 +25,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "map.h"
 #include "util/directiontables.h"
 
-/*
-       CachedMapBlockData
-*/
-
-CachedMapBlockData::~CachedMapBlockData()
-{
-       assert(refcount_from_queue == 0);
+static class BlockPlaceholder {
+public:
+       MapNode data[MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE];
 
-       delete[] data;
-}
+       BlockPlaceholder()
+       {
+               for (std::size_t i = 0; i < MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE; i++)
+                       data[i] = MapNode(CONTENT_IGNORE);
+       }
 
+} block_placeholder;
 /*
        QueuedMeshUpdate
 */
@@ -50,8 +50,7 @@ QueuedMeshUpdate::~QueuedMeshUpdate()
 */
 
 MeshUpdateQueue::MeshUpdateQueue(Client *client):
-       m_client(client),
-       m_next_cache_cleanup(0)
+       m_client(client)
 {
        m_cache_enable_shaders = g_settings->getBool("enable_shaders");
        m_cache_smooth_lighting = g_settings->getBool("smooth_lighting");
@@ -62,77 +61,82 @@ MeshUpdateQueue::~MeshUpdateQueue()
 {
        MutexAutoLock lock(m_mutex);
 
-       for (auto &i : m_cache) {
-               delete i.second;
-       }
-
        for (QueuedMeshUpdate *q : m_queue) {
+               for (auto block : q->map_blocks)
+                       if (block)
+                               block->refDrop();
                delete q;
        }
 }
 
 bool MeshUpdateQueue::addBlock(Map *map, v3s16 p, bool ack_block_to_server, bool urgent)
 {
-       MutexAutoLock lock(m_mutex);
+       MapBlock *main_block = map->getBlockNoCreateNoEx(p);
+       if (!main_block)
+               return false;
 
-       cleanupCache();
-
-       /*
-               Cache the block data (force-update the center block, don't update the
-               neighbors but get them if they aren't already cached)
-       */
-       std::vector<CachedMapBlockData*> cached_blocks;
-       size_t cache_hit_counter = 0;
-       CachedMapBlockData *cached_block = cacheBlock(map, p, FORCE_UPDATE);
-       if (!cached_block->data)
-               return false; // nothing to update
-       cached_blocks.reserve(3*3*3);
-       cached_blocks.push_back(cached_block);
-       for (v3s16 dp : g_26dirs)
-               cached_blocks.push_back(cacheBlock(map, p + dp,
-                               SKIP_UPDATE_IF_ALREADY_CACHED,
-                               &cache_hit_counter));
-       g_profiler->avg("MeshUpdateQueue: MapBlocks from cache [%]",
-                       100.0f * cache_hit_counter / cached_blocks.size());
+       MutexAutoLock lock(m_mutex);
 
+       // Mesh is placed at even positions at all coordinates
+       // (every 8-th block) and will cover 8 blocks
+       v3s16 mesh_position(p.X & ~1, p.Y & ~1, p.Z & ~1);
        /*
                Mark the block as urgent if requested
        */
        if (urgent)
-               m_urgents.insert(p);
+               m_urgents.insert(mesh_position);
 
        /*
                Find if block is already in queue.
                If it is, update the data and quit.
        */
        for (QueuedMeshUpdate *q : m_queue) {
-               if (q->p == p) {
+               if (q->p == mesh_position) {
                        // NOTE: We are not adding a new position to the queue, thus
                        //       refcount_from_queue stays the same.
                        if(ack_block_to_server)
-                               q->ack_block_to_server = true;
+                               q->ack_list.push_back(p);
                        q->crack_level = m_client->getCrackLevel();
                        q->crack_pos = m_client->getCrackPos();
                        q->urgent |= urgent;
+                       for (std::size_t i = 0; i < q->map_blocks.size(); i++) {
+                               if (!q->map_blocks[i]) {
+                                       MapBlock *block = map->getBlockNoCreateNoEx(q->p + g_64dirs[i]);
+                                       if (block) {
+                                               block->refGrab();
+                                               q->map_blocks[i] = block;
+                                       }
+                               }
+                       }
                        return true;
                }
        }
 
+       /*
+               Make a list of blocks necessary for mesh generation and lock the blocks in memory.
+       */
+       std::vector<MapBlock *> map_blocks;
+       map_blocks.reserve(4*4*4);
+       for (v3s16 dp : g_64dirs) {
+               MapBlock *block = map->getBlockNoCreateNoEx(mesh_position + dp);
+               map_blocks.push_back(block);
+               if (block)
+                       block->refGrab();
+       }
+
        /*
                Add the block
        */
        QueuedMeshUpdate *q = new QueuedMeshUpdate;
-       q->p = p;
-       q->ack_block_to_server = ack_block_to_server;
+       q->p = mesh_position;
+       if(ack_block_to_server)
+               q->ack_list.push_back(p);
        q->crack_level = m_client->getCrackLevel();
        q->crack_pos = m_client->getCrackPos();
        q->urgent = urgent;
+       q->map_blocks = std::move(map_blocks);
        m_queue.push_back(q);
 
-       // This queue entry is a new reference to the cached blocks
-       for (CachedMapBlockData *cached_block : cached_blocks) {
-               cached_block->refcount_from_queue++;
-       }
        return true;
 }
 
@@ -140,24 +144,31 @@ bool MeshUpdateQueue::addBlock(Map *map, v3s16 p, bool ack_block_to_server, bool
 // Returns NULL if queue is empty
 QueuedMeshUpdate *MeshUpdateQueue::pop()
 {
-       MutexAutoLock lock(m_mutex);
-
-       bool must_be_urgent = !m_urgents.empty();
-       for (std::vector<QueuedMeshUpdate*>::iterator i = m_queue.begin();
-                       i != m_queue.end(); ++i) {
-               QueuedMeshUpdate *q = *i;
-               if (must_be_urgent && m_urgents.count(q->p) == 0)
-                       continue;
-               // Make sure no two threads are processing the same mapblock, as that causes racing conditions
-               if (m_inflight_blocks.find(q->p) != m_inflight_blocks.end())
-                       continue;
-               m_queue.erase(i);
-               m_urgents.erase(q->p);
-               m_inflight_blocks.insert(q->p);
-               fillDataFromMapBlockCache(q);
-               return q;
+       QueuedMeshUpdate *result = NULL;
+       {
+               MutexAutoLock lock(m_mutex);
+
+               bool must_be_urgent = !m_urgents.empty();
+               for (std::vector<QueuedMeshUpdate*>::iterator i = m_queue.begin();
+                               i != m_queue.end(); ++i) {
+                       QueuedMeshUpdate *q = *i;
+                       if (must_be_urgent && m_urgents.count(q->p) == 0)
+                               continue;
+                       // Make sure no two threads are processing the same mapblock, as that causes racing conditions
+                       if (m_inflight_blocks.find(q->p) != m_inflight_blocks.end())
+                               continue;
+                       m_queue.erase(i);
+                       m_urgents.erase(q->p);
+                       m_inflight_blocks.insert(q->p);
+                       result = q;
+                       break;
+               }
        }
-       return NULL;
+
+       if (result)
+               fillDataFromMapBlocks(result);
+
+       return result;
 }
 
 void MeshUpdateQueue::done(v3s16 pos)
@@ -166,113 +177,24 @@ void MeshUpdateQueue::done(v3s16 pos)
        m_inflight_blocks.erase(pos);
 }
 
-CachedMapBlockData* MeshUpdateQueue::cacheBlock(Map *map, v3s16 p, UpdateMode mode,
-                       size_t *cache_hit_counter)
-{
-       CachedMapBlockData *cached_block = nullptr;
-       auto it = m_cache.find(p);
-
-       if (it != m_cache.end()) {
-               cached_block = it->second;
-
-               if (mode == SKIP_UPDATE_IF_ALREADY_CACHED) {
-                       if (cache_hit_counter)
-                               (*cache_hit_counter)++;
-                       return cached_block;
-               }
-       }
-
-       if (!cached_block) {
-               // Not yet in cache
-               cached_block = new CachedMapBlockData();
-               m_cache[p] = cached_block;
-       }
 
-       MapBlock *b = map->getBlockNoCreateNoEx(p);
-       if (b) {
-               if (!cached_block->data)
-                       cached_block->data =
-                                       new MapNode[MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE];
-               memcpy(cached_block->data, b->getData(),
-                               MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE * sizeof(MapNode));
-       } else {
-               delete[] cached_block->data;
-               cached_block->data = nullptr;
-       }
-       return cached_block;
-}
-
-CachedMapBlockData* MeshUpdateQueue::getCachedBlock(const v3s16 &p)
-{
-       auto it = m_cache.find(p);
-       if (it != m_cache.end()) {
-               return it->second;
-       }
-       return NULL;
-}
-
-void MeshUpdateQueue::fillDataFromMapBlockCache(QueuedMeshUpdate *q)
+void MeshUpdateQueue::fillDataFromMapBlocks(QueuedMeshUpdate *q)
 {
        MeshMakeData *data = new MeshMakeData(m_client, m_cache_enable_shaders);
        q->data = data;
+       data->side_length = 2 * MAP_BLOCKSIZE;
 
        data->fillBlockDataBegin(q->p);
 
-       std::time_t t_now = std::time(0);
-
-       // Collect data for 3*3*3 blocks from cache
-       for (v3s16 dp : g_27dirs) {
-               v3s16 p = q->p + dp;
-               CachedMapBlockData *cached_block = getCachedBlock(p);
-               if (cached_block) {
-                       cached_block->refcount_from_queue--;
-                       cached_block->last_used_timestamp = t_now;
-                       if (cached_block->data)
-                               data->fillBlockData(dp, cached_block->data);
-               }
+       for (std::size_t i = 0; i < 64; i++) {
+               MapBlock *block = q->map_blocks[i];
+               data->fillBlockData(g_64dirs[i], block ? block->getData() : block_placeholder.data);
        }
 
        data->setCrack(q->crack_level, q->crack_pos);
        data->setSmoothLighting(m_cache_smooth_lighting);
 }
 
-void MeshUpdateQueue::cleanupCache()
-{
-       const int mapblock_kB = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE *
-                       sizeof(MapNode) / 1000;
-       g_profiler->avg("MeshUpdateQueue MapBlock cache size kB",
-                       mapblock_kB * m_cache.size());
-
-       // Iterating the entire cache can get pretty expensive so don't do it too often
-       {
-               constexpr int cleanup_interval = 250;
-               const u64 now = porting::getTimeMs();
-               if (m_next_cache_cleanup > now)
-                       return;
-               m_next_cache_cleanup = now + cleanup_interval;
-       }
-
-       // The cache size is kept roughly below cache_soft_max_size, not letting
-       // anything get older than cache_seconds_max or deleted before 2 seconds.
-       const int cache_seconds_max = 10;
-       const int cache_soft_max_size = m_meshgen_block_cache_size * 1000 / mapblock_kB;
-       int cache_seconds = MYMAX(2, cache_seconds_max -
-                       m_cache.size() / (cache_soft_max_size / cache_seconds_max));
-
-       int t_now = time(0);
-
-       for (auto it = m_cache.begin(); it != m_cache.end(); ) {
-               CachedMapBlockData *cached_block = it->second;
-               if (cached_block->refcount_from_queue == 0 &&
-                               cached_block->last_used_timestamp < t_now - cache_seconds) {
-                       it = m_cache.erase(it);
-                       delete cached_block;
-               } else {
-                       ++it;
-               }
-       }
-}
-
 /*
        MeshUpdateWorkerThread
 */
@@ -300,8 +222,9 @@ void MeshUpdateWorkerThread::doUpdate()
                r.p = q->p;
                r.mesh = mesh_new;
                r.solid_sides = get_solid_sides(q->data);
-               r.ack_block_to_server = q->ack_block_to_server;
+               r.ack_list = std::move(q->ack_list);
                r.urgent = q->urgent;
+               r.map_blocks = q->map_blocks;
 
                m_manager->putResult(r);
                m_queue_in->done(q->p);