improve lod load time slightly

done by caching the ClientLevelWrapper used to determine block colors
This commit is contained in:
James Seibel
2025-09-07 16:15:05 -05:00
parent 771814af98
commit 670ec28b6f
11 changed files with 35 additions and 55 deletions
@@ -284,7 +284,7 @@ public class ColumnRenderBufferBuilder
}// for z
}// for x
quadBuilder.finalizeData();
quadBuilder.mergeQuads();
}
private static void addLodToBuffer(
IDhClientLevel clientLevel,
@@ -22,6 +22,7 @@ package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import com.seibel.distanthorizons.api.enums.config.EDhApiGrassSideRendering;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
@@ -210,9 +211,6 @@ public class LodQuadBuilder
// data finalizing //
//=================//
/** runs any final data cleanup, merging, etc. */
public void finalizeData() { this.mergeQuads(); }
/** Uses Greedy meshing to merge this builder's Quads. */
public void mergeQuads()
{
@@ -251,7 +249,9 @@ public class LodQuadBuilder
private static long mergeQuadsInternal(ArrayList<BufferQuad>[] list, int directionIndex, BufferMergeDirectionEnum mergeDirection)
{
if (list[directionIndex].size() <= 1)
{
return 0;
}
list[directionIndex].sort((objOne, objTwo) -> objOne.compare(objTwo, mergeDirection));
@@ -26,7 +26,6 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSour
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnArrayView;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pooling.PhantomArrayListCheckout;
import com.seibel.distanthorizons.core.pooling.PhantomArrayListPool;
@@ -37,6 +36,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
@@ -66,13 +66,13 @@ public class FullDataToRenderDataTransformer
//==============================//
@Nullable
public static ColumnRenderSource transformFullDataToRenderSource(@Nullable FullDataSourceV2 fullDataSource, @Nullable IDhClientLevel level)
public static ColumnRenderSource transformFullDataToRenderSource(@Nullable FullDataSourceV2 fullDataSource, @Nullable IClientLevelWrapper levelWrapper)
{
if (fullDataSource == null)
{
return null;
}
else if (level == null)
else if (levelWrapper == null)
{
// if the client is no longer loaded in the world, render sources cannot be created
return null;
@@ -81,7 +81,7 @@ public class FullDataToRenderDataTransformer
try
{
return transformCompleteFullDataToColumnData(level, fullDataSource);
return transformCompleteFullDataToColumnData(levelWrapper, fullDataSource);
}
catch (InterruptedException e)
{
@@ -102,7 +102,7 @@ public class FullDataToRenderDataTransformer
* @throws InterruptedException Can be caused by interrupting the thread upstream.
* Generally thrown if the method is running after the client leaves the current world.
*/
private static ColumnRenderSource transformCompleteFullDataToColumnData(IDhClientLevel level, FullDataSourceV2 fullDataSource) throws InterruptedException
private static ColumnRenderSource transformCompleteFullDataToColumnData(IClientLevelWrapper levelWrapper, FullDataSourceV2 fullDataSource) throws InterruptedException
{
final long pos = fullDataSource.getPos();
final byte dataDetail = fullDataSource.getDataDetailLevel();
@@ -111,7 +111,7 @@ public class FullDataToRenderDataTransformer
final ColumnRenderSource columnSource = ColumnRenderSource.createEmpty(pos, vertSize, level.getMinY());
final ColumnRenderSource columnSource = ColumnRenderSource.createEmpty(pos, vertSize, levelWrapper.getMinHeight());
if (fullDataSource.isEmpty)
{
return columnSource;
@@ -121,9 +121,9 @@ public class FullDataToRenderDataTransformer
int baseX = DhSectionPos.getMinCornerBlockX(pos);
int baseZ = DhSectionPos.getMinCornerBlockZ(pos);
for (int x = 0; x < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); x++)
for (int x = 0; x < FullDataSourceV2.WIDTH; x++)
{
for (int z = 0; z < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); z++)
for (int z = 0; z < FullDataSourceV2.WIDTH; z++)
{
throwIfThreadInterrupted();
@@ -131,7 +131,7 @@ public class FullDataToRenderDataTransformer
LongArrayList dataColumn = fullDataSource.get(x, z);
updateOrReplaceRenderDataViewColumnWithFullDataColumn(
level, fullDataSource.mapping,
levelWrapper, fullDataSource.mapping,
// bitshift is to account for LODs with a detail level greater than 0 so the block pos is correct
baseX + BitShiftUtil.pow(x,dataDetail), baseZ + BitShiftUtil.pow(z,dataDetail),
columnArrayView, dataColumn);
@@ -145,7 +145,7 @@ public class FullDataToRenderDataTransformer
/** Updates the given {@link ColumnArrayView} to match the incoming Full data {@link LongArrayList} */
public static void updateOrReplaceRenderDataViewColumnWithFullDataColumn(
IDhClientLevel level,
IClientLevelWrapper levelWrapper,
FullDataPointIdMap fullDataMapping, int blockX, int blockZ,
ColumnArrayView columnArrayView,
LongArrayList fullDataColumn)
@@ -160,7 +160,7 @@ public class FullDataToRenderDataTransformer
if (fullDataLength <= columnArrayView.verticalSize())
{
// Directly use the arrayView since it fits.
setRenderColumnView(level, fullDataMapping, blockX, blockZ, columnArrayView, fullDataColumn);
setRenderColumnView(levelWrapper, fullDataMapping, blockX, blockZ, columnArrayView, fullDataColumn);
}
else
{
@@ -171,7 +171,7 @@ public class FullDataToRenderDataTransformer
{
// expand the ColumnArrayView to fit the new larger max vertical size
ColumnArrayView newColumnArrayView = new ColumnArrayView(dataArrayList, fullDataLength, 0, fullDataLength);
setRenderColumnView(level, fullDataMapping, blockX, blockZ, newColumnArrayView, fullDataColumn);
setRenderColumnView(levelWrapper, fullDataMapping, blockX, blockZ, newColumnArrayView, fullDataColumn);
columnArrayView.changeVerticalSizeFrom(newColumnArrayView);
}
finally
@@ -181,7 +181,7 @@ public class FullDataToRenderDataTransformer
}
}
private static void setRenderColumnView(
IDhClientLevel level, FullDataPointIdMap fullDataMapping,
IClientLevelWrapper levelWrapper, FullDataPointIdMap fullDataMapping,
int blockX, int blockZ,
ColumnArrayView renderColumnData, LongArrayList fullColumnData)
{
@@ -192,18 +192,18 @@ public class FullDataToRenderDataTransformer
boolean ignoreNonCollidingBlocks = (Config.Client.Advanced.Graphics.Quality.blocksToIgnore.get() == EDhApiBlocksToAvoid.NON_COLLIDING);
boolean colorBelowWithAvoidedBlocks = Config.Client.Advanced.Graphics.Quality.tintWithAvoidedBlocks.get();
HashSet<IBlockStateWrapper> blockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredBlocks(level.getLevelWrapper());
HashSet<IBlockStateWrapper> caveBlockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredCaveBlocks(level.getLevelWrapper());
HashSet<IBlockStateWrapper> blockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredBlocks(levelWrapper);
HashSet<IBlockStateWrapper> caveBlockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredCaveBlocks(levelWrapper);
int caveCullingMaxY = Config.Client.Advanced.Graphics.Culling.caveCullingHeight.get() - level.getMinY();
int caveCullingMaxY = Config.Client.Advanced.Graphics.Culling.caveCullingHeight.get() - levelWrapper.getMinHeight();
boolean caveCullingEnabled =
Config.Client.Advanced.Graphics.Culling.enableCaveCulling.get()
&& (
// dimensions with a ceiling will be all caves so we don't want cave culling
!level.getLevelWrapper().hasCeiling()
!levelWrapper.hasCeiling()
// the end has a lot of overhangs with 0 lighting above the void, which look broken with
// the current cave culling logic (this could probably be improved, but just skipping it works best for now)
&& !level.getLevelWrapper().getDimensionType().isTheEnd()
&& !levelWrapper.getDimensionType().isTheEnd()
);
boolean isColumnVoid = true;
@@ -236,7 +236,7 @@ public class FullDataToRenderDataTransformer
int blockLight = FullDataPointUtil.getBlockLight(fullData);
int skyLight = FullDataPointUtil.getSkyLight(fullData);
mutableBlockPos.setY(bottomY + level.getMinY());
mutableBlockPos.setY(bottomY + levelWrapper.getMinHeight());
IBiomeWrapper biome;
IBlockStateWrapper block;
@@ -250,7 +250,7 @@ public class FullDataToRenderDataTransformer
if (!brokenPos.contains(fullDataMapping.getPos()))
{
brokenPos.add(fullDataMapping.getPos());
String levelId = level.getLevelWrapper().getDhIdentifier();
String levelId = levelWrapper.getDhIdentifier();
LOGGER.warn("Unable to get data point with id ["+id+"] " +
"(Max possible ID: ["+fullDataMapping.getMaxValidId()+"]) " +
"for pos ["+fullDataMapping.getPos()+"] in level ["+levelId+"]. " +
@@ -324,7 +324,8 @@ public class FullDataToRenderDataTransformer
{
if (colorBelowWithAvoidedBlocks)
{
int tempColor = level.computeBaseColor(mutableBlockPos, biome, block);
int tempColor = levelWrapper.getBlockColor(mutableBlockPos, biome, block);
// don't transfer the color when alpha is 0
// this prevents issues if grass is transparent
if (ColorUtil.getAlpha(tempColor) != 0)
@@ -344,7 +345,7 @@ public class FullDataToRenderDataTransformer
if (colorToApplyToNextBlock == -1)
{
// use this block's color
color = level.computeBaseColor(mutableBlockPos, biome, block);
color = levelWrapper.getBlockColor(mutableBlockPos, biome, block);
}
else
{
@@ -36,17 +36,14 @@ import com.seibel.distanthorizons.core.multiplayer.client.SyncOnLoadRequestQueue
import com.seibel.distanthorizons.core.network.event.ScopedNetworkEventSource;
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import org.apache.logging.log4j.LogManager;
@@ -297,9 +294,6 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
// getters //
//=========//
@Override
public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block) { return this.levelWrapper.getBlockColor(pos, biome, block); }
@Override
public IClientLevelWrapper getClientLevelWrapper() { return this.levelWrapper; }
@@ -94,20 +94,6 @@ public class DhClientServerLevel extends AbstractDhServerLevel implements IDhCli
// level handling //
//================//
@Override //FIXME this can fail if the clientLevel isn't available yet, maybe in that case we could return -1 and handle it upstream?
public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block)
{
IClientLevelWrapper clientLevel = this.getClientLevelWrapper();
if (clientLevel == null)
{
return 0;
}
else
{
return clientLevel.getBlockColor(pos, biome, block);
}
}
@Nullable
@Override
public IClientLevelWrapper getClientLevelWrapper() { return MC_CLIENT.getWrappedClientLevel(); }
@@ -34,8 +34,6 @@ public interface IDhClientLevel extends IDhLevel
void render(DhApiRenderParam renderEventParam, IProfilerWrapper profiler);
void renderDeferred(DhApiRenderParam renderEventParam, IProfilerWrapper profiler);
int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block);
@Nullable
IClientLevelWrapper getClientLevelWrapper();
@@ -21,8 +21,6 @@ package com.seibel.distanthorizons.core.render;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalCause;
import com.google.common.cache.RemovalNotification;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.render.CachedColumnRenderSource;
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
@@ -50,6 +50,7 @@ import com.seibel.distanthorizons.core.util.KeyedLockContainer;
import com.seibel.distanthorizons.core.util.threading.PriorityTaskPicker;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.coreapi.util.MathUtil;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import org.apache.logging.log4j.Logger;
@@ -78,6 +79,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
public final long pos;
private final IDhClientLevel level;
private final IClientLevelWrapper levelWrapper;
@WillNotClose
private final FullDataSourceProviderV2 fullDataSourceProvider;
private final LodQuadTree quadTree;
@@ -156,6 +158,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
this.cachedRenderSourceByPos = cachedRenderSourceByPos;
this.renderLoadLockContainer = renderLoadLockContainer;
this.level = level;
this.levelWrapper = level.getClientLevelWrapper();
this.fullDataSourceProvider = fullDataSourceProvider;
this.uploadTaskCountRef = uploadTaskCountRef;
@@ -309,6 +312,10 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
adjacentLoadFutures[1] = this.getRenderSourceForPosAsync(DhSectionPos.getAdjacentPos(this.pos, EDhDirection.SOUTH));
adjacentLoadFutures[2] = this.getRenderSourceForPosAsync(DhSectionPos.getAdjacentPos(this.pos, EDhDirection.EAST));
adjacentLoadFutures[3] = this.getRenderSourceForPosAsync(DhSectionPos.getAdjacentPos(this.pos, EDhDirection.WEST));
//adjacentLoadFutures[0] = CompletableFuture.completedFuture(null);
//adjacentLoadFutures[1] = CompletableFuture.completedFuture(null);
//adjacentLoadFutures[2] = CompletableFuture.completedFuture(null);
//adjacentLoadFutures[3] = CompletableFuture.completedFuture(null);
return CompletableFuture.allOf(adjacentLoadFutures).thenRun(() ->
{
try (CachedColumnRenderSource northRenderSource = adjacentLoadFutures[0].get();
@@ -387,7 +394,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
// generate new render source
try (FullDataSourceV2 fullDataSource = this.fullDataSourceProvider.get(pos))
{
newCachedRenderSource.columnRenderSource = FullDataToRenderDataTransformer.transformFullDataToRenderSource(fullDataSource, this.level);
newCachedRenderSource.columnRenderSource = FullDataToRenderDataTransformer.transformFullDataToRenderSource(fullDataSource, this.levelWrapper);
}
catch (Exception e)
{
@@ -33,14 +33,12 @@ import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.core.pos.DhLodPos;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.pos.Pos2D;
import com.seibel.distanthorizons.core.render.glObject.buffer.QuadElementBuffer;
import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.objects.SortedArraySet;
import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IOverrideInjector;
@@ -20,7 +20,6 @@
package com.seibel.distanthorizons.core.render.renderer;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.level.DhClientLevel;
import com.seibel.distanthorizons.core.render.glObject.GLState;
import com.seibel.distanthorizons.core.render.renderer.shaders.FadeApplyShader;
import com.seibel.distanthorizons.core.render.renderer.shaders.FadeShader;
@@ -28,7 +28,6 @@ import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
import com.seibel.distanthorizons.core.render.renderer.ScreenQuad;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;