From 8b580c51d6a3033d5e4f64bc5cc514366e6ace0d Mon Sep 17 00:00:00 2001 From: James Seibel Date: Fri, 12 May 2023 20:34:18 -0500 Subject: [PATCH] Have RenderSourceFileHandler update all detail levels --- .../transformers/FullToColumnTransformer.java | 97 ++++++------------- .../renderfile/RenderSourceFileHandler.java | 63 ++++++++---- 2 files changed, 75 insertions(+), 85 deletions(-) diff --git a/core/src/main/java/com/seibel/lod/core/dataObjects/transformers/FullToColumnTransformer.java b/core/src/main/java/com/seibel/lod/core/dataObjects/transformers/FullToColumnTransformer.java index 47f24922a..5b338cb7c 100644 --- a/core/src/main/java/com/seibel/lod/core/dataObjects/transformers/FullToColumnTransformer.java +++ b/core/src/main/java/com/seibel/lod/core/dataObjects/transformers/FullToColumnTransformer.java @@ -4,6 +4,8 @@ import com.seibel.lod.core.dataObjects.fullData.FullDataPointIdMap; import com.seibel.lod.core.dataObjects.fullData.accessor.SingleColumnFullDataAccessor; import com.seibel.lod.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.lod.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource; +import com.seibel.lod.core.logging.DhLoggerBuilder; +import com.seibel.lod.core.pos.DhChunkPos; import com.seibel.lod.core.util.RenderDataPointUtil; import com.seibel.lod.core.dataObjects.render.ColumnRenderSource; import com.seibel.lod.core.dataObjects.render.columnViews.ColumnArrayView; @@ -19,6 +21,8 @@ import com.seibel.lod.core.util.LodUtil; import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory; import com.seibel.lod.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper; +import com.seibel.lod.coreapi.util.BitShiftUtil; +import org.apache.logging.log4j.Logger; public class FullToColumnTransformer { @@ -87,19 +91,6 @@ public class FullToColumnTransformer columnSource.fillDebugFlag(0, 0, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.DebugSourceFlag.FULL); -// } else if (dataDetail == 0 && columnSource.getDataDetail() > dataDetail) { -// byte deltaDetail = (byte) (columnSource.getDataDetail() - dataDetail); -// int perColumnWidth = 1 << deltaDetail; -// int columnCount = pos.getWidth(dataDetail).value / perColumnWidth; -// -// -// for (int x = 0; x < pos.getWidth(dataDetail).value; x++) { -// for (int z = 0; z < pos.getWidth(dataDetail).value; z++) { -// ColumnArrayView columnArrayView = columnSource.getVerticalDataView(x, z); -// SingleColumnFullDataAccessor fullArrayView = data.get(x, z); -// convertColumnData(level, columnArrayView, fullArrayView); -// } -// } } else { @@ -144,6 +135,7 @@ public class FullToColumnTransformer ColumnArrayView columnArrayView = columnSource.getVerticalDataPointView(x, z); convertColumnData(level, baseX + x, baseZ + z, columnArrayView, fullArrayView, 1); + columnSource.fillDebugFlag(x, z, 1, 1, ColumnRenderSource.DebugSourceFlag.SPARSE); if (fullArrayView.doesColumnExist()) LodUtil.assertTrue(columnSource.doesDataPointExist(x, z)); @@ -162,20 +154,28 @@ public class FullToColumnTransformer * @throws InterruptedException Can be caused by interrupting the thread upstream. * Generally thrown if the method is running after the client leaves the current world. */ - public static void writeFullDataChunkToColumnData(ColumnRenderSource render, IDhClientLevel level, ChunkSizedFullDataAccessor chunkDataView) throws InterruptedException + public static void writeFullDataChunkToColumnData(ColumnRenderSource renderSource, IDhClientLevel level, ChunkSizedFullDataAccessor chunkDataView) throws InterruptedException, IllegalArgumentException { - final DhSectionPos pos = render.getSectionPos(); - final int renderOffsetX = (chunkDataView.pos.x * LodUtil.CHUNK_WIDTH) - pos.getCorner().getCornerBlockPos().x; - final int renderOffsetZ = (chunkDataView.pos.z * LodUtil.CHUNK_WIDTH) - pos.getCorner().getCornerBlockPos().z; - final int blockX = pos.getCorner().getCornerBlockPos().x; - final int blockZ = pos.getCorner().getCornerBlockPos().z; - final int perRenderWidth = 1 << render.getDataDetail(); - final int perDataWidth = 1 << chunkDataView.detailLevel; - render.markNotEmpty(); + final DhSectionPos renderSourcePos = renderSource.getSectionPos(); - if (chunkDataView.detailLevel == render.getDataDetail()) + final int sourceBlockX = renderSourcePos.getCorner().getCornerBlockPos().x; + final int sourceBlockZ = renderSourcePos.getCorner().getCornerBlockPos().z; + + // offset between the incoming chunk data and this render source + final int blockOffsetX = (chunkDataView.pos.x * LodUtil.CHUNK_WIDTH) - sourceBlockX; + final int blockOffsetZ = (chunkDataView.pos.z * LodUtil.CHUNK_WIDTH) - sourceBlockZ; + + final int sourceDataPointBlockWidth = BitShiftUtil.powerOfTwo(renderSource.getDataDetail()); + + + + if (chunkDataView.detailLevel == renderSource.getDataDetail()) { - if (renderOffsetX < 0 || renderOffsetX + LodUtil.CHUNK_WIDTH > render.getDataSize() || renderOffsetZ < 0 || renderOffsetZ + LodUtil.CHUNK_WIDTH > render.getDataSize()) + // confirm the render source contains this chunk + if (blockOffsetX < 0 + || blockOffsetX + LodUtil.CHUNK_WIDTH > renderSource.getWidthInDataPoints() + || blockOffsetZ < 0 + || blockOffsetZ + LodUtil.CHUNK_WIDTH > renderSource.getWidthInDataPoints()) { throw new IllegalArgumentException("Data offset is out of bounds"); } @@ -187,56 +187,23 @@ public class FullToColumnTransformer { throwIfThreadInterrupted(); - ColumnArrayView columnArrayView = render.getVerticalDataPointView(renderOffsetX + x, renderOffsetZ + z); + ColumnArrayView columnArrayView = renderSource.getVerticalDataPointView(blockOffsetX + x, blockOffsetZ + z); SingleColumnFullDataAccessor fullArrayView = chunkDataView.get(x, z); - convertColumnData(level, blockX + perRenderWidth * (renderOffsetX + x), - blockZ + perRenderWidth * (renderOffsetZ + z), + + convertColumnData(level, + sourceBlockX + sourceDataPointBlockWidth * (blockOffsetX + x), + sourceBlockZ + sourceDataPointBlockWidth * (blockOffsetZ + z), columnArrayView, fullArrayView, 2); if (fullArrayView.doesColumnExist()) { - LodUtil.assertTrue(render.doesDataPointExist(renderOffsetX + x, renderOffsetZ + z)); + LodUtil.assertTrue(renderSource.doesDataPointExist(blockOffsetX + x, blockOffsetZ + z)); } } } - render.fillDebugFlag(renderOffsetX, renderOffsetZ, LodUtil.CHUNK_WIDTH, LodUtil.CHUNK_WIDTH, ColumnRenderSource.DebugSourceFlag.DIRECT); - } - else - { - final int dataPerRender = 1 << (render.getDataDetail() - chunkDataView.detailLevel); - final int dataSize = LodUtil.CHUNK_WIDTH / dataPerRender; - final int vertSize = render.getVerticalSize(); - long[] tempRender = new long[dataPerRender * dataPerRender * vertSize]; - if (renderOffsetX < 0 || renderOffsetX + dataSize > render.getDataSize() || renderOffsetZ < 0 || renderOffsetZ + dataSize > render.getDataSize()) - { - throw new IllegalArgumentException("Data offset is out of bounds"); - } + renderSource.fillDebugFlag(blockOffsetX, blockOffsetZ, LodUtil.CHUNK_WIDTH, LodUtil.CHUNK_WIDTH, ColumnRenderSource.DebugSourceFlag.DIRECT); - for (int x = 0; x < dataSize; x++) - { - for (int z = 0; z < dataSize; z++) - { - - ColumnQuadView tempQuadView = new ColumnQuadView(tempRender, dataPerRender, vertSize, 0, 0, dataPerRender, dataPerRender); - for (int ox = 0; ox < dataPerRender; ox++) - { - for (int oz = 0; oz < dataPerRender; oz++) - { - throwIfThreadInterrupted(); - - - ColumnArrayView columnArrayView = tempQuadView.get(ox, oz); - SingleColumnFullDataAccessor fullArrayView = chunkDataView.get(x * dataPerRender + ox, z * dataPerRender + oz); - convertColumnData(level, blockX + perRenderWidth * (renderOffsetX + x) + perDataWidth * ox, - blockZ + perRenderWidth * (renderOffsetZ + z) + perDataWidth * oz, - columnArrayView, fullArrayView, 2); - } - } - ColumnArrayView downSampledArrayView = render.getVerticalDataPointView(renderOffsetX + x, renderOffsetZ + z); - downSampledArrayView.mergeMultiDataFrom(tempQuadView); - } - } - render.fillDebugFlag(renderOffsetX, renderOffsetZ, dataSize, dataSize, ColumnRenderSource.DebugSourceFlag.DIRECT); + renderSource.markNotEmpty(); } } diff --git a/core/src/main/java/com/seibel/lod/core/file/renderfile/RenderSourceFileHandler.java b/core/src/main/java/com/seibel/lod/core/file/renderfile/RenderSourceFileHandler.java index ae34dca08..4976c0ab5 100644 --- a/core/src/main/java/com/seibel/lod/core/file/renderfile/RenderSourceFileHandler.java +++ b/core/src/main/java/com/seibel/lod/core/file/renderfile/RenderSourceFileHandler.java @@ -18,12 +18,12 @@ import org.apache.logging.log4j.Logger; import java.io.File; import java.io.IOException; -import java.lang.ref.WeakReference; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.atomic.AtomicInteger; public class RenderSourceFileHandler implements ILodRenderSourceProvider { @@ -37,6 +37,8 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider private final IDhClientLevel level; private final File saveDir; + /** This is the lowest (highest numeric) detail level that this {@link RenderSourceFileHandler} is keeping track of. */ + AtomicInteger lowestDetailLevel = new AtomicInteger(6); private final IFullDataSourceProvider fullDataSourceProvider; private final ConcurrentHashMap cacheUpdateLockBySectionPos = new ConcurrentHashMap<>(); @@ -141,8 +143,15 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider fileToUse = metaFiles.iterator().next(); } + // Add this file to the list of files. this.filesBySectionPos.put(pos, fileToUse); + + // increase the lowest detail level if a new lower detail file is found + if (this.lowestDetailLevel.get() < pos.sectionDetailLevel) + { + this.lowestDetailLevel.set(pos.sectionDetailLevel); + } } } @@ -174,14 +183,21 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider return null; } + metaFile = this.filesBySectionPos.putIfAbsent(pos, newMetaFile); // This is a CAS with expected null value. if (metaFile == null) { metaFile = newMetaFile; } + + // increase the lowest detail level if a new lower detail file was added + if (this.lowestDetailLevel.get() < pos.sectionDetailLevel) + { + this.lowestDetailLevel.set(pos.sectionDetailLevel); + } } - return metaFile.loadOrGetCached(this.renderCacheThread, this.level).handle( + return metaFile.loadOrGetCachedDataSourceAsync(this.renderCacheThread, this.level).handle( (renderSource, exception) -> { if (exception != null) @@ -215,30 +231,43 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider @Override public void writeChunkDataToFile(DhSectionPos sectionPos, ChunkSizedFullDataAccessor chunkDataView) { - this.writeChunkDataToFileRecursively(sectionPos,chunkDataView); + // convert to the lowest detail level so all detail levels are updated + this.writeChunkDataToFileRecursively(sectionPos.convertToDetailLevel((byte) this.lowestDetailLevel.get()), chunkDataView); this.fullDataSourceProvider.write(sectionPos, chunkDataView); } - private void writeChunkDataToFileRecursively(DhSectionPos sectPos, ChunkSizedFullDataAccessor chunkDataView) + private void writeChunkDataToFileRecursively(DhSectionPos sectionPos, ChunkSizedFullDataAccessor chunkDataView) { - if (!sectPos.getSectionBBoxPos().overlapsExactly(chunkDataView.getLodPos())) + // only continue if the chunk data is in this sectionPos + if (!sectionPos.getSectionBBoxPos().overlapsExactly(chunkDataView.getLodPos())) { return; } - if (sectPos.sectionDetailLevel > ColumnRenderSource.SECTION_SIZE_OFFSET) + + if (sectionPos.sectionDetailLevel > ColumnRenderSource.SECTION_SIZE_OFFSET) { - this.writeChunkDataToFileRecursively(sectPos.getChildByIndex(0), chunkDataView); - this.writeChunkDataToFileRecursively(sectPos.getChildByIndex(1), chunkDataView); - this.writeChunkDataToFileRecursively(sectPos.getChildByIndex(2), chunkDataView); - this.writeChunkDataToFileRecursively(sectPos.getChildByIndex(3), chunkDataView); + this.writeChunkDataToFileRecursively(sectionPos.getChildByIndex(0), chunkDataView); + this.writeChunkDataToFileRecursively(sectionPos.getChildByIndex(1), chunkDataView); + this.writeChunkDataToFileRecursively(sectionPos.getChildByIndex(2), chunkDataView); + this.writeChunkDataToFileRecursively(sectionPos.getChildByIndex(3), chunkDataView); } - RenderMetaDataFile metaFile = this.filesBySectionPos.get(sectPos); - // Fast path: if there is a file for this section, just write to it. + + + RenderMetaDataFile metaFile = this.filesBySectionPos.get(sectionPos); if (metaFile != null) { - metaFile.updateChunkIfNeeded(chunkDataView, this.level); + metaFile.updateChunkIfSourceExists(chunkDataView, this.level); + } + else + { + // create a new file if necessary + this.readAsync(sectionPos).whenComplete((renderSource, ex) -> + { + RenderMetaDataFile newMetaFile = this.filesBySectionPos.get(sectionPos); + newMetaFile.updateChunkIfSourceExists(chunkDataView, this.level); + }); } } @@ -273,15 +302,9 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider } // get the full data source loading future - final WeakReference renderSourceReference = new WeakReference<>(renderSource); // TODO why is this a week reference? CompletableFuture fullDataSourceFuture = this.fullDataSourceProvider.read(renderSource.getSectionPos()); fullDataSourceFuture = fullDataSourceFuture.thenApply((fullDataSource) -> { - if (renderSourceReference.get() == null) - { - throw new UncheckedInterruptedException(); - } - // the fullDataSource can be null if the thread this was running on was interrupted return fullDataSource; }).exceptionally((ex) -> @@ -301,7 +324,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider { if (ex == null) { - this.writeRenderSourceToFile(renderSourceReference.get(), file, newRenderSource); + this.writeRenderSourceToFile(renderSource, file, newRenderSource); } else {