Close #32 (make distance generation independent of the buffer builder)

This commit is contained in:
James Seibel
2021-08-24 21:47:51 -05:00
parent a0dcde48ae
commit a464176a25
4 changed files with 332 additions and 257 deletions
@@ -18,21 +18,15 @@
package com.seibel.lod.builders;
import java.util.ArrayList;
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;
import org.lwjgl.opengl.GL11;
import com.seibel.lod.builders.worldGeneration.LodNodeGenWorker;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.handlers.LodConfig;
import com.seibel.lod.objects.LevelPos;
import com.seibel.lod.objects.LodDataPoint;
@@ -44,32 +38,25 @@ import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.vertex.VertexBuffer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.WorldWorkerManager;
/**
* This object is used to create NearFarBuffer objects.
*
* @author James Seibel
* @version 8-22-2021
* @version 8-24-2021
*/
public class LodBufferBuilder
{
private Minecraft mc;
/** 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 ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(2, new LodThreadFactory(this.getClass().getSimpleName() + " - builder"));
private LodBuilder LodQuadTreeNodeBuilder;
/** The buffers that are used to create LODs using far fog */
public volatile BufferBuilder[][] buildableBuffers;
@@ -85,35 +72,12 @@ public class LodBufferBuilder
*/
public boolean generatingBuffers = false;
/**
* if this is true the LOD buffers are currently being
* regenerated.
*/
public Set<ChunkPos> positionWaitingToBeGenerated = new HashSet<>();
/**
* if this is true new LOD buffers have been generated
* and are waiting to be swapped with the drawable buffers
*/
private boolean switchVbos = false;
/**
* 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 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).
* 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;
/** Size of the buffer builders in bytes last time we created them */
public int previousBufferSize = 0;
@@ -127,14 +91,12 @@ public class LodBufferBuilder
public LodBufferBuilder(LodBuilder newLodBuilder)
public LodBufferBuilder()
{
mc = Minecraft.getInstance();
LodQuadTreeNodeBuilder = newLodBuilder;
}
private LodDimension previousDimension = null;
/**
@@ -156,11 +118,6 @@ public class LodBufferBuilder
if (buildableBuffers == null)
throw new IllegalStateException("\"generateLodBuffersAsync\" was called before the \"setupBuffers\" method was called.");
if (previousDimension != lodDim)
{
previousDimension = lodDim;
}
generatingBuffers = true;
@@ -181,19 +138,12 @@ public class LodBufferBuilder
long treeEnd = System.currentTimeMillis();
long startTime = System.currentTimeMillis();
ArrayList<GenerationRequest> chunksToGen = new ArrayList<>(maxChunkGenRequests);
// if we don't have a full number of chunks to generate in chunksToGen
// we can top it off from the reserve
ArrayList<GenerationRequest> chunksToGenReserve = new ArrayList<>(maxChunkGenRequests);
ArrayList<Callable<Boolean>> 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;
// =====================//
// RENDERING PART //
// =====================//
@@ -286,182 +236,6 @@ public class LodBufferBuilder
}
}
long renderEnd = System.currentTimeMillis();
// =====================//
// GENERATION PART //
// =====================//
List<LevelPos> posListToGenerate;
List<GenerationRequest> generationRequestList = new ArrayList<>();
/**TODO can give a totally different generation*/
/*
for (byte detail = LodUtil.BLOCK_DETAIL_LEVEL; detail <= LodUtil.REGION_DETAIL_LEVEL; detail++)
{
if (!posListToGenerate.isEmpty()) break;
for (byte detailGen = LodUtil.BLOCK_DETAIL_LEVEL; detailGen <= LodUtil.REGION_DETAIL_LEVEL; detailGen++)
{
if (!posListToGenerate.isEmpty()) break;
posListToGenerate.addAll(lodDim.getDataToGenerate(
playerBlockPosRounded.getX(),
playerBlockPosRounded.getZ(),
(int) (distancesLinear[detailGen]*1.5),
(int) (distancesLinear[detailGen+1]*1.5),
(byte) distancesGenerators[detailGen].complexity,
detail,
16));
System.out.println("HERE");
}
}
*/
long genReqStart = 0;
long genReqEnd = 0;
long genStart = 0;
long genEnd = 0;
if (LodConfig.CLIENT.distanceGenerationMode.get() != DistanceGenerationMode.NONE)
{
int requesting = maxChunkGenRequests;
genReqStart = System.currentTimeMillis();
//we firstly make sure that the world is filled with half region wide block
for (byte detailGen = LodConfig.CLIENT.maxGenerationDetail.get().detailLevel; detailGen <= LodUtil.REGION_DETAIL_LEVEL; detailGen++)
{
if (requesting == 0) break;
posListToGenerate = lodDim.getDataToGenerate(
playerBlockPosRounded.getX(),
playerBlockPosRounded.getZ(),
DetailDistanceUtil.getDistanceGeneration(detailGen),
DetailDistanceUtil.getDistanceGeneration(detailGen + 1),
LodConfig.CLIENT.distanceGenerationMode.get().complexity,
(byte) 9,
requesting/2);
for(LevelPos levelPos : posListToGenerate){
generationRequestList.add(new GenerationRequest(levelPos,LodConfig.CLIENT.distanceGenerationMode.get(), DetailDistanceUtil.getLodDetail(detailGen)));
}
requesting = maxChunkGenRequests - generationRequestList.size();
}
//we then fill the world with the rest of the block
for (byte detailGen = LodConfig.CLIENT.maxGenerationDetail.get().detailLevel; detailGen <= LodUtil.REGION_DETAIL_LEVEL; detailGen++)
{
if (requesting == 0) break;
posListToGenerate = lodDim.getDataToGenerate(
playerBlockPosRounded.getX(),
playerBlockPosRounded.getZ(),
DetailDistanceUtil.getDistanceGeneration(detailGen),
DetailDistanceUtil.getDistanceGeneration(detailGen + 1),
LodConfig.CLIENT.distanceGenerationMode.get().complexity,
DetailDistanceUtil.getLodDetail(detailGen).detailLevel,
maxChunkGenRequests);
for(LevelPos levelPos : posListToGenerate){
generationRequestList.add(new GenerationRequest(levelPos,LodConfig.CLIENT.distanceGenerationMode.get(), DetailDistanceUtil.getLodDetail(detailGen)));
}
requesting = maxChunkGenRequests - generationRequestList.size();
}
// determine which points in the posListToGenerate
// should actually be queued up
for (GenerationRequest generationRequest : generationRequestList)
{
ChunkPos chunkPos = generationRequest.getChunkPos();
if (numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests)
{
if (positionWaitingToBeGenerated.contains(chunkPos))
{
//ClientProxy.LOGGER.debug(pos + " asked to be generated again.");
continue;
}
// determine if this position is closer to the player
// than the previous
int newDistance = playerChunkPos.getChessboardDistance(chunkPos);
if (newDistance < minChunkDist)
{
// this chunk is closer, clear any previous
// positions and update the new minimum distance
minChunkDist = newDistance;
// move all the old chunks into the reserve
ArrayList<GenerationRequest> oldReserve = new ArrayList<>(chunksToGenReserve);
chunksToGenReserve.clear();
chunksToGenReserve.addAll(chunksToGen);
// top off reserve with whatever was in oldReerve
for (int i = 0; i < oldReserve.size(); i++)
{
if (chunksToGenReserve.size() < maxChunkGenRequests)
chunksToGenReserve.add(oldReserve.get(i));
else
break;
}
chunksToGen.clear();
chunksToGen.add(generationRequest);
}
else if (newDistance == minChunkDist)
{
// this chunk position as close as the minimum distance
if (chunksToGen.size() < maxChunkGenRequests)
{
// we are still under the number of chunks to generate
// add this position to the list
chunksToGen.add(generationRequest);
}
}
else
{
// this chunk is farther away than the minimum distance,
// add it to the reserve to make sure we always have a full reserve
chunksToGenReserve.add(generationRequest);
}
} // lod null and can generate more chunks
} // positions to generate
genReqEnd = System.currentTimeMillis();
genStart = System.currentTimeMillis();
// queue up chunks to be generated
if (mc.hasSingleplayerServer())
{
// issue #19
// TODO add a way for a server side mod to generate chunks requested here
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension);
// make sure we have as many chunks to generate as we are allowed
if (chunksToGen.size() < maxChunkGenRequests)
{
Iterator<GenerationRequest> reserveIterator = chunksToGenReserve.iterator();
while (chunksToGen.size() < maxChunkGenRequests && reserveIterator.hasNext())
{
chunksToGen.add(reserveIterator.next());
}
}
// start chunk generation
for (GenerationRequest generationRequest : generationRequestList)
{
// don't add null chunkPos (which shouldn't happen anyway)
// or add more to the generation queue
ChunkPos chunkPos = generationRequest.getChunkPos();
if (chunkPos == null || numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests)
continue;
positionWaitingToBeGenerated.add(chunkPos);
numberOfChunksWaitingToGenerate.addAndGet(1);
LodNodeGenWorker genWorker = new LodNodeGenWorker(chunkPos,generationRequest.generationMode, generationRequest.detail, renderer, LodQuadTreeNodeBuilder, this, lodDim, serverWorld);
WorldWorkerManager.addWorker(genWorker);
}
}
genEnd = System.currentTimeMillis();
} // if distanceGenerationMode != DistanceGenerationMode.NONE
// finish the buffer building
@@ -476,18 +250,11 @@ public class LodBufferBuilder
long treeTime = treeEnd - treeStart;
long renderingTime = renderEnd - renderStart;
long genReqTime = genReqEnd - genReqStart;
long genTime = genEnd - genStart;
ClientProxy.LOGGER.info("Buffer Build time: " + buildTime + " ms" + '\n' +
"Tree cutting time: " + treeTime + " ms" + '\n' +
"Rendering time: " + renderingTime + " ms" + '\n' +
"Generation request time: " + genReqTime + " ms" + '\n' +
"Generation time: " + genTime + " ms");
"Rendering time: " + renderingTime + " ms");
// mark that the buildable buffers as ready to swap
switchVbos = true;