Tune memory use on generic chunks, lighting lookup performance

This commit is contained in:
Mike Primm 2021-12-28 22:14:19 -06:00
parent adcfedd68e
commit 0166895a7f
14 changed files with 300 additions and 37 deletions

View File

@ -9,6 +9,7 @@ import org.dynmap.common.BiomeMap;
public class GenericChunk { public class GenericChunk {
public final int cx, cz; // Chunk coord (world coord / 16) public final int cx, cz; // Chunk coord (world coord / 16)
public final GenericChunkSection[] sections; public final GenericChunkSection[] sections;
public final int sectionCnt;
public final int cy_min; // CY value of first section in sections list (index = (Y >> 4) - cy_min public final int cy_min; // CY value of first section in sections list (index = (Y >> 4) - cy_min
public final long inhabitedTicks; public final long inhabitedTicks;
public final int dataVersion; // Version of chunk data loaded public final int dataVersion; // Version of chunk data loaded
@ -31,15 +32,16 @@ public class GenericChunk {
empty = empty && sections[off].isEmpty; empty = empty && sections[off].isEmpty;
} }
} }
this.sectionCnt = sections.length;
this.isEmpty = empty; this.isEmpty = empty;
} }
// Get section for given block Y coord // Get section for given block Y coord
public final GenericChunkSection getSection(int y) { public final GenericChunkSection getSection(int y) {
try { int idx = (y >> 4) - this.cy_min;
return this.sections[(y >> 4) - cy_min]; if ((idx < 0) || (idx >= sectionCnt)) {
} catch (IndexOutOfBoundsException ioobx) { // Builder and padding should be avoiding this, but be safe
return GenericChunkSection.EMPTY; return GenericChunkSection.EMPTY;
} }
return this.sections[idx];
} }
public final DynmapBlockState getBlockType(int x, int y, int z) { public final DynmapBlockState getBlockType(int x, int y, int z) {

View File

@ -31,6 +31,21 @@ public class GenericChunkSection {
return blocks[pos.soffset]; return blocks[pos.soffset];
} }
} }
private static class BlockStateAccess3DPalette implements BlockStateAccess {
private final DynmapBlockState palette[];
private final short[] blocks; // YZX order
// Array given to us by builder
BlockStateAccess3DPalette(DynmapBlockState pal[], short[] blks) {
blocks = blks;
palette = pal;
}
public final DynmapBlockState getBlock(int x, int y, int z) {
return palette[blocks[(256 * (y & 0xF)) + (16 * (z & 0xF)) + (x & 0xF)]];
}
public final DynmapBlockState getBlock(GenericChunkPos pos) {
return palette[blocks[pos.soffset]];
}
}
private static class BlockStateAccessSingle implements BlockStateAccess { private static class BlockStateAccessSingle implements BlockStateAccess {
private final DynmapBlockState block; private final DynmapBlockState block;
BlockStateAccessSingle(DynmapBlockState bs) { BlockStateAccessSingle(DynmapBlockState bs) {
@ -157,6 +172,8 @@ public class GenericChunkSection {
private LightingAccess em; private LightingAccess em;
private DynmapBlockState bsaccumsing; // Used for single private DynmapBlockState bsaccumsing; // Used for single
private DynmapBlockState bsaccum[]; // Use for incremental setting of 3D - YZX order private DynmapBlockState bsaccum[]; // Use for incremental setting of 3D - YZX order
private short[] bsblks; // Use for incremental setting of 3D palette - XZY order
private DynmapBlockState[] bspal; // Palette for bsblks
private BiomeMap baaccumsingle; // Use for single private BiomeMap baaccumsingle; // Use for single
private BiomeMap baaccum[]; // Use for incremental setting of 3D biome - YZX order or 2D biome (ZX order) length used to control which private BiomeMap baaccum[]; // Use for incremental setting of 3D biome - YZX order or 2D biome (ZX order) length used to control which
private boolean empty; private boolean empty;
@ -170,6 +187,8 @@ public class GenericChunkSection {
bsaccum = null; bsaccum = null;
baaccumsingle = BiomeMap.NULL; baaccumsingle = BiomeMap.NULL;
baaccum = null; baaccum = null;
bsblks = null;
bspal = null;
sk = defaultLight; sk = defaultLight;
em = defaultLight; em = defaultLight;
empty = true; empty = true;
@ -224,6 +243,8 @@ public class GenericChunkSection {
public Builder singleBlockState(DynmapBlockState block) { public Builder singleBlockState(DynmapBlockState block) {
bsaccumsing = block; bsaccumsing = block;
bsaccum = null; bsaccum = null;
bsblks = null;
bspal = null;
empty = block.isAir(); empty = block.isAir();
return this; return this;
} }
@ -238,6 +259,23 @@ public class GenericChunkSection {
empty = false; empty = false;
return this; return this;
} }
// Set block state palette (states will be indexes vs this
public Builder xyzBlockStatePalette(DynmapBlockState[] bspalette) {
if (bsblks == null) {
bsblks = new short[4096];
}
bspal = Arrays.copyOf(bspalette, bspalette.length);
return this;
}
// Set block state using palette
public Builder xyzBlockStateInPalette(int x, int y, int z, short palidx) {
if (bsblks == null) {
bsblks = new short[4096];
}
bsblks[((y & 0xF) << 8) + ((z & 0xF) << 4) + (x & 0xF)] = palidx;
empty = false;
return this;
}
// Build copy from existing section with new skylight (YZX nibble array) // Build copy from existing section with new skylight (YZX nibble array)
public GenericChunkSection buildFrom(GenericChunkSection s, byte[] sky) { public GenericChunkSection buildFrom(GenericChunkSection s, byte[] sky) {
LightingAccess skyA = new LightingAccess3D(sky); LightingAccess skyA = new LightingAccess3D(sky);
@ -272,6 +310,17 @@ public class GenericChunkSection {
bsaccum = null; bsaccum = null;
empty = false; empty = false;
} }
else if (bspal != null) { // 3D palette
// Only one state in palette?
if (bspal.length == 1) {
bs = new BlockStateAccessSingle(bspal[0]); // Just single
}
else {
bs = new BlockStateAccess3DPalette(bspal, bsblks);
}
bspal = null;
bsblks = null;
}
else if (bsaccumsing == DynmapBlockState.AIR) { // Just air? else if (bsaccumsing == DynmapBlockState.AIR) { // Just air?
bs = defaultBlockState; bs = defaultBlockState;
empty = true; empty = true;

View File

@ -101,7 +101,32 @@ public abstract class GenericMapChunkCache extends MapChunkCache {
return 0; return 0;
} }
} }
@Override
/**
* Get block sky and emitted light, relative to current coordinate
* @return (emitted light * 256) + sky light
*/
public final int getBlockLight(BlockStep step) {
int emit = 0, sky = 15;
GenericChunkSection sect;
if (step.yoff != 0) { // Y coord - snap is valid already
int ny = y + step.yoff;
sect = snap.getSection(ny);
emit = sect.emitted.getLight(x, ny, z);
sky = sect.sky.getLight(x, ny, z);
}
else {
int nx = x + step.xoff;
int nz = z + step.zoff;
int nchunkindex = ((nx >> 4) - x_min) + (((nz >> 4) - z_min) * x_dim);
if ((nchunkindex < snapcnt) && (nchunkindex >= 0)) {
sect = snaparray[nchunkindex].getSection(y);
emit = sect.emitted.getLight(nx, y, nz);
sky = sect.sky.getLight(nx, y, nz);
}
}
return (emit << 8) + sky;
}
@Override @Override
public final BiomeMap getBiome() { public final BiomeMap getBiome() {
try { try {
@ -1007,11 +1032,11 @@ public abstract class GenericMapChunkCache extends MapChunkCache {
} }
} }
else { else {
for (int j = 0; j < 4096; j++) { sbld.xyzBlockStatePalette(palette); // Set palette
int v = (dbp != null) ? dbp.getAt(j) : db.get(j); for (int j = 0; j < 4096; j++) {
DynmapBlockState bs = (v < palette.length) ? palette[v] : DynmapBlockState.AIR; int v = db != null ? db.get(j) : dbp.getAt(j);
sbld.xyzBlockState(j & 0xF, (j & 0xF00) >> 8, (j & 0xF0) >> 4, bs); sbld.xyzBlockStateInPalette(j & 0xF, (j & 0xF00) >> 8, (j & 0xF0) >> 4, (short)v);
} }
} }
} }
else if (sec.contains("block_states", GenericNBTCompound.TAG_COMPOUND)) { // 1.18 else if (sec.contains("block_states", GenericNBTCompound.TAG_COMPOUND)) { // 1.18
@ -1059,10 +1084,10 @@ public abstract class GenericMapChunkCache extends MapChunkCache {
} }
} }
else { else {
sbld.xyzBlockStatePalette(palette); // Set palette
for (int j = 0; j < 4096; j++) { for (int j = 0; j < 4096; j++) {
int v = db != null ? db.get(j) : dbp.getAt(j); int v = db != null ? db.get(j) : dbp.getAt(j);
DynmapBlockState bs = (v < palette.length) ? palette[v] : DynmapBlockState.AIR; sbld.xyzBlockStateInPalette(j & 0xF, (j & 0xF00) >> 8, (j & 0xF0) >> 4, (short)v);
sbld.xyzBlockState(j & 0xF, (j & 0xF00) >> 8, (j & 0xF0) >> 4, bs);
} }
} }
} }

