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:
parent
81d62d01d1
commit
fcb5741dcb
4 changed files with 71 additions and 28 deletions
|
@ -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()
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue