Merge DataRenderTransformer and FullDataToRenderDataTransformer
This commit is contained in:
@@ -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();
|
||||
|
||||
-16
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//========================//
|
||||
|
||||
+113
-1
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
-134
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<Integer> configListener;
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// transformers //
|
||||
//==============//
|
||||
|
||||
public static CompletableFuture<ColumnRenderSource> transformFullDataToRenderSourceAsync(IFullDataSource fullDataSource, IDhClientLevel level)
|
||||
{
|
||||
return CompletableFuture.supplyAsync(() -> transformFullDataToRenderSource(fullDataSource, level), transformerThreadPool);
|
||||
}
|
||||
|
||||
public static CompletableFuture<ColumnRenderSource> transformFullDataToRenderSourceAsync(CompletableFuture<IFullDataSource> 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. <br>
|
||||
* 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. <br>
|
||||
* Does nothing if the executor isn't running.
|
||||
*/
|
||||
public static void shutdownExecutorService()
|
||||
{
|
||||
if (transformerThreadPool != null)
|
||||
{
|
||||
LOGGER.info("Stopping " + DataRenderTransformer.class.getSimpleName());
|
||||
transformerThreadPool.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+141
-138
@@ -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<Integer> configListener;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Called in loops that may run for an extended period of time. <br>
|
||||
* 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<ColumnRenderSource> transformFullDataToRenderSourceAsync(IFullDataSource fullDataSource, IDhClientLevel level) { return CompletableFuture.supplyAsync(() -> transformFullDataToRenderSource(fullDataSource, level), transformerThreadPool); }
|
||||
public static CompletableFuture<ColumnRenderSource> transformFullDataToRenderSourceAsync(CompletableFuture<IFullDataSource> 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. <br>
|
||||
* 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. <br>
|
||||
* 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. <br>
|
||||
* Does nothing if the executor isn't running.
|
||||
*/
|
||||
public static void shutdownExecutorService()
|
||||
{
|
||||
if (transformerThreadPool != null)
|
||||
{
|
||||
LOGGER.info("Stopping " + FullDataToRenderDataTransformer.class.getSimpleName());
|
||||
transformerThreadPool.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -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<Void> transformFuture = DataRenderTransformer.transformFullDataToRenderSourceAsync(fullDataSourceFuture, this.level)
|
||||
CompletableFuture<Void> transformFuture = FullDataToRenderDataTransformer.transformFullDataToRenderSourceAsync(fullDataSourceFuture, this.level)
|
||||
.handle((newRenderSource, ex) ->
|
||||
{
|
||||
if (ex == null)
|
||||
|
||||
Reference in New Issue
Block a user