Each chunk section gets its own palette, should decrease memory usage on vanilla-like worlds

This commit is contained in:
themode 2020-11-12 22:41:41 +01:00
parent e9d7de834f
commit d3d3cc6553
2 changed files with 47 additions and 27 deletions

View File

@ -47,16 +47,10 @@ public class PaletteStorage {
private long[][] sectionBlocks = new long[CHUNK_SECTION_COUNT][0]; private long[][] sectionBlocks = new long[CHUNK_SECTION_COUNT][0];
// palette index = block id // chunk section - palette index = block id
private Short2ShortLinkedOpenHashMap paletteBlockMap = new Short2ShortLinkedOpenHashMap(CHUNK_SECTION_SIZE); private Short2ShortLinkedOpenHashMap[] paletteBlockMaps = new Short2ShortLinkedOpenHashMap[CHUNK_SECTION_COUNT];
// block id = palette index // chunk section - block id = palette index
private Short2ShortOpenHashMap blockPaletteMap = new Short2ShortOpenHashMap(CHUNK_SECTION_SIZE); private Short2ShortOpenHashMap[] blockPaletteMaps = new Short2ShortOpenHashMap[CHUNK_SECTION_COUNT];
{
// Default value
this.paletteBlockMap.put((short) 0, (short) 0);
this.blockPaletteMap.put((short) 0, (short) 0);
}
/** /**
* Creates a new palette storage. * Creates a new palette storage.
@ -100,10 +94,12 @@ public class PaletteStorage {
/** /**
* Gets the palette with the index and the block id as the value. * Gets the palette with the index and the block id as the value.
* *
* @param section the chunk section to get the palette from
* @return the palette * @return the palette
*/ */
public short[] getPalette() { public short[] getPalette(int section) {
return paletteBlockMap.values().toShortArray(); Short2ShortLinkedOpenHashMap paletteBlockMap = paletteBlockMaps[section];
return paletteBlockMap != null ? paletteBlockMap.values().toShortArray() : null;
} }
/** /**
@ -120,11 +116,8 @@ public class PaletteStorage {
PaletteStorage paletteStorage = new PaletteStorage(bitsPerEntry, bitsIncrement); PaletteStorage paletteStorage = new PaletteStorage(bitsPerEntry, bitsIncrement);
paletteStorage.sectionBlocks = sectionBlocks.clone(); paletteStorage.sectionBlocks = sectionBlocks.clone();
paletteStorage.paletteBlockMap.clear(); paletteStorage.paletteBlockMaps = paletteBlockMaps.clone();
paletteStorage.blockPaletteMap.clear(); paletteStorage.blockPaletteMaps = blockPaletteMaps.clone();
paletteStorage.paletteBlockMap.putAll(paletteBlockMap);
paletteStorage.blockPaletteMap.putAll(blockPaletteMap);
return paletteStorage; return paletteStorage;
} }
@ -134,23 +127,36 @@ public class PaletteStorage {
* <p> * <p>
* Also responsible for resizing the palette when full. * Also responsible for resizing the palette when full.
* *
* @param section the chunk section
* @param blockId the block id to convert * @param blockId the block id to convert
* @return the palette index of {@code blockId} * @return the palette index of {@code blockId}
*/ */
private short getPaletteIndex(short blockId) { private short getPaletteIndex(int section, short blockId) {
if (!hasPalette) { if (!hasPalette) {
return blockId; return blockId;
} }
Short2ShortOpenHashMap blockPaletteMap = blockPaletteMaps[section];
if (blockPaletteMap == null) {
blockPaletteMap = createBlockPaletteMap();
blockPaletteMaps[section] = blockPaletteMap;
}
if (!blockPaletteMap.containsKey(blockId)) { if (!blockPaletteMap.containsKey(blockId)) {
Short2ShortLinkedOpenHashMap paletteBlockMap = paletteBlockMaps[section];
if (paletteBlockMap == null) {
paletteBlockMap = createPaletteBlockMap();
paletteBlockMaps[section] = paletteBlockMap;
}
// Resize the palette if full // Resize the palette if full
if (paletteBlockMap.size() >= getMaxPaletteSize()) { if (paletteBlockMap.size() >= getMaxPaletteSize()) {
resize(bitsPerEntry + bitsIncrement); resize(bitsPerEntry + bitsIncrement);
} }
final short paletteIndex = (short) (paletteBlockMap.lastShortKey() + 1); final short paletteIndex = (short) (paletteBlockMap.lastShortKey() + 1);
this.paletteBlockMap.put(paletteIndex, blockId); paletteBlockMap.put(paletteIndex, blockId);
this.blockPaletteMap.put(blockId, paletteIndex); blockPaletteMap.put(blockId, paletteIndex);
return paletteIndex; return paletteIndex;
} }
@ -166,8 +172,8 @@ public class PaletteStorage {
*/ */
private synchronized void resize(int newBitsPerEntry) { private synchronized void resize(int newBitsPerEntry) {
PaletteStorage paletteStorageCache = new PaletteStorage(newBitsPerEntry, bitsIncrement); PaletteStorage paletteStorageCache = new PaletteStorage(newBitsPerEntry, bitsIncrement);
paletteStorageCache.paletteBlockMap = paletteBlockMap; paletteStorageCache.paletteBlockMaps = paletteBlockMaps;
paletteStorageCache.blockPaletteMap = blockPaletteMap; paletteStorageCache.blockPaletteMaps = blockPaletteMaps;
for (int y = 0; y < Chunk.CHUNK_SIZE_Y; y++) { for (int y = 0; y < Chunk.CHUNK_SIZE_Y; y++) {
for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) { for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) {
@ -207,8 +213,10 @@ public class PaletteStorage {
x = toChunkCoordinate(x); x = toChunkCoordinate(x);
z = toChunkCoordinate(z); z = toChunkCoordinate(z);
final int section = ChunkUtils.getSectionAt(y);
// Change to palette value // Change to palette value
blockId = paletteStorage.getPaletteIndex(blockId); blockId = paletteStorage.getPaletteIndex(section, blockId);
final int sectionIndex = getSectionIndex(x, y % CHUNK_SECTION_SIZE, z); final int sectionIndex = getSectionIndex(x, y % CHUNK_SECTION_SIZE, z);
@ -218,8 +226,6 @@ public class PaletteStorage {
final int index = sectionIndex / valuesPerLong; final int index = sectionIndex / valuesPerLong;
final int bitIndex = (sectionIndex % valuesPerLong) * bitsPerEntry; final int bitIndex = (sectionIndex % valuesPerLong) * bitsPerEntry;
final int section = ChunkUtils.getSectionAt(y);
if (paletteStorage.sectionBlocks[section].length == 0) { if (paletteStorage.sectionBlocks[section].length == 0) {
if (blockId == 0) { if (blockId == 0) {
// Section is empty and method is trying to place an air block, stop unnecessary computation // Section is empty and method is trying to place an air block, stop unnecessary computation
@ -269,10 +275,24 @@ public class PaletteStorage {
// Change to palette value and return // Change to palette value and return
return paletteStorage.hasPalette ? return paletteStorage.hasPalette ?
paletteStorage.paletteBlockMap.get((short) value) : paletteStorage.paletteBlockMaps[section].get((short) value) :
(short) value; (short) value;
} }
private static Short2ShortLinkedOpenHashMap createPaletteBlockMap() {
Short2ShortLinkedOpenHashMap map = new Short2ShortLinkedOpenHashMap(CHUNK_SECTION_SIZE);
map.put((short) 0, (short) 0);
return map;
}
private static Short2ShortOpenHashMap createBlockPaletteMap() {
Short2ShortOpenHashMap map = new Short2ShortOpenHashMap(CHUNK_SECTION_SIZE);
map.put((short) 0, (short) 0);
return map;
}
/** /**
* Gets the array length of one section based on the number of values which can be stored in one long. * Gets the array length of one section based on the number of values which can be stored in one long.
* *

View File

@ -53,7 +53,7 @@ public class ChunkDataPacket implements ServerPacket {
final long[] section = paletteStorage.getSectionBlocks()[i]; final long[] section = paletteStorage.getSectionBlocks()[i];
if (section.length > 0) { // section contains at least one block if (section.length > 0) { // section contains at least one block
mask |= 1 << i; mask |= 1 << i;
Utils.writeBlocks(blocks, paletteStorage.getPalette(), section, paletteStorage.getBitsPerEntry()); Utils.writeBlocks(blocks, paletteStorage.getPalette(i), section, paletteStorage.getBitsPerEntry());
} else { } else {
mask |= 0; mask |= 0;
} }