Attempted optimizations for world processing
This commit is contained in:
@@ -10,6 +10,7 @@ public class VoxelizedSection {
|
||||
public int x;
|
||||
public int y;
|
||||
public int z;
|
||||
public int lvl0NonAirCount;
|
||||
public final long[] section;
|
||||
public VoxelizedSection(long[] section) {
|
||||
this.section = section;
|
||||
@@ -51,6 +52,7 @@ public class VoxelizedSection {
|
||||
}
|
||||
|
||||
public VoxelizedSection zero() {
|
||||
this.lvl0NonAirCount = 0;
|
||||
Arrays.fill(this.section, 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ public class WorldConversionFactory {
|
||||
}
|
||||
|
||||
|
||||
|
||||
int nonZeroCnt = 0;
|
||||
if (blockContainer.data.storage instanceof PackedIntegerArray bStor) {
|
||||
var bDat = bStor.getData();
|
||||
int iterPerLong = (64 / bStor.getElementBits()) - 1;
|
||||
@@ -168,7 +168,7 @@ public class WorldConversionFactory {
|
||||
sample >>>= eBits;
|
||||
|
||||
byte light = lightSupplier.supply(i&0xF, (i>>8)&0xF, (i>>4)&0xF);
|
||||
|
||||
nonZeroCnt += (bId != 0)?1:0;
|
||||
data[i] = Mapper.composeMappingId(light, bId, biomes[Integer.compress(i,0b1100_1100_1100)]);
|
||||
}
|
||||
} else {
|
||||
@@ -181,12 +181,14 @@ public class WorldConversionFactory {
|
||||
data[i] = Mapper.airWithLight(lightSupplier.supply(i&0xF, (i>>8)&0xF, (i>>4)&0xF));
|
||||
}
|
||||
} else {
|
||||
nonZeroCnt = 4096;
|
||||
for (int i = 0; i <= 0xFFF; i++) {
|
||||
byte light = lightSupplier.supply(i&0xF, (i>>8)&0xF, (i>>4)&0xF);
|
||||
data[i] = Mapper.composeMappingId(light, bId, biomes[Integer.compress(i,0b1100_1100_1100)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
section.lvl0NonAirCount = nonZeroCnt;
|
||||
return section;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ import me.cortex.voxy.commonImpl.VoxyCommon;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
public class SaveLoadSystem3 {
|
||||
public static final int STORAGE_VERSION = 0;
|
||||
|
||||
private record SerializationCache(Long2ShortOpenHashMap lutMapCache, MemoryBuffer memoryBuffer) {
|
||||
public SerializationCache() {
|
||||
this(new Long2ShortOpenHashMap(512), ThreadLocalMemoryBuffer.create(WorldSection.SECTION_VOLUME*2+WorldSection.SECTION_VOLUME*8+1024));
|
||||
@@ -60,6 +62,7 @@ public class SaveLoadSystem3 {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
//TODO: note! can actually have the first (last?) byte of metadata be the storage version!
|
||||
long metadata = 0;
|
||||
metadata |= Integer.toUnsignedLong(LUT.size());//Bottom 2 bytes
|
||||
metadata |= Byte.toUnsignedLong(section.getNonEmptyChildren())<<16;//Next byte
|
||||
@@ -82,21 +85,26 @@ public class SaveLoadSystem3 {
|
||||
return false;
|
||||
}
|
||||
|
||||
long metadata = MemoryUtil.memGetLong(ptr); ptr += 8;
|
||||
final long metadata = MemoryUtil.memGetLong(ptr); ptr += 8;
|
||||
section.nonEmptyChildren = (byte) ((metadata>>>16)&0xFF);
|
||||
|
||||
final long lutBasePtr = ptr + WorldSection.SECTION_VOLUME * 2;
|
||||
if (section.lvl == 0) {
|
||||
int nonEmptyBlockCount = 0;
|
||||
long lutBasePtr = ptr + WorldSection.SECTION_VOLUME*2;
|
||||
var blockData = section.data;
|
||||
final var blockData = section.data;
|
||||
for (int i = 0; i < WorldSection.SECTION_VOLUME; i++) {
|
||||
short lutId = MemoryUtil.memGetShort(ptr); ptr+=2;
|
||||
long blockId = MemoryUtil.memGetLong(lutBasePtr+Short.toUnsignedLong(lutId)*8L);
|
||||
final short lutId = MemoryUtil.memGetShort(ptr); ptr += 2;
|
||||
final long blockId = MemoryUtil.memGetLong(lutBasePtr + Short.toUnsignedLong(lutId) * 8L);
|
||||
nonEmptyBlockCount += Mapper.isAir(blockId) ? 0 : 1;
|
||||
blockData[i] = blockId;
|
||||
}
|
||||
section.nonEmptyBlockCount = nonEmptyBlockCount;
|
||||
} else {
|
||||
final var blockData = section.data;
|
||||
for (int i = 0; i < WorldSection.SECTION_VOLUME; i++) {
|
||||
blockData[i] = MemoryUtil.memGetLong(lutBasePtr + Short.toUnsignedLong(MemoryUtil.memGetShort(ptr)) * 8L);ptr += 2;
|
||||
}
|
||||
}
|
||||
ptr = lutBasePtr + (metadata & 0xFFFF) * 8L;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public final class WorldSection {
|
||||
//Serialized states
|
||||
long metadata;
|
||||
long[] data = null;
|
||||
volatile int nonEmptyBlockCount = 0;
|
||||
volatile int nonEmptyBlockCount = 0;//Note: only needed for level 0 sections
|
||||
volatile byte nonEmptyChildren;
|
||||
|
||||
final ActiveSectionTracker tracker;
|
||||
@@ -120,7 +120,7 @@ public final class WorldSection {
|
||||
public int acquire(int count) {
|
||||
int state = ((int) ATOMIC_STATE_HANDLE.getAndAdd(this, count<<1)) + (count<<1);
|
||||
if ((state & 1) == 0) {
|
||||
throw new IllegalStateException("Tried to acquire unloaded section");
|
||||
throw new IllegalStateException("Tried to acquire unloaded section: " + WorldEngine.pprintPos(this.key));
|
||||
}
|
||||
return state>>1;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import me.cortex.voxy.common.world.other.Mapper;
|
||||
import static me.cortex.voxy.common.world.WorldEngine.*;
|
||||
|
||||
public class WorldUpdater {
|
||||
//TODO: move this to auxilery class so that it can take into account larger than 4 mip levels
|
||||
//Executes an update to the world and automatically updates all the parent mip layers up to level 4 (e.g. where 1 chunk section is 1 block big)
|
||||
|
||||
//NOTE: THIS RUNS ON THE THREAD IT WAS EXECUTED ON, when this method exits, the calling method may assume that VoxelizedSection is no longer needed
|
||||
@@ -23,7 +22,7 @@ public class WorldUpdater {
|
||||
WorldSection previousSection = null;
|
||||
final var vdat = section.section;
|
||||
|
||||
for (int lvl = 0; lvl < MAX_LOD_LAYER+1; lvl++) {
|
||||
for (int lvl = 0; lvl <= MAX_LOD_LAYER; lvl++) {
|
||||
var worldSection = into.acquire(lvl, section.x >> (lvl + 1), section.y >> (lvl + 1), section.z >> (lvl + 1));
|
||||
|
||||
int emptinessStateChange = 0;
|
||||
@@ -41,15 +40,37 @@ public class WorldUpdater {
|
||||
int by = (section.y&msk)<<(4-lvl);
|
||||
int bz = (section.z&msk)<<(4-lvl);
|
||||
|
||||
int nonAirCountDelta = 0;
|
||||
int airCount = 0;
|
||||
boolean didStateChange = false;
|
||||
|
||||
|
||||
//TODO: remove the nonAirCountDelta stuff if level != 0
|
||||
|
||||
{//Do a bunch of funny math
|
||||
var secD = worldSection.data;
|
||||
|
||||
int baseVIdx = VoxelizedSection.getBaseIndexForLevel(lvl);
|
||||
int baseSec = bx | (bz << 5) | (by << 10);
|
||||
if (lvl == 0) {
|
||||
final int secMsk = 0b1100|(0xf << 5) | (0xf << 10);
|
||||
final int iSecMsk1 = (~secMsk) + 1;
|
||||
|
||||
int secIdx = 0;
|
||||
//TODO: manually unroll and do e.g. 4 iterations per loop
|
||||
for (int i = 0; i <= 0xFFF; i+=4) {
|
||||
int cSecIdx = secIdx + baseSec;
|
||||
secIdx = (secIdx + iSecMsk1) & secMsk;
|
||||
|
||||
long oldId0 = secD[cSecIdx+0]; secD[cSecIdx+0] = vdat[i+0];
|
||||
long oldId1 = secD[cSecIdx+1]; secD[cSecIdx+1] = vdat[i+1];
|
||||
long oldId2 = secD[cSecIdx+2]; secD[cSecIdx+2] = vdat[i+2];
|
||||
long oldId3 = secD[cSecIdx+3]; secD[cSecIdx+3] = vdat[i+3];
|
||||
|
||||
airCount += Mapper.isAir(oldId0)?1:0; didStateChange |= vdat[i+0] != oldId0;
|
||||
airCount += Mapper.isAir(oldId1)?1:0; didStateChange |= vdat[i+1] != oldId1;
|
||||
airCount += Mapper.isAir(oldId2)?1:0; didStateChange |= vdat[i+2] != oldId2;
|
||||
airCount += Mapper.isAir(oldId3)?1:0; didStateChange |= vdat[i+3] != oldId3;
|
||||
}
|
||||
} else {
|
||||
int baseVIdx = VoxelizedSection.getBaseIndexForLevel(lvl);
|
||||
|
||||
int secMsk = 0xF >> lvl;
|
||||
secMsk |= (secMsk << 5) | (secMsk << 10);
|
||||
@@ -60,17 +81,18 @@ public class WorldUpdater {
|
||||
for (int i = baseVIdx; i <= (0xFFF >> (lvl * 3)) + baseVIdx; i++) {
|
||||
int cSecIdx = secIdx + baseSec;
|
||||
secIdx = (secIdx + iSecMsk1) & secMsk;
|
||||
|
||||
long newId = vdat[i];
|
||||
long oldId = secD[cSecIdx]; secD[cSecIdx] = newId;
|
||||
nonAirCountDelta += (Mapper.isAir(newId)?0:1)-(Mapper.isAir(oldId)?0:1);//its 0:1 cause its nonAir
|
||||
long oldId = secD[cSecIdx];
|
||||
didStateChange |= newId != oldId;
|
||||
secD[cSecIdx] = newId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lvl == 0) {
|
||||
int nonAirCountDelta = section.lvl0NonAirCount-(4096-airCount);
|
||||
if (nonAirCountDelta != 0) {
|
||||
worldSection.addNonEmptyBlockCount(nonAirCountDelta);
|
||||
if (lvl == 0) {
|
||||
emptinessStateChange = worldSection.updateLvl0State() ? 2 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,9 +54,8 @@ public class Mapper {
|
||||
|
||||
|
||||
public static boolean isAir(long id) {
|
||||
int bId = getBlockId(id);
|
||||
//Note: air can mean void, cave or normal air, as the block state is remapped during ingesting
|
||||
return bId == 0;
|
||||
return (id&(((1L<<20)-1)<<27)) == 0;
|
||||
}
|
||||
|
||||
public static int getBlockId(long id) {
|
||||
|
||||
@@ -309,7 +309,16 @@ public class DHImporter implements IDataImporter {
|
||||
if ((x+1)%16==0) {
|
||||
for (int sz = 0; sz < 4; sz++) {
|
||||
for (int sy = 0; sy < this.worldHeightSections; sy++) {
|
||||
System.arraycopy(storage, (sz|(sy<<2))<<12, section.section, 0, 16 * 16 * 16);
|
||||
{
|
||||
int base = (sz|(sy<<2))<<12;
|
||||
int nonAirCount = 0;
|
||||
final var dat = section.section;
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
nonAirCount += Mapper.isAir(dat[i] = storage[i+base])?0:1;
|
||||
}
|
||||
section.lvl0NonAirCount = nonAirCount;
|
||||
}
|
||||
|
||||
WorldConversionFactory.mipSection(section, this.engine.getMapper());
|
||||
|
||||
section.setPosition(X*4+(x>>4), sy+(this.bottomOfWorld>>4), (Z*4)+sz);
|
||||
|
||||
Reference in New Issue
Block a user