diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV2.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV2.java
index 75ab0ec71..12fc16459 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV2.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV2.java
@@ -24,8 +24,8 @@ import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGeneratio
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
import com.seibel.distanthorizons.core.dataObjects.transformers.LodDataBuilder;
import com.seibel.distanthorizons.core.file.AbstractNewDataSourceHandler;
+import com.seibel.distanthorizons.core.file.DataSourcePool;
import com.seibel.distanthorizons.core.file.IDataSource;
-import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2;
import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
@@ -33,7 +33,6 @@ import com.seibel.distanthorizons.core.util.FullDataPointUtilV2;
import com.seibel.distanthorizons.core.util.FullDataPointUtilV1;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
-import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
@@ -41,16 +40,11 @@ import it.unimi.dsi.fastutil.longs.LongArrayList;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nullable;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.concurrent.locks.ReentrantLock;
/**
* This data source contains every datapoint over its given {@link DhSectionPos}.
*
- * TODO create a child object that extends AutoClosable
- * that can be pooled to reduce GC overhead
- *
* @see FullDataPointUtilV2
* @see FullDataSourceV1
*/
@@ -71,7 +65,9 @@ public class FullDataSourceV2 implements IDataSource
public static final int WIDTH = 64;
public static final byte DATA_FORMAT_VERSION = 1;
-
+
+ public static final DataSourcePool DATA_SOURCE_POOL = new DataSourcePool<>(FullDataSourceV2::createEmpty, FullDataSourceV2::prepPooledDataSource);
+
private int cachedHashCode = 0;
@@ -836,6 +832,33 @@ public class FullDataSourceV2 implements IDataSource
+ //=========//
+ // pooling //
+ //=========//
+
+ private static void prepPooledDataSource(DhSectionPos pos, boolean clearData, FullDataSourceV2 dataSource)
+ {
+ dataSource.pos = pos;
+
+ if (clearData)
+ {
+ dataSource.mapping.clear(pos);
+
+ for (int i = 0; i < dataSource.dataPoints.length; i++)
+ {
+ if (dataSource.dataPoints[i] != null)
+ {
+ dataSource.dataPoints[i].clear();
+ }
+ }
+
+ Arrays.fill(dataSource.columnGenerationSteps, (byte) 0);
+ Arrays.fill(dataSource.columnWorldCompressionMode, (byte) 0);
+ }
+ }
+
+
+
//=====================//
// setters and getters //
//=====================//
@@ -929,92 +952,10 @@ public class FullDataSourceV2 implements IDataSource
}
}
-
-
- //=========//
- // pooling //
- //=========//
-
@Override
public void close() throws Exception
{
- returnPooledDataSource(this);
+ DATA_SOURCE_POOL.returnPooledDataSource(this);
}
-
- /** used when pooling data sources */
- private static final ArrayList CACHED_SOURCES = new ArrayList<>();
- private static final ReentrantLock CACHE_LOCK = new ReentrantLock();
-
-
- /** @return an empty data source if non are cached */
- public static FullDataSourceV2 getPooledSource(DhSectionPos pos, boolean clearData)
- {
- try
- {
- CACHE_LOCK.lock();
-
- int index = CACHED_SOURCES.size() - 1;
- if (index == -1)
- {
- // no pooled sources exist
- return createEmpty(pos);
- }
- else
- {
- FullDataSourceV2 dataSource = CACHED_SOURCES.remove(index);
- dataSource.pos = pos;
-
- if (clearData)
- {
- dataSource.mapping.clear(pos);
-
- for (int i = 0; i < dataSource.dataPoints.length; i++)
- {
- if (dataSource.dataPoints[i] != null)
- {
- dataSource.dataPoints[i].clear();
- }
- }
-
- Arrays.fill(dataSource.columnGenerationSteps, (byte) 0);
- Arrays.fill(dataSource.columnWorldCompressionMode, (byte) 0);
- }
-
- return dataSource;
- }
- }
- finally
- {
- CACHE_LOCK.unlock();
- }
- }
-
- /**
- * Doesn't have to be called, if a data source isn't returned, nothing will be leaked.
- * It just means a new source must be constructed next time {@link FullDataSourceV2#getPooledSource} is called.
- */
- public static void returnPooledDataSource(FullDataSourceV2 dataSource)
- {
- if (dataSource == null)
- {
- return;
- }
- else if (CACHED_SOURCES.size() > 25)
- {
- return;
- }
-
- try
- {
- CACHE_LOCK.lock();
- CACHED_SOURCES.add(dataSource);
- }
- finally
- {
- CACHE_LOCK.unlock();
- }
- }
-
-
}
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 22278386a..6a2b44a7f 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
@@ -22,25 +22,22 @@ package com.seibel.distanthorizons.core.dataObjects.render;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.dataObjects.transformers.FullDataToRenderDataTransformer;
+import com.seibel.distanthorizons.core.file.DataSourcePool;
import com.seibel.distanthorizons.core.file.IDataSource;
-import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
-import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnArrayView;
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnQuadView;
-import com.seibel.distanthorizons.core.dataObjects.render.columnViews.IColumnDataView;
import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
-import com.seibel.distanthorizons.core.util.LodUtil;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import org.apache.logging.log4j.Logger;
-import java.io.*;
+import java.util.Arrays;
import java.util.concurrent.atomic.AtomicLong;
/**
@@ -56,27 +53,21 @@ public class ColumnRenderSource implements IDataSource
public static final byte SECTION_SIZE_OFFSET = DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
public static final int SECTION_SIZE = BitShiftUtil.powerOfTwo(SECTION_SIZE_OFFSET);
+ @Deprecated
public static final byte DATA_FORMAT_VERSION = 1;
@Override
public byte getDataFormatVersion() { return DATA_FORMAT_VERSION; }
- public static final String DATA_NAME = "ColumnRenderSource";
+ public static final DataSourcePool DATA_SOURCE_POOL = new DataSourcePool<>(ColumnRenderSource::createEmptyRenderSource, null /* data source prep/cleanup needs to be done outside the pool since it requires additional inputs */);
- /**
- * This is the byte put between different sections in the binary save file.
- * The presence and absence of this byte indicates if the file is correctly formatted.
- */
- public static final int DATA_GUARD_BYTE = 0xFFFFFFFF;
- /** indicates the binary save file represents an empty data source */
- public static final int NO_DATA_FLAG_BYTE = 0x00000001;
/** will be zero if an empty data source was created */
public int verticalDataCount;
- public final DhSectionPos sectionPos;
- public final int yOffset;
+ public DhSectionPos sectionPos;
+ public int yOffset;
- public long[] renderDataContainer;
+ public LongArrayList renderDataContainer;
public final DebugSourceFlag[] debugSourceFlags;
@@ -91,19 +82,52 @@ public class ColumnRenderSource implements IDataSource
// constructors //
//==============//
- public static ColumnRenderSource createEmptyRenderSource(DhSectionPos sectionPos) { return new ColumnRenderSource(sectionPos, 0, 0); }
+ /**
+ * This is separate from {@link DataSourcePool#getPooledSource(DhSectionPos, boolean)}
+ * because we need to pass in a couple extra values,
+ * specifically maxVerticalSize and yOffset.
+ */
+ public static ColumnRenderSource getPooledRenderSource(DhSectionPos pos, int maxVerticalSize, int yOffset, boolean clearData)
+ {
+ ColumnRenderSource renderSource = DATA_SOURCE_POOL.getPooledSource(pos);
+
+ // set necessary properties
+ renderSource.sectionPos = pos;
+ renderSource.verticalDataCount = maxVerticalSize;
+ renderSource.yOffset = yOffset;
+
+
+ // resize the array if necessary
+ int dataArraySize = SECTION_SIZE * SECTION_SIZE * maxVerticalSize;
+ renderSource.renderDataContainer.ensureCapacity(dataArraySize);
+ while (renderSource.renderDataContainer.size() < dataArraySize)
+ {
+ renderSource.renderDataContainer.add(0);
+ }
+
+ if (clearData)
+ {
+ Arrays.fill(renderSource.renderDataContainer.elements(), 0);
+ Arrays.fill(renderSource.debugSourceFlags, null);
+ }
+
+ return renderSource;
+ }
+
+
+ private static ColumnRenderSource createEmptyRenderSource(DhSectionPos sectionPos) { return new ColumnRenderSource(sectionPos, 0, 0); }
/**
* Creates an empty ColumnRenderSource.
*
- * @param sectionPos the relative position of the container
+ * @param pos the relative position of the container
* @param maxVerticalSize the maximum vertical size of the container
*/
- public ColumnRenderSource(DhSectionPos sectionPos, int maxVerticalSize, int yOffset)
+ private ColumnRenderSource(DhSectionPos pos, int maxVerticalSize, int yOffset)
{
this.verticalDataCount = maxVerticalSize;
- this.renderDataContainer = new long[SECTION_SIZE * SECTION_SIZE * this.verticalDataCount];
+ this.renderDataContainer = new LongArrayList(new long[SECTION_SIZE * SECTION_SIZE * this.verticalDataCount]);
this.debugSourceFlags = new DebugSourceFlag[SECTION_SIZE * SECTION_SIZE];
- this.sectionPos = sectionPos;
+ this.sectionPos = pos;
this.yOffset = yOffset;
this.worldGenStep = EDhApiWorldGenerationStep.EMPTY;
}
@@ -114,65 +138,7 @@ public class ColumnRenderSource implements IDataSource
// datapoint manipulation //
//========================//
- public void clearDataPoint(int posX, int posZ)
- {
- for (int verticalIndex = 0; verticalIndex < this.verticalDataCount; verticalIndex++)
- {
- this.renderDataContainer[posX * SECTION_SIZE * this.verticalDataCount + posZ * this.verticalDataCount + verticalIndex] = RenderDataPointUtil.EMPTY_DATA;
- }
- }
-
- public boolean setDataPoint(long data, int posX, int posZ, int verticalIndex)
- {
- this.renderDataContainer[posX * SECTION_SIZE * this.verticalDataCount + posZ * this.verticalDataCount + verticalIndex] = data;
- return true;
- }
-
- public boolean copyVerticalData(IColumnDataView newData, int posX, int posZ, boolean overwriteDataWithSameGenerationMode)
- {
- if (DO_SAFETY_CHECKS)
- {
- if (newData.size() != this.verticalDataCount)
- throw new IllegalArgumentException("newData size not the same as this column's vertical size");
- if (posX < 0 || posX >= SECTION_SIZE)
- throw new IllegalArgumentException("X position is out of bounds");
- if (posZ < 0 || posZ >= SECTION_SIZE)
- throw new IllegalArgumentException("Z position is out of bounds");
- }
-
- int dataOffset = posX * SECTION_SIZE * this.verticalDataCount + posZ * this.verticalDataCount;
- int compare = RenderDataPointUtil.compareDatapointPriority(newData.get(0), this.renderDataContainer[dataOffset]);
- if (overwriteDataWithSameGenerationMode)
- {
- if (compare < 0)
- {
- return false;
- }
- }
- else
- {
- if (compare <= 0)
- {
- return false;
- }
- }
-
- // copy the newData into this column's data
- newData.copyTo(this.renderDataContainer, dataOffset, newData.size());
- return true;
- }
-
-
- public long getFirstDataPoint(int posX, int posZ) { return getDataPoint(posX, posZ, 0); }
- public long getDataPoint(int posX, int posZ, int verticalIndex) { return this.renderDataContainer[posX * SECTION_SIZE * this.verticalDataCount + posZ * this.verticalDataCount + verticalIndex]; }
-
- public long[] getVerticalDataPointArray(int posX, int posZ)
- {
- long[] result = new long[this.verticalDataCount];
- int index = posX * SECTION_SIZE * this.verticalDataCount + posZ * this.verticalDataCount;
- System.arraycopy(this.renderDataContainer, index, result, 0, this.verticalDataCount);
- return result;
- }
+ public long getDataPoint(int posX, int posZ, int verticalIndex) { return this.renderDataContainer.getLong(posX * SECTION_SIZE * this.verticalDataCount + posZ * this.verticalDataCount + verticalIndex); }
public ColumnArrayView getVerticalDataPointView(int posX, int posZ)
{
@@ -184,8 +150,6 @@ public class ColumnRenderSource implements IDataSource
public ColumnQuadView getFullQuadView() { return this.getQuadViewOverRange(0, 0, SECTION_SIZE, SECTION_SIZE); }
public ColumnQuadView getQuadViewOverRange(int quadX, int quadZ, int quadXSize, int quadZSize) { return new ColumnQuadView(this.renderDataContainer, SECTION_SIZE, this.verticalDataCount, quadX, quadZ, quadXSize, quadZSize); }
- public int getVerticalSize() { return this.verticalDataCount; }
-
//=============//
@@ -357,7 +321,9 @@ public class ColumnRenderSource implements IDataSource
@Override
public void close() throws Exception
- { /* not currently needed */ }
+ {
+ DATA_SOURCE_POOL.returnPooledDataSource(this);
+ }
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/columnViews/ColumnArrayView.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/columnViews/ColumnArrayView.java
index 17b1e9be2..88bd337b3 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/columnViews/ColumnArrayView.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/columnViews/ColumnArrayView.java
@@ -21,12 +21,13 @@ package com.seibel.distanthorizons.core.dataObjects.render.columnViews;
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
+import it.unimi.dsi.fastutil.longs.LongArrayList;
import java.util.Arrays;
public final class ColumnArrayView implements IColumnDataView
{
- public final long[] data;
+ public final LongArrayList data;
public final int size;
public final int offset; // offset in longs
/** can be 0 if this column was created for an empty data source */
@@ -34,7 +35,7 @@ public final class ColumnArrayView implements IColumnDataView
- public ColumnArrayView(long[] data, int size, int offset, int vertSize)
+ public ColumnArrayView(LongArrayList data, int size, int offset, int vertSize)
{
this.data = data;
this.size = size;
@@ -45,9 +46,9 @@ public final class ColumnArrayView implements IColumnDataView
@Override
- public long get(int index) { return data[index + offset]; }
+ public long get(int index) { return data.getLong(index + offset); }
- public void set(int index, long value) { data[index + offset] = value; }
+ public void set(int index, long value) { data.set(index + offset, value); }
@Override
public int size() { return size; }
@@ -64,7 +65,7 @@ public final class ColumnArrayView implements IColumnDataView
return new ColumnArrayView(data, dataCount * vertSize, offset + dataIndexStart * vertSize, vertSize);
}
- public void fill(long value) { Arrays.fill(data, offset, offset + size, value); }
+ public void fill(long value) { Arrays.fill(data.elements(), offset, offset + size, value); }
public void copyFrom(IColumnDataView source) { copyFrom(source, 0); }
public void copyFrom(IColumnDataView source, int outputDataIndexOffset)
@@ -82,19 +83,19 @@ public final class ColumnArrayView implements IColumnDataView
for (int i = 0; i < source.dataCount(); i++)
{
int outputOffset = offset + outputDataIndexOffset * vertSize + i * vertSize;
- source.subView(i, 1).copyTo(data, outputOffset, source.verticalSize());
- Arrays.fill(data, outputOffset + source.verticalSize(),
+ source.subView(i, 1).copyTo(data.elements(), outputOffset, source.verticalSize());
+ Arrays.fill(data.elements(), outputOffset + source.verticalSize(),
outputOffset + vertSize, 0);
}
}
else
{
- source.copyTo(data, offset + outputDataIndexOffset * vertSize, source.size());
+ source.copyTo(data.elements(), offset + outputDataIndexOffset * vertSize, source.size());
}
}
@Override
- public void copyTo(long[] target, int offset, int size) { System.arraycopy(data, this.offset, target, offset, size); }
+ public void copyTo(long[] target, int offset, int size) { System.arraycopy(data.elements(), this.offset, target, offset, size); }
public boolean mergeWith(ColumnArrayView source, boolean override)
{
@@ -170,7 +171,7 @@ public final class ColumnArrayView implements IColumnDataView
sb.append(" [");
for (int i = 0; i < size; i++)
{
- sb.append(RenderDataPointUtil.toString(data[offset + i]));
+ sb.append(RenderDataPointUtil.toString(data.getLong(offset + i)));
if (i < size - 1)
{
sb.append(",\n");
@@ -186,15 +187,18 @@ public final class ColumnArrayView implements IColumnDataView
return arrayHash(data, offset, size);
}
- private static int arrayHash(long[] a, int offset, int length)
+ private static int arrayHash(LongArrayList a, int offset, int length)
{
if (a == null)
+ {
return 0;
+ }
+
int result = 1;
int end = offset + length;
for (int i = offset; i < end; i++)
{
- long element = a[i];
+ long element = a.getLong(i);
int elementHash = (int) (element ^ (element >>> 32));
result = 31 * result + elementHash;
}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/columnViews/ColumnQuadView.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/columnViews/ColumnQuadView.java
index 7c798dcb0..68d380735 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/columnViews/ColumnQuadView.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/columnViews/ColumnQuadView.java
@@ -19,19 +19,24 @@
package com.seibel.distanthorizons.core.dataObjects.render.columnViews;
+import it.unimi.dsi.fastutil.longs.LongArrayList;
+
public class ColumnQuadView implements IColumnDataView
{
- private final long[] data;
+ private final LongArrayList data;
private final int perColumnOffset; // per column (of columns of data) offset in longs
private final int xSize; // x size in datapoints
private final int zSize; // x size in datapoints
private final int offset; // offset in longs
private final int vertSize; // vertical size in longs
- public ColumnQuadView(long[] data, int dataZWidth, int dataVertSize, int viewXOffset, int viewZOffset, int xSize, int zSize)
+ public ColumnQuadView(LongArrayList data, int dataZWidth, int dataVertSize, int viewXOffset, int viewZOffset, int xSize, int zSize)
{
- if (viewXOffset + xSize > (data.length / (dataZWidth * dataVertSize)) || viewZOffset + zSize > dataZWidth)
+ if (viewXOffset + xSize > (data.size() / (dataZWidth * dataVertSize)) || viewZOffset + zSize > dataZWidth)
+ {
throw new IllegalArgumentException("View is out of bounds");
+ }
+
this.data = data;
this.xSize = xSize;
this.zSize = zSize;
@@ -39,7 +44,7 @@ public class ColumnQuadView implements IColumnDataView
this.perColumnOffset = dataZWidth * dataVertSize;
this.offset = viewXOffset * perColumnOffset + viewZOffset * dataVertSize;
}
- private ColumnQuadView(long[] data, int perColumnOffset, int offset, int vertSize, int xSize, int zSize)
+ private ColumnQuadView(LongArrayList data, int perColumnOffset, int offset, int vertSize, int xSize, int zSize)
{
this.data = data;
this.perColumnOffset = perColumnOffset;
@@ -60,12 +65,12 @@ public class ColumnQuadView implements IColumnDataView
public long get(int x, int z, int v)
{
- return data[offset + x * perColumnOffset + z * vertSize + v];
+ return data.getLong(offset + x * perColumnOffset + z * vertSize + v);
}
public long set(int x, int z, int v, long value)
{
- return data[offset + x * perColumnOffset + z * vertSize + v] = value;
+ return data.set(offset + x * perColumnOffset + z * vertSize + v, value);
}
public ColumnArrayView get(int x, int z)
@@ -82,7 +87,7 @@ public class ColumnQuadView implements IColumnDataView
{
if (singleColumn.verticalSize() != vertSize) throw new IllegalArgumentException("Vertical size of singleColumn must be equal to vertSize");
if (singleColumn.dataCount() != 1) throw new IllegalArgumentException("SingleColumn must contain exactly one data point");
- singleColumn.copyTo(data, offset + x * perColumnOffset + z * vertSize, singleColumn.size());
+ singleColumn.copyTo(data.elements(), offset + x * perColumnOffset + z * vertSize, singleColumn.size());
}
@Override
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 74fea11c1..65d1b873b 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
@@ -99,7 +99,7 @@ public class FullDataToRenderDataTransformer
final DhSectionPos pos = fullDataSource.getSectionPos();
final byte dataDetail = fullDataSource.getDataDetailLevel();
final int vertSize = Config.Client.Advanced.Graphics.Quality.verticalQuality.get().calculateMaxVerticalData(fullDataSource.getDataDetailLevel());
- final ColumnRenderSource columnSource = new ColumnRenderSource(pos, vertSize, level.getMinY());
+ final ColumnRenderSource columnSource = ColumnRenderSource.getPooledRenderSource(pos, vertSize, level.getMinY(), true);
if (fullDataSource.isEmpty)
{
return columnSource;
@@ -304,7 +304,7 @@ public class FullDataToRenderDataTransformer
int dataTotalLength = fullDataColumn.size();
if (dataTotalLength > columnArrayView.verticalSize())
{
- ColumnArrayView totalColumnData = new ColumnArrayView(new long[dataTotalLength], dataTotalLength, 0, dataTotalLength);
+ ColumnArrayView totalColumnData = new ColumnArrayView(new LongArrayList(new long[dataTotalLength]), dataTotalLength, 0, dataTotalLength);
iterateAndConvert(level, fullDataMapping, blockX, blockZ, totalColumnData, fullDataColumn);
columnArrayView.changeVerticalSizeFrom(totalColumnData);
}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/DataSourcePool.java b/core/src/main/java/com/seibel/distanthorizons/core/file/DataSourcePool.java
new file mode 100644
index 000000000..82ffb1fd9
--- /dev/null
+++ b/core/src/main/java/com/seibel/distanthorizons/core/file/DataSourcePool.java
@@ -0,0 +1,122 @@
+package com.seibel.distanthorizons.core.file;
+
+import com.seibel.distanthorizons.core.level.IDhLevel;
+import com.seibel.distanthorizons.core.pos.DhSectionPos;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Function;
+
+/**
+ * Data sources are often very large objects and aren't used for very long.
+ * This means their frequent construction and garbage collection can result in quite a bit of GC pressure.
+ * By pooling said data sources and reusing them we can drastically reduce this GC pressure and improve
+ * performance significantly.
+ */
+public class DataSourcePool, TDhLevel extends IDhLevel>
+{
+ private final ArrayList pooledDataSources = new ArrayList<>();
+ private final ReentrantLock poolLock = new ReentrantLock();
+
+ private final Function createEmptyDatasourceFunc;
+ @Nullable
+ private final IPrepPooledDataSourceFunc prepDatasourceFunc;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ public DataSourcePool(Function createEmptyDatasourceFunc, @Nullable IPrepPooledDataSourceFunc prepDatasourceFunc)
+ {
+ this.createEmptyDatasourceFunc = createEmptyDatasourceFunc;
+ this.prepDatasourceFunc = prepDatasourceFunc;
+ }
+
+
+
+ //===============//
+ // pool handlers //
+ //===============//
+
+ /**
+ * Returns a cleared data source.
+ * @see DataSourcePool#getPooledSource(DhSectionPos, boolean)
+ */
+ public TDataSource getPooledSource(DhSectionPos pos) { return this.getPooledSource(pos, true);}
+
+ /** @return an empty data source if non are cached */
+ public TDataSource getPooledSource(DhSectionPos pos, boolean clearData)
+ {
+ try
+ {
+ this.poolLock.lock();
+
+ int index = this.pooledDataSources.size() - 1;
+ if (index == -1)
+ {
+ // no pooled sources exist
+ return this.createEmptyDatasourceFunc.apply(pos);
+ }
+ else
+ {
+ TDataSource dataSource = this.pooledDataSources.remove(index);
+
+ // some data sources may want to handle prep themselves
+ // (due to needing additional inputs than what this pool keeps track of)
+ if (this.prepDatasourceFunc != null)
+ {
+ this.prepDatasourceFunc.prepDataSource(pos, clearData, dataSource);
+ }
+
+ return dataSource;
+ }
+ }
+ finally
+ {
+ this.poolLock.unlock();
+ }
+ }
+
+ /**
+ * Doesn't have to be called, if a data source isn't returned, nothing will be leaked.
+ * It just means a new source must be constructed next time {@link DataSourcePool#getPooledSource} is called.
+ */
+ public void returnPooledDataSource(TDataSource dataSource)
+ {
+ if (dataSource == null)
+ {
+ return;
+ }
+ else if (this.pooledDataSources.size() > 25)
+ {
+ return;
+ }
+
+ try
+ {
+ this.poolLock.lock();
+ this.pooledDataSources.add(dataSource);
+ }
+ finally
+ {
+ this.poolLock.unlock();
+ }
+ }
+
+
+
+ //================//
+ // helper classes //
+ //================//
+
+ @FunctionalInterface
+ public interface IPrepPooledDataSourceFunc, TDhLevel extends IDhLevel>
+ {
+ /** @param clearData will be false if the data will be immediately overwritten anyway */
+ void prepDataSource(DhSectionPos pos, boolean clearData, TDataSource dataSource);
+ }
+
+}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java
index 94ce359b7..f479d5a16 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java
@@ -162,11 +162,11 @@ public class FullDataSourceProviderV2
{
// TODO maybe just set children update flags to true?
// TODO is any special logic necessary? All DTOs should be generated using their children via the update system anyway
- return FullDataSourceV2.getPooledSource(pos, true);
+ return FullDataSourceV2.DATA_SOURCE_POOL.getPooledSource(pos, true);
}
@Override
- protected FullDataSourceV2 makeEmptyDataSource(DhSectionPos pos) { return FullDataSourceV2.getPooledSource(pos, true); }
+ protected FullDataSourceV2 makeEmptyDataSource(DhSectionPos pos) { return FullDataSourceV2.DATA_SOURCE_POOL.getPooledSource(pos, true); }
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java
index 289e2c332..7b856c78d 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java
@@ -112,12 +112,13 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
{
FullDataSourceV2 fullDataSource = null;
ColumnRenderSource[] adjacentRenderSections = null;
+ ColumnRenderSource renderSource = null;
try
{
// get this positions data source
fullDataSource = this.fullDataSourceProvider.get(this.pos);
- ColumnRenderSource renderSource = FullDataToRenderDataTransformer.transformFullDataToRenderSource(fullDataSource, this.level);
+ renderSource = FullDataToRenderDataTransformer.transformFullDataToRenderSource(fullDataSource, this.level);
if (renderSource.isEmpty())
{
// nothing needs to be rendered
@@ -147,14 +148,19 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
fullDataSource.close();
}
+ if (renderSource != null)
+ {
+ renderSource.close();
+ }
+
if (adjacentRenderSections != null)
{
for (int i = 0; i < adjacentRenderSections.length; i++)
{
- ColumnRenderSource renderSource = adjacentRenderSections[i];
- if (renderSource != null)
+ ColumnRenderSource adjacentRenderSource = adjacentRenderSections[i];
+ if (adjacentRenderSource != null)
{
- renderSource.close();
+ adjacentRenderSource.close();
}
}
}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java
index 35ec4a892..5a87a0cb2 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java
@@ -120,7 +120,7 @@ public class FullDataSourceV2DTO implements IBaseDTO
public FullDataSourceV2 createPooledDataSource(@NotNull ILevelWrapper levelWrapper) throws IOException, InterruptedException
{
- FullDataSourceV2 dataSource = FullDataSourceV2.getPooledSource(this.pos, false);
+ FullDataSourceV2 dataSource = FullDataSourceV2.DATA_SOURCE_POOL.getPooledSource(this.pos, false);
return this.populateDataSource(dataSource, levelWrapper);
}