Minor Lighting Engine GC optimizations

This commit is contained in:
James Seibel
2023-09-10 17:09:33 -05:00
parent 7d84e05b1f
commit d3865551a5
4 changed files with 42 additions and 24 deletions
@@ -37,6 +37,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
@@ -204,7 +205,7 @@ public class ServerApi
{
// generate the chunk's lighting, ignoring neighbors.
// not a perfect solution, but should prevent chunks from having completely broken lighting
List<IChunkWrapper> nearbyChunkList = new LinkedList<>();
ArrayList<IChunkWrapper> nearbyChunkList = new ArrayList<>(1);
nearbyChunkList.add(chunkWrapper);
DhLightingEngine.INSTANCE.lightChunk(chunkWrapper, nearbyChunkList, level.hasSkyLight() ? 15 : 0);
chunkWrapper.setUseDhLighting(true);
@@ -19,7 +19,6 @@
package com.seibel.distanthorizons.core.generation;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
@@ -42,6 +41,15 @@ public class DhLightingEngine
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public static final DhLightingEngine INSTANCE = new DhLightingEngine();
/**
* Minor garbage collection optimization. <br>
* Since these objects are always mutated anyway, using a {@link ThreadLocal} will allow us to
* only create as many of these {@link DhBlockPos} as necessary.
*/
private static final ThreadLocal<DhBlockPos> PRIMARY_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new DhBlockPos());
private static final ThreadLocal<DhBlockPos> SECONDARY_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new DhBlockPos());
private DhLightingEngine() { }
@@ -56,7 +64,7 @@ public class DhLightingEngine
* @param nearbyChunkList should also contain centerChunk
* @param maxSkyLight should be a value between 0 and 15
*/
public void lightChunk(IChunkWrapper centerChunk, List<IChunkWrapper> nearbyChunkList, int maxSkyLight)
public void lightChunk(IChunkWrapper centerChunk, ArrayList<IChunkWrapper> nearbyChunkList, int maxSkyLight)
{
DhChunkPos centerChunkPos = centerChunk.getChunkPos();
AdjacentChunkHolder adjacentChunkHolder = new AdjacentChunkHolder(centerChunk);
@@ -88,8 +96,10 @@ public class DhLightingEngine
// find all adjacent chunks
// and get any necessary info from them
boolean warningLogged = false;
for (IChunkWrapper chunk : nearbyChunkList)
for (int chunkIndex = 0; chunkIndex < nearbyChunkList.size(); chunkIndex++) // using iterators in high traffic areas can cause GC issues due to allocating a bunch of iterators, use an indexed for-loop instead
{
IChunkWrapper chunk = nearbyChunkList.get(chunkIndex);
if (chunk != null && requestedAdjacentPositions.contains(chunk.getChunkPos()))
{
// remove the newly found position
@@ -101,17 +111,22 @@ public class DhLightingEngine
// get and set the adjacent chunk's initial block lights
List<DhBlockPos> blockLightPosList = chunk.getBlockLightPosList();
for (DhBlockPos blockLightPos : blockLightPosList)
final DhBlockPos relLightBlockPos = PRIMARY_BLOCK_POS_REF.get();
final DhBlockPos relBlockPos = SECONDARY_BLOCK_POS_REF.get();
ArrayList<DhBlockPos> blockLightPosList = chunk.getBlockLightPosList();
for (int blockLightIndex = 0; blockLightIndex < blockLightPosList.size(); blockLightIndex++) // using iterators in high traffic areas can cause GC issues due to allocating a bunch of iterators, use an indexed for-loop instead
{
DhBlockPos blockLightPos = blockLightPosList.get(blockLightIndex);
blockLightPos.mutateToChunkRelativePos(relLightBlockPos);
// get the light
DhBlockPos relLightBlockPos = blockLightPos.convertToChunkRelativePos();
IBlockStateWrapper blockState = chunk.getBlockState(relLightBlockPos);
int lightValue = blockState.getLightEmission();
blockLightPosQueue.push(blockLightPos.x, blockLightPos.y, blockLightPos.z, lightValue);
// set the light
DhBlockPos relBlockPos = blockLightPos.convertToChunkRelativePos();
blockLightPos.mutateToChunkRelativePos(relBlockPos);
chunk.setDhBlockLight(relBlockPos.x, relBlockPos.y, relBlockPos.z, lightValue);
}
@@ -142,7 +157,7 @@ public class DhLightingEngine
// set the light
DhBlockPos relBlockPos = skyLightPos.convertToChunkRelativePos();
skyLightPos.mutateToChunkRelativePos(relBlockPos);
chunk.setDhSkyLight(relBlockPos.x, relBlockPos.y, relBlockPos.z, maxSkyLight);
}
}
@@ -199,8 +214,8 @@ public class DhLightingEngine
{
// 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();
final DhBlockPos neighbourBlockPos = PRIMARY_BLOCK_POS_REF.get();
final DhBlockPos relNeighbourBlockPos = SECONDARY_BLOCK_POS_REF.get();
// update each light position
@@ -215,11 +230,10 @@ public class DhLightingEngine
// propagate the lighting in each cardinal direction, IE: -x, +x, -y, +y, -z, +z
for (EDhDirection direction : EDhDirection.CARDINAL_DIRECTIONS)
for (EDhDirection direction : EDhDirection.CARDINAL_DIRECTIONS) // since this is an array instead of an ArrayList this advanced for-loop shouldn't cause any GC issues
{
pos.offset(direction, neighbourBlockPos); // mutates neighbourBlockPos
// converting the block pos into a relative position is necessary for accessing the light values in the chunk
neighbourBlockPos.convertToChunkRelativePos(relNeighbourBlockPos); // mutates relNeighbourBlockPos
pos.mutateOffset(direction, neighbourBlockPos);
neighbourBlockPos.mutateToChunkRelativePos(relNeighbourBlockPos);
// only continue if the light position is inside one of our chunks
@@ -313,9 +327,11 @@ public class DhLightingEngine
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)
for (int i = 0; i < this.chunkArray.size(); i++) // using iterators in high traffic areas can cause GC issues due to allocating a bunch of iterators, use an indexed for-loop instead
{
if (chunk != null
IChunkWrapper chunk = this.chunkArray.get(i);
if (chunk != null
&& chunk.getChunkPos().x == chunkX && chunk.getChunkPos().z == chunkZ)
{
return chunk;
@@ -130,12 +130,12 @@ public class DhBlockPos
}
/** creates a new {@link DhBlockPos} with the given offset from the current pos. */
public DhBlockPos offset(EDhDirection direction) { return this.offset(direction, null); }
public DhBlockPos offset(EDhDirection direction) { return this.mutateOffset(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); }
public DhBlockPos mutateOffset(EDhDirection direction, @Nullable DhBlockPos mutablePos) { return this.mutateOffset(direction.getNormal().x, direction.getNormal().y, direction.getNormal().z, mutablePos); }
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)
public DhBlockPos offset(int x, int y, int z) { return this.mutateOffset(x,y,z, null); }
public DhBlockPos mutateOffset(int x, int y, int z, @Nullable DhBlockPos mutablePos)
{
int newX = this.x + x;
int newY = this.y + y;
@@ -156,12 +156,12 @@ public class DhBlockPos
}
/** Returns a new {@link DhBlockPos} limits to a value between 0 and 15 (inclusive) */
public DhBlockPos convertToChunkRelativePos() { return this.convertToChunkRelativePos(null); }
public DhBlockPos convertToChunkRelativePos() { return this.mutateToChunkRelativePos(null); }
/**
* Limits the block position to a value between 0 and 15 (inclusive)
* If not null, mutates "mutableBlockPos"
*/
public DhBlockPos convertToChunkRelativePos(@Nullable DhBlockPos mutableBlockPos)
public DhBlockPos mutateToChunkRelativePos(@Nullable DhBlockPos mutableBlockPos)
{
// move the position into the range -15 and +15
int relX = (this.x % LodUtil.CHUNK_WIDTH);
@@ -28,6 +28,7 @@ import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindab
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import java.util.ArrayList;
import java.util.List;
public interface IChunkWrapper extends IBindable
@@ -98,7 +99,7 @@ public interface IChunkWrapper extends IBindable
List<DhBlockPos> getBlockLightPosList();
ArrayList<DhBlockPos> getBlockLightPosList();
default boolean blockPosInsideChunk(DhBlockPos blockPos) { return this.blockPosInsideChunk(blockPos.x, blockPos.y, blockPos.z); }