1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-08-01 17:38:41 +00:00

Monoblocks: optimize blocks that contain a single type of node

This commit is contained in:
Lars 2025-05-17 11:29:43 -07:00
parent 81d62d01d1
commit fcb5741dcb
4 changed files with 71 additions and 28 deletions

View file

@ -102,11 +102,9 @@ static const char *modified_reason_strings[] = {
MapBlock::MapBlock(v3s16 pos, IGameDef *gamedef): MapBlock::MapBlock(v3s16 pos, IGameDef *gamedef):
m_pos(pos), m_pos(pos),
m_pos_relative(pos * MAP_BLOCKSIZE), m_pos_relative(pos * MAP_BLOCKSIZE),
data(new MapNode[nodecount]),
m_gamedef(gamedef) m_gamedef(gamedef)
{ {
reallocate(); reallocate(nodecount, MapNode(CONTENT_IGNORE));
assert(m_modified > MOD_STATE_CLEAN);
} }
MapBlock::~MapBlock() MapBlock::~MapBlock()
@ -212,6 +210,10 @@ void MapBlock::copyTo(VoxelManipulator &dst)
v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE); v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1)); VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
if (m_is_mono_block) {
reallocate(nodecount, data[0]);
m_is_mono_block = false;
}
// Copy from data to VoxelManipulator // Copy from data to VoxelManipulator
dst.copyFrom(data, data_area, v3s16(0,0,0), dst.copyFrom(data, data_area, v3s16(0,0,0),
getPosRelative(), data_size); getPosRelative(), data_size);
@ -222,6 +224,10 @@ void MapBlock::copyFrom(const VoxelManipulator &src)
v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE); v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1)); VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
if (m_is_mono_block) {
reallocate(nodecount, data[0]);
m_is_mono_block = false;
}
// Copy from VoxelManipulator to data // Copy from VoxelManipulator to data
src.copyTo(data, data_area, v3s16(0,0,0), src.copyTo(data, data_area, v3s16(0,0,0),
getPosRelative(), data_size); getPosRelative(), data_size);
@ -232,6 +238,10 @@ void MapBlock::actuallyUpdateIsAir()
// Running this function un-expires m_is_air // Running this function un-expires m_is_air
m_is_air_expired = false; m_is_air_expired = false;
if (m_is_mono_block) {
m_is_air = data[0].getContent() == CONTENT_AIR;
return;
}
bool only_air = true; bool only_air = true;
for (u32 i = 0; i < nodecount; i++) { for (u32 i = 0; i < nodecount; i++) {
MapNode &n = data[i]; MapNode &n = data[i];
@ -259,12 +269,12 @@ void MapBlock::expireIsAirCache()
// Note that there's no technical reason why we *have to* renumber the IDs, // Note that there's no technical reason why we *have to* renumber the IDs,
// but we do it anyway as it also helps compressability. // but we do it anyway as it also helps compressability.
static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes, static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
const NodeDefManager *nodedef) const NodeDefManager *nodedef, bool is_mono_block)
{ {
IdIdMapping &mapping = IdIdMapping::giveClearedThreadLocalInstance(); IdIdMapping &mapping = IdIdMapping::giveClearedThreadLocalInstance();
content_t id_counter = 0; content_t id_counter = 0;
for (u32 i = 0; i < MapBlock::nodecount; i++) { for (u32 i = 0; i < (is_mono_block ? 1 : MapBlock::nodecount); i++) {
content_t global_id = nodes[i].getContent(); content_t global_id = nodes[i].getContent();
content_t id = CONTENT_IGNORE; content_t id = CONTENT_IGNORE;
@ -377,12 +387,19 @@ void MapBlock::serialize(std::ostream &os_compressed, u8 version, bool disk, int
const u8 params_width = 2; const u8 params_width = 2;
if(disk) if(disk)
{ {
MapNode *tmp_nodes = new MapNode[nodecount]; MapNode *tmp_nodes;
memcpy(tmp_nodes, data, nodecount * sizeof(MapNode)); if (m_is_mono_block) {
getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef()); tmp_nodes = new MapNode[1];
tmp_nodes[0] = data[0];
}
else {
tmp_nodes = new MapNode[nodecount];
memcpy(tmp_nodes, data, nodecount * sizeof(MapNode));
}
getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef(), m_is_mono_block);
buf = MapNode::serializeBulk(version, tmp_nodes, nodecount, buf = MapNode::serializeBulk(version, tmp_nodes, nodecount,
content_width, params_width); content_width, params_width, m_is_mono_block);
delete[] tmp_nodes; delete[] tmp_nodes;
// write timestamp and node/id mapping first // write timestamp and node/id mapping first
@ -395,7 +412,7 @@ void MapBlock::serialize(std::ostream &os_compressed, u8 version, bool disk, int
else else
{ {
buf = MapNode::serializeBulk(version, data, nodecount, buf = MapNode::serializeBulk(version, data, nodecount,
content_width, params_width); content_width, params_width, m_is_mono_block);
} }
writeU8(os, content_width); writeU8(os, content_width);
@ -592,9 +609,15 @@ void MapBlock::deSerialize(std::istream &in_compressed, u8 version, bool disk)
m_node_timers.deSerialize(is, version); m_node_timers.deSerialize(is, version);
} }
u16 dummy; if (nimap.size() == 1) {
m_is_air = nimap.size() == 1 && nimap.getId("air", dummy); m_is_mono_block = true;
m_is_air_expired = false; reallocate(1, data[0]);
u16 dummy;
if (nimap.getId("air", dummy)) {
m_is_air = true;
m_is_air_expired = false;
}
}
} }
TRACESTREAM(<<"MapBlock::deSerialize "<<getPos() TRACESTREAM(<<"MapBlock::deSerialize "<<getPos()

View file

@ -73,15 +73,12 @@ public:
m_orphan = true; m_orphan = true;
} }
void reallocate()
{
for (u32 i = 0; i < nodecount; i++)
data[i] = MapNode(CONTENT_IGNORE);
raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_REALLOCATE);
}
MapNode* getData() MapNode* getData()
{ {
if (m_is_mono_block) {
reallocate(nodecount, data[0]);
m_is_mono_block = false;
}
return data; return data;
} }
@ -236,7 +233,10 @@ public:
if (!*valid_position) if (!*valid_position)
return {CONTENT_IGNORE}; return {CONTENT_IGNORE};
return data[z * zstride + y * ystride + x]; if (m_is_mono_block)
return data[0];
else
return data[z * zstride + y * ystride + x];
} }
inline MapNode getNode(v3s16 p, bool *valid_position) inline MapNode getNode(v3s16 p, bool *valid_position)
@ -255,6 +255,10 @@ public:
if (!isValidPosition(x, y, z)) if (!isValidPosition(x, y, z))
throw InvalidPositionException(); throw InvalidPositionException();
if (m_is_mono_block) {
reallocate(nodecount, data[0]);
m_is_mono_block = false;
}
data[z * zstride + y * ystride + x] = n; data[z * zstride + y * ystride + x] = n;
raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE); raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE);
} }
@ -270,7 +274,10 @@ public:
inline MapNode getNodeNoCheck(s16 x, s16 y, s16 z) inline MapNode getNodeNoCheck(s16 x, s16 y, s16 z)
{ {
return data[z * zstride + y * ystride + x]; if (m_is_mono_block)
return data[0];
else
return data[z * zstride + y * ystride + x];
} }
inline MapNode getNodeNoCheck(v3s16 p) inline MapNode getNodeNoCheck(v3s16 p)
@ -280,6 +287,10 @@ public:
inline void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode n) inline void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode n)
{ {
if (m_is_mono_block) {
reallocate(nodecount, data[0]);
m_is_mono_block = false;
}
data[z * zstride + y * ystride + x] = n; data[z * zstride + y * ystride + x] = n;
raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE); raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE);
} }
@ -439,6 +450,14 @@ private:
void deSerialize_pre22(std::istream &is, u8 version, bool disk); void deSerialize_pre22(std::istream &is, u8 version, bool disk);
void reallocate(u32 c, MapNode n)
{
delete[] data;
data = new MapNode[c];
for (u32 i = 0; i < c; i++)
data[i] = n;
}
/* /*
* PLEASE NOTE: When adding something here be mindful of position and size * PLEASE NOTE: When adding something here be mindful of position and size
* of member variables! This is also the reason for the weird public-private * of member variables! This is also the reason for the weird public-private
@ -457,6 +476,7 @@ public:
private: private:
// see isOrphan() // see isOrphan()
bool m_orphan = false; bool m_orphan = false;
bool m_is_mono_block = false;
// Position in blocks on parent // Position in blocks on parent
v3s16 m_pos; v3s16 m_pos;
@ -480,7 +500,7 @@ private:
* heap fragmentation (the array is exactly 16K), CPU caches and/or * heap fragmentation (the array is exactly 16K), CPU caches and/or
* optimizability of algorithms working on this array. * optimizability of algorithms working on this array.
*/ */
MapNode *const data; // of `nodecount` elements MapNode * data = nullptr; // of `nodecount` elements
// provides the item and node definitions // provides the item and node definitions
IGameDef *m_gamedef; IGameDef *m_gamedef;

