chunk storage: store blocks as flat array (improve memory usage A LOT)

This commit is contained in:
Bixilon 2021-03-04 00:36:07 +01:00
parent a32c6c6074
commit 71a75b7b53
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
5 changed files with 46 additions and 27 deletions

View File

@ -93,7 +93,11 @@ object VersionTweaker {
return return
} }
for ((sectionHeight, section) in sections) { for ((sectionHeight, section) in sections) {
for ((location, blockInfo) in section.blocks) { for ((index, blockInfo) in section.blocks.withIndex()) {
if (blockInfo == null) {
continue
}
val location = ChunkSection.getPosition(index)
val newBlock = transformBlock(blockInfo.block, sections, location, sectionHeight) val newBlock = transformBlock(blockInfo.block, sections, location, sectionHeight)
if (newBlock === blockInfo.block) { if (newBlock === blockInfo.block) {
continue continue

View File

@ -56,7 +56,7 @@ class Chunk(
} }
// replace all chunk sections // replace all chunk sections
for ((sectionHeight, chunkSection) in it) { for ((sectionHeight, chunkSection) in it) {
getSectionOrCreate(sectionHeight).setData(chunkSection, merge) getSectionOrCreate(sectionHeight).setData(chunkSection)
} }
} }
data.biomeAccessor?.let { data.biomeAccessor?.let {
@ -82,7 +82,7 @@ class Chunk(
getSectionOrCreate(position.getSectionHeight()).let { getSectionOrCreate(position.getSectionHeight()).let {
val inChunkSectionLocation = position.getInChunkSectionLocation() val inChunkSectionLocation = position.getInChunkSectionLocation()
if (block == null) { if (block == null) {
it.blocks.remove(inChunkSectionLocation) it.blocks[ChunkSection.getIndex(inChunkSectionLocation)] = null
return return
} }
it.setBlockInfo(inChunkSectionLocation, BlockInfo(block)) it.setBlockInfo(inChunkSectionLocation, BlockInfo(block))

View File

@ -13,24 +13,21 @@
package de.bixilon.minosoft.data.world package de.bixilon.minosoft.data.world
import de.bixilon.minosoft.data.mappings.blocks.BlockState import de.bixilon.minosoft.data.mappings.blocks.BlockState
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
/** /**
* Collection of 16x16x16 blocks * Collection of 16x16x16 blocks
*/ */
class ChunkSection constructor( class ChunkSection(
val blocks: MutableMap<InChunkSectionPosition, BlockInfo> = mutableMapOf(), val blocks: Array<BlockInfo?> = arrayOfNulls(ProtocolDefinition.BLOCKS_PER_SECTION),
) { ) {
fun getBlockInfo(position: InChunkSectionPosition): BlockInfo? { fun getBlockInfo(position: InChunkSectionPosition): BlockInfo? {
return blocks[position] return blocks[getIndex(position)]
} }
fun setBlockInfo(position: InChunkSectionPosition, blockInfo: BlockInfo?) { fun setBlockInfo(position: InChunkSectionPosition, blockInfo: BlockInfo?) {
if (blockInfo == null) { blocks[getIndex(position)] = blockInfo
blocks.remove(position)
return
}
blocks[position] = blockInfo
} }
fun getBlockInfo(x: Int, y: Int, z: Int): BlockInfo? { fun getBlockInfo(x: Int, y: Int, z: Int): BlockInfo? {
@ -45,10 +42,23 @@ class ChunkSection constructor(
setBlockInfo(position, BlockInfo(block)) setBlockInfo(position, BlockInfo(block))
} }
fun setData(chunkSection: ChunkSection, merge: Boolean = false) { fun setData(chunkSection: ChunkSection) {
if (!merge) { for ((index, blockInfo) in chunkSection.blocks.withIndex()) {
this.blocks.clear() blocks[index] = blockInfo
}
}
companion object {
fun getIndex(position: InChunkSectionPosition): Int {
return getIndex(position.x, position.y, position.z)
}
fun getIndex(x: Int, y: Int, z: Int): Int {
return y shl 8 or (z shl 4) or x
}
fun getPosition(index: Int): InChunkSectionPosition {
return InChunkSectionPosition(index and 0x0F, (index shr 8) and 0x0F, (index shr 4) and 0x0F)
} }
this.blocks.putAll(chunkSection.blocks)
} }
} }

View File

@ -58,8 +58,11 @@ class WorldRenderer(
val mesh = ChunkMesh() val mesh = ChunkMesh()
for ((position, blockInfo) in section.blocks) { for ((index, blockInfo) in section.blocks.withIndex()) {
val blockPosition = BlockPosition(chunkPosition, sectionHeight, position) if (blockInfo == null) {
continue
}
val blockPosition = BlockPosition(chunkPosition, sectionHeight, ChunkSection.getPosition(index))
val neighborBlocks: Array<BlockInfo?> = arrayOfNulls(Directions.DIRECTIONS.size) val neighborBlocks: Array<BlockInfo?> = arrayOfNulls(Directions.DIRECTIONS.size)
for (direction in Directions.DIRECTIONS) { for (direction in Directions.DIRECTIONS) {

View File

@ -19,7 +19,6 @@ import de.bixilon.minosoft.data.mappings.blocks.BlockState;
import de.bixilon.minosoft.data.world.BlockInfo; import de.bixilon.minosoft.data.world.BlockInfo;
import de.bixilon.minosoft.data.world.ChunkData; import de.bixilon.minosoft.data.world.ChunkData;
import de.bixilon.minosoft.data.world.ChunkSection; import de.bixilon.minosoft.data.world.ChunkSection;
import de.bixilon.minosoft.data.world.InChunkSectionPosition;
import de.bixilon.minosoft.data.world.biome.DummyBiomeAccessor; import de.bixilon.minosoft.data.world.biome.DummyBiomeAccessor;
import de.bixilon.minosoft.data.world.biome.XZBiomeAccessor; import de.bixilon.minosoft.data.world.biome.XZBiomeAccessor;
import de.bixilon.minosoft.data.world.light.DummyLightAccessor; import de.bixilon.minosoft.data.world.light.DummyLightAccessor;
@ -62,7 +61,7 @@ public final class ChunkUtil {
HashMap<Integer, ChunkSection> sectionMap = new HashMap<>(); HashMap<Integer, ChunkSection> sectionMap = new HashMap<>();
for (int sectionHeight = dimension.getLowestSection(); sectionHeight < dimension.getHighestSection(); sectionHeight++) { // max sections per chunks in chunk column for (int sectionHeight = dimension.getLowestSection(); sectionHeight < dimension.getHighestSection(); sectionHeight++) { // max sections per chunks in chunk column
if (BitByte.isBitSet(sectionBitMasks[0], sectionHeight)) { if (BitByte.isBitSet(sectionBitMasks[0], sectionHeight)) {
HashMap<InChunkSectionPosition, BlockInfo> blockMap = new HashMap<>(); BlockInfo[] blocks = new BlockInfo[ProtocolDefinition.BLOCKS_PER_SECTION];
for (int nibbleY = 0; nibbleY < ProtocolDefinition.SECTION_HEIGHT_Y; nibbleY++) { for (int nibbleY = 0; nibbleY < ProtocolDefinition.SECTION_HEIGHT_Y; nibbleY++) {
for (int nibbleZ = 0; nibbleZ < ProtocolDefinition.SECTION_WIDTH_Z; nibbleZ++) { for (int nibbleZ = 0; nibbleZ < ProtocolDefinition.SECTION_WIDTH_Z; nibbleZ++) {
@ -90,12 +89,15 @@ public final class ChunkUtil {
continue; continue;
} }
BlockState block = buffer.getConnection().getMapping().getBlockState(fullBlockId); BlockState block = buffer.getConnection().getMapping().getBlockState(fullBlockId);
blockMap.put(new InChunkSectionPosition(nibbleX, nibbleY, nibbleZ), new BlockInfo(block)); if (block == null) {
continue;
}
blocks[ChunkSection.Companion.getIndex(nibbleX, nibbleY, nibbleZ)] = new BlockInfo(block);
arrayPos++; arrayPos++;
} }
} }
} }
sectionMap.put(dimension.getLowestSection() + sectionHeight, new ChunkSection(blockMap)); // ToDo sectionMap.put(dimension.getLowestSection() + sectionHeight, new ChunkSection(blocks)); // ToDo
} }
} }
return new ChunkData(sectionMap, new DummyBiomeAccessor(buffer.getConnection().getMapping().getBiomeRegistry().get(0)), DummyLightAccessor.INSTANCE); return new ChunkData(sectionMap, new DummyBiomeAccessor(buffer.getConnection().getMapping().getBiomeRegistry().get(0)), DummyLightAccessor.INSTANCE);
@ -126,7 +128,7 @@ public final class ChunkUtil {
if (!BitByte.isBitSet(sectionBitMasks[0], sectionHeight)) { if (!BitByte.isBitSet(sectionBitMasks[0], sectionHeight)) {
continue; continue;
} }
HashMap<InChunkSectionPosition, BlockInfo> blockMap = new HashMap<>(); BlockInfo[] blocks = new BlockInfo[ProtocolDefinition.BLOCKS_PER_SECTION];
for (int nibbleY = 0; nibbleY < ProtocolDefinition.SECTION_HEIGHT_Y; nibbleY++) { for (int nibbleY = 0; nibbleY < ProtocolDefinition.SECTION_HEIGHT_Y; nibbleY++) {
for (int nibbleZ = 0; nibbleZ < ProtocolDefinition.SECTION_WIDTH_Z; nibbleZ++) { for (int nibbleZ = 0; nibbleZ < ProtocolDefinition.SECTION_WIDTH_Z; nibbleZ++) {
@ -137,12 +139,12 @@ public final class ChunkUtil {
arrayPos++; arrayPos++;
continue; continue;
} }
blockMap.put(new InChunkSectionPosition(nibbleX, nibbleY, nibbleZ), new BlockInfo(block)); blocks[ChunkSection.Companion.getIndex(nibbleX, nibbleY, nibbleZ)] = new BlockInfo(block);
arrayPos++; arrayPos++;
} }
} }
} }
sectionMap.put(dimension.getLowestSection() + sectionHeight, new ChunkSection(blockMap)); sectionMap.put(dimension.getLowestSection() + sectionHeight, new ChunkSection(blocks));
} }
return new ChunkData(sectionMap, new DummyBiomeAccessor(buffer.getConnection().getMapping().getBiomeRegistry().get(0)), DummyLightAccessor.INSTANCE); // ToDo return new ChunkData(sectionMap, new DummyBiomeAccessor(buffer.getConnection().getMapping().getBiomeRegistry().get(0)), DummyLightAccessor.INSTANCE); // ToDo
} }
@ -162,7 +164,7 @@ public final class ChunkUtil {
long[] data = buffer.readLongArray(); long[] data = buffer.readLongArray();
HashMap<InChunkSectionPosition, BlockInfo> blockMap = new HashMap<>(); BlockInfo[] blocks = new BlockInfo[ProtocolDefinition.BLOCKS_PER_SECTION];
for (int nibbleY = 0; nibbleY < ProtocolDefinition.SECTION_HEIGHT_Y; nibbleY++) { for (int nibbleY = 0; nibbleY < ProtocolDefinition.SECTION_HEIGHT_Y; nibbleY++) {
for (int nibbleZ = 0; nibbleZ < ProtocolDefinition.SECTION_WIDTH_Z; nibbleZ++) { for (int nibbleZ = 0; nibbleZ < ProtocolDefinition.SECTION_WIDTH_Z; nibbleZ++) {
for (int nibbleX = 0; nibbleX < ProtocolDefinition.SECTION_WIDTH_X; nibbleX++) { for (int nibbleX = 0; nibbleX < ProtocolDefinition.SECTION_WIDTH_X; nibbleX++) {
@ -193,7 +195,7 @@ public final class ChunkUtil {
if (block == null) { if (block == null) {
continue; continue;
} }
blockMap.put(new InChunkSectionPosition(nibbleX, nibbleY, nibbleZ), new BlockInfo(block)); blocks[ChunkSection.Companion.getIndex(nibbleX, nibbleY, nibbleZ)] = new BlockInfo(block);
} }
} }
} }
@ -206,7 +208,7 @@ public final class ChunkUtil {
// ToDo // ToDo
} }
sectionMap.put(dimension.getLowestSection() + sectionHeight, new ChunkSection(blockMap)); sectionMap.put(dimension.getLowestSection() + sectionHeight, new ChunkSection(blocks));
} }
ChunkData chunkData = new ChunkData(); ChunkData chunkData = new ChunkData();
chunkData.setBlocks(sectionMap); chunkData.setBlocks(sectionMap);