From 6545710c8e087d073504d7dfc5eee1a5e6cfe2e5 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 23 Jun 2025 12:30:44 +0200 Subject: [PATCH] Refactor meshgen-related code --- src/client/client.cpp | 7 +- src/client/content_mapblock.cpp | 3 +- src/client/content_mapblock.h | 2 +- src/client/mapblock_mesh.h | 16 ++++- src/client/mesh_generator_thread.cpp | 95 +++++++++++++++------------- src/client/mesh_generator_thread.h | 33 ++++++++-- 6 files changed, 95 insertions(+), 61 deletions(-) diff --git a/src/client/client.cpp b/src/client/client.cpp index 02e1805af..3fd0983f0 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -612,12 +612,7 @@ void Client::step(float dtime) if (minimap_mapblocks.empty()) do_mapper_update = false; - bool is_empty = true; - for (int l = 0; l < MAX_TILE_LAYERS; l++) - if (r.mesh->getMesh(l)->getMeshBufferCount() != 0) - is_empty = false; - - if (is_empty) { + if (r.mesh->isEmpty()) { delete r.mesh; } else { // Replace with the new mesh diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp index 6c1cfea33..35da8b51a 100644 --- a/src/client/content_mapblock.cpp +++ b/src/client/content_mapblock.cpp @@ -472,7 +472,6 @@ void MapblockMeshGenerator::drawSolidNode() if (!faces) return; u8 mask = faces ^ 0b0011'1111; // k-th bit is set if k-th face is to be *omitted*, as expected by cuboid drawing functions. - cur_node.origin = intToFloat(cur_node.p, BS); auto box = aabb3f(v3f(-0.5 * BS), v3f(0.5 * BS)); box.MinEdge += cur_node.origin; box.MaxEdge += cur_node.origin; @@ -1773,6 +1772,7 @@ void MapblockMeshGenerator::errorUnknownDrawtype() void MapblockMeshGenerator::drawNode() { + cur_node.origin = intToFloat(cur_node.p, BS); switch (cur_node.f->drawtype) { case NDT_AIRLIKE: // Not drawn at all return; @@ -1783,7 +1783,6 @@ void MapblockMeshGenerator::drawNode() default: break; } - cur_node.origin = intToFloat(cur_node.p, BS); if (data->m_smooth_lighting) { getSmoothLightFrame(); } else { diff --git a/src/client/content_mapblock.h b/src/client/content_mapblock.h index 9d51ba2bc..8f938eab3 100644 --- a/src/client/content_mapblock.h +++ b/src/client/content_mapblock.h @@ -27,7 +27,7 @@ struct LightInfo { float light_night; float light_boosted; - LightPair getPair(float sunlight_boost = 0.0) const + LightPair getPair(float sunlight_boost = 0.0f) const { return LightPair( (1 - sunlight_boost) * light_day diff --git a/src/client/mapblock_mesh.h b/src/client/mapblock_mesh.h index 6d8ddc36c..38db55b0f 100644 --- a/src/client/mapblock_mesh.h +++ b/src/client/mapblock_mesh.h @@ -212,6 +212,20 @@ public: return minimap_mapblocks; } + /// @return true if the mesh contains nothing to draw + bool isEmpty() const + { + if (!m_transparent_triangles.empty()) + return false; + for (auto &mesh : m_mesh) { + for (u32 i = 0; i < mesh->getMeshBufferCount(); i++) { + if (mesh->getMeshBuffer(i)->getIndexCount() != 0) + return false; + } + } + return true; + } + bool isAnimationForced() const { return m_animation_force_timer == 0; @@ -242,7 +256,7 @@ public: /// get the list of transparent buffers const std::vector &getTransparentBuffers() const { - return this->m_transparent_buffers; + return m_transparent_buffers; } private: diff --git a/src/client/mesh_generator_thread.cpp b/src/client/mesh_generator_thread.cpp index 6765c6837..4f7dd7fb5 100644 --- a/src/client/mesh_generator_thread.cpp +++ b/src/client/mesh_generator_thread.cpp @@ -20,6 +20,38 @@ QueuedMeshUpdate::~QueuedMeshUpdate() delete data; } +void QueuedMeshUpdate::retrieveBlocks(Map *map, u16 cell_size) +{ + const size_t total = (cell_size+2)*(cell_size+2)*(cell_size+2); + if (map_blocks.empty()) + map_blocks.resize(total); + else + assert(map_blocks.size() == total); // must not change + size_t i = 0; + v3s16 pos; + for (pos.X = p.X - 1; pos.X <= p.X + cell_size; pos.X++) + for (pos.Z = p.Z - 1; pos.Z <= p.Z + cell_size; pos.Z++) + for (pos.Y = p.Y - 1; pos.Y <= p.Y + cell_size; pos.Y++) { + if (!map_blocks[i]) { + MapBlock *block = map->getBlockNoCreateNoEx(pos); + if (block) { + block->refGrab(); + map_blocks[i] = block; + } + } + i++; + } +} + +void QueuedMeshUpdate::dropBlocks() +{ + for (auto *block : map_blocks) { + if (block) + block->refDrop(); + } + map_blocks.clear(); +} + /* MeshUpdateQueue */ @@ -36,26 +68,28 @@ MeshUpdateQueue::~MeshUpdateQueue() MutexAutoLock lock(m_mutex); for (QueuedMeshUpdate *q : m_queue) { - for (auto block : q->map_blocks) - if (block) - block->refDrop(); + q->dropBlocks(); delete q; } } -bool MeshUpdateQueue::addBlock(Map *map, v3s16 p, bool ack_block_to_server, bool urgent) +bool MeshUpdateQueue::addBlock(Map *map, v3s16 p, bool ack_block_to_server, + bool urgent, bool from_neighbor) { + // FIXME: with cell_size > 1 there isn't a "main block" and this check is + // probably incorrect and broken MapBlock *main_block = map->getBlockNoCreateNoEx(p); if (!main_block) return false; - MutexAutoLock lock(m_mutex); - MeshGrid mesh_grid = m_client->getMeshGrid(); // Mesh is placed at the corner block of a chunk // (where all coordinate are divisible by the chunk size) - v3s16 mesh_position(mesh_grid.getMeshPos(p)); + v3s16 mesh_position = mesh_grid.getMeshPos(p); + + MutexAutoLock lock(m_mutex); + /* Mark the block as urgent if requested */ @@ -68,57 +102,27 @@ bool MeshUpdateQueue::addBlock(Map *map, v3s16 p, bool ack_block_to_server, bool */ for (QueuedMeshUpdate *q : m_queue) { 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) + 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; - v3s16 pos; - int i = 0; - for (pos.X = q->p.X - 1; pos.X <= q->p.X + mesh_grid.cell_size; pos.X++) - for (pos.Z = q->p.Z - 1; pos.Z <= q->p.Z + mesh_grid.cell_size; pos.Z++) - for (pos.Y = q->p.Y - 1; pos.Y <= q->p.Y + mesh_grid.cell_size; pos.Y++) { - if (!q->map_blocks[i]) { - MapBlock *block = map->getBlockNoCreateNoEx(pos); - if (block) { - block->refGrab(); - q->map_blocks[i] = block; - } - } - i++; - } + q->retrieveBlocks(map, mesh_grid.cell_size); return true; } } - /* - Make a list of blocks necessary for mesh generation and lock the blocks in memory. - */ - std::vector map_blocks; - map_blocks.reserve((mesh_grid.cell_size+2)*(mesh_grid.cell_size+2)*(mesh_grid.cell_size+2)); - v3s16 pos; - for (pos.X = mesh_position.X - 1; pos.X <= mesh_position.X + mesh_grid.cell_size; pos.X++) - for (pos.Z = mesh_position.Z - 1; pos.Z <= mesh_position.Z + mesh_grid.cell_size; pos.Z++) - for (pos.Y = mesh_position.Y - 1; pos.Y <= mesh_position.Y + mesh_grid.cell_size; pos.Y++) { - MapBlock *block = map->getBlockNoCreateNoEx(pos); - map_blocks.push_back(block); - if (block) - block->refGrab(); - } - /* Add the block */ QueuedMeshUpdate *q = new QueuedMeshUpdate; q->p = mesh_position; - if(ack_block_to_server) + 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); + q->retrieveBlocks(map, mesh_grid.cell_size); m_queue.push_back(q); return true; @@ -208,6 +212,7 @@ void MeshUpdateWorkerThread::doUpdate() ScopeProfiler sp(g_profiler, "Client: Mesh making (sum)"); + // This generates the mesh: MapBlockMesh *mesh_new = new MapBlockMesh(m_client, q->data); MeshUpdateResult r; @@ -216,7 +221,7 @@ void MeshUpdateWorkerThread::doUpdate() r.solid_sides = get_solid_sides(q->data); r.ack_list = std::move(q->ack_list); r.urgent = q->urgent; - r.map_blocks = q->map_blocks; + r.map_blocks = std::move(q->map_blocks); m_manager->putResult(r); m_queue_in->done(q->p); @@ -251,7 +256,7 @@ void MeshUpdateManager::updateBlock(Map *map, v3s16 p, bool ack_block_to_server, static thread_local const bool many_neighbors = g_settings->getBool("smooth_lighting") && !g_settings->getFlag("performance_tradeoffs"); - if (!m_queue_in.addBlock(map, p, ack_block_to_server, urgent)) { + if (!m_queue_in.addBlock(map, p, ack_block_to_server, urgent, false)) { warningstream << "Update requested for non-existent block at " << p << std::endl; return; @@ -259,10 +264,10 @@ void MeshUpdateManager::updateBlock(Map *map, v3s16 p, bool ack_block_to_server, if (update_neighbors) { if (many_neighbors) { for (v3s16 dp : g_26dirs) - m_queue_in.addBlock(map, p + dp, false, urgent); + m_queue_in.addBlock(map, p + dp, false, urgent, true); } else { for (v3s16 dp : g_6dirs) - m_queue_in.addBlock(map, p + dp, false, urgent); + m_queue_in.addBlock(map, p + dp, false, urgent, true); } } deferUpdate(); diff --git a/src/client/mesh_generator_thread.h b/src/client/mesh_generator_thread.h index c8db03d28..133c54b3f 100644 --- a/src/client/mesh_generator_thread.h +++ b/src/client/mesh_generator_thread.h @@ -22,11 +22,24 @@ struct QueuedMeshUpdate int crack_level = -1; v3s16 crack_pos; MeshMakeData *data = nullptr; // This is generated in MeshUpdateQueue::pop() - std::vector map_blocks; + std::vector map_blocks; bool urgent = false; QueuedMeshUpdate() = default; ~QueuedMeshUpdate(); + + /** + * Get blocks needed for this mesh update from the map. + * Blocks that were already loaded are skipped. + * @param map Map + * @param cell_size mesh grid cell size + */ + void retrieveBlocks(Map *map, u16 cell_size); + /** + * Drop block references. + * @note not done by destructor, since this is only safe on main thread + */ + void dropBlocks(); }; /* @@ -45,9 +58,16 @@ public: ~MeshUpdateQueue(); - // Caches the block at p and its neighbors (if needed) and queues a mesh - // update for the block at p - bool addBlock(Map *map, v3s16 p, bool ack_block_to_server, bool urgent); + /** + * Caches the block at p and its neighbors (if needed) and queues a mesh + * update for the block p. + * @param map Map + * @param p block position + * @param ack_to_server Should be acked to server when done? + * @param urget High-priority? + * @param from_neighbor was this update only necessary due to a neighbor change? + */ + bool addBlock(Map *map, v3s16 p, bool ack_to_server, bool urgent, bool from_neighbor); // Returned pointer must be deleted // Returns NULL if queue is empty @@ -56,7 +76,7 @@ public: // Marks a position as finished, unblocking the next update void done(v3s16 pos); - u32 size() + size_t size() { MutexAutoLock lock(m_mutex); return m_queue.size(); @@ -83,7 +103,7 @@ struct MeshUpdateResult u8 solid_sides; std::vector ack_list; bool urgent = false; - std::vector map_blocks; + std::vector map_blocks; MeshUpdateResult() = default; }; @@ -117,6 +137,7 @@ public: void updateBlock(Map *map, v3s16 p, bool ack_block_to_server, bool urgent, bool update_neighbors = false); void putResult(const MeshUpdateResult &r); + /// @note caller needs to refDrop() the affected map_blocks bool getNextResult(MeshUpdateResult &r);