Fix critical mem leak in BlockDetail + Add spaced out worldGenThread + make worldGenUpdate update once per 10 ticks + Semi-impl proper EarthCurveRatio limits + make worldGenThreads terminate faster + impl temp bypass to stop deadlocks on BufferFactory destroy() + fix chat logging messages with Throwable twice

This commit is contained in:
TomTheFurry
2022-04-17 18:07:50 +08:00
parent d2fef22719
commit a3022d2c64
8 changed files with 86 additions and 67 deletions
@@ -70,9 +70,11 @@ public class EventApi
// tick events //
// =============//
public BatchGenerator batchGenerator = null;
private int lastWorldGenTickDelta = 0;
public void serverTickEvent()
{
lastWorldGenTickDelta--;
if (!MC.playerExists() || ApiShared.lodWorld.getIsWorldNotLoaded())
return;
@@ -81,19 +83,19 @@ public class EventApi
return;
if (ApiShared.isShuttingDown)
return;
if (CONFIG.client().worldGenerator().getEnableDistantGeneration())
{
try
{
if (batchGenerator == null)
batchGenerator = new BatchGenerator(ApiShared.lodBuilder, lodDim);
batchGenerator.queueGenerationRequests(lodDim, ApiShared.lodBuilder);
}
catch (Exception e)
{
// Exception may happen if world got unloaded unorderly
e.printStackTrace();
if (lastWorldGenTickDelta <= 0) {
lastWorldGenTickDelta = 20; // 20 ticks is 1 second. We don't need to refresh world gen status every tick.
try {
if (batchGenerator == null)
batchGenerator = new BatchGenerator(ApiShared.lodBuilder, lodDim);
batchGenerator.queueGenerationRequests(lodDim, ApiShared.lodBuilder);
} catch (Exception e) {
// Exception may happen if world got unloaded unorderly
e.printStackTrace();
}
}
}
else
@@ -79,8 +79,8 @@ public class LodBufferBuilderFactory {
/** The threads used to generate buffers. */
private static LodThreadFactory bufferBuilderThreadFactory = new LodThreadFactory("BufferBuilder",
Thread.NORM_PRIORITY - 2);
public static ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(
CONFIG.client().advanced().threading().getNumberOfBufferBuilderThreads(), bufferBuilderThreadFactory);
private static int previousBufferBuilderThreads = CONFIG.client().advanced().threading().getNumberOfBufferBuilderThreads();
public static ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(previousBufferBuilderThreads, bufferBuilderThreadFactory);
/** The thread used to upload buffers. */
private static LodThreadFactory bufferUploadThreadFactory = new LodThreadFactory(
@@ -170,11 +170,9 @@ public class LodBufferBuilderFactory {
}
bufferBuilderThreads.shutdownNow();
bufferUploadThread.shutdownNow();
previousBufferBuilderThreads = CONFIG.client().advanced().threading().getNumberOfBufferBuilderThreads();
bufferBuilderThreadFactory = new LodThreadFactory("BufferBuilder", Thread.NORM_PRIORITY - 2);
bufferBuilderThreads = Executors.newFixedThreadPool(
CONFIG.client().advanced().threading().getNumberOfBufferBuilderThreads(),
bufferBuilderThreadFactory);
bufferBuilderThreads = Executors.newFixedThreadPool(previousBufferBuilderThreads, bufferBuilderThreadFactory);
bufferUploadThreadFactory = new LodThreadFactory(
LodBufferBuilderFactory.class.getSimpleName() + " - upload", Thread.NORM_PRIORITY - 1);
@@ -186,6 +184,8 @@ public class LodBufferBuilderFactory {
int playerZ, boolean fullRegen) {
//ArrayList<RenderRegion> regionsToCleanup = new ArrayList<RenderRegion>();
try {
if (previousBufferBuilderThreads != CONFIG.client().advanced().threading().getNumberOfBufferBuilderThreads())
resetThreadPools(false);
regionsListLock.lockInterruptibly();
if (ENABLE_EVENT_LOGGING)
ApiShared.LOGGER.info("BufferBuilderStarter locked the region lock! LodDim: [{}], RenderRegion: [{}]",
@@ -206,28 +206,28 @@ public class LodBufferBuilderFactory {
// minCullingRange);
// int cullingRangeZ = Math.max((int)(1.5 * Math.abs(lastZ - playerZ)),
// minCullingRange);
Pos2D minPos = renderRegions.getMinInRange();
Pos2D maxPos = renderRegions.getMaxInRange();
CompletableFuture<Void> future = CompletableFuture.completedFuture(null);
try {
int numOfJobs = 0;
for (int regX = minPos.x; regX < maxPos.x; regX++) {
for (int regZ = minPos.y; regZ < maxPos.y; regZ++) {
RenderRegion r = renderRegions.get(regX, regZ);
RegionPos regPos = new RegionPos(regX, regZ);
if (r!=null && !r.canRender(lodDim, regPos)) {
if (r != null && !r.canRender(lodDim, regPos)) {
renderRegions.set(regX, regZ, null);
r.close();
r = null;
}
if (r==null) {
if (r == null) {
r = new RenderRegion(regPos, lodDim);
renderRegions.set(regX, regZ, r);
}
CompletableFuture<Void> newFuture =
r.updateStatus(bufferUploadThread, bufferBuilderThreads, fullRegen, playerX, playerZ).orElse(null);
if (newFuture != null) {
@@ -240,19 +240,17 @@ public class LodBufferBuilderFactory {
// ================================//
// wait on completion //
// ================================//
long executeStart = System.currentTimeMillis();
try {
future.get(1, TimeUnit.MINUTES);
} catch (InterruptedException | TimeoutException ie) {
throw ie;
} catch (CancellationException ce) {
throw new InterruptedException("Future interrupted");
} catch (ExecutionException ee) {
ApiShared.LOGGER.error("LodBufferBuilder ran into trouble: ", ee.getCause());
}
long executeEnd = System.currentTimeMillis();
long endTime = System.currentTimeMillis();
long buildTime = endTime - startTime;
long executeTime = executeEnd - executeStart;
@@ -260,22 +258,24 @@ public class LodBufferBuilderFactory {
ApiShared.LOGGER.info("Thread Build&Upload(" + numOfJobs + "/"
+ (lodDim.getWidth() * lodDim.getWidth()) + (fullRegen ? "FULL" : "") + ") time: " + buildTime
+ " ms" + '\n' + "thread execute time: " + executeTime + " ms");
} catch (InterruptedException ie) {
resetThreadPools(false);
try {
future.get();
} catch (Throwable t) {}
throw ie;
} catch (Throwable ignored) {
}
} catch (TimeoutException te) {
ApiShared.LOGGER.error("LodBufferBuilder timed out: ", te);
resetThreadPools(true);
}
} catch (Exception e) {
}
catch (RuntimeException e) {
ApiShared.LOGGER.error("\"LodNodeBufferBuilder.generateLodBuffersAsync\" ran into trouble: ", e);
}
} catch (InterruptedException ie) {
} finally {
}
catch (InterruptedException ignored) { }
finally {
regionsListLock.unlock();
if (ENABLE_EVENT_LOGGING) ApiShared.LOGGER.info("BufferBuilderStarter unlocked the region lock!");
builderThreadRunning = false;
@@ -321,12 +321,15 @@ public class LodBufferBuilderFactory {
ApiShared.LOGGER.info("Destroying LodBufferBuilder...");
mainGenThread.shutdownNow();
mainGenThread = Executors.newSingleThreadExecutor(mainGenThreadFactory);
regionsListLock.lock();
boolean locked = false;
try {
locked = regionsListLock.tryLock(5, TimeUnit.SECONDS); // FIXME: For some reason we get a deadlock here sometimes
} catch (InterruptedException ignored) {}
try {
if (renderRegions != null) renderRegions.clear(RenderRegion::close);
renderRegions = null;
} finally {
regionsListLock.unlock();
if (locked) regionsListLock.unlock();
}
ApiShared.LOGGER.info("LodBufferBuilder destroyed.");
}
@@ -45,7 +45,7 @@ public class BatchGenerator {
public AbstractBatchGenerationEnvionmentWrapper generationGroup;
public LodDimension targetLodDim;
public static final int generationGroupSize = 4;
public static int previousThreadCount = CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads();
public static int previousThreadCount = CONFIG.client().advanced().threading()._getWorldGenerationThreadPoolSize();
private int estimatedSampleNeeded = 128;
private int estimatedPointsToQueue = 1;
@@ -69,7 +69,7 @@ public class BatchGenerator {
}
DistanceGenerationMode mode = CONFIG.client().worldGenerator().getDistanceGenerationMode();
int newThreadCount = CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads();
int newThreadCount = CONFIG.client().advanced().threading()._getWorldGenerationThreadPoolSize();
if (newThreadCount != previousThreadCount) {
generationGroup.resizeThreadPool(newThreadCount);
previousThreadCount = newThreadCount;
@@ -102,6 +102,7 @@ public class BatchGenerator {
// round the player's block position down to the nearest chunk BlockPos
int playerPosX = MC.getPlayerBlockPos().getX();
int playerPosZ = MC.getPlayerBlockPos().getZ();
double runTimeRatio = CONFIG.client().advanced().threading()._getWorldGenerationPartialRunTime();
PosToGenerateContainer posToGenerate = lodDim.getPosToGenerate(estimatedSampleNeeded, playerPosX, playerPosZ,
priority, mode);
@@ -160,7 +161,7 @@ public class BatchGenerator {
int chunkX = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosX(i, false));
int chunkZ = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosZ(i, false));
int genSize = detailLevel > LodUtil.CHUNK_DETAIL_LEVEL ? 0 : generationGroupSize;
if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep, false)) {
if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep, false, runTimeRatio)) {
toGenerate--;
}
}
@@ -174,7 +175,7 @@ public class BatchGenerator {
int chunkX = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosX(i, true));
int chunkZ = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosZ(i, true));
int genSize = detailLevel > LodUtil.CHUNK_DETAIL_LEVEL ? 0 : generationGroupSize;
if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep, true)) {
if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep, true, runTimeRatio)) {
toGenerate--;
}
}
@@ -193,7 +194,7 @@ public class BatchGenerator {
int chunkX = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosX(i, true));
int chunkZ = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosZ(i, true));
int genSize = detailLevel > LodUtil.CHUNK_DETAIL_LEVEL ? 0 : generationGroupSize;
if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep, true)) {
if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep, true, runTimeRatio)) {
toGenerate--;
}
if (toGenerate <= 0)
@@ -212,7 +213,7 @@ public class BatchGenerator {
int chunkX = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosX(i, false));
int chunkZ = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosZ(i, false));
int genSize = detailLevel > LodUtil.CHUNK_DETAIL_LEVEL ? 0 : generationGroupSize;
if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep, false)) {
if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep, false, runTimeRatio)) {
toGenerate--;
}
}
@@ -70,7 +70,7 @@ public class ConfigBasedLogger {
if (mode.levelForChat.isLessSpecificThan(level)) {
if (param.length > 0 && param[param.length-1] instanceof Throwable)
ClientApi.logToChat(level, msgStr + "\nat\n" + Arrays.toString(((Throwable) param[param.length - 1]).getStackTrace()));
ClientApi.logToChat(level, msgStr);
else ClientApi.logToChat(level, msgStr);
}
}
@@ -79,7 +79,7 @@ public class ConfigBasedSpamLogger {
if (mode.levelForChat.isLessSpecificThan(level)) {
if (param.length > 0 && param[param.length - 1] instanceof Throwable)
ClientApi.logToChat(level, msgStr + "\nat\n" + Arrays.toString(((Throwable) param[param.length - 1]).getStackTrace()));
ClientApi.logToChat(level, msgStr);
else ClientApi.logToChat(level, msgStr);
}
}
@@ -117,7 +117,7 @@ public class ConfigBasedSpamLogger {
if (mode.levelForChat.isLessSpecificThan(level)) {
if (param.length > 0 && param[param.length - 1] instanceof Throwable)
ClientApi.logToChat(level, msgStr + "\nat\n" + Arrays.toString(((Throwable) param[param.length - 1]).getStackTrace()));
ClientApi.logToChat(level, msgStr);
else ClientApi.logToChat(level, msgStr);
}
}
@@ -544,7 +544,7 @@ public interface ILodConfigWrapperSingleton extends IBindable
void setCaveCullingHeight(int newCaveCullingHeight);
MinDefaultMax<Integer> EARTH_CURVE_RATIO_MIN_DEFAULT_MAX = new MinDefaultMax<>(0,0,10000);
MinDefaultMax<Integer> EARTH_CURVE_RATIO_MIN_DEFAULT_MAX = new MinDefaultMax<>(0,0,5000);
String EARTH_CURVE_RATIO_DESC = ""
+ " This is the earth size ratio when applying the curvature shader effect. \n"
+ "\n"
@@ -554,7 +554,11 @@ public interface ILodConfigWrapperSingleton extends IBindable
+ " 0 = flat/disabled \n"
+ " 1 = 1 to 1 (6,371,000 blocks) \n"
+ " 100 = 1 to 100 (63,710 blocks) \n"
+ " 10000 = 1 to 10000 (637.1 blocks) \n";
+ " 10000 = 1 to 10000 (637.1 blocks) \n"
+ "\n"
+ " NOTE: Due to current limitations, the min value is 50 \n"
+ " and the max value is 5000. Any values outside this range \n"
+ " will be set to 0(disabled).";
int getEarthCurveRatio();
void setEarthCurveRatio(int newEarthCurveRatio);
@@ -763,26 +767,36 @@ public interface ILodConfigWrapperSingleton extends IBindable
{
String DESC = "These settings control how many CPU threads the mod uses for different tasks.";
MinDefaultMax<Integer> NUMBER_OF_WORLD_GENERATION_THREADS_DEFAULT
= new MinDefaultMax<Integer>(1,
Math.min(Runtime.getRuntime().availableProcessors()/2, 4),
Runtime.getRuntime().availableProcessors());
MinDefaultMax<Double> NUMBER_OF_WORLD_GENERATION_THREADS_DEFAULT
= new MinDefaultMax<Double>(0.1,
(double) Math.min(Runtime.getRuntime().availableProcessors()/2, 4),
(double) Runtime.getRuntime().availableProcessors());
String NUMBER_OF_WORLD_GENERATION_THREADS_DESC = ""
+ " How many threads should be used when generating fake chunks outside \n"
+ " the normal render distance? \n"
+ " How many threads should be used when generating fake \n"
+ " chunks outside the normal render distance? \n"
+ "\n"
+ " If you experience stuttering when generating distant LODs, decrease \n"
+ " this number. If you want to increase LOD generation speed, \n"
+ " increase this number. \n"
+ " If it's less than 1, it will be treated as a percentage \n"
+ " of time single thread can run before going to idle. \n"
+ "\n"
+ " If you experience stuttering when generating distant LODs, \n"
+ " decrease this number. If you want to increase LOD \n"
+ " generation speed, increase this number. \n"
+ "\n"
+ " This and the number of buffer builder threads are independent, \n"
+ " so if they add up to more threads than your CPU has cores, \n"
+ " that shouldn't cause an issue. \n"
+ "\n"
+ " The maximum value is the number of logical processors on your CPU. \n"
+ " Requires a restart to take effect. \n";
int getNumberOfWorldGenerationThreads();
void setNumberOfWorldGenerationThreads(int newNumberOfWorldGenerationThreads);
+ " that shouldn't cause an issue. \n";
double getNumberOfWorldGenerationThreads();
void setNumberOfWorldGenerationThreads(double newNumberOfWorldGenerationThreads);
default int _getWorldGenerationThreadPoolSize()
{
return getNumberOfWorldGenerationThreads()<1 ? 1 :
(int) Math.ceil(getNumberOfWorldGenerationThreads());
}
default double _getWorldGenerationPartialRunTime()
{
return getNumberOfWorldGenerationThreads()>1 ? 1.0 : getNumberOfWorldGenerationThreads();
}
MinDefaultMax<Integer> NUMBER_OF_BUFFER_BUILDER_THREADS_MIN_DEFAULT_MAX
= new MinDefaultMax<Integer>(1,
@@ -800,8 +814,7 @@ public interface ILodConfigWrapperSingleton extends IBindable
+ " so if they add up to more threads than your CPU has cores, \n"
+ " that shouldn't cause an issue. \n"
+ "\n"
+ " The maximum value is the number of logical processors on your CPU. \n"
+ " Requires a restart to take effect. \n";
+ " The maximum value is the number of logical processors on your CPU. \n";
int getNumberOfBufferBuilderThreads();
void setNumberOfBufferBuilderThreads(int newNumberOfWorldBuilderThreads);
}
@@ -38,7 +38,7 @@ public abstract class AbstractBatchGenerationEnvionmentWrapper {
public abstract int getEventCount();
public abstract boolean tryAddPoint(int chunkX, int chunkZ, int genSize, Steps targetStep, boolean genAllDetails);
public abstract boolean tryAddPoint(int chunkX, int chunkZ, int genSize, Steps targetStep, boolean genAllDetails, double runTimeRatio);
public abstract void stop(boolean blocking);
}
@@ -175,7 +175,7 @@
"DistantHorizons.config.client.graphics.advancedGraphics.earthCurveRatio":
"Earth Curve Ratio §6(EXPERIMENTAL)§r",
"DistantHorizons.config.client.graphics.advancedGraphics.earthCurveRatio.@tooltip":
"This is the earth size ratio when applying the curvature shader effect. \n\n§6NOTE§r: This feature is just for fun and is VERY experimental! \nPlease don't report any issues related to this feature. \n\n0 = flat/disabled \n1 = 1 to 1 (6,371,000 blocks) \n100 = 1 to 100 (63,710 blocks) \n10000 = 1 to 10000 (637.1 blocks) \n",
"This is the earth size ratio when applying the curvature shader effect. \n\n§6NOTE§r: This feature is just for fun and is VERY experimental! \nPlease don't report any issues related to this feature. \n\n0 = flat/disabled \n1 = 1 to 1 (6,371,000 blocks) \n100 = 1 to 100 (63,710 blocks) \n10000 = 1 to 10000 (637.1 blocks) \n\n§6NOTE§r: due to current limitations, the min value is 50 \n and the max value is 5000. Any values outside this range \n will be set to 0(disabled).",
"DistantHorizons.config.client.worldGenerator":
"World generator",
"DistantHorizons.config.client.worldGenerator.generationPriority":
@@ -219,7 +219,7 @@
"DistantHorizons.config.client.advanced.threading.numberOfWorldGenerationThreads":
"NO. of world generation threads",
"DistantHorizons.config.client.advanced.threading.numberOfWorldGenerationThreads.@tooltip":
"The number of threads used to generate fake chunks\noutside the normal render distance.\nIf you experience stuttering when generating fake chunks: decrease this number.\nIf you want to increase LOD generation speed: increase this number.\n\nCan only be between 1 and your CPU's processor count.",
"How many threads should be used when generating fake \nchunks outside the normal render distance? \n\nIf it's less than 1, it will be treated as a percentage \nof time single thread can run before going to idle. \n\nIf you experience stuttering when generating distant LODs, \ndecrease this number. If you want to increase LOD \ngeneration speed, increase this number. \n\nThis and the number of buffer builder threads are independent, \nso if they add up to more threads than your CPU has cores, \nthat shouldn't cause an issue.",
"DistantHorizons.config.client.advanced.threading.numberOfBufferBuilderThreads":
"NO. of buffer builder threads",
"DistantHorizons.config.client.advanced.threading.numberOfBufferBuilderThreads.@tooltip":