Make light propagation mutate instead of create BlockPos to reduce GC pressure

This commit is contained in:
James Seibel
2023-08-31 07:55:43 -05:00
parent d4a75eb73c
commit 3148991667
2 changed files with 96 additions and 16 deletions
@@ -47,8 +47,7 @@ public class DhLightingEngine
DhChunkPos centerChunkPos = centerChunk.getChunkPos();
HashMap<DhChunkPos, IChunkWrapper> chunksByChunkPos = new HashMap<>(9);
AdjacentChunkHolder adjacentChunkHolder = new AdjacentChunkHolder(centerChunk);
// try-finally to handle the stableArray resources
@@ -85,7 +84,7 @@ public class DhLightingEngine
requestedAdjacentPositions.remove(chunk.getChunkPos());
// add the adjacent chunk
chunksByChunkPos.put(chunk.getChunkPos(), chunk);
adjacentChunkHolder.add(chunk);
@@ -147,7 +146,7 @@ public class DhLightingEngine
}
// validate that at least 1 chunk was found
if (chunksByChunkPos.size() == 0)
if (adjacentChunkHolder.size() == 0)
{
LOGGER.warn("Attempted to generate lighting for position [" + centerChunkPos + "], but neither that chunk nor any adjacent chunks were found. No chunk lighting was performed.");
return;
@@ -156,12 +155,12 @@ public class DhLightingEngine
// block light
this.propagateLightPosList(blockLightPosQueue, chunksByChunkPos,
this.propagateLightPosList(blockLightPosQueue, adjacentChunkHolder,
(neighbourChunk, relBlockPos) -> neighbourChunk.getDhBlockLight(relBlockPos.x, relBlockPos.y, relBlockPos.z),
(neighbourChunk, relBlockPos, newLightValue) -> neighbourChunk.setDhBlockLight(relBlockPos.x, relBlockPos.y, relBlockPos.z, newLightValue));
// sky light
this.propagateLightPosList(skyLightPosQueue, chunksByChunkPos,
this.propagateLightPosList(skyLightPosQueue, adjacentChunkHolder,
(neighbourChunk, relBlockPos) -> neighbourChunk.getDhSkyLight(relBlockPos.x, relBlockPos.y, relBlockPos.z),
(neighbourChunk, relBlockPos, newLightValue) -> neighbourChunk.setDhSkyLight(relBlockPos.x, relBlockPos.y, relBlockPos.z, newLightValue));
}
@@ -183,9 +182,15 @@ public class DhLightingEngine
/** Applies each {@link LightPos} from the queue to the given set of {@link IChunkWrapper}'s. */
private void propagateLightPosList(
StableLightPosStack lightPosQueue, HashMap<DhChunkPos, IChunkWrapper> chunksByChunkPos,
StableLightPosStack lightPosQueue, AdjacentChunkHolder adjacentChunkHolder,
IGetLightFunc getLightFunc, ISetLightFunc setLightFunc)
{
// these objects are saved so they can be mutated throughout the method,
// this reduces the number of allocations necessary, reducing GC pressure
final DhBlockPos neighbourBlockPos = new DhBlockPos();
final DhBlockPos relNeighbourBlockPos = new DhBlockPos();
// update each light position
while (!lightPosQueue.isEmpty())
{
@@ -200,14 +205,13 @@ public class DhLightingEngine
// propagate the lighting in each cardinal direction, IE: -x, +x, -y, +y, -z, +z
for (EDhDirection direction : EDhDirection.CARDINAL_DIRECTIONS)
{
DhBlockPos neighbourBlockPos = pos.offset(direction);
DhChunkPos neighbourChunkPos = new DhChunkPos(neighbourBlockPos);
pos.offset(direction, neighbourBlockPos); // mutates neighbourBlockPos
// converting the block pos into a relative position is necessary for accessing the light values in the chunk
DhBlockPos relNeighbourBlockPos = neighbourBlockPos.convertToChunkRelativePos();
neighbourBlockPos.convertToChunkRelativePos(relNeighbourBlockPos); // mutates relNeighbourBlockPos
// only continue if the light position is inside one of our chunks
IChunkWrapper neighbourChunk = chunksByChunkPos.get(neighbourChunkPos);
IChunkWrapper neighbourChunk = adjacentChunkHolder.getByBlockPos(neighbourBlockPos.x, neighbourBlockPos.z);
if (neighbourChunk == null)
{
// the light pos is outside our generator's range, ignore it
@@ -274,6 +278,41 @@ public class DhLightingEngine
}
/** holds the adjacent chunks without having to create new Pos objects */
private static class AdjacentChunkHolder
{
ArrayList<IChunkWrapper> chunkArray = new ArrayList<>(9);
public AdjacentChunkHolder(IChunkWrapper centerWrapper)
{
this.chunkArray.add(centerWrapper);
}
public int size() { return this.chunkArray.size(); }
public void add(IChunkWrapper centerWrapper) { this.chunkArray.add(centerWrapper); }
public IChunkWrapper getByBlockPos(int blockX, int blockZ)
{
// >> 4 is equivalent to dividing by 16
int chunkX = blockX >> 4;
int chunkZ = blockZ >> 4;
// since there will only ever be 9 items in the array, this sequential search should be fast enough
for (IChunkWrapper chunk : this.chunkArray)
{
if (chunk != null
&& chunk.getChunkPos().x == chunkX && chunk.getChunkPos().z == chunkZ)
{
return chunk;
}
}
return null;
}
}
/**
* Holds all potential {@link LightPos} objects a lighting task may need.
* This is done so existing {@link LightPos} objects can be repurposed instead of destroyed,
@@ -22,6 +22,7 @@ package com.seibel.distanthorizons.core.pos;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.util.LodUtil;
import javax.annotation.Nullable;
import java.util.Objects;
public class DhBlockPos
@@ -128,11 +129,39 @@ public class DhBlockPos
return asLong(x, y, z);
}
public DhBlockPos offset(EDhDirection direction) { return this.offset(direction.getNormal().x, direction.getNormal().y, direction.getNormal().z); }
public DhBlockPos offset(int x, int y, int z) { return new DhBlockPos(this.x + x, this.y + y, this.z + z); }
/** creates a new {@link DhBlockPos} with the given offset from the current pos. */
public DhBlockPos offset(EDhDirection direction) { return this.offset(direction, null); }
/** if not null, mutates "mutablePos" so it matches the current pos after being offset. Otherwise creates a new {@link DhBlockPos}. */
public DhBlockPos offset(EDhDirection direction, @Nullable DhBlockPos mutablePos) { return this.offset(direction.getNormal().x, direction.getNormal().y, direction.getNormal().z, mutablePos); }
/** Limits the block position to a value between 0 and 15 (inclusive) */
public DhBlockPos convertToChunkRelativePos()
public DhBlockPos offset(int x, int y, int z) { return this.offset(x,y,z, null); }
public DhBlockPos offset(int x, int y, int z, @Nullable DhBlockPos mutablePos)
{
int newX = this.x + x;
int newY = this.y + y;
int newZ = this.z + z;
if (mutablePos != null)
{
mutablePos.x = newX;
mutablePos.y = newY;
mutablePos.z = newZ;
return mutablePos;
}
else
{
return new DhBlockPos(this.x + x, this.y + y, this.z + z);
}
}
/** Returns a new {@link DhBlockPos} limits to a value between 0 and 15 (inclusive) */
public DhBlockPos convertToChunkRelativePos() { return this.convertToChunkRelativePos(null); }
/**
* Limits the block position to a value between 0 and 15 (inclusive)
* If not null, mutates "mutableBlockPos"
*/
public DhBlockPos convertToChunkRelativePos(@Nullable DhBlockPos mutableBlockPos)
{
// move the position into the range -15 and +15
int relX = (this.x % LodUtil.CHUNK_WIDTH);
@@ -143,7 +172,19 @@ public class DhBlockPos
relZ = (relZ < 0) ? (relZ + LodUtil.CHUNK_WIDTH) : relZ;
// the y value shouldn't need to be changed
return new DhBlockPos(relX, this.y, relZ);
if (mutableBlockPos != null)
{
mutableBlockPos.x = relX;
mutableBlockPos.z = relX;
return mutableBlockPos;
}
else
{
return new DhBlockPos(relX, this.y, relZ);
}
}
/**