From ef6fc07cd3261210f9195726fc3accd1b950b28d Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sun, 10 Sep 2023 17:10:01 -0500 Subject: [PATCH 1/8] Minor ChunkWrapper.getBlockState() GC optimization --- .../common/wrappers/chunk/ChunkWrapper.java | 26 +++++++++++++++++-- coreSubProjects | 2 +- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java index ba93d3755..b62bdbb08 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java @@ -76,6 +76,9 @@ public class ChunkWrapper implements IChunkWrapper private final byte[] blockLightArray; private final byte[] skyLightArray; + /** cache so we don't have to hit the vanilla chunk as often. */ + private final IBlockStateWrapper[] blockStateWrappers; + private ArrayList blockLightPosList = null; private boolean useDhLighting; @@ -115,6 +118,8 @@ public class ChunkWrapper implements IChunkWrapper this.blockLightArray = new byte[LodUtil.CHUNK_WIDTH * LodUtil.CHUNK_WIDTH * (this.getHeight() + 1)]; this.skyLightArray = new byte[LodUtil.CHUNK_WIDTH * LodUtil.CHUNK_WIDTH * (this.getHeight() + 1)]; + this.blockStateWrappers = new IBlockStateWrapper[LodUtil.CHUNK_WIDTH * LodUtil.CHUNK_WIDTH * (this.getHeight() + 1)]; + chunksNeedingClientLightUpdating.add(this); } @@ -314,7 +319,7 @@ public class ChunkWrapper implements IChunkWrapper } @Override - public List getBlockLightPosList() + public ArrayList getBlockLightPosList() { // only populate the list once if (this.blockLightPosList == null) @@ -372,7 +377,21 @@ public class ChunkWrapper implements IChunkWrapper @Override public IBlockStateWrapper getBlockState(int relX, int relY, int relZ) { - return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(new BlockPos(relX, relY, relZ)), this.wrappedLevel); + this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ); + + int index = this.relativeBlockPosToIndex(relX, relY, relZ); + IBlockStateWrapper cachedBlockState = this.blockStateWrappers[index]; + if (cachedBlockState != null) + { + return cachedBlockState; + } + else + { + // the wrapper is cached to prevent having to create a bunch of new BlockPos and hitting the vanilla chunk object + IBlockStateWrapper blockState = BlockStateWrapper.fromBlockState(this.chunk.getBlockState(new BlockPos(relX, relY, relZ)), this.wrappedLevel); + this.blockStateWrappers[index] = blockState; + return blockState; + } } @Override @@ -442,7 +461,10 @@ public class ChunkWrapper implements IChunkWrapper private void throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(int x, int y, int z) throws IndexOutOfBoundsException { if (RUN_RELATIVE_POS_INDEX_VALIDATION) + { return; + } + // FIXME +1 is to handle the fact that LodDataBuilder adds +1 to all block lighting calculations, also done in the constructor int minHeight = this.getMinBuildHeight(); diff --git a/coreSubProjects b/coreSubProjects index 7d84e05b1..d3865551a 160000 --- a/coreSubProjects +++ b/coreSubProjects @@ -1 +1 @@ -Subproject commit 7d84e05b1f18240df8dab79002d6d8103b5054a6 +Subproject commit d3865551a56b94b7fde500d09c1a181fafe1a827 From 2107d3cbbdf59709103800ec04fcbc1a2768b9fc Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sun, 10 Sep 2023 19:35:37 -0500 Subject: [PATCH 2/8] Optimize BlockState/Biome Wrapper getter methods The lambdas were being newly created for each get() which became difficult for the GC to handle. --- .../common/wrappers/block/BiomeWrapper.java | 16 +++++++++++++--- .../common/wrappers/block/BlockStateWrapper.java | 12 +++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BiomeWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BiomeWrapper.java index 906882cd1..ff03efcab 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BiomeWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BiomeWrapper.java @@ -66,9 +66,9 @@ public class BiomeWrapper implements IBiomeWrapper private static final Logger LOGGER = LogManager.getLogger(); #if PRE_MC_1_18_2 - public static final ConcurrentMap biomeWrapperMap = new ConcurrentHashMap<>(); + public static final ConcurrentMap WRAPPER_BY_BIOME = new ConcurrentHashMap<>(); #else - public static final ConcurrentMap, BiomeWrapper> biomeWrapperMap = new ConcurrentHashMap<>(); + public static final ConcurrentMap, BiomeWrapper> WRAPPER_BY_BIOME = new ConcurrentHashMap<>(); #endif public static final String EMPTY_STRING = "EMPTY"; @@ -100,7 +100,17 @@ public class BiomeWrapper implements IBiomeWrapper return EMPTY_WRAPPER; } - return biomeWrapperMap.computeIfAbsent(biome, newBiome -> new BiomeWrapper(newBiome, levelWrapper)); + + if (WRAPPER_BY_BIOME.containsKey(biome)) + { + return WRAPPER_BY_BIOME.get(biome); + } + else + { + BiomeWrapper newWrapper = new BiomeWrapper(biome, levelWrapper); + WRAPPER_BY_BIOME.put(biome, newWrapper); + return newWrapper; + } } private BiomeWrapper(#if PRE_MC_1_18_2 Biome #else Holder #endif biome, ILevelWrapper levelWrapper) 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 8f69f8b2b..87fcfc854 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 @@ -91,7 +91,17 @@ public class BlockStateWrapper implements IBlockStateWrapper return AIR; } - return WRAPPER_BY_BLOCK_STATE.computeIfAbsent(blockState, newBlockState -> new BlockStateWrapper(newBlockState, levelWrapper)); + + if (WRAPPER_BY_BLOCK_STATE.containsKey(blockState)) + { + return WRAPPER_BY_BLOCK_STATE.get(blockState); + } + else + { + BlockStateWrapper newWrapper = new BlockStateWrapper(blockState, levelWrapper); + WRAPPER_BY_BLOCK_STATE.put(blockState, newWrapper); + return newWrapper; + } } private BlockStateWrapper(BlockState blockState, ILevelWrapper levelWrapper) From e4a7056d486107cd43540e6b35fe417adcb371c0 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sun, 10 Sep 2023 19:39:34 -0500 Subject: [PATCH 3/8] Optimize ChunkWrapper.getBlockState() --- .../common/wrappers/chunk/ChunkWrapper.java | 30 +++++++------------ 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java index b62bdbb08..dab14002e 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java @@ -61,7 +61,10 @@ public class ChunkWrapper implements IChunkWrapper private static final Logger LOGGER = DhLoggerBuilder.getLogger(); /** useful for debugging, but can slow down chunk operations quite a bit due to being called every time. */ - private static final boolean RUN_RELATIVE_POS_INDEX_VALIDATION = false; + private static final boolean RUN_RELATIVE_POS_INDEX_VALIDATION = false; + + /** can be used for interactions with the underlying chunk where creating new BlockPos objects could cause issues for the garbage collector. */ + private static final ThreadLocal MUTABLE_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new BlockPos.MutableBlockPos()); private final ChunkAccess chunk; @@ -76,9 +79,6 @@ public class ChunkWrapper implements IChunkWrapper private final byte[] blockLightArray; private final byte[] skyLightArray; - /** cache so we don't have to hit the vanilla chunk as often. */ - private final IBlockStateWrapper[] blockStateWrappers; - private ArrayList blockLightPosList = null; private boolean useDhLighting; @@ -118,8 +118,6 @@ public class ChunkWrapper implements IChunkWrapper this.blockLightArray = new byte[LodUtil.CHUNK_WIDTH * LodUtil.CHUNK_WIDTH * (this.getHeight() + 1)]; this.skyLightArray = new byte[LodUtil.CHUNK_WIDTH * LodUtil.CHUNK_WIDTH * (this.getHeight() + 1)]; - this.blockStateWrappers = new IBlockStateWrapper[LodUtil.CHUNK_WIDTH * LodUtil.CHUNK_WIDTH * (this.getHeight() + 1)]; - chunksNeedingClientLightUpdating.add(this); } @@ -377,21 +375,13 @@ public class ChunkWrapper implements IChunkWrapper @Override public IBlockStateWrapper getBlockState(int relX, int relY, int relZ) { - this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ); + BlockPos.MutableBlockPos blockPos = MUTABLE_BLOCK_POS_REF.get(); - int index = this.relativeBlockPosToIndex(relX, relY, relZ); - IBlockStateWrapper cachedBlockState = this.blockStateWrappers[index]; - if (cachedBlockState != null) - { - return cachedBlockState; - } - else - { - // the wrapper is cached to prevent having to create a bunch of new BlockPos and hitting the vanilla chunk object - IBlockStateWrapper blockState = BlockStateWrapper.fromBlockState(this.chunk.getBlockState(new BlockPos(relX, relY, relZ)), this.wrappedLevel); - this.blockStateWrappers[index] = blockState; - return blockState; - } + blockPos.setX(relX); + blockPos.setY(relY); + blockPos.setZ(relZ); + + return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(blockPos), this.wrappedLevel); } @Override From c1fda715d082aa8d9d47deed80ab09376b3ef24a Mon Sep 17 00:00:00 2001 From: James Seibel Date: Mon, 11 Sep 2023 07:26:09 -0500 Subject: [PATCH 4/8] Improve LOD Building and Lighting speed for BigGlobe worlds Thanks Builderb0y! --- .../common/wrappers/chunk/ChunkWrapper.java | 16 ++++++++++++++++ coreSubProjects | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java index dab14002e..70d81e48b 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java @@ -31,6 +31,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.coreapi.util.BitShiftUtil; import net.minecraft.client.multiplayer.ClientChunkCache; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.BlockPos; @@ -149,6 +150,21 @@ public class ChunkWrapper implements IChunkWrapper @Override public int getMaxBuildHeight() { return this.chunk.getMaxBuildHeight(); } + @Override + public int getMinFilledHeight() + { + LevelChunkSection[] sections = this.chunk.getSections(); + for (int index = 0; index < sections.length; index++) + { + if (!sections[index].hasOnlyAir()) + { + // convert from an index to a block coordinate + return this.chunk.getSectionYFromSectionIndex(index) * 16; + } + } + return Integer.MAX_VALUE; + } + @Override public int getSolidHeightMapValue(int xRel, int zRel) { return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE).getFirstAvailable(xRel, zRel); } diff --git a/coreSubProjects b/coreSubProjects index d3865551a..5ec874d4a 160000 --- a/coreSubProjects +++ b/coreSubProjects @@ -1 +1 @@ -Subproject commit d3865551a56b94b7fde500d09c1a181fafe1a827 +Subproject commit 5ec874d4a0df3e81767c210e62feac41334503af From 3ea6bee3cf82e866752b88bd2ad151e9dcba2727 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Mon, 11 Sep 2023 07:38:04 -0500 Subject: [PATCH 5/8] Merge branch 'minecraft-lod-mod-iris.shadow.fix' --- coreSubProjects | 2 +- .../distanthorizons/fabric/FabricMain.java | 9 ++++ .../wrappers/modAccessor/IrisAccessor.java | 50 +++++++++++++++++++ versionProperties/1.16.5.properties | 4 +- versionProperties/1.18.2.properties | 4 +- versionProperties/1.19.2.properties | 4 +- versionProperties/1.19.4.properties | 4 +- versionProperties/1.20.1.properties | 4 +- 8 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 fabric/src/main/java/com/seibel/distanthorizons/fabric/wrappers/modAccessor/IrisAccessor.java diff --git a/coreSubProjects b/coreSubProjects index 5ec874d4a..61564d891 160000 --- a/coreSubProjects +++ b/coreSubProjects @@ -1 +1 @@ -Subproject commit 5ec874d4a0df3e81767c210e62feac41334503af +Subproject commit 61564d89146b8e70b2bbfca46dcd6aa952766f00 diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricMain.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricMain.java index b29afbc16..f06b7374a 100644 --- a/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricMain.java +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricMain.java @@ -99,6 +99,15 @@ public class FabricMain { ModAccessorInjector.INSTANCE.bind(IBCLibAccessor.class, new BCLibAccessor()); } + + #if MC_1_16_5 || MC_1_18_2 || MC_1_19_2 || MC_1_19_4 || MC_1_20_1 + // 1.17.1 won't support this since there isn't a matching Iris version + if (SingletonInjector.INSTANCE.get(IModChecker.class).isModLoaded("iris")) + { + ModAccessorInjector.INSTANCE.bind(IIrisAccessor.class, new IrisAccessor()); + } + #endif + LOGGER.info(ModInfo.READABLE_NAME + " Initialized"); ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null); diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/wrappers/modAccessor/IrisAccessor.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/wrappers/modAccessor/IrisAccessor.java new file mode 100644 index 000000000..37fe1b2a0 --- /dev/null +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/wrappers/modAccessor/IrisAccessor.java @@ -0,0 +1,50 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.fabric.wrappers.modAccessor; + +#if MC_1_16_5 || MC_1_18_2 || MC_1_19_2 || MC_1_19_4 || MC_1_20_1 + +import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor; +import net.coderbot.iris.Iris; +import net.irisshaders.iris.api.v0.IrisApi; + +public class IrisAccessor implements IIrisAccessor +{ + @Override + public String getModName() + { + //return "Iris-Fabric"; + return Iris.MODID; + } + + @Override + public boolean isShaderPackInUse() + { + return IrisApi.getInstance().isShaderPackInUse(); + } + + @Override + public boolean isRenderingShadowPass() + { + return IrisApi.getInstance().isRenderingShadowPass(); + } +} + +#endif diff --git a/versionProperties/1.16.5.properties b/versionProperties/1.16.5.properties index c3683be84..4d9b764c6 100644 --- a/versionProperties/1.16.5.properties +++ b/versionProperties/1.16.5.properties @@ -15,7 +15,7 @@ fabric_api_version=0.42.0+1.16 phosphor_version_fabric= lithium_version=mc1.16.5-0.6.6 sodium_version=mc1.16.5-0.2.0 - iris_version=1.16.x-v1.2.5 + iris_version=1.4.4+1.16.5 bclib_version= immersive_portals_version= canvas_version= @@ -30,7 +30,7 @@ fabric_api_version=0.42.0+1.16 enable_phosphor=0 enable_lithium=0 enable_sodium=1 - enable_iris=0 + enable_iris=1 # not available via github, use curse.maven if necessary enable_bclib=0 enable_immersive_portals=0 diff --git a/versionProperties/1.18.2.properties b/versionProperties/1.18.2.properties index 62c33f7aa..6c188d603 100644 --- a/versionProperties/1.18.2.properties +++ b/versionProperties/1.18.2.properties @@ -16,7 +16,7 @@ fabric_api_version=0.76.0+1.18.2 phosphor_version_fabric=3573395 lithium_version=mc1.18.2-0.10.3 sodium_version=mc1.18.2-0.4.1 - iris_version=1.18.x-v1.6.4 + iris_version=1.6.6+1.18.2 bclib_version=1.4.6 immersive_portals_version=v1.4.11-1.18 canvas_version=mc118:1.0.2616 @@ -31,7 +31,7 @@ fabric_api_version=0.76.0+1.18.2 enable_phosphor=0 enable_sodium=1 enable_lithium=0 - enable_iris=0 + enable_iris=1 enable_bclib=1 enable_immersive_portals=0 enable_canvas=0 diff --git a/versionProperties/1.19.2.properties b/versionProperties/1.19.2.properties index cdde6a134..5f2da8945 100644 --- a/versionProperties/1.19.2.properties +++ b/versionProperties/1.19.2.properties @@ -15,7 +15,7 @@ fabric_api_version=0.76.0+1.19.2 phosphor_version_fabric= lithium_version= sodium_version=mc1.19.2-0.4.4 - iris_version=1.6.4+1.19.2 + iris_version=1.6.6+1.19.2 bclib_version=2.1.6 immersive_portals_version= canvas_version=mc119-1.0.2480 @@ -30,7 +30,7 @@ fabric_api_version=0.76.0+1.19.2 enable_phosphor=0 enable_sodium=1 enable_lithium=0 - enable_iris=0 + enable_iris=1 enable_bclib=1 enable_immersive_portals=0 enable_canvas=0 diff --git a/versionProperties/1.19.4.properties b/versionProperties/1.19.4.properties index 2783daeab..5763b87c8 100644 --- a/versionProperties/1.19.4.properties +++ b/versionProperties/1.19.4.properties @@ -15,7 +15,7 @@ fabric_api_version=0.83.0+1.19.4 phosphor_version_fabric= lithium_version= sodium_version=mc1.19.4-0.4.10 - iris_version=1.6.4+1.19.4 + iris_version=1.6.6+1.19.4 bclib_version=2.3.3 immersive_portals_version= canvas_version= @@ -30,7 +30,7 @@ fabric_api_version=0.83.0+1.19.4 enable_phosphor=0 enable_sodium=1 enable_lithium=0 - enable_iris=0 + enable_iris=1 enable_bclib=1 enable_immersive_portals=0 enable_canvas=0 diff --git a/versionProperties/1.20.1.properties b/versionProperties/1.20.1.properties index 0e88c741d..da1395f4d 100644 --- a/versionProperties/1.20.1.properties +++ b/versionProperties/1.20.1.properties @@ -15,7 +15,7 @@ fabric_api_version=0.85.0+1.20.1 phosphor_version_fabric= lithium_version= sodium_version=mc1.20.1-0.5.1 - iris_version=1.6.4+1.20.1 + iris_version=1.6.8+1.20.1 bclib_version=3.0.12 immersive_portals_version= canvas_version= @@ -30,7 +30,7 @@ fabric_api_version=0.85.0+1.20.1 enable_phosphor=0 enable_sodium=1 enable_lithium=0 - enable_iris=0 + enable_iris=1 enable_bclib=1 enable_immersive_portals=0 enable_canvas=0 From 8e69174d5ad2af3cbcebf2ceda828cf642626d21 Mon Sep 17 00:00:00 2001 From: Builderb0y Date: Tue, 12 Sep 2023 00:44:58 +0000 Subject: [PATCH 6/8] Update 2 files - /common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkLightStorage.java - /common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java --- .../wrappers/chunk/ChunkLightStorage.java | 146 ++++++++++++++++++ .../common/wrappers/chunk/ChunkWrapper.java | 49 +++--- 2 files changed, 173 insertions(+), 22 deletions(-) create mode 100644 common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkLightStorage.java diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkLightStorage.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkLightStorage.java new file mode 100644 index 000000000..ff5a0014e --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkLightStorage.java @@ -0,0 +1,146 @@ +package com.seibel.distanthorizons.common.wrappers.chunk; + +import java.util.ArrayList; +import java.util.Arrays; + +/** +compact, efficient storage for light levels. +all blocks only take up 4 bits in total, +and if a 16x16x16 area is detected to have the same light level in all positions, +then we store a single byte for that light level, instead of 2 kilobytes. + +@author Builderb0y +*/ +public class ChunkLightStorage { + + /** the minimum Y level in the chunk which this storage is storing light levels for (inclusive). */ + public int minY; + /** the maximum Y level in the chunk which this storage is storing light levels for (exclusive). */ + public int maxY; + /** the data stored in this storage, split up into 16x16x16 areas. */ + public Section[] sections; + + public ChunkLightStorage(int minY, int maxY) { + this.minY = minY; + this.maxY = maxY; + } + + public int get(int x, int y, int z) { + if (y < this.minY) return 0; + if (y >= this.maxY) return 15; + if (this.sections != null) { + Section section = this.sections[(y - this.minY) >> 4]; + if (section != null) { + return section.get(x, y, z); + } + } + return 0; + } + + public void set(int x, int y, int z, int lightLevel) { + if (y < this.minY || y >= this.maxY) return; + //populate array if it doesn't exist. + if (this.sections == null) { + this.sections = new Section[(this.maxY - this.minY) >> 4]; + } + int index = (y - this.minY) >> 4; + Section section = this.sections[index]; + //populate section in array if it doesn't exist. + if (section == null) { + section = new Section(0); + this.sections[index] = section; + } + section.set(x, y, z, lightLevel); + } + + public static class Section { + + public byte constantValue; + public long[] data; + public short[] counts; + + public Section(int initialValue) { + this.constantValue = (byte)(initialValue); + this.counts = new short[16]; + this.counts[initialValue] = 16 * 16 * 16; + } + + public int get(int x, int y, int z) { + if (this.constantValue >= 0) return this.constantValue; + x &= 15; + y &= 15; + z &= 15; + long bits = this.data[(z << 4) | x]; + return ((int)(bits >>> (y << 2))) & 15; + } + + public void set(int x, int y, int z, int lightLevel) { + int oldLightLevel = -1; + if (this.constantValue >= 0) { + oldLightLevel = this.constantValue; + + //if the light level didn't change, then there's nothing to do. + if (oldLightLevel == lightLevel) return; + + //if we are a constant value and need to change something, + //then that means we need to convert to a non-constant value. + this.data = DataRecycler.get(); + + //repeat oldLightLevel 16 times as a bit pattern. + long payload = oldLightLevel; + payload |= payload << 4; + payload |= payload << 8; + payload |= payload << 16; + payload |= payload << 32; + + //fill our data with our constant value. + Arrays.fill(this.data, payload); + + //we are no longer a constant value. + this.constantValue = -1; + } + + x &= 15; + y &= 15; + z &= 15; + int index = (z << 4) | x; + long bits = this.data[index]; + //if we weren't a constant value before, now's the time to initialize oldLightLevel. + if (oldLightLevel < 0) { + oldLightLevel = ((int)(bits >>> (y << 2))) & 15; + } + //clear the 4 bits that correspond to the light level at x, y, z... + bits &= ~(15L << (y << 2)); + //...and then re-populate those bits with the new light level. + bits |= ((long)(lightLevel)) << (y << 2); + //store the updated bits in our data. + this.data[index] = bits; + + //we have one less of the old light level... + this.counts[oldLightLevel]--; + //...and one more of the new level. + //if the number associated with the new level is now 4096 (AKA 16 ^ 3), + //then this implies every position in this section has the same light level, + //and therefore we can convert back to a constant value. + if (++this.counts[lightLevel] == 4096) { + this.constantValue = (byte)(lightLevel); + DataRecycler.reclaim(this.data); + this.data = null; + } + } + } + + static class DataRecycler { + + private static final ArrayList recycled = new ArrayList<>(256); + + static synchronized long[] get() { + if (recycled.isEmpty()) return new long[256]; + else return recycled.remove(recycled.size() - 1); + } + + static synchronized void reclaim(long[] data) { + if (recycled.size() < 256) recycled.add(data); + } + } +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java index 70d81e48b..51103f0b2 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java @@ -77,8 +77,8 @@ public class ChunkWrapper implements IChunkWrapper /** only used when connected to a dedicated server */ private boolean isMcClientLightingCorrect = false; - private final byte[] blockLightArray; - private final byte[] skyLightArray; + private ChunkLightStorage blockLightArray; + private ChunkLightStorage skyLightArray; private ArrayList blockLightPosList = null; @@ -116,9 +116,7 @@ public class ChunkWrapper implements IChunkWrapper this.useDhLighting = isDhGeneratedChunk; // FIXME +1 is to handle the fact that LodDataBuilder adds +1 to all block lighting calculations, also done in the relative position validator - this.blockLightArray = new byte[LodUtil.CHUNK_WIDTH * LodUtil.CHUNK_WIDTH * (this.getHeight() + 1)]; - this.skyLightArray = new byte[LodUtil.CHUNK_WIDTH * LodUtil.CHUNK_WIDTH * (this.getHeight() + 1)]; - + chunksNeedingClientLightUpdating.add(this); } @@ -256,39 +254,48 @@ public class ChunkWrapper implements IChunkWrapper #endif } - + private ChunkLightStorage getBlockLightArray() + { + if (this.blockLightArray == null) + { + this.blockLightArray = new ChunkLightStorage(this.getMinBuildHeight(), this.getMaxBuildHeight()); + } + return this.blockLightArray; + } + + private ChunkLightStorage getSkyLightArray() + { + if (this.skyLightArray == null) + { + this.skyLightArray = new ChunkLightStorage(this.getMinBuildHeight(), this.getMaxBuildHeight()); + } + return this.skyLightArray; + } + @Override public int getDhBlockLight(int relX, int y, int relZ) { this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ); - - int index = this.relativeBlockPosToIndex(relX, y, relZ); - return this.blockLightArray[index]; + return this.getBlockLightArray().get(relX, y, relZ); } @Override public void setDhBlockLight(int relX, int y, int relZ, int lightValue) { this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ); - - int index = this.relativeBlockPosToIndex(relX, y, relZ); - this.blockLightArray[index] = (byte) lightValue; + this.getBlockLightArray().set(relX, y, relZ, lightValue); } @Override public int getDhSkyLight(int relX, int y, int relZ) { this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ); - - int index = this.relativeBlockPosToIndex(relX, y, relZ); - return this.skyLightArray[index]; + return this.getSkyLightArray().get(relX, y, relZ); } @Override public void setDhSkyLight(int relX, int y, int relZ, int lightValue) { this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ); - - int index = this.relativeBlockPosToIndex(relX, y, relZ); - this.skyLightArray[index] = (byte) lightValue; + this.getSkyLightArray().set(relX, y, relZ, lightValue); } @@ -301,8 +308,7 @@ public class ChunkWrapper implements IChunkWrapper if (this.useDhLighting) { // DH lighting method - int index = this.relativeBlockPosToIndex(relX, y, relZ); - return this.blockLightArray[index]; + return this.getBlockLightArray().get(relX, y, relZ); } else { @@ -322,8 +328,7 @@ public class ChunkWrapper implements IChunkWrapper if (this.useDhLighting) { // DH lighting method - int index = this.relativeBlockPosToIndex(relX, y, relZ); - return this.skyLightArray[index]; + return this.getSkyLightArray().get(relX, y, relZ); } else { From e12f33a9384f793188ba62589bbaeca27152af75 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Mon, 11 Sep 2023 20:35:46 -0500 Subject: [PATCH 7/8] DhLightingEngine Optimization from Builderb0y --- coreSubProjects | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreSubProjects b/coreSubProjects index 61564d891..13b7a20ff 160000 --- a/coreSubProjects +++ b/coreSubProjects @@ -1 +1 @@ -Subproject commit 61564d89146b8e70b2bbfca46dcd6aa952766f00 +Subproject commit 13b7a20ff6ce15630ff963fb84a855e9694f79a2 From 0cac09aec189c680ac68a031cba834637fedb054 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Mon, 11 Sep 2023 21:02:15 -0500 Subject: [PATCH 8/8] reformat ChunkLightStorage --- .../wrappers/chunk/ChunkLightStorage.java | 202 ++++++++++++------ .../common/wrappers/chunk/ChunkWrapper.java | 55 ++--- 2 files changed, 164 insertions(+), 93 deletions(-) diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkLightStorage.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkLightStorage.java index ff5a0014e..8d0a465c5 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkLightStorage.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkLightStorage.java @@ -1,146 +1,216 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.seibel.distanthorizons.common.wrappers.chunk; +import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; + import java.util.ArrayList; import java.util.Arrays; /** -compact, efficient storage for light levels. -all blocks only take up 4 bits in total, -and if a 16x16x16 area is detected to have the same light level in all positions, -then we store a single byte for that light level, instead of 2 kilobytes. - -@author Builderb0y + * Compact, efficient storage for light levels. + * all blocks only take up 4 bits in total, + * and if a 16x16x16 area is detected to have the same light level in all positions, + * then we store a single byte for that light level, instead of 2 kilobytes. + * + * @author Builderb0y */ -public class ChunkLightStorage { - +public class ChunkLightStorage +{ /** the minimum Y level in the chunk which this storage is storing light levels for (inclusive). */ public int minY; /** the maximum Y level in the chunk which this storage is storing light levels for (exclusive). */ public int maxY; + /** the data stored in this storage, split up into 16x16x16 areas. */ - public Section[] sections; - - public ChunkLightStorage(int minY, int maxY) { + public LightSection[] lightSections; + + + + public ChunkLightStorage(int minY, int maxY) + { this.minY = minY; this.maxY = maxY; } - - public int get(int x, int y, int z) { - if (y < this.minY) return 0; - if (y >= this.maxY) return 15; - if (this.sections != null) { - Section section = this.sections[(y - this.minY) >> 4]; - if (section != null) { - return section.get(x, y, z); + + + + public int get(int x, int y, int z) + { + if (y < this.minY) + { + return 0; + } + if (y >= this.maxY) + { + return 15; + } + + if (this.lightSections != null) + { + LightSection lightSection = this.lightSections[BitShiftUtil.divideByPowerOfTwo(y - this.minY, 4)]; + if (lightSection != null) + { + return lightSection.get(x, y, z); } } + return 0; } - - public void set(int x, int y, int z, int lightLevel) { - if (y < this.minY || y >= this.maxY) return; + + public void set(int x, int y, int z, int lightLevel) + { + if (y < this.minY || y >= this.maxY) + { + return; + } + //populate array if it doesn't exist. - if (this.sections == null) { - this.sections = new Section[(this.maxY - this.minY) >> 4]; + if (this.lightSections == null) + { + this.lightSections = new LightSection[BitShiftUtil.divideByPowerOfTwo(this.maxY - this.minY, 4)]; } + int index = (y - this.minY) >> 4; - Section section = this.sections[index]; - //populate section in array if it doesn't exist. - if (section == null) { - section = new Section(0); - this.sections[index] = section; + LightSection lightSection = this.lightSections[index]; + + //populate lightSection in array if it doesn't exist. + if (lightSection == null) + { + lightSection = new LightSection(0); + this.lightSections[index] = lightSection; } - section.set(x, y, z, lightLevel); + lightSection.set(x, y, z, lightLevel); } - - public static class Section { - + + + + //================// + // helper classes // + //================// + + public static class LightSection + { public byte constantValue; public long[] data; public short[] counts; - - public Section(int initialValue) { - this.constantValue = (byte)(initialValue); + + public LightSection(int initialValue) + { + this.constantValue = (byte) (initialValue); this.counts = new short[16]; this.counts[initialValue] = 16 * 16 * 16; } - - public int get(int x, int y, int z) { - if (this.constantValue >= 0) return this.constantValue; + + public int get(int x, int y, int z) + { + if (this.constantValue >= 0) + { + return this.constantValue; + } + x &= 15; y &= 15; z &= 15; long bits = this.data[(z << 4) | x]; - return ((int)(bits >>> (y << 2))) & 15; + return ((int) (bits >>> (y << 2))) & 15; } - - public void set(int x, int y, int z, int lightLevel) { + + public void set(int x, int y, int z, int lightLevel) + { int oldLightLevel = -1; - if (this.constantValue >= 0) { + if (this.constantValue >= 0) + { oldLightLevel = this.constantValue; - + //if the light level didn't change, then there's nothing to do. if (oldLightLevel == lightLevel) return; - + //if we are a constant value and need to change something, //then that means we need to convert to a non-constant value. this.data = DataRecycler.get(); - + //repeat oldLightLevel 16 times as a bit pattern. long payload = oldLightLevel; payload |= payload << 4; payload |= payload << 8; payload |= payload << 16; payload |= payload << 32; - + //fill our data with our constant value. Arrays.fill(this.data, payload); - + //we are no longer a constant value. this.constantValue = -1; } - + x &= 15; y &= 15; z &= 15; int index = (z << 4) | x; long bits = this.data[index]; //if we weren't a constant value before, now's the time to initialize oldLightLevel. - if (oldLightLevel < 0) { - oldLightLevel = ((int)(bits >>> (y << 2))) & 15; + if (oldLightLevel < 0) + { + oldLightLevel = ((int) (bits >>> (y << 2))) & 15; } //clear the 4 bits that correspond to the light level at x, y, z... bits &= ~(15L << (y << 2)); //...and then re-populate those bits with the new light level. - bits |= ((long)(lightLevel)) << (y << 2); + bits |= ((long) (lightLevel)) << (y << 2); //store the updated bits in our data. this.data[index] = bits; - + //we have one less of the old light level... this.counts[oldLightLevel]--; //...and one more of the new level. //if the number associated with the new level is now 4096 (AKA 16 ^ 3), //then this implies every position in this section has the same light level, //and therefore we can convert back to a constant value. - if (++this.counts[lightLevel] == 4096) { - this.constantValue = (byte)(lightLevel); + if (++this.counts[lightLevel] == 4096) + { + this.constantValue = (byte) (lightLevel); DataRecycler.reclaim(this.data); this.data = null; } } + } - - static class DataRecycler { - + + static class DataRecycler + { private static final ArrayList recycled = new ArrayList<>(256); - - static synchronized long[] get() { - if (recycled.isEmpty()) return new long[256]; - else return recycled.remove(recycled.size() - 1); - } - - static synchronized void reclaim(long[] data) { - if (recycled.size() < 256) recycled.add(data); + + static synchronized long[] get() + { + if (recycled.isEmpty()) + { + return new long[256]; + } + else + { + return recycled.remove(recycled.size() - 1); + } } + + static synchronized void reclaim(long[] data) { if (recycled.size() < 256) recycled.add(data); } } + } \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java index 51103f0b2..755877581 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java @@ -31,7 +31,6 @@ 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.coreapi.util.BitShiftUtil; import net.minecraft.client.multiplayer.ClientChunkCache; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.BlockPos; @@ -77,8 +76,8 @@ public class ChunkWrapper implements IChunkWrapper /** only used when connected to a dedicated server */ private boolean isMcClientLightingCorrect = false; - private ChunkLightStorage blockLightArray; - private ChunkLightStorage skyLightArray; + private ChunkLightStorage blockLightStorage; + private ChunkLightStorage skyLightStorage; private ArrayList blockLightPosList = null; @@ -254,48 +253,50 @@ public class ChunkWrapper implements IChunkWrapper #endif } - private ChunkLightStorage getBlockLightArray() - { - if (this.blockLightArray == null) - { - this.blockLightArray = new ChunkLightStorage(this.getMinBuildHeight(), this.getMaxBuildHeight()); - } - return this.blockLightArray; - } - - private ChunkLightStorage getSkyLightArray() - { - if (this.skyLightArray == null) - { - this.skyLightArray = new ChunkLightStorage(this.getMinBuildHeight(), this.getMaxBuildHeight()); - } - return this.skyLightArray; - } - + @Override public int getDhBlockLight(int relX, int y, int relZ) { this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ); - return this.getBlockLightArray().get(relX, y, relZ); + return this.getBlockLightStorage().get(relX, y, relZ); } @Override public void setDhBlockLight(int relX, int y, int relZ, int lightValue) { this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ); - this.getBlockLightArray().set(relX, y, relZ, lightValue); + this.getBlockLightStorage().set(relX, y, relZ, lightValue); } + private ChunkLightStorage getBlockLightStorage() + { + if (this.blockLightStorage == null) + { + this.blockLightStorage = new ChunkLightStorage(this.getMinBuildHeight(), this.getMaxBuildHeight()); + } + return this.blockLightStorage; + } + + @Override public int getDhSkyLight(int relX, int y, int relZ) { this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ); - return this.getSkyLightArray().get(relX, y, relZ); + return this.getSkyLightStorage().get(relX, y, relZ); } @Override public void setDhSkyLight(int relX, int y, int relZ, int lightValue) { this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ); - this.getSkyLightArray().set(relX, y, relZ, lightValue); + this.getSkyLightStorage().set(relX, y, relZ, lightValue); + } + + private ChunkLightStorage getSkyLightStorage() + { + if (this.skyLightStorage == null) + { + this.skyLightStorage = new ChunkLightStorage(this.getMinBuildHeight(), this.getMaxBuildHeight()); + } + return this.skyLightStorage; } @@ -308,7 +309,7 @@ public class ChunkWrapper implements IChunkWrapper if (this.useDhLighting) { // DH lighting method - return this.getBlockLightArray().get(relX, y, relZ); + return this.getBlockLightStorage().get(relX, y, relZ); } else { @@ -328,7 +329,7 @@ public class ChunkWrapper implements IChunkWrapper if (this.useDhLighting) { // DH lighting method - return this.getSkyLightArray().get(relX, y, relZ); + return this.getSkyLightStorage().get(relX, y, relZ); } else {