Fix world gen queue and rendering for new QuadTree Iterators

This commit is contained in:
James Seibel
2023-04-03 21:33:42 -05:00
parent 3d13f6f46f
commit 5f84f01fb8
3 changed files with 92 additions and 117 deletions
@@ -9,6 +9,7 @@ import com.seibel.lod.core.dependencyInjection.SingletonInjector;
import com.seibel.lod.core.generation.tasks.*;
import com.seibel.lod.core.pos.*;
import com.seibel.lod.core.util.ThreadUtil;
import com.seibel.lod.core.util.objects.quadTree.QuadNode;
import com.seibel.lod.core.util.objects.quadTree.QuadTree;
import com.seibel.lod.core.util.objects.UncheckedInterruptedException;
import com.seibel.lod.core.logging.DhLoggerBuilder;
@@ -20,8 +21,6 @@ import org.apache.logging.log4j.Logger;
import java.io.Closeable;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
public class WorldGenerationQueue implements Closeable
@@ -123,7 +122,7 @@ public class WorldGenerationQueue implements Closeable
if (this.waitingTaskQuadTree.isSectionPosInBounds(requestPos))
{
CompletableFuture<WorldGenResult> future = new CompletableFuture<>();
this.waitingTaskQuadTree.set(requestPos, new WorldGenTask(pos, requiredDataDetail, tracker, future));
this.waitingTaskQuadTree.setValue(requestPos, new WorldGenTask(pos, requiredDataDetail, tracker, future));
return future;
}
else
@@ -241,73 +240,28 @@ public class WorldGenerationQueue implements Closeable
*/
private boolean startNextWorldGenTask(DhBlockPos2D targetPos)
{
final AtomicReference<WorldGenTask> closestTaskRef = new AtomicReference<>(null);
long closestGenDist = Long.MAX_VALUE;
// TODO improve
this.waitingTaskQuadTree.forEachRootNode((rootQuadNode) ->
WorldGenTask closestTask = null;
// TODO improve, having to go over every item isn't super efficient
Iterator<QuadNode<WorldGenTask>> leafNodeIterator = this.waitingTaskQuadTree.leafNodeIterator();
while (leafNodeIterator.hasNext())
{
if (closestTaskRef.get() == null)
WorldGenTask newGenTask = leafNodeIterator.next().value;
if (newGenTask != null) // TODO add an option to skip leaves with null values and potentially auto-prune them
{
rootQuadNode.forAllLeafValues((worldGenTask) ->
// use chebyShev distance in order to generate in rings around the target pos (also because it is a fast distance calculation)
int chebDistToTargetPos = newGenTask.pos.getCenterBlockPos().toPos2D().chebyshevDist(targetPos.toPos2D());
if (chebDistToTargetPos < closestGenDist)
{
if (closestTaskRef.get() == null)
{
closestTaskRef.set(worldGenTask);
}
});
// this task is closer than the last one
closestTask = newGenTask;
closestGenDist = chebDistToTargetPos;
}
}
});
WorldGenTask closestTask = closestTaskRef.get();
// // look through the tree from lowest to highest detail level to find the next task to generate
// for (byte detailLevel = QuadTree.TREE_LOWEST_DETAIL_LEVEL; detailLevel < this.waitingTaskQuadTree.treeMaxDetailLevel; detailLevel++)
// {
// // look for the task that is closest to the targetPos
// long closestGenDist = Long.MAX_VALUE;
//
// MovableGridRingList<WorldGenTask> gridRingList = this.waitingTaskQuadTree.getRingList(detailLevel);
// for (WorldGenTask newGenTask : gridRingList)
// {
// if (newGenTask != null)
// {
// if (queueFirstGenerationRequestFound)
// {
// // queue the first task we can find
// closestTask = newGenTask;
// break;
// }
// else
// {
// // use chebyShev distance in order to generate in rings around the target pos (also because it is a fast distance calculation)
// int chebDistToTargetPos = newGenTask.pos.getCenterBlockPos().toPos2D().chebyshevDist(targetPos.toPos2D());
// if (chebDistToTargetPos < closestGenDist)
// {
// // this task is closer than the last one
// closestTask = newGenTask;
// closestGenDist = chebDistToTargetPos;
// }
// else if (closestTask != null)
// {
// // this task is farther than the last one,
// // assume we have gotten as close as we can
// // and queue the task
// break;
// }
// }
// }
// }
//
// // a task has been found, don't look at the next detail level,
// // everything there will be farther away
// if (closestTask != null)
// {
// break;
// }
// }
}
if (closestTask == null)
{
@@ -318,7 +272,7 @@ public class WorldGenerationQueue implements Closeable
// remove the task we found, we are going to start it and don't want to run it multiple times
WorldGenTask removedWorldGenTask = this.waitingTaskQuadTree.set(new DhSectionPos(closestTask.pos.detailLevel, closestTask.pos.x, closestTask.pos.z), null);
WorldGenTask removedWorldGenTask = this.waitingTaskQuadTree.setValue(new DhSectionPos(closestTask.pos.detailLevel, closestTask.pos.x, closestTask.pos.z), null);
// removedWorldGenTask can be null // TODO when?
@@ -366,9 +320,9 @@ public class WorldGenerationQueue implements Closeable
childFutures.add(newFuture);
WorldGenTask newGenTask = new WorldGenTask(new DhLodPos(childDhSectionPos.sectionDetailLevel, childDhSectionPos.sectionX, childDhSectionPos.sectionZ), childDhSectionPos.sectionDetailLevel, removedWorldGenTask.taskTracker, newFuture);
this.waitingTaskQuadTree.set(new DhSectionPos(childDhSectionPos.sectionDetailLevel, childDhSectionPos.sectionX, childDhSectionPos.sectionZ), newGenTask);
this.waitingTaskQuadTree.setValue(new DhSectionPos(childDhSectionPos.sectionDetailLevel, childDhSectionPos.sectionX, childDhSectionPos.sectionZ), newGenTask);
boolean valueAdded = this.waitingTaskQuadTree.get(new DhSectionPos(childDhSectionPos.sectionDetailLevel, childDhSectionPos.sectionX, childDhSectionPos.sectionZ)) != null;
boolean valueAdded = this.waitingTaskQuadTree.getValue(new DhSectionPos(childDhSectionPos.sectionDetailLevel, childDhSectionPos.sectionX, childDhSectionPos.sectionZ)) != null;
LodUtil.assertTrue(valueAdded); // failed to add world gen task to quad tree, this means the quad tree was the wrong size
// LOGGER.info("split feature "+sectionPos+" into "+childDhSectionPos+" "+(valueAdded ? "added" : "notAdded"));
@@ -11,6 +11,8 @@ import com.seibel.lod.core.util.objects.quadTree.QuadNode;
import com.seibel.lod.core.util.objects.quadTree.QuadTree;
import org.apache.logging.log4j.Logger;
import java.util.Iterator;
/**
* This quadTree structure is our core data structure and holds
* all rendering data. <br><br>
@@ -82,8 +84,8 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
* @param z z coordinate of the section
* @return the LodSection
*/
public LodRenderSection getSection(byte detailLevel, int x, int z) { return this.get(new DhSectionPos(detailLevel, x, z)); }
public LodRenderSection getSection(DhSectionPos pos) { return this.get(pos); }
public LodRenderSection getSection(byte detailLevel, int x, int z) { return this.getValue(new DhSectionPos(detailLevel, x, z)); }
public LodRenderSection getSection(DhSectionPos pos) { return this.getValue(pos); }
@@ -163,21 +165,34 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
}
private void updateAllRenderSections(DhBlockPos2D playerPos)
{
this.forEachRootNodePos((rootNode, rootSectionPos) ->
// make sure all root nodes are created
Iterator<DhSectionPos> rootPosIterator = this.rootNodePosIterator();
while (rootPosIterator.hasNext())
{
if (rootNode == null)
DhSectionPos rootSectionPos = rootPosIterator.next();
if (this.getNode(rootSectionPos) == null)
{
LodRenderSection newRenderSection = new LodRenderSection(rootSectionPos);
this.set(rootSectionPos, newRenderSection);
return; // update next tick
this.setValue(rootSectionPos, newRenderSection);
}
}
// update all nodes in the tree
Iterator<DhSectionPos> rootNodeIterator = this.rootNodePosIterator();
while (rootNodeIterator.hasNext())
{
DhSectionPos rootPos = rootNodeIterator.next();
QuadNode<LodRenderSection> rootNode = this.getNode(rootPos); // should never be null
rootNode.forEachDirectChildNode((quadNode, sectionPos) ->
// iterate over nodes in this root
Iterator<QuadNode<LodRenderSection>> nodeIterator = rootNode.getNodeIterator();
while (nodeIterator.hasNext())
{
recursivelyUpdateRenderSectionNode(playerPos, rootNode, quadNode, sectionPos);
});
});
QuadNode<LodRenderSection> quadNode = nodeIterator.next();
recursivelyUpdateRenderSectionNode(playerPos, rootNode, quadNode, quadNode.sectionPos);
}
}
}
private void recursivelyUpdateRenderSectionNode(DhBlockPos2D playerPos, QuadNode<LodRenderSection> rootNode, QuadNode<LodRenderSection> nullableQuadNode, DhSectionPos sectionPos)
{
@@ -215,10 +230,15 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
}
else
{
nullableQuadNode.forEachDirectChildNode((childQuadNode, childSectionPosition) ->
// TODO this never returns anything
Iterator<DhSectionPos> childPosIterator = nullableQuadNode.getChildPosIterator();
while (childPosIterator.hasNext())
{
recursivelyUpdateRenderSectionNode(playerPos, rootNode, childQuadNode, childSectionPosition);
});
DhSectionPos childPos = childPosIterator.next();
QuadNode<LodRenderSection> childNode = rootNode.getNode(childPos);
recursivelyUpdateRenderSectionNode(playerPos, rootNode, childNode, childPos);
}
}
}
// TODO this should only equal the expected detail level, the (expectedDetailLevel-1) is a temporary fix to prevent corners from being cut out
@@ -336,28 +356,16 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
{
LOGGER.info("Clearing render cache...");
this.forEachRootNode((rootNode) ->
Iterator<QuadNode<LodRenderSection>> nodeIterator = this.nodeIterator();
while (nodeIterator.hasNext())
{
rootNode.forEachDirectChildNode((quadNode, sectionPos) ->
QuadNode<LodRenderSection> quadNode = nodeIterator.next();
if (quadNode.value != null)
{
if (quadNode != null && quadNode.value != null)
{
quadNode.value.disposeRenderData();
}
});
});
this.forEachRootNode((rootNode) ->
{
rootNode.forEachDirectChildNode((quadNode, sectionPos) ->
{
if (quadNode != null && quadNode.value != null)
{
quadNode.value.disposeRenderData();
quadNode.value = null;
}
});
});
quadNode.value.disposeRenderData();
quadNode.value = null;
}
}
// delete the cache files
this.renderSourceProvider.deleteRenderCache();
@@ -402,16 +410,16 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
{
LOGGER.info("Shutting down "+ LodQuadTree.class.getSimpleName()+"...");
this.forEachRootNode((rootNode) ->
Iterator<QuadNode<LodRenderSection>> nodeIterator = this.nodeIterator();
while (nodeIterator.hasNext())
{
rootNode.forEachDirectChildNode((quadNode, sectionPos) ->
QuadNode<LodRenderSection> quadNode = nodeIterator.next();
if (quadNode.value != null)
{
if (quadNode != null && quadNode.value != null)
{
quadNode.value.disposeRenderData();
}
});
});
quadNode.value.disposeRenderData();
quadNode.value = null;
}
}
LOGGER.info("Finished shutting down "+ LodQuadTree.class.getSimpleName());
}
@@ -9,9 +9,11 @@ import com.seibel.lod.core.render.renderer.LodRenderer;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.util.math.Vec3f;
import com.seibel.lod.core.util.objects.SortedArraySet;
import com.seibel.lod.core.util.objects.quadTree.QuadNode;
import org.apache.logging.log4j.Logger;
import java.util.Comparator;
import java.util.Iterator;
/**
* This object tells the {@link LodRenderer} what buffers to render
@@ -135,8 +137,15 @@ public class RenderBufferHandler
// Build the sorted list
this.loadedNearToFarBuffers = new SortedArraySet<>((a, b) -> -farToNearComparator.compare(a, b)); // TODO is the comparator named wrong?
this.quadTree.forEachValue((renderSection, sectionPos) ->
Iterator<QuadNode<LodRenderSection>> nodeIterator = this.quadTree.nodeIterator();
while (nodeIterator.hasNext())
{
QuadNode<LodRenderSection> node = nodeIterator.next();
DhSectionPos sectionPos = node.sectionPos;
LodRenderSection renderSection = node.value;
if (renderSection != null && renderSection.shouldRender())
{
if (renderSection.renderBufferRef.get() != null && renderSection.renderBufferRef.get().areBuffersUploaded())
@@ -144,7 +153,7 @@ public class RenderBufferHandler
this.loadedNearToFarBuffers.add(new LoadedRenderBuffer(renderSection.renderBufferRef.get(), sectionPos));
}
}
});
}
}
public void renderOpaque(LodRenderer renderContext)
@@ -160,8 +169,10 @@ public class RenderBufferHandler
public void update()
{
this.quadTree.forEachValue((renderSection, sectionPos) ->
Iterator<QuadNode<LodRenderSection>> nodeIterator = this.quadTree.nodeIterator();
while (nodeIterator.hasNext())
{
LodRenderSection renderSection = nodeIterator.next().value;
if (renderSection != null)
{
ColumnRenderSource currentRenderSource = renderSection.getRenderSource();
@@ -182,19 +193,21 @@ public class RenderBufferHandler
currentRenderSource.trySwapInNewlyBuiltRenderBuffer(renderSection.getRenderSource(), renderSection.renderBufferRef);
}
}
});
}
}
public void close()
{
this.quadTree.forEachValue((renderSection) ->
{
Iterator<QuadNode<LodRenderSection>> nodeIterator = this.quadTree.nodeIterator();
while (nodeIterator.hasNext())
{
LodRenderSection renderSection = nodeIterator.next().value;
if (renderSection != null && renderSection.renderBufferRef.get() != null)
{
renderSection.renderBufferRef.get().close();
renderSection.renderBufferRef.set(null);
}
});
}
}