diff --git a/src/main/java/com/seibel/lod/core/api/EventApi.java b/src/main/java/com/seibel/lod/core/api/EventApi.java index fcd071fa2..c1aebf893 100644 --- a/src/main/java/com/seibel/lod/core/api/EventApi.java +++ b/src/main/java/com/seibel/lod/core/api/EventApi.java @@ -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 diff --git a/src/main/java/com/seibel/lod/core/builders/lodBuilding/bufferBuilding/LodBufferBuilderFactory.java b/src/main/java/com/seibel/lod/core/builders/lodBuilding/bufferBuilding/LodBufferBuilderFactory.java index e5afe52a7..699485acd 100644 --- a/src/main/java/com/seibel/lod/core/builders/lodBuilding/bufferBuilding/LodBufferBuilderFactory.java +++ b/src/main/java/com/seibel/lod/core/builders/lodBuilding/bufferBuilding/LodBufferBuilderFactory.java @@ -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 regionsToCleanup = new ArrayList(); 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 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 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."); } diff --git a/src/main/java/com/seibel/lod/core/builders/worldGeneration/BatchGenerator.java b/src/main/java/com/seibel/lod/core/builders/worldGeneration/BatchGenerator.java index 7c490462e..57a15a280 100644 --- a/src/main/java/com/seibel/lod/core/builders/worldGeneration/BatchGenerator.java +++ b/src/main/java/com/seibel/lod/core/builders/worldGeneration/BatchGenerator.java @@ -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--; } } diff --git a/src/main/java/com/seibel/lod/core/logging/ConfigBasedLogger.java b/src/main/java/com/seibel/lod/core/logging/ConfigBasedLogger.java index c16fbbb93..b4169cbd1 100644 --- a/src/main/java/com/seibel/lod/core/logging/ConfigBasedLogger.java +++ b/src/main/java/com/seibel/lod/core/logging/ConfigBasedLogger.java @@ -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); } } diff --git a/src/main/java/com/seibel/lod/core/logging/ConfigBasedSpamLogger.java b/src/main/java/com/seibel/lod/core/logging/ConfigBasedSpamLogger.java index edc1709ee..74aaa7922 100644 --- a/src/main/java/com/seibel/lod/core/logging/ConfigBasedSpamLogger.java +++ b/src/main/java/com/seibel/lod/core/logging/ConfigBasedSpamLogger.java @@ -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); } } diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/config/ILodConfigWrapperSingleton.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/config/ILodConfigWrapperSingleton.java index e68149c91..a3c459f20 100644 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/config/ILodConfigWrapperSingleton.java +++ b/src/main/java/com/seibel/lod/core/wrapperInterfaces/config/ILodConfigWrapperSingleton.java @@ -544,7 +544,7 @@ public interface ILodConfigWrapperSingleton extends IBindable void setCaveCullingHeight(int newCaveCullingHeight); - MinDefaultMax EARTH_CURVE_RATIO_MIN_DEFAULT_MAX = new MinDefaultMax<>(0,0,10000); + MinDefaultMax 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 NUMBER_OF_WORLD_GENERATION_THREADS_DEFAULT - = new MinDefaultMax(1, - Math.min(Runtime.getRuntime().availableProcessors()/2, 4), - Runtime.getRuntime().availableProcessors()); + MinDefaultMax NUMBER_OF_WORLD_GENERATION_THREADS_DEFAULT + = new MinDefaultMax(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 NUMBER_OF_BUFFER_BUILDER_THREADS_MIN_DEFAULT_MAX = new MinDefaultMax(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); } diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractBatchGenerationEnvionmentWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractBatchGenerationEnvionmentWrapper.java index 1ec69e070..16199c85b 100644 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractBatchGenerationEnvionmentWrapper.java +++ b/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractBatchGenerationEnvionmentWrapper.java @@ -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); } diff --git a/src/main/resources/assets/lod/lang/en_us.json b/src/main/resources/assets/lod/lang/en_us.json index e36d78a76..bd4091ab8 100644 --- a/src/main/resources/assets/lod/lang/en_us.json +++ b/src/main/resources/assets/lod/lang/en_us.json @@ -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":