From fdd8204a46eb55b915446800c7a743a11c8a0460 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sun, 22 Aug 2021 22:54:16 -0500 Subject: [PATCH] Add multi-threaded buffer building --- .../seibel/lod/builders/LodBufferBuilder.java | 102 +++++++++++------- 1 file changed, 66 insertions(+), 36 deletions(-) diff --git a/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java b/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java index 00be6552a..a0788c870 100644 --- a/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java +++ b/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java @@ -22,8 +22,10 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; +import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; @@ -54,7 +56,7 @@ import net.minecraftforge.common.WorldWorkerManager; * This object is used to create NearFarBuffer objects. * * @author James Seibel - * @version 8-21-2021 + * @version 8-22-2021 */ public class LodBufferBuilder { @@ -62,6 +64,8 @@ public class LodBufferBuilder /** This holds the thread used to generate new LODs off the main thread. */ private ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - main")); + /** This holds the threads used to generate buffers. */ + private ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new LodThreadFactory(this.getClass().getSimpleName() + " - builder")); private LodBuilder LodQuadTreeNodeBuilder; @@ -177,20 +181,19 @@ public class LodBufferBuilder // if we don't have a full number of chunks to generate in chunksToGen // we can top it off from the reserve ArrayList chunksToGenReserve = new ArrayList<>(maxChunkGenRequests); + ArrayList> builderThreads = new ArrayList<>(lodDim.regions.length * lodDim.regions.length); startBuffers(); // used when determining which chunks are closer when queuing distance // generation int minChunkDist = Integer.MAX_VALUE; - int width; // =====================// // RENDERING PART // // =====================// - List posListToRender = new ArrayList<>(); - LodDataPoint lodData; + for (int xRegion = 0; xRegion < lodDim.regions.length; xRegion++) { for (int zRegion = 0; zRegion < lodDim.regions.length; zRegion++) @@ -205,50 +208,77 @@ public class LodBufferBuilder if (currentBuffer == null || (currentBuffer != null && !currentBuffer.building())) return; - for (byte detail = LodConfig.CLIENT.maxGenerationDetail.get().detailLevel; detail <= LodUtil.REGION_DETAIL_LEVEL; detail++) + + byte detailLevel = LodConfig.CLIENT.maxGenerationDetail.get().detailLevel; + + Callable bufferBuildingThread = () -> { - posListToRender.addAll(lodDim.getDataToRender( - regionPos, - playerBlockPosRounded.getX(), - playerBlockPosRounded.getZ(), - DetailUtil.getDistanceRendering(detail), - DetailUtil.getDistanceRendering(detail + 1), - detail)); - } - for (LevelPos pos : posListToRender) - { - LevelPos chunkPos = pos.convert(LodUtil.CHUNK_DETAIL_LEVEL); - // skip any chunks that Minecraft is going to render + List posListToRender = new ArrayList<>(); - if (renderer.vanillaRenderedChunks.contains(new ChunkPos(chunkPos.posX, chunkPos.posZ))) + for (byte detail = detailLevel; detail <= LodUtil.REGION_DETAIL_LEVEL; detail++) { - continue; + posListToRender.addAll(lodDim.getDataToRender( + regionPos, + playerBlockPosRounded.getX(), + playerBlockPosRounded.getZ(), + DetailUtil.getDistanceRendering(detail), + DetailUtil.getDistanceRendering(detail + 1), + detail)); } - if (lodDim.doesDataExist(pos)) + + for (LevelPos pos : posListToRender) { - try + LevelPos chunkPos = pos.convert(LodUtil.CHUNK_DETAIL_LEVEL); + // skip any chunks that Minecraft is going to render + + if (renderer.vanillaRenderedChunks.contains(new ChunkPos(chunkPos.posX, chunkPos.posZ))) { - width = (int) Math.pow(2, pos.detailLevel); - lodData = lodDim.getData(pos); - - if (lodData != null) + continue; + } + + if (lodDim.doesDataExist(pos)) + { + try { - LodConfig.CLIENT.lodTemplate.get().template.addLodToBuffer(currentBuffer, lodDim, lodData, - pos.posX * width, 0, pos.posZ * width, renderer.debugging, pos.detailLevel); + int width = (int) Math.pow(2, pos.detailLevel); + LodDataPoint lodData = lodDim.getData(pos); + + if (lodData != null) + { + LodConfig.CLIENT.lodTemplate.get().template.addLodToBuffer(currentBuffer, lodDim, lodData, + pos.posX * width, 0, pos.posZ * width, renderer.debugging, pos.detailLevel); + } + } + catch (ArrayIndexOutOfBoundsException e) + { + return false; } } - catch (ArrayIndexOutOfBoundsException e) - { - ClientProxy.LOGGER.warn("LodBufferBuilder ran into trouble and had to start over."); - closeBuffers(); - return; - } - } + + }// for pos to in list to render - } + // the thread executed successfully + return true; + };// buffer builder worker thread - posListToRender.clear(); + + builderThreads.add(bufferBuildingThread); + + }// region z + }// region z + + + // wait for all threads to finish + List> futures = bufferBuilderThreads.invokeAll(builderThreads); + for(Future future : futures) + { + // the future will be false if its thread failed + if (!future.get()) + { + ClientProxy.LOGGER.warn("LodBufferBuilder ran into trouble and had to start over."); + closeBuffers(); + return; } }