From 163ee63f9eca3c2cce97ea0a3b6ebd1581d4e00a Mon Sep 17 00:00:00 2001 From: James Seibel Date: Tue, 10 Feb 2026 07:40:16 -0600 Subject: [PATCH] Reduce memory allocation slightly during LOD loading --- .../common/wrappers/WrapperFactory.java | 5 +- .../wrappers/block/BlockStateWrapper.java | 65 ++++++++--- .../wrappers/world/ClientLevelWrapper.java | 103 ++++++++++++++++-- coreSubProjects | 2 +- 4 files changed, 146 insertions(+), 29 deletions(-) diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/WrapperFactory.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/WrapperFactory.java index 2ef8041ab..b217242ff 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/WrapperFactory.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/WrapperFactory.java @@ -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 getRendererIgnoredBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredBlocks(levelWrapper); } + public ObjectOpenHashSet getRendererIgnoredBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredBlocks(levelWrapper); } @Override - public HashSet getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredCaveBlocks(levelWrapper); } + public ObjectOpenHashSet getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredCaveBlocks(levelWrapper); } @Override public void resetRendererIgnoredCaveBlocks() { BlockStateWrapper.clearRendererIgnoredCaveBlocks(); } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java index eec8492c5..70a0c5423 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java @@ -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 rendererIgnoredBlocks = null; - public static HashSet rendererIgnoredCaveBlocks = null; + public static ObjectOpenHashSet rendererIgnoredBlocks = null; + public static ObjectOpenHashSet 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 getRendererIgnoredBlocks(ILevelWrapper levelWrapper) + public static ObjectOpenHashSet getRendererIgnoredBlocks(ILevelWrapper levelWrapper) { // use the cached version if possible if (rendererIgnoredBlocks != null) @@ -317,7 +323,7 @@ public class BlockStateWrapper implements IBlockStateWrapper return rendererIgnoredBlocks; } - HashSet baseIgnoredBlock = new HashSet<>(); + ObjectOpenHashSet 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 getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper) + public static ObjectOpenHashSet getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper) { // use the cached version if possible if (rendererIgnoredCaveBlocks != null) @@ -334,7 +340,7 @@ public class BlockStateWrapper implements IBlockStateWrapper return rendererIgnoredCaveBlocks; } - HashSet baseIgnoredBlock = new HashSet<>(); + ObjectOpenHashSet 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 getAllBlockWrappers(ConfigEntry config, HashSet baseResourceLocations, ILevelWrapper levelWrapper) + private static ObjectOpenHashSet getAllBlockWrappers(ConfigEntry config, ObjectOpenHashSet baseResourceLocations, ILevelWrapper levelWrapper) { // get the base blocks - HashSet blockStringList = new HashSet<>(); + ObjectOpenHashSet 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 getAllBlockWrappers(HashSet blockResourceLocationSet, ILevelWrapper levelWrapper) + private static ObjectOpenHashSet getAllBlockWrappers(ObjectOpenHashSet blockResourceLocationSet, ILevelWrapper levelWrapper) { // deserialize each of the given resource locations - HashSet blockStateWrappers = new HashSet<>(); + ObjectOpenHashSet 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 + + } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ClientLevelWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ClientLevelWrapper.java index 98eed79be..14d20b086 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ClientLevelWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ClientLevelWrapper.java @@ -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 + + + } diff --git a/coreSubProjects b/coreSubProjects index 4d3242a37..457bbebbd 160000 --- a/coreSubProjects +++ b/coreSubProjects @@ -1 +1 @@ -Subproject commit 4d3242a3705e74d4285b16c89eddfe57654a49df +Subproject commit 457bbebbdde4066fa3092cf29ee41280cfeb11e3