Skip to content

Commit

Permalink
8x block meshes (#13133)
Browse files Browse the repository at this point in the history
Reduce the number of drawcalls by generating a mesh per 8 blocks (2x2x2). Only blocks with even coordinates (lowest bit set to 0) will get a mesh.

Note: This also removes the old 'loops' algorithm for building the draw list, because it produces visual artifacts and cannot be made compatible with the approach of having a mesh for every 8th block without hurting performance.

Co-authored-by: Jude Melton-Houghton <jwmhjwmh@gmail.com>
Co-authored-by: Lars <larsh@apache.org>
Co-authored-by: sfan5 <sfan5@live.de>
  • Loading branch information
4 people committed Jan 31, 2023
1 parent cded6a3 commit 69fc206
Show file tree
Hide file tree
Showing 14 changed files with 269 additions and 121 deletions.
43 changes: 34 additions & 9 deletions src/client/client.cpp
Expand Up @@ -41,6 +41,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "filesys.h"
#include "mapblock_mesh.h"
#include "mapblock.h"
#include "mapsector.h"
#include "minimap.h"
#include "modchannels.h"
#include "content/mods.h"
Expand Down Expand Up @@ -555,19 +556,27 @@ void Client::step(float dtime)
{
num_processed_meshes++;

MinimapMapblock *minimap_mapblock = NULL;
std::vector<MinimapMapblock*> minimap_mapblocks;
bool do_mapper_update = true;

MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
MapSector *sector = m_env.getMap().emergeSector(v2s16(r.p.X, r.p.Z));

MapBlock *block = sector->getBlockNoCreateNoEx(r.p.Y);

// The block in question is not visible (perhaps it is culled at the server),
// create a blank block just to hold the 2x2x2 mesh.
// If the block becomes visible later it will replace the blank block.
if (!block && r.mesh)
block = sector->createBlankBlock(r.p.Y);

if (block) {
// Delete the old mesh
delete block->mesh;
block->mesh = nullptr;

if (r.mesh) {
block->solid_sides = r.solid_sides;
minimap_mapblock = r.mesh->moveMinimapMapblock();
if (minimap_mapblock == NULL)
minimap_mapblocks = r.mesh->moveMinimapMapblocks();
if (minimap_mapblocks.empty())
do_mapper_update = false;

bool is_empty = true;
Expand All @@ -588,16 +597,32 @@ void Client::step(float dtime)
delete r.mesh;
}

if (m_minimap && do_mapper_update)
m_minimap->addBlock(r.p, minimap_mapblock);
for (auto p : r.solid_sides) {
auto block = m_env.getMap().getBlockNoCreateNoEx(p.first);
if (block)
block->solid_sides = p.second;
}

if (m_minimap && do_mapper_update) {
v3s16 ofs;

// See also mapblock_mesh.cpp for the code that creates the array of minimap blocks.
for (ofs.Z = 0; ofs.Z <= 1; ofs.Z++)
for (ofs.Y = 0; ofs.Y <= 1; ofs.Y++)
for (ofs.X = 0; ofs.X <= 1; ofs.X++) {
size_t i = ofs.Z * 4 + ofs.Y * 2 + ofs.X;
if (i < minimap_mapblocks.size() && minimap_mapblocks[i])
m_minimap->addBlock(r.p + ofs, minimap_mapblocks[i]);
}
}

if (r.ack_block_to_server) {
for (auto p : r.ack_list) {
if (blocks_to_ack.size() == 255) {
sendGotBlocks(blocks_to_ack);
blocks_to_ack.clear();
}

blocks_to_ack.emplace_back(r.p);
blocks_to_ack.emplace_back(p);
}

for (auto block : r.map_blocks)
Expand Down
37 changes: 30 additions & 7 deletions src/client/clientmap.cpp
Expand Up @@ -298,6 +298,8 @@ void ClientMap::updateDrawList()
blocks_to_consider.push(camera_block);
blocks_seen.getChunk(camera_block).getBits(camera_block) = 0x07; // mark all sides as visible

std::set<v3s16> shortlist;

// Recursively walk the space and pick mapblocks for drawing
while (blocks_to_consider.size() > 0) {

Expand Down Expand Up @@ -369,11 +371,13 @@ void ClientMap::updateDrawList()
continue;
}

// The block is visible, add to the draw list
if (mesh) {
// Add to set
// Block meshes are stored in blocks where all coordinates are even (lowest bit set to 0)
// Add them to the de-dup set.
shortlist.emplace(block_coord.X & ~1, block_coord.Y & ~1, block_coord.Z & ~1);
// All other blocks we can grab and add to the drawlist right away.
if (block && m_drawlist.emplace(block_coord, block).second) {
// only grab the ref if the block exists and was not in the list
block->refGrab();
m_drawlist[block_coord] = block;
}

// Decide which sides to traverse next or to block away
Expand Down Expand Up @@ -474,6 +478,16 @@ void ClientMap::updateDrawList()
}
}

g_profiler->avg("MapBlocks shortlist [#]", shortlist.size());

for (auto pos : shortlist) {
MapBlock * block = getBlockNoCreateNoEx(pos);
if (block && m_drawlist.emplace(pos, block).second) {
// only grab the ref if the block exists and was not in the list
block->refGrab();
}
}

g_profiler->avg("MapBlocks occlusion culled [#]", blocks_occlusion_culled);
g_profiler->avg("MapBlocks sides skipped [#]", sides_skipped);
g_profiler->avg("MapBlocks examined [#]", blocks_visited);
Expand Down Expand Up @@ -597,7 +611,11 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
MapBlock *block = i.second;
MapBlockMesh *block_mesh = block->mesh;

// If the mesh of the block happened to get deleted, ignore it
// Meshes are only stored every 8-th block (where all coordinates are even),
// but we keep all the visible blocks in the draw list to prevent client
// from dropping them.
// On top of that, in some cases block mesh can be removed
// before the block is removed from the draw list.
if (!block_mesh)
continue;

Expand Down Expand Up @@ -720,7 +738,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
material.TextureLayer[ShadowRenderer::TEXTURE_LAYER_SHADOW].Texture = nullptr;
}

v3f block_wpos = intToFloat(descriptor.m_pos * MAP_BLOCKSIZE, BS);
v3f block_wpos = intToFloat(descriptor.m_pos / 8 * 8 * MAP_BLOCKSIZE, BS);
m.setTranslation(block_wpos - offset);

driver->setTransform(video::ETS_WORLD, m);
Expand Down Expand Up @@ -1046,7 +1064,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
++material_swaps;
}

v3f block_wpos = intToFloat(descriptor.m_pos * MAP_BLOCKSIZE, BS);
v3f block_wpos = intToFloat(descriptor.m_pos / 8 * 8 * MAP_BLOCKSIZE, BS);
m.setTranslation(block_wpos - offset);

driver->setTransform(video::ETS_WORLD, m);
Expand Down Expand Up @@ -1133,6 +1151,11 @@ void ClientMap::updateDrawListShadow(v3f shadow_light_pos, v3f shadow_light_dir,
g_profiler->avg("SHADOW MapBlocks loaded [#]", blocks_loaded);
}

void ClientMap::reportMetrics(u64 save_time_us, u32 saved_blocks, u32 all_blocks)
{
g_profiler->avg("CM::reportMetrics loaded blocks [#]", all_blocks);
}

void ClientMap::updateTransparentMeshBuffers()
{
ScopeProfiler sp(g_profiler, "CM::updateTransparentMeshBuffers", SPT_AVG);
Expand Down
2 changes: 2 additions & 0 deletions src/client/clientmap.h
Expand Up @@ -140,6 +140,8 @@ class ClientMap : public Map, public scene::ISceneNode

void onSettingChanged(const std::string &name);

protected:
void reportMetrics(u64 save_time_us, u32 saved_blocks, u32 all_blocks) override;
private:

// update the vertex order in transparent mesh buffers
Expand Down
6 changes: 3 additions & 3 deletions src/client/content_mapblock.cpp
Expand Up @@ -1619,9 +1619,9 @@ void MapblockMeshGenerator::drawNode()
*/
void MapblockMeshGenerator::generate()
{
for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
for (p.Z = 0; p.Z < data->side_length; p.Z++)
for (p.Y = 0; p.Y < data->side_length; p.Y++)
for (p.X = 0; p.X < data->side_length; p.X++) {
n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
f = &nodedef->get(n);
drawNode();
Expand Down

0 comments on commit 69fc206

Please sign in to comment.