diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java
index 8300f7f00..aeb9951d1 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java
@@ -22,7 +22,7 @@ package com.seibel.distanthorizons.core.api.internal;
import com.seibel.distanthorizons.core.Initializer;
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.ColumnRenderBufferBuilder;
import com.seibel.distanthorizons.core.dataObjects.transformers.ChunkToLodBuilder;
-import com.seibel.distanthorizons.core.dataObjects.transformers.DataRenderTransformer;
+import com.seibel.distanthorizons.core.dataObjects.transformers.FullDataToRenderDataTransformer;
import com.seibel.distanthorizons.core.file.fullDatafile.FullDataFileHandler;
import com.seibel.distanthorizons.core.generation.WorldGenerationQueue;
import com.seibel.distanthorizons.core.world.*;
@@ -52,7 +52,7 @@ public class SharedApi
if (currentWorld != null)
{
// static thread pool setup
- DataRenderTransformer.setupExecutorService();
+ FullDataToRenderDataTransformer.setupExecutorService();
FullDataFileHandler.setupExecutorService();
ColumnRenderBufferBuilder.setupExecutorService();
WorldGenerationQueue.setupWorldGenThreadPool();
@@ -61,7 +61,7 @@ public class SharedApi
else
{
// static thread pool shutdown
- DataRenderTransformer.shutdownExecutorService();
+ FullDataToRenderDataTransformer.shutdownExecutorService();
FullDataFileHandler.shutdownExecutorService();
ColumnRenderBufferBuilder.shutdownExecutorService();
WorldGenerationQueue.shutdownWorldGenThreadPool();
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderLoader.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderLoader.java
index b83769d4e..013f964dc 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderLoader.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderLoader.java
@@ -75,22 +75,6 @@ public class ColumnRenderLoader
}
}
- /** @throws InterruptedException see {@link FullDataToRenderDataTransformer#transformFullDataToColumnData(IDhClientLevel, CompleteFullDataSource) FullDataToRenderDataTransformer#transformFullDataToColumnData} for documentation */
- public ColumnRenderSource createRenderSource(IFullDataSource fullDataSource, IDhClientLevel level) throws InterruptedException
- {
- if (fullDataSource instanceof CompleteFullDataSource)
- {
- return FullDataToRenderDataTransformer.transformFullDataToColumnData(level, (CompleteFullDataSource) fullDataSource);
- }
- else if (fullDataSource instanceof IIncompleteFullDataSource)
- {
- return FullDataToRenderDataTransformer.transformIncompleteDataToColumnData(level, (IIncompleteFullDataSource) fullDataSource);
- }
-
- LodUtil.assertNotReach();
- return null;
- }
-
//========================//
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java
index d0c95adbd..41c4acd79 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java
@@ -21,8 +21,10 @@ package com.seibel.distanthorizons.core.dataObjects.render;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor;
+import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.SingleColumnFullDataAccessor;
import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
+import com.seibel.distanthorizons.core.pos.DhLodPos;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.dataObjects.transformers.FullDataToRenderDataTransformer;
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream;
@@ -305,7 +307,7 @@ public class ColumnRenderSource
{
try
{
- if (FullDataToRenderDataTransformer.writeFullDataChunkToColumnData(this, level, chunkData))
+ if (writeFullDataChunkToColumnData(this, level, chunkData))
{
this.localVersion.incrementAndGet();
return true;
@@ -327,6 +329,116 @@ public class ColumnRenderSource
}
return false;
}
+ /**
+ * @throws InterruptedException Can be caused by interrupting the thread upstream.
+ * Generally thrown if the method is running after the client leaves the current world.
+ *
+ * @return true if any data was changed, false otherwise
+ */
+ public static boolean writeFullDataChunkToColumnData(ColumnRenderSource renderSource, IDhClientLevel level, ChunkSizedFullDataAccessor chunkDataView) throws InterruptedException, IllegalArgumentException
+ {
+ final DhSectionPos renderSourcePos = renderSource.getSectionPos();
+
+ final int sourceBlockX = renderSourcePos.getCorner().getCornerBlockPos().x;
+ final int sourceBlockZ = renderSourcePos.getCorner().getCornerBlockPos().z;
+
+ // offset between the incoming chunk data and this render source
+ final int blockOffsetX = (chunkDataView.pos.x * LodUtil.CHUNK_WIDTH) - sourceBlockX;
+ final int blockOffsetZ = (chunkDataView.pos.z * LodUtil.CHUNK_WIDTH) - sourceBlockZ;
+
+ final int sourceDataPointBlockWidth = BitShiftUtil.powerOfTwo(renderSource.getDataDetail());
+
+ boolean changed = false;
+
+ if (chunkDataView.detailLevel == renderSource.getDataDetail())
+ {
+ renderSource.markNotEmpty();
+ // confirm the render source contains this chunk
+ if (blockOffsetX < 0
+ || blockOffsetX + LodUtil.CHUNK_WIDTH > renderSource.getWidthInDataPoints()
+ || blockOffsetZ < 0
+ || blockOffsetZ + LodUtil.CHUNK_WIDTH > renderSource.getWidthInDataPoints())
+ {
+ throw new IllegalArgumentException("Data offset is out of bounds");
+ }
+
+
+ if (Thread.interrupted())
+ {
+ throw new InterruptedException(ColumnRenderSource.class.getSimpleName() + " write interrupted.");
+ }
+
+
+ for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
+ {
+ for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
+ {
+ ColumnArrayView columnArrayView = renderSource.getVerticalDataPointView(blockOffsetX + x, blockOffsetZ + z);
+ int hash = columnArrayView.getDataHash();
+ SingleColumnFullDataAccessor fullArrayView = chunkDataView.get(x, z);
+ FullDataToRenderDataTransformer.convertColumnData(level,
+ sourceBlockX + sourceDataPointBlockWidth * (blockOffsetX + x),
+ sourceBlockZ + sourceDataPointBlockWidth * (blockOffsetZ + z),
+ columnArrayView, fullArrayView, 2);
+ changed |= hash != columnArrayView.getDataHash();
+ }
+ }
+ renderSource.fillDebugFlag(blockOffsetX, blockOffsetZ, LodUtil.CHUNK_WIDTH, LodUtil.CHUNK_WIDTH, ColumnRenderSource.DebugSourceFlag.DIRECT);
+ }
+ else if (chunkDataView.detailLevel < renderSource.getDataDetail() && renderSource.getDataDetail() <= chunkDataView.getLodPos().detailLevel)
+ {
+ renderSource.markNotEmpty();
+ // multiple chunk data points converting to 1 column data point
+ DhLodPos dataCornerPos = chunkDataView.getLodPos().getCornerLodPos(chunkDataView.detailLevel);
+ DhLodPos sourceCornerPos = renderSourcePos.getCorner(renderSource.getDataDetail());
+ DhLodPos sourceStartingChangePos = dataCornerPos.convertToDetailLevel(renderSource.getDataDetail());
+ int relStartX = Math.floorMod(sourceStartingChangePos.x, renderSource.getWidthInDataPoints());
+ int relStartZ = Math.floorMod(sourceStartingChangePos.z, renderSource.getWidthInDataPoints());
+ int dataToSourceScale = sourceCornerPos.getWidthAtDetail(chunkDataView.detailLevel);
+ int columnsInChunk = chunkDataView.getLodPos().getWidthAtDetail(renderSource.getDataDetail());
+
+ for (int ox = 0; ox < columnsInChunk; ox++)
+ {
+ for (int oz = 0; oz < columnsInChunk; oz++)
+ {
+ int relSourceX = relStartX + ox;
+ int relSourceZ = relStartZ + oz;
+ ColumnArrayView columnArrayView = renderSource.getVerticalDataPointView(relSourceX, relSourceZ);
+ int hash = columnArrayView.getDataHash();
+ SingleColumnFullDataAccessor fullArrayView = chunkDataView.get(ox * dataToSourceScale, oz * dataToSourceScale);
+ FullDataToRenderDataTransformer.convertColumnData(level,
+ sourceBlockX + sourceDataPointBlockWidth * relSourceX,
+ sourceBlockZ + sourceDataPointBlockWidth * relSourceZ,
+ columnArrayView, fullArrayView, 2);
+ changed |= hash != columnArrayView.getDataHash();
+ }
+ }
+ renderSource.fillDebugFlag(relStartX, relStartZ, columnsInChunk, columnsInChunk, ColumnRenderSource.DebugSourceFlag.DIRECT);
+ }
+ else if (chunkDataView.getLodPos().detailLevel < renderSource.getDataDetail())
+ {
+ // The entire chunk is being converted to a single column data point, possibly.
+ DhLodPos dataCornerPos = chunkDataView.getLodPos().getCornerLodPos(chunkDataView.detailLevel);
+ DhLodPos sourceCornerPos = renderSourcePos.getCorner(renderSource.getDataDetail());
+ DhLodPos sourceStartingChangePos = dataCornerPos.convertToDetailLevel(renderSource.getDataDetail());
+ int chunksPerColumn = sourceStartingChangePos.getWidthAtDetail(chunkDataView.getLodPos().detailLevel);
+ if (chunkDataView.getLodPos().x % chunksPerColumn != 0 || chunkDataView.getLodPos().z % chunksPerColumn != 0)
+ {
+ return false; // not a multiple of the column size, so no change
+ }
+ int relStartX = Math.floorMod(sourceStartingChangePos.x, renderSource.getWidthInDataPoints());
+ int relStartZ = Math.floorMod(sourceStartingChangePos.z, renderSource.getWidthInDataPoints());
+ ColumnArrayView columnArrayView = renderSource.getVerticalDataPointView(relStartX, relStartZ);
+ int hash = columnArrayView.getDataHash();
+ SingleColumnFullDataAccessor fullArrayView = chunkDataView.get(0, 0);
+ FullDataToRenderDataTransformer.convertColumnData(level, dataCornerPos.x * sourceDataPointBlockWidth,
+ dataCornerPos.z * sourceDataPointBlockWidth,
+ columnArrayView, fullArrayView, 2);
+ changed = hash != columnArrayView.getDataHash();
+ renderSource.fillDebugFlag(relStartX, relStartZ, 1, 1, ColumnRenderSource.DebugSourceFlag.DIRECT);
+ }
+ return changed;
+ }
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/DataRenderTransformer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/DataRenderTransformer.java
deleted file mode 100644
index 7506491e9..000000000
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/DataRenderTransformer.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 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.core.dataObjects.transformers;
-
-import com.seibel.distanthorizons.core.config.Config;
-import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
-import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource;
-import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderLoader;
-import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
-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.util.ThreadUtil;
-import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
-import org.apache.logging.log4j.Logger;
-
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutorService;
-
-/** TODO: Merge this with {@link FullDataToRenderDataTransformer} */
-public class DataRenderTransformer
-{
- private static final Logger LOGGER = DhLoggerBuilder.getLogger();
- private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
-
- private static ExecutorService transformerThreadPool = null;
- private static ConfigChangeListener configListener;
-
-
-
- //==============//
- // transformers //
- //==============//
-
- public static CompletableFuture transformFullDataToRenderSourceAsync(IFullDataSource fullDataSource, IDhClientLevel level)
- {
- return CompletableFuture.supplyAsync(() -> transformFullDataToRenderSource(fullDataSource, level), transformerThreadPool);
- }
-
- public static CompletableFuture transformFullDataToRenderSourceAsync(CompletableFuture fullDataSourceFuture, IDhClientLevel level)
- {
- return fullDataSourceFuture.thenApplyAsync((fullDataSource) -> transformFullDataToRenderSource(fullDataSource, level), transformerThreadPool);
- }
-
- private static ColumnRenderSource transformFullDataToRenderSource(IFullDataSource fullDataSource, IDhClientLevel level)
- {
- if (fullDataSource == null)
- {
- return null;
- }
- else if (MC.getWrappedClientWorld() == null)
- {
- // if the client is no longer loaded in the world, render sources cannot be created
- return null;
- }
-
- try
- {
- return ColumnRenderLoader.INSTANCE.createRenderSource(fullDataSource, level);
- }
- catch (InterruptedException e)
- {
- return null;
- }
- }
-
-
-
- //==========================//
- // executor handler methods //
- //==========================//
-
- /**
- * Creates a new executor.
- * Does nothing if an executor already exists.
- */
- public static void setupExecutorService()
- {
- // static setup
- if (configListener == null)
- {
- configListener = new ConfigChangeListener<>(Config.Client.Advanced.MultiThreading.numberOfDataTransformerThreads, (threadCount) -> { setThreadPoolSize(threadCount); });
- }
-
-
- // TODO this didn't seem to be re-sizing when changed via the config
- if (transformerThreadPool == null || transformerThreadPool.isTerminated())
- {
- LOGGER.info("Starting " + DataRenderTransformer.class.getSimpleName());
- setThreadPoolSize(Config.Client.Advanced.MultiThreading.numberOfDataTransformerThreads.get());
- }
- }
- public static void setThreadPoolSize(int threadPoolSize)
- {
- if (transformerThreadPool != null)
- {
- // close the previous thread pool if one exists
- transformerThreadPool.shutdown();
- }
-
- transformerThreadPool = ThreadUtil.makeRateLimitedThreadPool(threadPoolSize, "Full/Render Data Transformer", Config.Client.Advanced.MultiThreading.runTimeRatioForDataTransformerThreads);
- }
-
- /**
- * Stops any executing tasks and destroys the executor.
- * Does nothing if the executor isn't running.
- */
- public static void shutdownExecutorService()
- {
- if (transformerThreadPool != null)
- {
- LOGGER.info("Stopping " + DataRenderTransformer.class.getSimpleName());
- transformerThreadPool.shutdownNow();
- }
- }
-
-}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java
index 6d53e09ba..40e707746 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java
@@ -21,6 +21,7 @@ package com.seibel.distanthorizons.core.dataObjects.transformers;
import com.seibel.distanthorizons.api.enums.config.EBlocksToAvoid;
import com.seibel.distanthorizons.core.config.Config;
+import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor;
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.SingleColumnFullDataAccessor;
@@ -31,18 +32,22 @@ 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.pos.DhBlockPos;
-import com.seibel.distanthorizons.core.pos.DhLodPos;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
+import com.seibel.distanthorizons.core.util.ThreadUtil;
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.coreapi.util.BitShiftUtil;
+import org.apache.logging.log4j.Logger;
import java.util.HashSet;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
/**
* Handles converting {@link ChunkSizedFullDataAccessor}, {@link IIncompleteFullDataSource},
@@ -50,24 +55,57 @@ import java.util.HashSet;
*/
public class FullDataToRenderDataTransformer
{
+ private static final Logger LOGGER = DhLoggerBuilder.getLogger();
+
private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
+ private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
+
+ private static ExecutorService transformerThreadPool = null;
+ private static ConfigChangeListener configListener;
- /**
- * Called in loops that may run for an extended period of time.
- * This is necessary to allow canceling these transformers since running
- * them after the client has left a given world will throw exceptions here.
- */
- private static void throwIfThreadInterrupted() throws InterruptedException
+ //==============================//
+ // public transformer interface //
+ //==============================//
+
+ public static CompletableFuture transformFullDataToRenderSourceAsync(IFullDataSource fullDataSource, IDhClientLevel level) { return CompletableFuture.supplyAsync(() -> transformFullDataToRenderSource(fullDataSource, level), transformerThreadPool); }
+ public static CompletableFuture transformFullDataToRenderSourceAsync(CompletableFuture fullDataSourceFuture, IDhClientLevel level) { return fullDataSourceFuture.thenApplyAsync((fullDataSource) -> transformFullDataToRenderSource(fullDataSource, level), transformerThreadPool); }
+ private static ColumnRenderSource transformFullDataToRenderSource(IFullDataSource fullDataSource, IDhClientLevel level)
{
- if (Thread.interrupted())
+ if (fullDataSource == null)
{
- throw new InterruptedException(FullDataToRenderDataTransformer.class.getSimpleName() + " task interrupted.");
+ return null;
+ }
+ else if (MC.getWrappedClientWorld() == null)
+ {
+ // if the client is no longer loaded in the world, render sources cannot be created
+ return null;
+ }
+
+
+ try
+ {
+ if (fullDataSource instanceof CompleteFullDataSource)
+ {
+ return transformCompleteFullDataToColumnData(level, (CompleteFullDataSource) fullDataSource);
+ }
+ else if (fullDataSource instanceof IIncompleteFullDataSource)
+ {
+ return transformIncompleteFullDataToColumnData(level, (IIncompleteFullDataSource) fullDataSource);
+ }
+
+ LodUtil.assertNotReach("Unimplemented Full Data transformer for "+IFullDataSource.class.getSimpleName()+" of type ["+fullDataSource.getClass().getSimpleName()+"].");
+ return null;
+ }
+ catch (InterruptedException e)
+ {
+ return null;
}
}
+
//==============//
// transformers //
//==============//
@@ -79,7 +117,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.
*/
- public static ColumnRenderSource transformFullDataToColumnData(IDhClientLevel level, CompleteFullDataSource fullDataSource) throws InterruptedException
+ private static ColumnRenderSource transformCompleteFullDataToColumnData(IDhClientLevel level, CompleteFullDataSource fullDataSource) throws InterruptedException
{
final DhSectionPos pos = fullDataSource.getSectionPos();
final byte dataDetail = fullDataSource.getDataDetailLevel();
@@ -129,7 +167,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.
*/
- public static ColumnRenderSource transformIncompleteDataToColumnData(IDhClientLevel level, IIncompleteFullDataSource data) throws InterruptedException
+ private static ColumnRenderSource transformIncompleteFullDataToColumnData(IDhClientLevel level, IIncompleteFullDataSource data) throws InterruptedException
{
final DhSectionPos pos = data.getSectionPos();
final byte dataDetail = data.getDataDetailLevel();
@@ -175,137 +213,27 @@ public class FullDataToRenderDataTransformer
return columnSource;
}
+
+
+ //================//
+ // helper methods //
+ //================//
+
/**
- * @throws InterruptedException Can be caused by interrupting the thread upstream.
- * Generally thrown if the method is running after the client leaves the current world.
- *
- * @return true if any data was changed, false otherwise
+ * Called in loops that may run for an extended period of time.
+ * This is necessary to allow canceling these transformers since running
+ * them after the client has left a given world will throw exceptions.
*/
- public static boolean writeFullDataChunkToColumnData(ColumnRenderSource renderSource, IDhClientLevel level, ChunkSizedFullDataAccessor chunkDataView) throws InterruptedException, IllegalArgumentException
+ private static void throwIfThreadInterrupted() throws InterruptedException
{
- final DhSectionPos renderSourcePos = renderSource.getSectionPos();
-
- final int sourceBlockX = renderSourcePos.getCorner().getCornerBlockPos().x;
- final int sourceBlockZ = renderSourcePos.getCorner().getCornerBlockPos().z;
-
- // offset between the incoming chunk data and this render source
- final int blockOffsetX = (chunkDataView.pos.x * LodUtil.CHUNK_WIDTH) - sourceBlockX;
- final int blockOffsetZ = (chunkDataView.pos.z * LodUtil.CHUNK_WIDTH) - sourceBlockZ;
-
- final int sourceDataPointBlockWidth = BitShiftUtil.powerOfTwo(renderSource.getDataDetail());
-
- boolean changed = false;
-
- if (chunkDataView.detailLevel == renderSource.getDataDetail())
+ if (Thread.interrupted())
{
- renderSource.markNotEmpty();
- // confirm the render source contains this chunk
- if (blockOffsetX < 0
- || blockOffsetX + LodUtil.CHUNK_WIDTH > renderSource.getWidthInDataPoints()
- || blockOffsetZ < 0
- || blockOffsetZ + LodUtil.CHUNK_WIDTH > renderSource.getWidthInDataPoints())
- {
- throw new IllegalArgumentException("Data offset is out of bounds");
- }
-
- throwIfThreadInterrupted();
-
- for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
- {
- for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
- {
- ColumnArrayView columnArrayView = renderSource.getVerticalDataPointView(blockOffsetX + x, blockOffsetZ + z);
- int hash = columnArrayView.getDataHash();
- SingleColumnFullDataAccessor fullArrayView = chunkDataView.get(x, z);
- convertColumnData(level,
- sourceBlockX + sourceDataPointBlockWidth * (blockOffsetX + x),
- sourceBlockZ + sourceDataPointBlockWidth * (blockOffsetZ + z),
- columnArrayView, fullArrayView, 2);
- changed |= hash != columnArrayView.getDataHash();
- }
- }
- renderSource.fillDebugFlag(blockOffsetX, blockOffsetZ, LodUtil.CHUNK_WIDTH, LodUtil.CHUNK_WIDTH, ColumnRenderSource.DebugSourceFlag.DIRECT);
- }
- else if (chunkDataView.detailLevel < renderSource.getDataDetail() && renderSource.getDataDetail() <= chunkDataView.getLodPos().detailLevel)
- {
- renderSource.markNotEmpty();
- // multiple chunk data points converting to 1 column data point
- DhLodPos dataCornerPos = chunkDataView.getLodPos().getCornerLodPos(chunkDataView.detailLevel);
- DhLodPos sourceCornerPos = renderSourcePos.getCorner(renderSource.getDataDetail());
- DhLodPos sourceStartingChangePos = dataCornerPos.convertToDetailLevel(renderSource.getDataDetail());
- int relStartX = Math.floorMod(sourceStartingChangePos.x, renderSource.getWidthInDataPoints());
- int relStartZ = Math.floorMod(sourceStartingChangePos.z, renderSource.getWidthInDataPoints());
- int dataToSourceScale = sourceCornerPos.getWidthAtDetail(chunkDataView.detailLevel);
- int columnsInChunk = chunkDataView.getLodPos().getWidthAtDetail(renderSource.getDataDetail());
-
- for (int ox = 0; ox < columnsInChunk; ox++)
- {
- for (int oz = 0; oz < columnsInChunk; oz++)
- {
- int relSourceX = relStartX + ox;
- int relSourceZ = relStartZ + oz;
- ColumnArrayView columnArrayView = renderSource.getVerticalDataPointView(relSourceX, relSourceZ);
- int hash = columnArrayView.getDataHash();
- SingleColumnFullDataAccessor fullArrayView = chunkDataView.get(ox * dataToSourceScale, oz * dataToSourceScale);
- convertColumnData(level,
- sourceBlockX + sourceDataPointBlockWidth * relSourceX,
- sourceBlockZ + sourceDataPointBlockWidth * relSourceZ,
- columnArrayView, fullArrayView, 2);
- changed |= hash != columnArrayView.getDataHash();
- }
- }
- renderSource.fillDebugFlag(relStartX, relStartZ, columnsInChunk, columnsInChunk, ColumnRenderSource.DebugSourceFlag.DIRECT);
- }
- else if (chunkDataView.getLodPos().detailLevel < renderSource.getDataDetail())
- {
- // The entire chunk is being converted to a single column data point, possibly.
- DhLodPos dataCornerPos = chunkDataView.getLodPos().getCornerLodPos(chunkDataView.detailLevel);
- DhLodPos sourceCornerPos = renderSourcePos.getCorner(renderSource.getDataDetail());
- DhLodPos sourceStartingChangePos = dataCornerPos.convertToDetailLevel(renderSource.getDataDetail());
- int chunksPerColumn = sourceStartingChangePos.getWidthAtDetail(chunkDataView.getLodPos().detailLevel);
- if (chunkDataView.getLodPos().x % chunksPerColumn != 0 || chunkDataView.getLodPos().z % chunksPerColumn != 0)
- {
- return false; // not a multiple of the column size, so no change
- }
- int relStartX = Math.floorMod(sourceStartingChangePos.x, renderSource.getWidthInDataPoints());
- int relStartZ = Math.floorMod(sourceStartingChangePos.z, renderSource.getWidthInDataPoints());
- ColumnArrayView columnArrayView = renderSource.getVerticalDataPointView(relStartX, relStartZ);
- int hash = columnArrayView.getDataHash();
- SingleColumnFullDataAccessor fullArrayView = chunkDataView.get(0, 0);
- convertColumnData(level, dataCornerPos.x * sourceDataPointBlockWidth,
- dataCornerPos.z * sourceDataPointBlockWidth,
- columnArrayView, fullArrayView, 2);
- changed = hash != columnArrayView.getDataHash();
- renderSource.fillDebugFlag(relStartX, relStartZ, 1, 1, ColumnRenderSource.DebugSourceFlag.DIRECT);
- }
- return changed;
- }
-
- private static void convertColumnData(IDhClientLevel level, int blockX, int blockZ, ColumnArrayView columnArrayView, SingleColumnFullDataAccessor fullArrayView, int genMode)
- {
- if (!fullArrayView.doesColumnExist())
- {
- return;
- }
-
- int dataTotalLength = fullArrayView.getSingleLength();
- if (dataTotalLength == 0)
- {
- return;
- }
-
- if (dataTotalLength > columnArrayView.verticalSize())
- {
- ColumnArrayView totalColumnData = new ColumnArrayView(new long[dataTotalLength], dataTotalLength, 0, dataTotalLength);
- iterateAndConvert(level, blockX, blockZ, genMode, totalColumnData, fullArrayView);
- columnArrayView.changeVerticalSizeFrom(totalColumnData);
- }
- else
- {
- iterateAndConvert(level, blockX, blockZ, genMode, columnArrayView, fullArrayView); //Directly use the arrayView since it fits.
+ throw new InterruptedException(FullDataToRenderDataTransformer.class.getSimpleName() + " task interrupted.");
}
}
+
+ // TODO what does this mean?
private static void iterateAndConvert(IDhClientLevel level, int blockX, int blockZ, int genMode, ColumnArrayView column, SingleColumnFullDataAccessor data)
{
boolean avoidSolidBlocks = (Config.Client.Advanced.Graphics.Quality.blocksToIgnore.get() == EBlocksToAvoid.NON_COLLIDING);
@@ -376,6 +304,81 @@ public class FullDataToRenderDataTransformer
column.set(0, RenderDataPointUtil.createVoidDataPoint((byte) genMode));
}
}
-
-
+
+ // TODO what does this mean?
+ public static void convertColumnData(IDhClientLevel level, int blockX, int blockZ, ColumnArrayView columnArrayView, SingleColumnFullDataAccessor fullArrayView, int genMode)
+ {
+ if (!fullArrayView.doesColumnExist())
+ {
+ return;
+ }
+
+ int dataTotalLength = fullArrayView.getSingleLength();
+ if (dataTotalLength == 0)
+ {
+ return;
+ }
+
+ if (dataTotalLength > columnArrayView.verticalSize())
+ {
+ ColumnArrayView totalColumnData = new ColumnArrayView(new long[dataTotalLength], dataTotalLength, 0, dataTotalLength);
+ iterateAndConvert(level, blockX, blockZ, genMode, totalColumnData, fullArrayView);
+ columnArrayView.changeVerticalSizeFrom(totalColumnData);
+ }
+ else
+ {
+ iterateAndConvert(level, blockX, blockZ, genMode, columnArrayView, fullArrayView); //Directly use the arrayView since it fits.
+ }
+ }
+
+
+
+ //==========================//
+ // executor handler methods //
+ //==========================//
+
+ /**
+ * Creates a new executor.
+ * Does nothing if an executor already exists.
+ */
+ public static void setupExecutorService()
+ {
+ // static setup
+ if (configListener == null)
+ {
+ configListener = new ConfigChangeListener<>(Config.Client.Advanced.MultiThreading.numberOfDataTransformerThreads, (threadCount) -> { setThreadPoolSize(threadCount); });
+ }
+
+
+ // TODO this didn't seem to be re-sizing when changed via the config
+ if (transformerThreadPool == null || transformerThreadPool.isTerminated())
+ {
+ LOGGER.info("Starting " + FullDataToRenderDataTransformer.class.getSimpleName());
+ setThreadPoolSize(Config.Client.Advanced.MultiThreading.numberOfDataTransformerThreads.get());
+ }
+ }
+ public static void setThreadPoolSize(int threadPoolSize)
+ {
+ if (transformerThreadPool != null)
+ {
+ // close the previous thread pool if one exists
+ transformerThreadPool.shutdown();
+ }
+
+ transformerThreadPool = ThreadUtil.makeRateLimitedThreadPool(threadPoolSize, "Full/Render Data Transformer", Config.Client.Advanced.MultiThreading.runTimeRatioForDataTransformerThreads);
+ }
+
+ /**
+ * Stops any executing tasks and destroys the executor.
+ * Does nothing if the executor isn't running.
+ */
+ public static void shutdownExecutorService()
+ {
+ if (transformerThreadPool != null)
+ {
+ LOGGER.info("Stopping " + FullDataToRenderDataTransformer.class.getSimpleName());
+ transformerThreadPool.shutdownNow();
+ }
+ }
+
}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java
index 6d8683ac0..bd8a536ad 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java
@@ -22,6 +22,7 @@ package com.seibel.distanthorizons.core.file.renderfile;
import com.google.common.collect.HashMultimap;
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource;
+import com.seibel.distanthorizons.core.dataObjects.transformers.FullDataToRenderDataTransformer;
import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile;
import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
@@ -29,7 +30,6 @@ 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.dataObjects.render.ColumnRenderSource;
-import com.seibel.distanthorizons.core.dataObjects.transformers.DataRenderTransformer;
import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider;
import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
@@ -474,7 +474,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider
// convert the full data source into a render source
- CompletableFuture transformFuture = DataRenderTransformer.transformFullDataToRenderSourceAsync(fullDataSourceFuture, this.level)
+ CompletableFuture transformFuture = FullDataToRenderDataTransformer.transformFullDataToRenderSourceAsync(fullDataSourceFuture, this.level)
.handle((newRenderSource, ex) ->
{
if (ex == null)