View file

@ -589,7 +589,7 @@ void MapNode::deSerialize(const u8 *source, u8 version)
Buffer<u8> MapNode::serializeBulk(int version, Buffer<u8> MapNode::serializeBulk(int version,
const MapNode *nodes, u32 nodecount, const MapNode *nodes, u32 nodecount,
u8 content_width, u8 params_width) u8 content_width, u8 params_width, bool is_mono_block)
{ {
if (!ser_ver_supported_write(version)) if (!ser_ver_supported_write(version))
throw VersionMismatchException("ERROR: MapNode format not supported"); throw VersionMismatchException("ERROR: MapNode format not supported");
@ -602,13 +602,13 @@ Buffer<u8> MapNode::serializeBulk(int version,
// Writing to the buffer linearly is faster // Writing to the buffer linearly is faster
u8 *p = &databuf[0]; u8 *p = &databuf[0];
for (u32 i = 0; i < nodecount; i++, p += 2) for (u32 i = 0; i < nodecount; i++, p += 2)
writeU16(p, nodes[i].param0); writeU16(p, nodes[is_mono_block ? 0 : i].param0);
for (u32 i = 0; i < nodecount; i++, p++) for (u32 i = 0; i < nodecount; i++, p++)
writeU8(p, nodes[i].param1); writeU8(p, nodes[is_mono_block ? 0 : i].param1);
for (u32 i = 0; i < nodecount; i++, p++) for (u32 i = 0; i < nodecount; i++, p++)
writeU8(p, nodes[i].param2); writeU8(p, nodes[is_mono_block ? 0 : i].param2);
return databuf; return databuf;
} }

View file

@ -307,7 +307,7 @@ struct alignas(u32) MapNode
// compressed = true to zlib-compress output // compressed = true to zlib-compress output
static Buffer<u8> serializeBulk(int version, static Buffer<u8> serializeBulk(int version,
const MapNode *nodes, u32 nodecount, const MapNode *nodes, u32 nodecount,
u8 content_width, u8 params_width); u8 content_width, u8 params_width, bool is_mono_block = false);
static void deSerializeBulk(std::istream &is, int version, static void deSerializeBulk(std::istream &is, int version,
MapNode *nodes, u32 nodecount, MapNode *nodes, u32 nodecount,
u8 content_width, u8 params_width); u8 content_width, u8 params_width);