From 0b1fdba6eab61afa6dc7a31e4085a7063b443c2e Mon Sep 17 00:00:00 2001 From: James Seibel Date: Mon, 5 Jul 2021 12:41:20 -0500 Subject: [PATCH] Optimize world generation and add a config to set the number of generation threads --- .../seibel/lod/builders/LodBufferBuilder.java | 67 +++++++++++++++---- .../worldGeneration/LodChunkGenWorker.java | 4 +- .../com/seibel/lod/handlers/LodConfig.java | 16 +++++ .../com/seibel/lod/proxy/ClientProxy.java | 11 +-- 4 files changed, 80 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java b/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java index d97a505d3..5de08140c 100644 --- a/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java +++ b/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java @@ -1,6 +1,7 @@ package com.seibel.lod.builders; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; import org.lwjgl.opengl.GL11; @@ -48,15 +49,20 @@ public class LodBufferBuilder * and are waiting to be swapped with the drawable buffers*/ private volatile boolean switchBuffers = false; - /** If this is greater than 0 no new chunk generation requests will be made - * this is to prevent chunks from being generated for a long time in an area + /** This keeps track of how many chunk generation requests are on going. + * This is to prevent chunks from being generated for a long time in an area * the player is no longer in. */ - public volatile int numberOfChunksWaitingToGenerate = 0; + public volatile AtomicInteger numberOfChunksWaitingToGenerate = new AtomicInteger(0); + + /** how many chunks to generate outside of the player's * view distance at one time. (or more specifically how - * many requests to make at one time) */ - public int maxChunkGenRequests = Runtime.getRuntime().availableProcessors(); + * many requests to make at one time). + * I multiply by 8 to make sure there is always a buffer of chunk requests, + * to make sure the CPU is always busy and we can generate LODs as quickly as + * possible. */ + public int maxChunkGenRequests = LodConfig.CLIENT.numberOfWorldGenerationThreads.get() * 8; public LodBufferBuilder(LodChunkBuilder newLodBuilder) @@ -95,8 +101,6 @@ public class LodBufferBuilder previousDimension = lodDim; } - - generatingBuffers = true; @@ -111,7 +115,7 @@ public class LodBufferBuilder int startZ = (-LodChunk.WIDTH * (numbChunksWide / 2)) + playerZChunkOffset; - Thread t = new Thread(()-> + Thread thread = new Thread(()-> { // index of the chunk currently being added to the // generation list @@ -129,6 +133,8 @@ public class LodBufferBuilder buildableNearBuffer.begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT); buildableFarBuffer.begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT); + + // x axis for (int i = 0; i < numbChunksWide; i++) { @@ -158,7 +164,7 @@ public class LodBufferBuilder { // generate a new chunk if no chunk currently exists // and we aren't waiting on any other chunks to generate - if (lod == null && numberOfChunksWaitingToGenerate < maxChunkGenRequests) + if (lod == null && numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests) { ChunkPos pos = new ChunkPos(chunkX, chunkZ); @@ -171,7 +177,41 @@ public class LodBufferBuilder // this chunk is closer, clear any previous // positions and update the new minimum distance minChunkDist = newDistance; - chunksToGenReserve = chunksToGen; + + + // move all the old chunks into the reserve + ChunkPos[] newReserve = new ChunkPos[maxChunkGenRequests]; + int oldToGenIndex = 0; + int oldReserveIndex = 0; + for(int tmpIndex = 0; tmpIndex < newReserve.length; tmpIndex++) + { + // we don't check if the boundaries are good since + // the tmp array will always be the same length + // as chunksToGen and chunksToGenReserve + + if (chunksToGen[oldToGenIndex] != null) + { + // add all the closest chunks... + newReserve[tmpIndex] = chunksToGen[oldToGenIndex]; + oldToGenIndex++; + } + else if (chunksToGenReserve[oldReserveIndex] != null) + { + // ...then add all the previous reserve chunks + // (which are farther away) + newReserve[tmpIndex] = chunksToGenReserve[oldToGenIndex]; + oldReserveIndex++; + } + else + { + // we have moved all the items from + // the old chunksToGen and reserve + break; + } + } + chunksToGenReserve = newReserve; + + chunkGenIndex = 0; chunksToGen = new ChunkPos[maxChunkGenRequests]; @@ -231,7 +271,10 @@ public class LodBufferBuilder if(chunkPos == null) break; - numberOfChunksWaitingToGenerate++; + // make sure we don't generate this chunk again + lodDim.addLod(new LodChunk(chunkPos)); + + numberOfChunksWaitingToGenerate.addAndGet(1); LodChunkGenWorker genWorker = new LodChunkGenWorker(chunkPos, renderer, lodChunkBuilder, this, lodDim, serverWorld, biomeContainer); WorldWorkerManager.addWorker(genWorker); @@ -247,7 +290,7 @@ public class LodBufferBuilder switchBuffers = true; }); - genThread.execute(t); + genThread.execute(thread); return; } diff --git a/src/main/java/com/seibel/lod/builders/worldGeneration/LodChunkGenWorker.java b/src/main/java/com/seibel/lod/builders/worldGeneration/LodChunkGenWorker.java index 1a4a0a34e..ee5e1c988 100644 --- a/src/main/java/com/seibel/lod/builders/worldGeneration/LodChunkGenWorker.java +++ b/src/main/java/com/seibel/lod/builders/worldGeneration/LodChunkGenWorker.java @@ -57,7 +57,7 @@ import net.minecraftforge.common.WorldWorkerManager.IWorker; */ public class LodChunkGenWorker implements IWorker { - public static final ExecutorService genThreads = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + public static final ExecutorService genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.numberOfWorldGenerationThreads.get()); private boolean threadStarted = false; private LodChunkGenThread thread; @@ -87,7 +87,7 @@ public class LodChunkGenWorker implements IWorker { if (!threadStarted) { - thread.lodBufferBuilder.numberOfChunksWaitingToGenerate--; + thread.lodBufferBuilder.numberOfChunksWaitingToGenerate.addAndGet(-1); if (LodConfig.CLIENT.distanceGenerationMode.get() == DistanceGenerationMode.SERVER) { diff --git a/src/main/java/com/seibel/lod/handlers/LodConfig.java b/src/main/java/com/seibel/lod/handlers/LodConfig.java index ef80d0066..8bc368da5 100644 --- a/src/main/java/com/seibel/lod/handlers/LodConfig.java +++ b/src/main/java/com/seibel/lod/handlers/LodConfig.java @@ -46,10 +46,14 @@ public class LodConfig public ForgeConfigSpec.BooleanValue allowUnstableFeatureGeneration; + public ForgeConfigSpec.IntValue numberOfWorldGenerationThreads; + /** this is multiplied by the default view distance * to determine how far out to generate/render LODs */ public ForgeConfigSpec.IntValue lodChunkRadiusMultiplier; + + Client(ForgeConfigSpec.Builder builder) { builder.comment(ModInfo.MODNAME + " configuration settings").push("client"); @@ -176,6 +180,18 @@ public class LodConfig + " https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/35 \n") .define("allowUnstableFeatureGeneration", false); + numberOfWorldGenerationThreads = builder + .comment("\n\n" + + " This is how many threads are used when generating terrain. \n" + + " If you experience stuttering when generating terrain, decrease \n" + + " this number. If you want to increase LOD generation speed, \n" + + " increase the number. \n" + + " The max is the number of processors on your CPU. \n" + + "\n" + + " Requires a restart to take effect. \n" + ) + .defineInRange("numberOfWorldGenerationThreads", Runtime.getRuntime().availableProcessors(), 1, Runtime.getRuntime().availableProcessors()); + builder.pop(); } diff --git a/src/main/java/com/seibel/lod/proxy/ClientProxy.java b/src/main/java/com/seibel/lod/proxy/ClientProxy.java index 02a450897..f8d235001 100644 --- a/src/main/java/com/seibel/lod/proxy/ClientProxy.java +++ b/src/main/java/com/seibel/lod/proxy/ClientProxy.java @@ -93,15 +93,18 @@ public class ClientProxy } - // TODO for testing + // for testing // LodConfig.CLIENT.debugMode.set(false); + // LodConfig.CLIENT.lodDetail.set(LodDetail.DOUBLE); -// LodConfig.CLIENT.lodColorStyle.set(LodColorStyle.INDIVIDUAL_SIDES); // LodConfig.CLIENT.lodChunkRadiusMultiplier.set(12); -// LodConfig.CLIENT.distanceGenerationMode.set(DistanceGenerationMode.FEATURES); // LodConfig.CLIENT.fogDistance.set(FogDistance.FAR); // LodConfig.CLIENT.fogDrawOverride.set(FogDrawOverride.ALWAYS_DRAW_FOG_FANCY); +// LodConfig.CLIENT.distanceGenerationMode.set(DistanceGenerationMode.FEATURES); +// LodConfig.CLIENT.allowUnstableFeatureGeneration.set(false); + + // Note to self: // if "unspecified" shows up in the pie chart, it is // possibly because the amount of time between sections @@ -147,7 +150,7 @@ public class ClientProxy if(mc.getConnection().getLevel() == null) { - lodBufferBuilder.numberOfChunksWaitingToGenerate = 0; + lodBufferBuilder.numberOfChunksWaitingToGenerate.set(0); // the player has disconnected from a server lodWorld.deselectWorld(); }