Clean up LodRenderSection async loading logic
This commit is contained in:
+3
-3
@@ -111,7 +111,7 @@ public class ColumnRenderBufferBuilder
|
||||
/** @link adjData should be null for adjacent sections that cross detail level boundaries */
|
||||
public static CompletableFuture<ColumnRenderBuffer> uploadBuffersAsync(
|
||||
IDhClientLevel clientLevel,
|
||||
ColumnRenderSource renderSource,
|
||||
long pos,
|
||||
LodQuadBuilder quadBuilder
|
||||
)
|
||||
{
|
||||
@@ -132,7 +132,7 @@ public class ColumnRenderBufferBuilder
|
||||
{
|
||||
try
|
||||
{
|
||||
ColumnRenderBuffer buffer = new ColumnRenderBuffer(new DhBlockPos(DhSectionPos.getMinCornerBlockX(renderSource.pos), clientLevel.getMinY(), DhSectionPos.getMinCornerBlockZ(renderSource.pos)));
|
||||
ColumnRenderBuffer buffer = new ColumnRenderBuffer(new DhBlockPos(DhSectionPos.getMinCornerBlockX(pos), clientLevel.getMinY(), DhSectionPos.getMinCornerBlockZ(pos)));
|
||||
try
|
||||
{
|
||||
buffer.uploadBuffer(quadBuilder, GLProxy.getInstance().getGpuUploadMethod());
|
||||
@@ -158,7 +158,7 @@ public class ColumnRenderBufferBuilder
|
||||
}
|
||||
catch (Throwable e3)
|
||||
{
|
||||
LOGGER.error("LodNodeBufferBuilder was unable to upload buffer for pos ["+DhSectionPos.toString(renderSource.pos)+"], error: [" + e3.getMessage() + "].", e3);
|
||||
LOGGER.error("LodNodeBufferBuilder was unable to upload buffer for pos ["+DhSectionPos.toString(pos)+"], error: [" + e3.getMessage() + "].", e3);
|
||||
throw e3;
|
||||
}
|
||||
}, bufferUploaderExecutor);
|
||||
|
||||
@@ -36,11 +36,11 @@ import com.seibel.distanthorizons.core.render.glObject.GLProxy;
|
||||
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
||||
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.ColumnRenderBuffer;
|
||||
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
|
||||
import com.seibel.distanthorizons.core.util.ListUtil;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.WillNotClose;
|
||||
import java.awt.*;
|
||||
@@ -48,7 +48,6 @@ import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
@@ -97,9 +96,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
|
||||
private final ReentrantLock getRenderSourceLock = new ReentrantLock();
|
||||
/** Stored as a class variable so we can reuse it's result across multiple LOD loads if necessary */
|
||||
private ReferencedFutureWrapper renderSourceLoadingRefFuture = null;
|
||||
/** Stored as a class variable so we can decrement reference counts as each {@link LodRenderSection} finishes using them. */
|
||||
private ReferencedFutureWrapper[] adjacentLoadRefFutures;
|
||||
private CompletableFuture<ColumnRenderSource> renderSourceLoadingRefFuture = null;
|
||||
|
||||
private boolean missingPositionsCalculated = false;
|
||||
/** should be an empty array if no positions need to be generated */
|
||||
@@ -123,11 +120,10 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
|
||||
|
||||
|
||||
//===============================//
|
||||
// render data loading/uploading //
|
||||
//===============================//
|
||||
//======================================//
|
||||
// render data generation and uploading //
|
||||
//======================================//
|
||||
|
||||
// TODO cleanup, there's a lot of nested futures and duplicate error handling here and it's hard to read
|
||||
public synchronized void uploadRenderDataToGpuAsync()
|
||||
{
|
||||
if (!GLProxy.hasInstance())
|
||||
@@ -143,148 +139,167 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
|
||||
if (executor == null || executor.isTerminated())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.buildAndUploadRenderDataToGpuFuture = CompletableFuture.runAsync(() ->
|
||||
try
|
||||
{
|
||||
//==================//
|
||||
// load render data //
|
||||
//==================//
|
||||
|
||||
this.tryDecrementingLoadFutureArray(this.adjacentLoadRefFutures);
|
||||
|
||||
ReferencedFutureWrapper thisRenderSourceLoadFuture = this.getRenderSourceAsync();
|
||||
ReferencedFutureWrapper[] adjRenderSourceLoadRefFutures = this.getNeighborRenderSourcesAsync();
|
||||
|
||||
|
||||
// wait for all futures to complete together,
|
||||
// merging the futures makes loading significantly faster than loading this position then loading its neighbors
|
||||
ArrayList<CompletableFuture<ColumnRenderSource>> futureList = new ArrayList<>();
|
||||
futureList.add(thisRenderSourceLoadFuture.future);
|
||||
for (ReferencedFutureWrapper refFuture : adjRenderSourceLoadRefFutures)
|
||||
{
|
||||
futureList.add(refFuture.future);
|
||||
}
|
||||
|
||||
CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0])).thenAccept((voidObj) ->
|
||||
this.buildAndUploadRenderDataToGpuFuture = CompletableFuture.runAsync(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
ColumnRenderSource renderSource = thisRenderSourceLoadFuture.future.get();
|
||||
if (renderSource == null || renderSource.isEmpty())
|
||||
{
|
||||
thisRenderSourceLoadFuture.decrementRefCount();
|
||||
for (ReferencedFutureWrapper futureWrapper : adjRenderSourceLoadRefFutures)
|
||||
this.loadRenderDataAsync()
|
||||
.thenCompose((loadedRenderSources) ->
|
||||
{
|
||||
futureWrapper.decrementRefCount();
|
||||
}
|
||||
|
||||
// nothing needs to be rendered
|
||||
this.canRender = false;
|
||||
this.buildAndUploadRenderDataToGpuFuture = null;
|
||||
this.bufferBuildFuture = null;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=======================//
|
||||
// build new render data //
|
||||
//=======================//
|
||||
|
||||
try
|
||||
{
|
||||
ColumnRenderBuffer previousBuffer = this.renderBuffer;
|
||||
|
||||
ColumnRenderSource[] adjacentRenderSections = new ColumnRenderSource[EDhDirection.ADJ_DIRECTIONS.length];
|
||||
boolean[] adjIsSameDetailLevel = new boolean[EDhDirection.ADJ_DIRECTIONS.length];
|
||||
for (int i = 0; i < EDhDirection.ADJ_DIRECTIONS.length; i++)
|
||||
{
|
||||
adjacentRenderSections[i] = adjRenderSourceLoadRefFutures[i].future.getNow(null);
|
||||
|
||||
// if the adjacent position isn't the same detail level the buffer building logic
|
||||
// will need to be slightly different in order to reduce holes in the LODs
|
||||
EDhDirection direction = EDhDirection.ADJ_DIRECTIONS[i];
|
||||
adjIsSameDetailLevel[direction.ordinal() - 2] = this.isAdjacentPosSameDetailLevel(direction);
|
||||
}
|
||||
|
||||
if (this.bufferBuildFuture != null)
|
||||
{
|
||||
// shouldn't normally happen, but just in case canceling the previous future
|
||||
// prevents the CPU from working on something that won't be used
|
||||
this.bufferBuildFuture.cancel(true);
|
||||
}
|
||||
this.bufferBuildFuture = ColumnRenderBufferBuilder.buildBuffersAsync(this.level, renderSource, adjacentRenderSections, adjIsSameDetailLevel);
|
||||
this.bufferBuildFuture.thenAccept((lodQuadBuilder) ->
|
||||
{
|
||||
|
||||
|
||||
|
||||
//===================================//
|
||||
// upload new render data to the GPU //
|
||||
//===================================//
|
||||
|
||||
if (this.bufferUploadFuture != null)
|
||||
try
|
||||
{
|
||||
// shouldn't normally happen, but just in case canceling the previous future
|
||||
// prevents the CPU from working on something that won't be used
|
||||
this.bufferUploadFuture.cancel(true);
|
||||
}
|
||||
this.bufferUploadFuture = ColumnRenderBufferBuilder.uploadBuffersAsync(this.level, renderSource, lodQuadBuilder);
|
||||
this.bufferUploadFuture.thenAccept((buffer) ->
|
||||
{
|
||||
// upload complete, clean up the old data if
|
||||
this.renderBuffer = buffer;
|
||||
this.canRender = (buffer != null);
|
||||
this.buildAndUploadRenderDataToGpuFuture = null;
|
||||
this.bufferBuildFuture = null;
|
||||
|
||||
|
||||
if (previousBuffer != null)
|
||||
ColumnRenderSource thisRenderSource = loadedRenderSources.getThisRenderSource();
|
||||
if (thisRenderSource != null && !thisRenderSource.isEmpty())
|
||||
{
|
||||
previousBuffer.close();
|
||||
CompletableFuture<LodQuadBuilder> buildDataFuture = this.buildNewRenderDataAsync(thisRenderSource, loadedRenderSources);
|
||||
buildDataFuture.thenRun(() ->
|
||||
{
|
||||
ColumnRenderSource.DATA_SOURCE_POOL.returnPooledDataSource(thisRenderSource);
|
||||
ArrayList<ColumnRenderSource> adjacentSourceList = loadedRenderSources.getAdjacentRenderSourceList();
|
||||
for (int i = 0; i < adjacentSourceList.size(); i++)
|
||||
{
|
||||
ColumnRenderSource.DATA_SOURCE_POOL.returnPooledDataSource(adjacentSourceList.get(i));
|
||||
}
|
||||
});
|
||||
return buildDataFuture;
|
||||
}
|
||||
|
||||
thisRenderSourceLoadFuture.decrementRefCount();
|
||||
this.tryDecrementingLoadFutureArray(adjRenderSourceLoadRefFutures);
|
||||
this.adjacentLoadRefFutures = null;
|
||||
});
|
||||
else
|
||||
{
|
||||
// nothing needs to be rendered
|
||||
this.canRender = false;
|
||||
this.buildAndUploadRenderDataToGpuFuture = null;
|
||||
this.bufferBuildFuture = null;
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// exception handling is done here since attempting to do so in the final future's
|
||||
// .exceptionally() block doesn't return the correct stack traces, making debugging impossible
|
||||
this.handleException(e);
|
||||
throw e;
|
||||
}
|
||||
})
|
||||
.thenCompose((lodQuadBuilder) ->
|
||||
{
|
||||
try
|
||||
{
|
||||
// can be null if there was a problem or if there's nothing to render
|
||||
if (lodQuadBuilder != null)
|
||||
{
|
||||
return this.uploadToGpuAsync(lodQuadBuilder);
|
||||
}
|
||||
else
|
||||
{
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.handleException(e);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
thisRenderSourceLoadFuture.decrementRefCount();
|
||||
this.tryDecrementingLoadFutureArray(adjRenderSourceLoadRefFutures);
|
||||
this.adjacentLoadRefFutures = null;
|
||||
|
||||
LOGGER.error("Unexpected error in LodRenderSection loading, Error: "+e.getMessage(), e);
|
||||
this.buildAndUploadRenderDataToGpuFuture = null;
|
||||
this.bufferBuildFuture = null;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
thisRenderSourceLoadFuture.decrementRefCount();
|
||||
this.tryDecrementingLoadFutureArray(adjRenderSourceLoadRefFutures);
|
||||
this.adjacentLoadRefFutures = null;
|
||||
|
||||
LOGGER.error("Unexpected error in LodRenderSection loading, Error: "+e.getMessage(), e);
|
||||
this.buildAndUploadRenderDataToGpuFuture = null;
|
||||
this.bufferBuildFuture = null;
|
||||
// this catch is just for the first loadRenderDataAsync(),
|
||||
// each subsequent method has their own handleException() block.
|
||||
this.handleException(e);
|
||||
}
|
||||
});
|
||||
}, executor);
|
||||
}, executor);
|
||||
}
|
||||
catch (RejectedExecutionException ignore)
|
||||
{ /* the thread pool was probably shut down because it's size is being changed, just wait a sec and it should be back */ }
|
||||
}
|
||||
/** Should be called on the {@link ThreadPoolUtil#getFileHandlerExecutor()} */
|
||||
private ReferencedFutureWrapper[] getNeighborRenderSourcesAsync()
|
||||
private CompletableFuture<LoadedRenderSourcesFutureWrapper> loadRenderDataAsync()
|
||||
{
|
||||
ReferencedFutureWrapper[] futureArray = new ReferencedFutureWrapper[EDhDirection.ADJ_DIRECTIONS.length];
|
||||
CompletableFuture<ColumnRenderSource> thisRenderSourceLoadFuture = this.getRenderSourceAsync();
|
||||
ArrayList<CompletableFuture<ColumnRenderSource>> adjRenderSourceLoadRefFutures = this.getNeighborRenderSourcesAsync();
|
||||
|
||||
|
||||
// wait for all futures to complete together,
|
||||
// merging the futures makes loading significantly faster than loading this position then loading its neighbors
|
||||
ArrayList<CompletableFuture<ColumnRenderSource>> futureList = new ArrayList<>();
|
||||
futureList.add(thisRenderSourceLoadFuture);
|
||||
futureList.addAll(adjRenderSourceLoadRefFutures);
|
||||
CompletableFuture<Void> allLoadedFuture = CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0]));
|
||||
|
||||
return allLoadedFuture.thenApply((voidObj) -> new LoadedRenderSourcesFutureWrapper(allLoadedFuture, thisRenderSourceLoadFuture, adjRenderSourceLoadRefFutures));
|
||||
}
|
||||
private CompletableFuture<LodQuadBuilder> buildNewRenderDataAsync(
|
||||
ColumnRenderSource thisRenderSource,
|
||||
LoadedRenderSourcesFutureWrapper loadedRenderSources)
|
||||
{
|
||||
ColumnRenderSource[] adjacentRenderSections = new ColumnRenderSource[EDhDirection.ADJ_DIRECTIONS.length];
|
||||
boolean[] adjIsSameDetailLevel = new boolean[EDhDirection.ADJ_DIRECTIONS.length];
|
||||
for (int i = 0; i < EDhDirection.ADJ_DIRECTIONS.length; i++)
|
||||
{
|
||||
adjacentRenderSections[i] = loadedRenderSources.getAdjacentRenderSource(i);
|
||||
|
||||
// if the adjacent position isn't the same detail level the buffer building logic
|
||||
// will need to be slightly different in order to reduce holes in the LODs
|
||||
EDhDirection direction = EDhDirection.ADJ_DIRECTIONS[i];
|
||||
adjIsSameDetailLevel[direction.ordinal() - 2] = this.isAdjacentPosSameDetailLevel(direction);
|
||||
}
|
||||
|
||||
if (this.bufferBuildFuture != null)
|
||||
{
|
||||
// shouldn't normally happen, but just in case canceling the previous future
|
||||
// prevents the CPU from working on something that won't be used
|
||||
this.bufferBuildFuture.cancel(true);
|
||||
}
|
||||
this.bufferBuildFuture = ColumnRenderBufferBuilder.buildBuffersAsync(this.level, thisRenderSource, adjacentRenderSections, adjIsSameDetailLevel);
|
||||
return this.bufferBuildFuture;
|
||||
}
|
||||
private CompletableFuture<Void> uploadToGpuAsync(LodQuadBuilder lodQuadBuilder)
|
||||
{
|
||||
if (this.bufferUploadFuture != null)
|
||||
{
|
||||
// shouldn't normally happen, but just in case canceling the previous future
|
||||
// prevents the CPU from working on something that won't be used
|
||||
this.bufferUploadFuture.cancel(true);
|
||||
}
|
||||
|
||||
this.bufferUploadFuture = ColumnRenderBufferBuilder.uploadBuffersAsync(this.level, this.pos, lodQuadBuilder);
|
||||
return this.bufferUploadFuture.thenCompose((buffer) ->
|
||||
{
|
||||
ColumnRenderBuffer previousBuffer = this.renderBuffer;
|
||||
|
||||
// upload complete, clean up the old data if
|
||||
this.renderBuffer = buffer;
|
||||
this.canRender = (buffer != null);
|
||||
this.buildAndUploadRenderDataToGpuFuture = null;
|
||||
this.bufferBuildFuture = null;
|
||||
|
||||
if (previousBuffer != null)
|
||||
{
|
||||
previousBuffer.close();
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=====================//
|
||||
// render data helpers //
|
||||
//=====================//
|
||||
|
||||
/** Should be called on the {@link ThreadPoolUtil#getFileHandlerExecutor()} */
|
||||
private ArrayList<CompletableFuture<ColumnRenderSource>> getNeighborRenderSourcesAsync()
|
||||
{
|
||||
ArrayList<CompletableFuture<ColumnRenderSource>> futureList = ListUtil.createEmptyList(EDhDirection.ADJ_DIRECTIONS.length);
|
||||
|
||||
for (int i = 0; i < EDhDirection.ADJ_DIRECTIONS.length; i++)
|
||||
{
|
||||
EDhDirection direction = EDhDirection.ADJ_DIRECTIONS[i];
|
||||
@@ -296,46 +311,34 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
LodRenderSection adjRenderSection = this.quadTree.getValue(adjPos);
|
||||
if (adjRenderSection != null)
|
||||
{
|
||||
futureArray[arrayIndex] = adjRenderSection.getRenderSourceAsync();
|
||||
futureList.set(arrayIndex, adjRenderSection.getRenderSourceAsync());
|
||||
}
|
||||
}
|
||||
catch (IndexOutOfBoundsException ignore) {}
|
||||
|
||||
if (futureArray[arrayIndex] == null)
|
||||
if (futureList.get(arrayIndex) == null)
|
||||
{
|
||||
futureArray[arrayIndex] = new ReferencedFutureWrapper(CompletableFuture.completedFuture(null));
|
||||
futureList.set(arrayIndex, CompletableFuture.completedFuture(null));
|
||||
}
|
||||
}
|
||||
|
||||
this.adjacentLoadRefFutures = futureArray;
|
||||
return futureArray;
|
||||
return futureList;
|
||||
}
|
||||
|
||||
/** Will try to return the same {@link CompletableFuture} if multiple requests are made for the same position */
|
||||
private ReferencedFutureWrapper getRenderSourceAsync()
|
||||
private CompletableFuture<ColumnRenderSource> getRenderSourceAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.getRenderSourceLock.lock();
|
||||
|
||||
|
||||
// if a load is already in progress, use that existing one
|
||||
// (this reduces the number of duplicate loads that may happen when initially loading the world)
|
||||
if (this.renderSourceLoadingRefFuture != null)
|
||||
{
|
||||
// increment the number of objects needing this future
|
||||
this.renderSourceLoadingRefFuture.incrementRefCount();
|
||||
return this.renderSourceLoadingRefFuture;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
|
||||
if (executor == null || executor.isTerminated())
|
||||
{
|
||||
return new ReferencedFutureWrapper(CompletableFuture.completedFuture(null));
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
this.renderSourceLoadingRefFuture = new ReferencedFutureWrapper(CompletableFuture.supplyAsync(() ->
|
||||
this.renderSourceLoadingRefFuture = CompletableFuture.supplyAsync(() ->
|
||||
{
|
||||
try (FullDataSourceV2 fullDataSource = this.fullDataSourceProvider.get(this.pos))
|
||||
{
|
||||
@@ -349,7 +352,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
this.renderSourceLoadingRefFuture = null;
|
||||
return null;
|
||||
}
|
||||
}, executor));
|
||||
}, executor);
|
||||
return this.renderSourceLoadingRefFuture;
|
||||
}
|
||||
finally
|
||||
@@ -362,26 +365,14 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
long adjPos = DhSectionPos.getAdjacentPos(this.pos, direction);
|
||||
byte detailLevel = this.quadTree.calculateExpectedDetailLevel(new DhBlockPos2D(MC.getPlayerBlockPos()), adjPos);
|
||||
detailLevel += DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
||||
boolean adjacentIsSameDetailLevel = (detailLevel == DhSectionPos.getDetailLevel(this.pos));
|
||||
return adjacentIsSameDetailLevel;
|
||||
return detailLevel == DhSectionPos.getDetailLevel(this.pos);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Note: can cause issues with neighboring LOD sections
|
||||
* if only some (vs all) futures are canceled.
|
||||
*/
|
||||
public void cancelGpuUpload()
|
||||
private void handleException(Throwable e)
|
||||
{
|
||||
CompletableFuture<Void> future = this.buildAndUploadRenderDataToGpuFuture;
|
||||
LOGGER.error("Unexpected error in LodRenderSection loading, Error: " + e.getMessage(), e);
|
||||
this.buildAndUploadRenderDataToGpuFuture = null;
|
||||
this.bufferBuildFuture = null;
|
||||
if (future != null)
|
||||
{
|
||||
// interrupting the future speeds things up, but also causes
|
||||
// some LODs to never load in properly
|
||||
future.cancel(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -430,7 +421,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
// full data retrieval (world gen) //
|
||||
//=================================//
|
||||
|
||||
public boolean isFullyGenerated() { return this.missingPositionsCalculated && this.missingGenerationPos.size() == 0; }
|
||||
public boolean isFullyGenerated() { return this.missingPositionsCalculated && this.missingGenerationPos.isEmpty(); }
|
||||
public boolean missingPositionsCalculated() { return this.missingPositionsCalculated; }
|
||||
public int ungeneratedPositionCount() { return (this.missingGenerationPos != null) ? this.missingGenerationPos.size() : 0; }
|
||||
|
||||
@@ -475,27 +466,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// cleanup //
|
||||
//=========//
|
||||
|
||||
/** does nothing if the passed in value is null. */
|
||||
private void tryDecrementingLoadFutureArray(@Nullable ReferencedFutureWrapper[] refFutures)
|
||||
{
|
||||
if (refFutures != null)
|
||||
{
|
||||
for (ReferencedFutureWrapper futureWrapper : refFutures)
|
||||
{
|
||||
if (futureWrapper != null)
|
||||
{
|
||||
futureWrapper.decrementRefCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// base methods //
|
||||
//==============//
|
||||
@@ -546,12 +516,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
this.bufferUploadFuture.cancel(true);
|
||||
}
|
||||
|
||||
// this render section won't be rendering, we don't need to load any data for it
|
||||
this.tryDecrementingLoadFutureArray(this.adjacentLoadRefFutures);
|
||||
if (this.renderSourceLoadingRefFuture != null)
|
||||
{
|
||||
this.renderSourceLoadingRefFuture.decrementRefCount();
|
||||
}
|
||||
|
||||
|
||||
// remove any active world gen requests that may be for this position
|
||||
@@ -596,44 +560,40 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
// helper classes //
|
||||
//================//
|
||||
|
||||
/**
|
||||
* Used to keep track of whether a {@link ColumnRenderSource} {@link CompletableFuture}
|
||||
* is in use or not, and if not in use cancels the future. <br> <br>
|
||||
*
|
||||
* This helps speed up LOD loading by canceling loads that are no longer needed,
|
||||
* IE out of range or in an unloaded dimension.
|
||||
*/
|
||||
private static class ReferencedFutureWrapper
|
||||
/** Used to easily pass around the loaded {@link ColumnRenderSource}'s. */
|
||||
private static class LoadedRenderSourcesFutureWrapper
|
||||
{
|
||||
public final CompletableFuture<ColumnRenderSource> future;
|
||||
// starts at 1 since the constructing method is referencing this future
|
||||
private final AtomicInteger refCount = new AtomicInteger(1);
|
||||
//private final CompletableFuture<Void> future;
|
||||
private final CompletableFuture<ColumnRenderSource> thisRenderSourceFuture;
|
||||
private final ArrayList<CompletableFuture<ColumnRenderSource>> adjacentRenderSourceFutures;
|
||||
|
||||
|
||||
|
||||
public ReferencedFutureWrapper(CompletableFuture<ColumnRenderSource> future) { this.future = future; }
|
||||
|
||||
public void incrementRefCount() { this.refCount.incrementAndGet(); }
|
||||
public void decrementRefCount()
|
||||
public LoadedRenderSourcesFutureWrapper(CompletableFuture<Void> future, CompletableFuture<ColumnRenderSource> thisRenderSourceFuture, ArrayList<CompletableFuture<ColumnRenderSource>> adjacentRenderSourceFutures)
|
||||
{
|
||||
// automatically clean up this future if no one else is referencing it
|
||||
if (this.refCount.decrementAndGet() <= 0)
|
||||
{
|
||||
if (this.future != null)
|
||||
{
|
||||
if (!this.future.isDone())
|
||||
{
|
||||
this.future.cancel(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
//this.future = future;
|
||||
this.thisRenderSourceFuture = thisRenderSourceFuture;
|
||||
this.adjacentRenderSourceFutures = adjacentRenderSourceFutures;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() { return this.future.toString() + " - " + this.refCount.get(); }
|
||||
|
||||
//public CompletableFuture<Void> getFuture() { return this.future; }
|
||||
public ColumnRenderSource getThisRenderSource() { return this.thisRenderSourceFuture != null ? this.thisRenderSourceFuture.getNow(null) : null; }
|
||||
public ColumnRenderSource getAdjacentRenderSource(int i)
|
||||
{
|
||||
CompletableFuture<ColumnRenderSource> future = this.adjacentRenderSourceFutures.get(i);
|
||||
return future != null ? future.getNow(null) : null;
|
||||
}
|
||||
public ArrayList<ColumnRenderSource> getAdjacentRenderSourceList()
|
||||
{
|
||||
ArrayList<ColumnRenderSource> list = new ArrayList<>();
|
||||
for (int i = 0; i < this.adjacentRenderSourceFutures.size(); i++)
|
||||
{
|
||||
list.add(this.getAdjacentRenderSource(i));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.seibel.distanthorizons.core.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ListUtil
|
||||
{
|
||||
/** Create list filled with null up to the size */
|
||||
public static <T> ArrayList<T> createEmptyList(int size)
|
||||
{
|
||||
ArrayList<T> list = new ArrayList<T>();
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
list.add(null);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user