FIx misc bugs and issues, and polish, simplify and remove no-longer needed logic

This commit is contained in:
TomTheFurry
2023-06-12 13:25:37 +08:00
parent 0e6f294ad7
commit 082f7597b2
10 changed files with 179 additions and 39 deletions
@@ -65,7 +65,7 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer implements IDebugRe
return;
}
Color c = Color.green;
r.renderBox(debugPos, 128, 128, 0.05f, c);
//r.renderBox(debugPos, 128, 128, 0.05f, c);
}
@@ -168,28 +168,28 @@ public class FullDataFileHandler implements IFullDataSourceProvider
* @param preexistingFiles the list of {@link FullDataMetaFile}'s that have been created for the given position.
* @param missingFilePositions the list of {@link DhSectionPos}'s that don't have {@link FullDataMetaFile} created for them yet.
*/
protected void getDataFilesForPosition(DhSectionPos basePos, DhSectionPos pos,
protected void getDataFilesForPosition(DhSectionPos effectivePos, DhSectionPos posAreaToGet,
ArrayList<FullDataMetaFile> preexistingFiles, ArrayList<DhSectionPos> missingFilePositions)
{
byte sectionDetail = pos.sectionDetailLevel;
byte sectionDetail = posAreaToGet.sectionDetailLevel;
boolean allEmpty = true;
outerLoop:
while (--sectionDetail >= this.minDetailLevel)
{
DhLodPos minPos = pos.getCorner().getCornerLodPos(sectionDetail);
int count = pos.getSectionBBoxPos().getWidthAtDetail(sectionDetail);
DhLodPos minPos = posAreaToGet.getCorner().getCornerLodPos(sectionDetail);
int count = posAreaToGet.getSectionBBoxPos().getWidthAtDetail(sectionDetail);
for (int xOffset = 0; xOffset < count; xOffset++)
{
for (int zOffset = 0; zOffset < count; zOffset++)
{
DhSectionPos subPos = new DhSectionPos(sectionDetail, xOffset+minPos.x, zOffset+minPos.z);
LodUtil.assertTrue(pos.overlaps(basePos) && subPos.overlaps(pos));
LodUtil.assertTrue(posAreaToGet.overlaps(effectivePos) && subPos.overlaps(posAreaToGet));
//TODO: The following check is temporary as we only sample corner points, which means
// on a very different level, we may not need the entire section at all.
if (!CompleteFullDataSource.firstDataPosCanAffectSecond(basePos, subPos))
if (!CompleteFullDataSource.firstDataPosCanAffectSecond(effectivePos, subPos))
{
continue;
}
@@ -207,15 +207,15 @@ public class FullDataFileHandler implements IFullDataSourceProvider
{
// there are no children to this quad tree,
// add this leaf's position
missingFilePositions.add(pos);
missingFilePositions.add(posAreaToGet);
}
else
{
// there are children in this quad tree, search them
this.recursiveGetDataFilesForPosition(0, basePos, pos, preexistingFiles, missingFilePositions);
this.recursiveGetDataFilesForPosition(1, basePos, pos, preexistingFiles, missingFilePositions);
this.recursiveGetDataFilesForPosition(2, basePos, pos, preexistingFiles, missingFilePositions);
this.recursiveGetDataFilesForPosition(3, basePos, pos, preexistingFiles, missingFilePositions);
this.recursiveGetDataFilesForPosition(0, effectivePos, posAreaToGet, preexistingFiles, missingFilePositions);
this.recursiveGetDataFilesForPosition(1, effectivePos, posAreaToGet, preexistingFiles, missingFilePositions);
this.recursiveGetDataFilesForPosition(2, effectivePos, posAreaToGet, preexistingFiles, missingFilePositions);
this.recursiveGetDataFilesForPosition(3, effectivePos, posAreaToGet, preexistingFiles, missingFilePositions);
}
}
private void recursiveGetDataFilesForPosition(int childIndex, DhSectionPos basePos, DhSectionPos pos, ArrayList<FullDataMetaFile> preexistingFiles, ArrayList<DhSectionPos> missingFilePositions)
@@ -403,7 +403,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider
@Override
public IFullDataSource onDataFileLoaded(IFullDataSource source, BaseMetaData metaData,
Consumer<IFullDataSource> onUpdated, Function<IFullDataSource, Boolean> updater)
Consumer<IFullDataSource> onUpdated, Function<IFullDataSource, Boolean> updater, boolean justCreated)
{
boolean changed = updater.apply(source);
// if (changed)
@@ -163,7 +163,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile
this.baseMetaData = this._makeBaseMetaData(fullDataSource);
return fullDataSource;
})
.thenApply((fullDataSource) -> this.fullDataSourceProvider.onDataFileLoaded(fullDataSource, this.baseMetaData, this::_updateAndWriteDataSource, this::_applyWriteQueueToFullDataSource))
.thenApply((fullDataSource) -> this.fullDataSourceProvider.onDataFileLoaded(fullDataSource, this.baseMetaData, this::_updateAndWriteDataSource, this::_applyWriteQueueToFullDataSource, true))
.whenComplete((fullDataSource, exception) ->
{
if (exception != null)
@@ -225,7 +225,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile
"loading one into the cache! Is this a deadlock?");
// fire the onDataLoaded method
fullDataSource = this.fullDataSourceProvider.onDataFileLoaded(fullDataSource, this.baseMetaData, this::_updateAndWriteDataSource, this::_applyWriteQueueToFullDataSource);
fullDataSource = this.fullDataSourceProvider.onDataFileLoaded(fullDataSource, this.baseMetaData, this::_updateAndWriteDataSource, this::_applyWriteQueueToFullDataSource, false);
return fullDataSource;
}, executorService)
@@ -17,6 +17,7 @@ import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.lod.core.util.LodUtil;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nullable;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.*;
@@ -24,6 +25,8 @@ import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class GeneratedFullDataFileHandler extends FullDataFileHandler
{
@@ -52,7 +55,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler
{
return super.read(pos).whenComplete((fullDataSource, ex) ->
{
this.checkIfSectionNeedsAdditionalGeneration(pos, fullDataSource);
//this.checkIfSectionNeedsAdditionalGeneration(pos, fullDataSource);
});
}
@@ -86,11 +89,125 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler
//========//
// events //
//========//
private CompletableFuture<IFullDataSource> spawnGenTasks(FullDataMetaFile file, @Nullable IIncompleteFullDataSource data)
{
DhSectionPos pos = file.pos;
ArrayList<FullDataMetaFile> existingFiles = new ArrayList<>();
ArrayList<DhSectionPos> missingPositions = new ArrayList<>();
this.getDataFilesForPosition(pos, pos, existingFiles, missingPositions);
// confirm the quad tree has at least one node in it
LodUtil.assertTrue(!missingPositions.isEmpty() || !existingFiles.isEmpty());
// determine the type of dataSource that should be used for this position
IIncompleteFullDataSource incompleteFullDataSource;
if (data == null)
{
if (pos.sectionDetailLevel <= HighDetailIncompleteFullDataSource.MAX_SECTION_DETAIL)
{
incompleteFullDataSource = HighDetailIncompleteFullDataSource.createEmpty(pos);
}
else
{
incompleteFullDataSource = LowDetailIncompleteFullDataSource.createEmpty(pos);
}
}
else
{
incompleteFullDataSource = data;
}
// breaks down the missing positions into the desired detail level that the gen queue could accept
byte maxSectDataDetailLevel = worldGenQueueRef.get().largestDataDetail;
byte targetDataDetailLevel = incompleteFullDataSource.getDataDetailLevel();
if (targetDataDetailLevel > maxSectDataDetailLevel) {
byte sectDetailLevel = (byte) (DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL + maxSectDataDetailLevel);
missingPositions = missingPositions.stream()
.flatMap(missingPos -> {
if (missingPos.sectionDetailLevel > sectDetailLevel) {
// split this position into smaller positions
ArrayList<DhSectionPos> splitPositions = new ArrayList<>();
missingPos.forEachChildAtLevel(sectDetailLevel, splitPositions::add);
return splitPositions.stream();
}
else {
return Stream.of(missingPos);
}
})
.collect(Collectors.toCollection(ArrayList::new));
}
if (missingPositions.size() == 1 && existingFiles.isEmpty() && missingPositions.get(0).equals(pos))
{
// No LOD data exists for this position yet
WorldGenerationQueue worldGenQueue = this.worldGenQueueRef.get();
if (worldGenQueue != null)
{
this.incompleteSourceGenRequests.add(pos);
// queue this section to be generated
GenTask genTask = new GenTask(pos, new WeakReference<>(incompleteFullDataSource));
worldGenQueue.submitGenTask(new DhLodPos(pos), incompleteFullDataSource.getDataDetailLevel(), genTask)
.whenComplete((genTaskResult, ex) ->
{
this.onWorldGenTaskComplete(genTaskResult, ex, genTask, pos);
this.fireOnGenPosSuccessListeners(pos);
this.incompleteSourceGenRequests.remove(pos);
});
}
// return the empty dataSource (it will be populated later)
return CompletableFuture.completedFuture(incompleteFullDataSource);
}
else
{
// LOD data exists for this position
// create the missing metaData files
for (DhSectionPos missingPos : missingPositions)
{
FullDataMetaFile newFile = this.getOrMakeFile(missingPos);
if (newFile != null)
{
existingFiles.add(newFile);
}
}
// LOGGER.debug("Creating "+pos+" from sampling "+existingFiles.size()+" files: "+existingFiles);
// read in the existing data
final ArrayList<CompletableFuture<Void>> loadDataFutures = new ArrayList<>(existingFiles.size());
for (FullDataMetaFile existingFile : existingFiles)
{
loadDataFutures.add(existingFile.loadOrGetCachedDataSourceAsync()
.exceptionally((ex) -> /*Ignore file read errors*/null)
.thenAccept((fullDataSource) ->
{
if (fullDataSource == null)
{
return;
}
//this.checkIfSectionNeedsAdditionalGeneration(pos, fullDataSource);
//LOGGER.info("Merging data from {} into {}", data.getSectionPos(), pos);
incompleteFullDataSource.sampleFrom(fullDataSource);
})
);
}
return CompletableFuture.allOf(loadDataFutures.toArray(new CompletableFuture[0]))
.thenApply((voidValue) -> incompleteFullDataSource.tryPromotingToCompleteDataSource());
}
}
@Override
public CompletableFuture<IFullDataSource> onCreateDataFile(FullDataMetaFile file)
{
DhSectionPos pos = file.pos;
return this.spawnGenTasks(file, null);
/* DhSectionPos pos = file.pos;
ArrayList<FullDataMetaFile> existingFiles = new ArrayList<>();
ArrayList<DhSectionPos> missingPositions = new ArrayList<>();
@@ -156,7 +273,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler
for (FullDataMetaFile existingFile : existingFiles)
{
loadDataFutures.add(existingFile.loadOrGetCachedDataSourceAsync()
.exceptionally((ex) -> /*Ignore file read errors*/null)
.exceptionally((ex) -> *//*Ignore file read errors*//*null)
.thenAccept((fullDataSource) ->
{
if (fullDataSource == null)
@@ -174,7 +291,20 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler
return CompletableFuture.allOf(loadDataFutures.toArray(new CompletableFuture[0]))
.thenApply((voidValue) -> incompleteFullDataSource.tryPromotingToCompleteDataSource());
}*/
}
@Override
public IFullDataSource onDataFileLoaded(IFullDataSource source, BaseMetaData metaData,
Consumer<IFullDataSource> onUpdated, Function<IFullDataSource, Boolean> updater, boolean justCreated)
{
IFullDataSource fullDataSource = super.onDataFileLoaded(source, metaData, onUpdated, updater, justCreated);
if (fullDataSource instanceof CompleteFullDataSource || justCreated) {
return fullDataSource;
}
this.spawnGenTasks(getOrMakeFile(metaData.pos), (IIncompleteFullDataSource)fullDataSource);
//checkIfSectionNeedsAdditionalGeneration(metaData.pos, fullDataSource);
return fullDataSource;
}
/* @Override
@@ -230,6 +360,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler
// don't queue the same section twice
if (this.incompleteSourceGenRequests.contains(ungenChildPos))
{
LOGGER.warn("checkIfSectionNeedsAdditionalGeneration skipping duplicate gen request for pos: ["+ungenChildPos+"].");
continue;
}
this.incompleteSourceGenRequests.add(ungenChildPos);
@@ -25,7 +25,7 @@ public interface IFullDataSourceProvider extends AutoCloseable
//boolean isCacheVersionValid(DhSectionPos sectionPos, long cacheVersion);
CompletableFuture<IFullDataSource> onCreateDataFile(FullDataMetaFile file);
IFullDataSource onDataFileLoaded(IFullDataSource source, BaseMetaData metaData, Consumer<IFullDataSource> onUpdated, Function<IFullDataSource, Boolean> updater);
IFullDataSource onDataFileLoaded(IFullDataSource source, BaseMetaData metaData, Consumer<IFullDataSource> onUpdated, Function<IFullDataSource, Boolean> updater, boolean justCreated);
CompletableFuture<IFullDataSource> onDataFileRefresh(IFullDataSource source, BaseMetaData metaData, Function<IFullDataSource, Boolean> updater, Consumer<IFullDataSource> onUpdated);
File computeDataFilePath(DhSectionPos pos);
ExecutorService getIOExecutor();
@@ -44,7 +44,7 @@ public class WorldGenerationQueue implements Closeable, IDebugRenderable
public final byte minGranularity;
/** largest numerical detail level allowed */
final byte largestDataDetail;
public final byte largestDataDetail;
/** lowest numerical detail level allowed */
public final byte smallestDataDetail;
@@ -65,7 +65,7 @@ public class WorldGenerationQueue implements Closeable, IDebugRenderable
// debug variables to test for duplicate world generator requests //
/** limits how many of the previous world gen requests we should track */
private static final int MAX_ALREADY_GENERATED_COUNT = 100;
private final HashSet<DhLodPos> alreadyGeneratedPosHashSet = new HashSet<>(MAX_ALREADY_GENERATED_COUNT);
private final HashMap<DhLodPos, StackTraceElement[]> alreadyGeneratedPosHashSet = new HashMap<>(MAX_ALREADY_GENERATED_COUNT);
private final Queue<DhLodPos> alreadyGeneratedPosQueue = new LinkedList<>();
private static ExecutorService worldGeneratorThreadPool;
@@ -271,8 +271,7 @@ public class WorldGenerationQueue implements Closeable, IDebugRenderable
if (newGenTask != null) // TODO add an option to skip leaves with null values and potentially auto-prune them
{
CheckingTasks.add(newGenTask);
// TODO this isn't a long term fix, in the long term the tree should automatically remove out of bound nodes when moved
if (!this.waitingTaskQuadTree.isSectionPosInBounds(taskSectionPos) || !newGenTask.StillValid())
if (!newGenTask.StillValid())
{
// skip and remove out-of-bound tasks or tasks that are no longer valid
taskNode.value = null;
@@ -298,7 +297,6 @@ public class WorldGenerationQueue implements Closeable, IDebugRenderable
}
// remove the task we found, we are going to start it and don't want to run it multiple times
// TODO the setValue can fail if the user is moving and the task that was once in range is no longer in range
WorldGenTask removedWorldGenTask = this.waitingTaskQuadTree.setValue(new DhSectionPos(closestTask.pos.detailLevel, closestTask.pos.x, closestTask.pos.z), null);
// do we need to modify this task to generate it?
@@ -322,6 +320,8 @@ public class WorldGenerationQueue implements Closeable, IDebugRenderable
// Note: Due to concurrency reasons, even if the currently running task is compatible with
// the newly selected task, we cannot use it,
// as some chunks may have already been written into.
LOGGER.warn("A task already exists for this position, todo: {}", closestTask.pos);
}
// a task has been started
@@ -338,13 +338,6 @@ public class WorldGenerationQueue implements Closeable, IDebugRenderable
DhSectionPos sectionPos = new DhSectionPos(closestTask.pos.detailLevel, closestTask.pos.x, closestTask.pos.z);
sectionPos.forEachChild((childDhSectionPos) ->
{
if (!this.waitingTaskQuadTree.isSectionPosInBounds(childDhSectionPos))
{
// don't attempt to generate terrain outside the user's render distance
return;
}
CompletableFuture<WorldGenResult> newFuture = new CompletableFuture<>();
childFutures.add(newFuture);
@@ -376,16 +369,18 @@ public class WorldGenerationQueue implements Closeable, IDebugRenderable
DhChunkPos chunkPosMin = new DhChunkPos(taskPos.getCornerBlockPos());
// check if this is a duplicate generation task
if (this.alreadyGeneratedPosHashSet.contains(inProgressTaskGroup.group.pos))
if (this.alreadyGeneratedPosHashSet.containsKey(inProgressTaskGroup.group.pos))
{
// temporary solution to prevent generating the same section multiple times
LOGGER.warn("Duplicate generation section " + taskPos + " with granularity [" + granularity + "] at " + chunkPosMin + ". Skipping...");
StackTraceElement[] stackTrace = this.alreadyGeneratedPosHashSet.get(inProgressTaskGroup.group.pos);
// sending a success result is necessary to make sure the render sections are reloaded correctly
inProgressTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(new DhSectionPos(granularity, taskPos))));
return;
}
this.alreadyGeneratedPosHashSet.add(inProgressTaskGroup.group.pos);
this.alreadyGeneratedPosHashSet.put(inProgressTaskGroup.group.pos, Thread.currentThread().getStackTrace());
this.alreadyGeneratedPosQueue.add(inProgressTaskGroup.group.pos);
// remove extra tracked duplicate positions
@@ -627,10 +622,10 @@ public class WorldGenerationQueue implements Closeable, IDebugRenderable
public void debugRender(DebugRenderer r) {
CheckingTasks.forEach((t) -> {
DhLodPos pos = t.pos;
r.renderBox(pos, -32f, 64f, 0.05f, Color.blue);
r.renderBox(pos, -32f, 128f, 0.05f, Color.blue);
});
this.inProgressGenTasksByLodPos.forEach((pos, t) -> {
r.renderBox(pos, -30f, 64f, 0.05f, Color.red);
r.renderBox(pos, -30f, 128f, 0.05f, Color.red);
});
}
}
@@ -167,6 +167,20 @@ public class DhSectionPos
callback.accept(this.getChildByIndex(i));
}
}
/** Applies the given consumer to all children of the position at the given section detail level. */
public void forEachChildAtLevel(byte sectionDetailLevel, Consumer<DhSectionPos> callback)
{
if (sectionDetailLevel == this.sectionDetailLevel)
{
callback.accept(this);
return;
}
for (int i = 0; i < 4; i++)
{
this.getChildByIndex(i).forEachChildAtLevel(sectionDetailLevel, callback);
}
}
public DhSectionPos getParentPos() { return new DhSectionPos((byte) (this.sectionDetailLevel + 1), BitShiftUtil.half(this.sectionX), BitShiftUtil.half(this.sectionZ)); }
@@ -64,7 +64,7 @@ public class LodRenderSection implements IDebugRenderable
float yOffset = Objects.hashCode(this) / (float) Integer.MAX_VALUE * 16f;
r.renderBox(this.pos, yOffset, yOffset, 0.1f, color);
//r.renderBox(this.pos, yOffset, yOffset, 0.1f, color);
}
@@ -188,7 +188,7 @@ public class DebugRenderer {
GL32.glViewport(0,0, MC_RENDER.getTargetFrameBufferViewportWidth(), MC_RENDER.getTargetFrameBufferViewportHeight());
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE);
//GL32.glLineWidth(2);
GL32.glDisable(GL32.GL_DEPTH_TEST);
GL32.glEnable(GL32.GL_DEPTH_TEST);
GL32.glDisable(GL32.GL_STENCIL_TEST);
GL32.glDisable(GL32.GL_BLEND);
GL32.glDisable(GL32.GL_SCISSOR_TEST);
@@ -268,6 +268,7 @@ public class LodRenderer
shaderProgram.unbind();
//lightmapTexture.free();
DebugRenderer.INSTANCE.render(modelViewProjectionMatrix);
GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT);
currentState.restore();
@@ -277,7 +278,6 @@ public class LodRenderer
profiler.pop();
tickLogger.incLogTries();
DebugRenderer.INSTANCE.render(modelViewProjectionMatrix);
}