Reduce memory allocation slightly during LOD loading

This commit is contained in:
James Seibel
2026-02-10 07:40:16 -06:00
parent 5897fc816c
commit 163ee63f9e
4 changed files with 146 additions and 29 deletions
@@ -39,6 +39,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.IBatchGeneratorEnvironmentWrapper;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.minecraft.client.multiplayer.ClientLevel;
#if MC_VER > MC_1_17_1
import net.minecraft.core.Holder;
@@ -121,9 +122,9 @@ public class WrapperFactory implements IWrapperFactory
public IBlockStateWrapper getAirBlockStateWrapper() { return BlockStateWrapper.AIR; }
@Override
public HashSet<IBlockStateWrapper> getRendererIgnoredBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredBlocks(levelWrapper); }
public ObjectOpenHashSet<IBlockStateWrapper> getRendererIgnoredBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredBlocks(levelWrapper); }
@Override
public HashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredCaveBlocks(levelWrapper); }
public ObjectOpenHashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredCaveBlocks(levelWrapper); }
@Override
public void resetRendererIgnoredCaveBlocks() { BlockStateWrapper.clearRendererIgnoredCaveBlocks(); }
@@ -28,6 +28,7 @@ import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.level.block.BeaconBeamBlock;
import net.minecraft.world.level.block.Block;
@@ -43,6 +44,7 @@ import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
@@ -100,8 +102,8 @@ public class BlockStateWrapper implements IBlockStateWrapper
"netherite_block"
);
public static HashSet<IBlockStateWrapper> rendererIgnoredBlocks = null;
public static HashSet<IBlockStateWrapper> rendererIgnoredCaveBlocks = null;
public static ObjectOpenHashSet<IBlockStateWrapper> rendererIgnoredBlocks = null;
public static ObjectOpenHashSet<IBlockStateWrapper> rendererIgnoredCaveBlocks = null;
/** keep track of broken blocks so we don't log every time */
#if MC_VER <= MC_1_21_10
@@ -136,6 +138,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
//==============//
// constructors //
//==============//
//region
public static BlockStateWrapper fromBlockState(BlockState blockState, ILevelWrapper levelWrapper)
{
@@ -177,7 +180,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
}
}
private BlockStateWrapper(BlockState blockState, ILevelWrapper levelWrapper)
private BlockStateWrapper(@Nullable BlockState blockState, ILevelWrapper levelWrapper)
{
this.blockState = blockState;
this.serialString = this.serialize(levelWrapper);
@@ -299,17 +302,20 @@ public class BlockStateWrapper implements IBlockStateWrapper
//LOGGER.trace("Created BlockStateWrapper ["+this.serialString+"] for ["+blockState+"] with material ID ["+this.EDhApiBlockMaterialId+"]");
}
//endregion
//====================//
// LodBuilder methods //
//====================//
//region
/**
* Requires a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one.
* This way the method won't accidentally be called before the deserialization can be completed.
*/
public static HashSet<IBlockStateWrapper> getRendererIgnoredBlocks(ILevelWrapper levelWrapper)
public static ObjectOpenHashSet<IBlockStateWrapper> getRendererIgnoredBlocks(ILevelWrapper levelWrapper)
{
// use the cached version if possible
if (rendererIgnoredBlocks != null)
@@ -317,7 +323,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
return rendererIgnoredBlocks;
}
HashSet<String> baseIgnoredBlock = new HashSet<>();
ObjectOpenHashSet<String> baseIgnoredBlock = new ObjectOpenHashSet<>();
baseIgnoredBlock.add(AIR_STRING);
rendererIgnoredBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderBlockCsv, baseIgnoredBlock, levelWrapper);
return rendererIgnoredBlocks;
@@ -326,7 +332,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
* Requires a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one.
* This way the method won't accidentally be called before the deserialization can be completed.
*/
public static HashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper)
public static ObjectOpenHashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper)
{
// use the cached version if possible
if (rendererIgnoredCaveBlocks != null)
@@ -334,7 +340,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
return rendererIgnoredCaveBlocks;
}
HashSet<String> baseIgnoredBlock = new HashSet<>();
ObjectOpenHashSet<String> baseIgnoredBlock = new ObjectOpenHashSet<>();
baseIgnoredBlock.add(AIR_STRING);
rendererIgnoredCaveBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderCaveBlockCsv, baseIgnoredBlock, levelWrapper);
return rendererIgnoredCaveBlocks;
@@ -343,14 +349,19 @@ public class BlockStateWrapper implements IBlockStateWrapper
public static void clearRendererIgnoredBlocks() { rendererIgnoredBlocks = null; }
public static void clearRendererIgnoredCaveBlocks() { rendererIgnoredCaveBlocks = null; }
//endregion
//=====================//
// lod builder helpers //
//=====================//
//region
private static HashSet<IBlockStateWrapper> getAllBlockWrappers(ConfigEntry<String> config, HashSet<String> baseResourceLocations, ILevelWrapper levelWrapper)
private static ObjectOpenHashSet<IBlockStateWrapper> getAllBlockWrappers(ConfigEntry<String> config, ObjectOpenHashSet<String> baseResourceLocations, ILevelWrapper levelWrapper)
{
// get the base blocks
HashSet<String> blockStringList = new HashSet<>();
ObjectOpenHashSet<String> blockStringList = new ObjectOpenHashSet<>();
if (baseResourceLocations != null)
{
blockStringList.addAll(baseResourceLocations);
@@ -365,10 +376,10 @@ public class BlockStateWrapper implements IBlockStateWrapper
return getAllBlockWrappers(blockStringList, levelWrapper);
}
private static HashSet<IBlockStateWrapper> getAllBlockWrappers(HashSet<String> blockResourceLocationSet, ILevelWrapper levelWrapper)
private static ObjectOpenHashSet<IBlockStateWrapper> getAllBlockWrappers(ObjectOpenHashSet<String> blockResourceLocationSet, ILevelWrapper levelWrapper)
{
// deserialize each of the given resource locations
HashSet<IBlockStateWrapper> blockStateWrappers = new HashSet<>();
ObjectOpenHashSet<IBlockStateWrapper> blockStateWrappers = new ObjectOpenHashSet<>();
for (String blockResourceLocation : blockResourceLocationSet)
{
try
@@ -417,11 +428,14 @@ public class BlockStateWrapper implements IBlockStateWrapper
return blockStateWrappers;
}
//endregion
//=================//
// wrapper methods //
//=================//
//region
@Override
public int getOpacity() { return this.opacity; }
@@ -529,25 +543,37 @@ public class BlockStateWrapper implements IBlockStateWrapper
public boolean isAir() { return this.isAir(this.blockState); }
public boolean isAir(BlockState blockState) { return blockState == null || blockState.isAir(); }
private Boolean blockIsSolid = null;
@Override
public boolean isSolid()
{
if (this.isAir())
if (this.isAir()
|| this.blockState == null) // == null isn't necessary since its handled in isAir() but is here to prevent intellij from complaining
{
return false;
}
// cached since getCollisionShape() is a dictionary lookup that allocates objects
// and this call is used in a high traffic location
if (this.blockIsSolid != null)
{
return this.blockIsSolid;
}
#if MC_VER < MC_1_20_1
return this.blockState.getMaterial().isSolid();
this.blockIsSolid = this.blockState.getMaterial().isSolid();
#else
return !this.blockState.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).isEmpty();
this.blockIsSolid = !this.blockState.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).isEmpty();
#endif
return this.blockIsSolid;
}
@Override
public boolean isLiquid()
{
if (this.isAir())
if (this.isAir()
|| this.blockState == null) // == null isn't necessary since its handled in isAir() but is here to prevent intellij from complaining
{
return false;
}
@@ -579,11 +605,14 @@ public class BlockStateWrapper implements IBlockStateWrapper
@Override
public String toString() { return this.getSerialString(); }
//endregion
//=======================//
// serialization methods //
//=======================//
//region
private String serialize(ILevelWrapper levelWrapper)
{
@@ -811,11 +840,14 @@ public class BlockStateWrapper implements IBlockStateWrapper
return stringBuilder.toString();
}
//endregion
//==============//
// Iris methods //
//==============//
//region
private EDhApiBlockMaterial calculateEDhApiBlockMaterialId()
{
@@ -920,4 +952,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
}
}
//endregion
}
@@ -87,14 +87,18 @@ public class ClientLevelWrapper implements IClientLevelWrapper
//=============//
// constructor //
//=============//
//region
protected ClientLevelWrapper(ClientLevel level) { this.level = level; }
//endregion
//==================//
// instance methods //
//==================//
//region
/**
* can be used when speed is important and the same level is likely to be passed in,
@@ -201,11 +205,14 @@ public class ClientLevelWrapper implements IClientLevelWrapper
}
}
//endregion
//====================//
// base level methods //
//====================//
//region
@Override
public int getBlockColor(DhBlockPos blockPos, IBiomeWrapper biome, FullDataSourceV2 fullDataSource, IBlockStateWrapper blockWrapper)
@@ -221,7 +228,6 @@ public class ClientLevelWrapper implements IClientLevelWrapper
return blockColorCache.getColor((BiomeWrapper) biome, fullDataSource, blockPos);
}
@Override
public int getDirtBlockColor()
{
@@ -245,24 +251,43 @@ public class ClientLevelWrapper implements IClientLevelWrapper
@Override
public void clearBlockColorCache() { this.blockColorCacheByBlockState.clear(); }
private IDimensionTypeWrapper dimensionTypeWrapper = null;
@Override
public IDimensionTypeWrapper getDimensionType()
{
// cached since dimensionType() is a dictionary lookup that allocates objects
// and this call is used in a high traffic location
if (this.dimensionTypeWrapper != null)
{
return this.dimensionTypeWrapper;
}
#if MC_VER <= MC_1_21_10
return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType());
this.dimensionTypeWrapper = DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType());
#else
return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType(), this.getDimensionName());
this.dimensionTypeWrapper = DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType(), this.getDimensionName());
#endif
return this.dimensionTypeWrapper;
}
private String dimensionName = null;
@Override
public String getDimensionName()
{
// cached since toString() allocates a new string each time
// and this call is used in a high traffic location
if (this.dimensionName != null)
{
return this.dimensionName;
}
#if MC_VER <= MC_1_21_10
return this.level.dimension().location().toString();
this.dimensionName = this.level.dimension().location().toString();
#else
return this.level.dimension().identifier().toString();
this.dimensionName = this.level.dimension().identifier().toString();
#endif
return this.dimensionName;
}
@Override
@@ -276,25 +301,72 @@ public class ClientLevelWrapper implements IClientLevelWrapper
public ClientLevel getLevel() { return this.level; }
private Boolean dimHasCeiling = null;
@Override
public boolean hasCeiling() { return this.level.dimensionType().hasCeiling(); }
public boolean hasCeiling()
{
// cached since dimensionType() is a dictionary lookup that allocates objects
// and this call is used in a high traffic location
if (this.dimHasCeiling != null)
{
return this.dimHasCeiling;
}
this.dimHasCeiling = this.level.dimensionType().hasCeiling();
return this.dimHasCeiling;
}
private Boolean dimHasSkyLight = null;
@Override
public boolean hasSkyLight() { return this.level.dimensionType().hasSkyLight(); }
public boolean hasSkyLight()
{
// cached since dimensionType() is a dictionary lookup that allocates objects
// and this call is used in a high traffic location
if (this.dimHasSkyLight != null)
{
return this.dimHasSkyLight;
}
this.dimHasSkyLight = this.level.dimensionType().hasSkyLight();
return this.dimHasSkyLight;
}
private Integer dimMaxHeight = null;
@Override
public int getMaxHeight() { return this.level.getHeight(); }
public int getMaxHeight()
{
// cached since getHeight() is a dictionary lookup that allocates objects
// and this call is used in a high traffic location
if (this.dimMaxHeight != null)
{
return this.dimMaxHeight;
}
this.dimMaxHeight = this.level.getHeight();
return this.dimMaxHeight;
}
private Integer dimMinHeight = null;
@Override
public int getMinHeight()
{
// cached since getMinY() is a dictionary lookup that allocates objects
// and this call is used in a high traffic location
if (this.dimMinHeight != null)
{
return this.dimMinHeight;
}
#if MC_VER < MC_1_17_1
return 0;
this.dimMinHeight = 0;
#elif MC_VER < MC_1_21_3
return this.level.getMinBuildHeight();
this.dimMinHeight = this.level.getMinBuildHeight();
#else
return this.level.getMinY();
this.dimMinHeight = this.level.getMinY();
#endif
return this.dimMinHeight;
}
@Override
@@ -318,12 +390,14 @@ public class ClientLevelWrapper implements IClientLevelWrapper
return this.dhLevel.getSaveStructure().getSaveFolder(this);
}
//endregion
//===================//
// generic rendering //
//===================//
//region
@Override
public void setDhLevel(IDhLevel dhLevel) { this.dhLevel = dhLevel; }
@@ -410,11 +484,14 @@ public class ClientLevelWrapper implements IClientLevelWrapper
#endif
}
//endregion
//================//
// base overrides //
//================//
//region
@Override
public String toString()
@@ -427,4 +504,8 @@ public class ClientLevelWrapper implements IClientLevelWrapper
return "Wrapped{" + this.level.toString() + "@" + this.getDhIdentifier() + "}";
}
//endregion
}