Improve initial LOD loading speed

This commit is contained in:
James Seibel
2024-04-03 22:07:25 -05:00
parent 4bc7cf1e86
commit 4e1155f8a7
2 changed files with 77 additions and 26 deletions
@@ -116,6 +116,15 @@ public class DataSourcePool<TDataSource extends IDataSource<TDhLevel>, TDhLevel
//===============//
// debug methods //
//===============//
/** Returns how many data sources are in the pool */
public int size() { return this.pooledDataSources.size(); }
//================//
// helper classes //
//================//
@@ -33,13 +33,14 @@ 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.TimerUtil;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import org.apache.logging.log4j.Logger;
import java.awt.*;
import java.util.ArrayList;
import java.util.Objects;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;
/**
@@ -50,6 +51,16 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
/**
* Only the adjacent render sources should be cached to prevent accidentally using cached data when the LOD data was changed.
* This cache should really only be used when initially loading LODs or generating new terrain.
*/
private static final ConcurrentHashMap<DhSectionPos, ColumnRenderSource> ADJACENT_RENDER_SOURCE_BY_POS = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<DhSectionPos, TimerTask> RENDER_SOURCE_CLOSING_TIMER_TASK_BY_POS = new ConcurrentHashMap<>();
private static final Timer RENDER_SOURCE_CACHE_REMOVAL_TIMER = TimerUtil.CreateTimer("LodRenderSection Render Source Cache Removal Timer");
public final DhSectionPos pos;
@@ -119,7 +130,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
this.renderSourceLoadingFuture = CompletableFuture.runAsync(() ->
{
FullDataSourceV2 fullDataSource = null;
ColumnRenderSource[] adjacentRenderSections = null;
ColumnRenderSource renderSource = null;
try
@@ -135,7 +145,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
}
adjacentRenderSections = this.getAndCreateNeighborRenderSources();
ColumnRenderSource[] adjacentRenderSections = this.getAndCreateNeighborRenderSources();
ColumnRenderBuffer previousBuffer = this.renderBuffer;
@@ -167,18 +177,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
{
renderSource.close();
}
if (adjacentRenderSections != null)
{
for (int i = 0; i < adjacentRenderSections.length; i++)
{
ColumnRenderSource adjacentRenderSource = adjacentRenderSections[i];
if (adjacentRenderSource != null)
{
adjacentRenderSource.close();
}
}
}
}
catch (Exception ignore){ }
@@ -193,20 +191,64 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
for (EDhDirection direction : EDhDirection.ADJ_DIRECTIONS)
{
DhSectionPos adjPos = this.pos.getAdjacentPos(direction);
try (FullDataSourceV2 fullDataSource = this.fullDataSourceProvider.get(adjPos))
{
// TODO some temporary caching could be done here
ColumnRenderSource renderSource = FullDataToRenderDataTransformer.transformFullDataToRenderSource(fullDataSource, this.level);
adjacentRenderSections[direction.ordinal() - 2] = renderSource;
}
catch (Exception e)
{
LOGGER.warn("Unable to get neighbor render source "+this.pos+" - "+adjPos+", error: "+e.getMessage(), e);
}
ColumnRenderSource renderSource = ADJACENT_RENDER_SOURCE_BY_POS.compute(adjPos, this::computeCachedRenderSource);
adjacentRenderSections[direction.ordinal() - 2] = renderSource;
}
return adjacentRenderSections;
}
private ColumnRenderSource computeCachedRenderSource(DhSectionPos pos, ColumnRenderSource oldRenderSource)
{
try (FullDataSourceV2 fullDataSource = this.fullDataSourceProvider.get(pos))
{
// use the old render source if it isn't null
if (oldRenderSource == null)
{
oldRenderSource = FullDataToRenderDataTransformer.transformFullDataToRenderSource(fullDataSource, this.level);
}
// create a new timer task to reset the cache timeout
TimerTask timerTask = RENDER_SOURCE_CLOSING_TIMER_TASK_BY_POS.compute(pos, (timerPos, oldTimerTask) ->
{
if (oldTimerTask != null)
{
oldTimerTask.cancel();
}
return new TimerTask()
{
@Override
public void run()
{
// remove the finished task
RENDER_SOURCE_CLOSING_TIMER_TASK_BY_POS.remove(pos);
// return the pooled data source if present
ColumnRenderSource expiredRenderSource = ADJACENT_RENDER_SOURCE_BY_POS.remove(pos);
if (expiredRenderSource != null)
{
try { expiredRenderSource.close(); } catch (Exception ignored) { }
}
//LOGGER.info("cache size " +cachedNeighborSections.size()+" pool size:"+ColumnRenderSource.DATA_SOURCE_POOL.size());
}
};
});
try
{
RENDER_SOURCE_CACHE_REMOVAL_TIMER.schedule(timerTask, 1000L);
}
catch (IllegalStateException ignore) { /* can rarely happen due to some minor concurrency bug with how Timer works. It isn't an issue and can be ignored. */ }
return oldRenderSource;
}
catch (Exception e)
{
LOGGER.warn("Unable to get neighbor render source " + this.pos + " - " + pos + ", error: " + e.getMessage(), e);
return null;
}
}