View File

@ -149,8 +149,8 @@ public class IsoHDPerspective implements HDPerspective {
llcache = new LightLevels[4]; llcache = new LightLevels[4];
for(int i = 0; i < llcache.length; i++) for(int i = 0; i < llcache.length; i++)
llcache[i] = new LightLevels(); llcache[i] = new LightLevels();
custom_meshes = new DynLongHashMap(); custom_meshes = new DynLongHashMap(4096);
custom_fluid_meshes = new DynLongHashMap(); custom_fluid_meshes = new DynLongHashMap(4096);
modscale = basemodscale << scaled; modscale = basemodscale << scaled;
scalemodels = HDBlockModels.getModelsForScale(basemodscale << scaled); scalemodels = HDBlockModels.getModelsForScale(basemodscale << scaled);
} }
@ -158,13 +158,9 @@ public class IsoHDPerspective implements HDPerspective {
private final void updateSemitransparentLight(LightLevels ll) { private final void updateSemitransparentLight(LightLevels ll) {
int emitted = 0, sky = 0; int emitted = 0, sky = 0;
for(int i = 0; i < semi_steps.length; i++) { for(int i = 0; i < semi_steps.length; i++) {
BlockStep s = semi_steps[i]; int emit_sky_light = mapiter.getBlockLight(semi_steps[i]);
mapiter.stepPosition(s); if ((emit_sky_light >> 8) > emitted) emitted = (emit_sky_light >> 8);
int v = mapiter.getBlockEmittedLight(); if ((emit_sky_light & 0xF) > sky) sky = (emit_sky_light & 0xF);
if(v > emitted) emitted = v;
v = mapiter.getBlockSkyLight();
if(v > sky) sky = v;
mapiter.unstepPosition(s);
} }
ll.sky = sky; ll.sky = sky;
ll.emitted = emitted; ll.emitted = emitted;
@ -181,16 +177,10 @@ public class IsoHDPerspective implements HDPerspective {
ll.emitted = mapiter.getBlockEmittedLight(); ll.emitted = mapiter.getBlockEmittedLight();
break; break;
case OPAQUE: case OPAQUE:
if(HDBlockStateTextureMap.getTransparency(lastblocktype) != BlockTransparency.SEMITRANSPARENT) { if (HDBlockStateTextureMap.getTransparency(lastblocktype) != BlockTransparency.SEMITRANSPARENT) {
mapiter.unstepPosition(laststep); /* Back up to block we entered on */ int emit_sky_light = mapiter.getBlockLight(laststep.opposite());
if(mapiter.getY() < worldheight) { ll.sky = emit_sky_light & 0xF;
ll.sky = mapiter.getBlockSkyLight(); ll.emitted = emit_sky_light >> 8;
ll.emitted = mapiter.getBlockEmittedLight();
} else {
ll.sky = 15;
ll.emitted = 0;
}
mapiter.stepPosition(laststep);
} }
else { else {
mapiter.unstepPosition(laststep); /* Back up to block we entered on */ mapiter.unstepPosition(laststep); /* Back up to block we entered on */

View File

@ -236,6 +236,9 @@ public class DynLongHashMap
return getEntry(key) != null; return getEntry(key) != null;
} }
private static final int toHash(long key) {
return (int)((key >>> 32) ^ key);
}
/** /**
* Returns the entry associated with the specified key in the * Returns the entry associated with the specified key in the
* HashMap. Returns null if the HashMap contains no mapping * HashMap. Returns null if the HashMap contains no mapping
@ -243,7 +246,7 @@ public class DynLongHashMap
*/ */
Entry getEntry(long key) { Entry getEntry(long key) {
Entry tab[] = table; Entry tab[] = table;
int hash = (int) key; int hash = toHash(key);
int index = (hash & 0x7FFFFFFF) % tab.length; int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry e = tab[index]; e != null; e = e.next) for (Entry e = tab[index]; e != null; e = e.next)
@ -293,7 +296,7 @@ public class DynLongHashMap
*/ */
public Object put(long key, Object value) { public Object put(long key, Object value) {
Entry tab[] = table; Entry tab[] = table;
int hash = (int) key; int hash = toHash(key);
int index = (hash & 0x7FFFFFFF) % tab.length; int index = (hash & 0x7FFFFFFF) % tab.length;
// Look for entry in hash table // Look for entry in hash table
@ -340,7 +343,7 @@ public class DynLongHashMap
*/ */
Entry removeEntryForKey(long key) { Entry removeEntryForKey(long key) {
Entry tab[] = table; Entry tab[] = table;
int hash = (int) key; int hash = toHash(key);
int index = (hash & 0x7FFFFFFF) % tab.length; int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry e = tab[index], prev = null; e != null; for (Entry e = tab[index], prev = null; e != null;

View File

@ -26,6 +26,11 @@ public interface MapIterator extends MapDataContext {
* @return emitted light level * @return emitted light level
*/ */
int getBlockEmittedLight(); int getBlockEmittedLight();
/**
* Get block sky and emitted light, relative to current coordinate
* @return (emitted light * 256) + sky light
*/
int getBlockLight(BlockStep step);
/** /**
* Get biome at coordinates * Get biome at coordinates
* @return biome * @return biome

View File

@ -158,6 +158,30 @@ public abstract class AbstractMapChunkCache extends MapChunkCache {
} }
return 0; return 0;
} }
@Override
/**
* Get block sky and emitted light, relative to current coordinate
* @return (emitted light * 256) + sky light
*/
public final int getBlockLight(BlockStep step) {
int emit = 0, sky = 15;
if (step.yoff != 0) { // Y coord - snap is valid already
int ny = y + step.yoff;
emit = snap.getBlockEmittedLight(x, ny, z);
sky = snap.getBlockSkyLight(x, ny, z);
}
else {
int nx = x + step.xoff;
int nz = z + step.zoff;
int nchunkindex = ((nx >> 4) - x_min) + (((nz >> 4) - z_min) * x_dim);
if ((nchunkindex < snapcnt) && (nchunkindex >= 0)) {
emit = snaparray[nchunkindex].getBlockEmittedLight(nx, y, nz);
sky = snaparray[nchunkindex].getBlockSkyLight(nx, y, nz);
}
}
return (emit << 8) + sky;
}
private void biomePrep() { private void biomePrep() {
if(sameneighborbiomecnt != null) if(sameneighborbiomecnt != null)
return; return;

View File

@ -132,6 +132,29 @@ public class FabricMapChunkCache extends MapChunkCache {
return 0; return 0;
} }
} }
@Override
/**
* Get block sky and emitted light, relative to current coordinate
* @return (emitted light * 256) + sky light
*/
public final int getBlockLight(BlockStep step) {
int emit = 0, sky = 15;
if (step.yoff != 0) { // Y coord - snap is valid already
int ny = y + step.yoff;
emit = snap.getBlockEmittedLight(x, ny, z);
sky = snap.getBlockSkyLight(x, ny, z);
}
else {
int nx = x + step.xoff;
int nz = z + step.zoff;
int nchunkindex = ((nx >> 4) - x_min) + (((nz >> 4) - z_min) * x_dim);
if ((nchunkindex < snapcnt) && (nchunkindex >= 0)) {
emit = snaparray[nchunkindex].getBlockEmittedLight(nx, y, nz);
sky = snaparray[nchunkindex].getBlockSkyLight(nx, y, nz);
}
}
return (emit << 8) + sky;
}
private void biomePrep() { private void biomePrep() {
if (sameneighborbiomecnt != null) { if (sameneighborbiomecnt != null) {

View File

@ -132,6 +132,29 @@ public class FabricMapChunkCache extends MapChunkCache {
return 0; return 0;
} }
} }
@Override
/**
* Get block sky and emitted light, relative to current coordinate
* @return (emitted light * 256) + sky light
*/
public final int getBlockLight(BlockStep step) {
int emit = 0, sky = 15;
if (step.yoff != 0) { // Y coord - snap is valid already
int ny = y + step.yoff;
emit = snap.getBlockEmittedLight(x, ny, z);
sky = snap.getBlockSkyLight(x, ny, z);
}
else {
int nx = x + step.xoff;
int nz = z + step.zoff;
int nchunkindex = ((nx >> 4) - x_min) + (((nz >> 4) - z_min) * x_dim);
if ((nchunkindex < snapcnt) && (nchunkindex >= 0)) {
emit = snaparray[nchunkindex].getBlockEmittedLight(nx, y, nz);
sky = snaparray[nchunkindex].getBlockSkyLight(nx, y, nz);
}
}
return (emit << 8) + sky;
}
private void biomePrep() { private void biomePrep() {
if (sameneighborbiomecnt != null) { if (sameneighborbiomecnt != null) {

View File

@ -177,6 +177,29 @@ public class ForgeMapChunkCache extends MapChunkCache
return 0; return 0;
} }
} }
@Override
/**
* Get block sky and emitted light, relative to current coordinate
* @return (emitted light * 256) + sky light
*/
public final int getBlockLight(BlockStep step) {
int emit = 0, sky = 15;
if (step.yoff != 0) { // Y coord - snap is valid already
int ny = y + step.yoff;
emit = snap.getBlockEmittedLight(x, ny, z);
sky = snap.getBlockSkyLight(x, ny, z);
}
else {
int nx = x + step.xoff;
int nz = z + step.zoff;
int nchunkindex = ((nx >> 4) - x_min) + (((nz >> 4) - z_min) * x_dim);
if ((nchunkindex < snapcnt) && (nchunkindex >= 0)) {
emit = snaparray[nchunkindex].getBlockEmittedLight(nx, y, nz);
sky = snaparray[nchunkindex].getBlockSkyLight(nx, y, nz);
}
}
return (emit << 8) + sky;
}
private void biomePrep() private void biomePrep()
{ {
if (sameneighborbiomecnt != null) if (sameneighborbiomecnt != null)

View File

@ -177,7 +177,31 @@ public class ForgeMapChunkCache extends MapChunkCache
return 0; return 0;
} }
} }
private void biomePrep() @Override
/**
* Get block sky and emitted light, relative to current coordinate
* @return (emitted light * 256) + sky light
*/
public final int getBlockLight(BlockStep step) {
int emit = 0, sky = 15;
if (step.yoff != 0) { // Y coord - snap is valid already
int ny = y + step.yoff;
emit = snap.getBlockEmittedLight(x, ny, z);
sky = snap.getBlockSkyLight(x, ny, z);
}
else {
int nx = x + step.xoff;
int nz = z + step.zoff;
int nchunkindex = ((nx >> 4) - x_min) + (((nz >> 4) - z_min) * x_dim);
if ((nchunkindex < snapcnt) && (nchunkindex >= 0)) {
emit = snaparray[nchunkindex].getBlockEmittedLight(nx, y, nz);
sky = snaparray[nchunkindex].getBlockSkyLight(nx, y, nz);
}
}
return (emit << 8) + sky;
}
private void biomePrep()
{ {
if (sameneighborbiomecnt != null) if (sameneighborbiomecnt != null)
{ {

View File

@ -178,7 +178,31 @@ public class ForgeMapChunkCache extends MapChunkCache
return 0; return 0;
} }
} }
private void biomePrep() @Override
/**
* Get block sky and emitted light, relative to current coordinate
* @return (emitted light * 256) + sky light
*/
public final int getBlockLight(BlockStep step) {
int emit = 0, sky = 15;
if (step.yoff != 0) { // Y coord - snap is valid already
int ny = y + step.yoff;
emit = snap.getBlockEmittedLight(x, ny, z);
sky = snap.getBlockSkyLight(x, ny, z);
}
else {
int nx = x + step.xoff;
int nz = z + step.zoff;
int nchunkindex = ((nx >> 4) - x_min) + (((nz >> 4) - z_min) * x_dim);
if ((nchunkindex < snapcnt) && (nchunkindex >= 0)) {
emit = snaparray[nchunkindex].getBlockEmittedLight(nx, y, nz);
sky = snaparray[nchunkindex].getBlockSkyLight(nx, y, nz);
}
}
return (emit << 8) + sky;
}
private void biomePrep()
{ {
if (sameneighborbiomecnt != null) if (sameneighborbiomecnt != null)
{ {

View File

@ -174,7 +174,31 @@ public class ForgeMapChunkCache extends MapChunkCache
return 0; return 0;
} }
} }
private void biomePrep() @Override
/**
* Get block sky and emitted light, relative to current coordinate
* @return (emitted light * 256) + sky light
*/
public final int getBlockLight(BlockStep step) {
int emit = 0, sky = 15;
if (step.yoff != 0) { // Y coord - snap is valid already
int ny = y + step.yoff;
emit = snap.getBlockEmittedLight(x, ny, z);
sky = snap.getBlockSkyLight(x, ny, z);
}
else {
int nx = x + step.xoff;
int nz = z + step.zoff;
int nchunkindex = ((nx >> 4) - x_min) + (((nz >> 4) - z_min) * x_dim);
if ((nchunkindex < snapcnt) && (nchunkindex >= 0)) {
emit = snaparray[nchunkindex].getBlockEmittedLight(nx, y, nz);
sky = snaparray[nchunkindex].getBlockSkyLight(nx, y, nz);
}
}
return (emit << 8) + sky;
}
private void biomePrep()
{ {
if (sameneighborbiomecnt != null) if (sameneighborbiomecnt != null)
{ {

View File

@ -174,7 +174,31 @@ public class ForgeMapChunkCache extends MapChunkCache
return 0; return 0;
} }
} }
private void biomePrep() @Override
/**
* Get block sky and emitted light, relative to current coordinate
* @return (emitted light * 256) + sky light
*/
public final int getBlockLight(BlockStep step) {
int emit = 0, sky = 15;
if (step.yoff != 0) { // Y coord - snap is valid already
int ny = y + step.yoff;
emit = snap.getBlockEmittedLight(x, ny, z);
sky = snap.getBlockSkyLight(x, ny, z);
}
else {
int nx = x + step.xoff;
int nz = z + step.zoff;
int nchunkindex = ((nx >> 4) - x_min) + (((nz >> 4) - z_min) * x_dim);
if ((nchunkindex < snapcnt) && (nchunkindex >= 0)) {
emit = snaparray[nchunkindex].getBlockEmittedLight(nx, y, nz);
sky = snaparray[nchunkindex].getBlockSkyLight(nx, y, nz);
}
}
return (emit << 8) + sky;
}
private void biomePrep()
{ {
if (sameneighborbiomecnt != null) if (sameneighborbiomecnt != null)
{ {