diff --git a/src/main/java/com/seibel/lod/core/builders/bufferBuilding/LodBufferBuilderFactory.java b/src/main/java/com/seibel/lod/core/builders/bufferBuilding/LodBufferBuilderFactory.java index f1136b3bb..a47ef6437 100644 --- a/src/main/java/com/seibel/lod/core/builders/bufferBuilding/LodBufferBuilderFactory.java +++ b/src/main/java/com/seibel/lod/core/builders/bufferBuilding/LodBufferBuilderFactory.java @@ -121,6 +121,14 @@ public class LodBufferBuilderFactory { } + public void setRegionNeedRegen(int regionX, int regionZ) { + MovableGridRingList r = renderRegions; + if (r==null) return; + RenderRegion rr = r.get(regionX, regionZ); + if (rr==null) return; + rr.setNeedRegen(); + } + /** * Create a thread to asynchronously generate LOD buffers centered around the * given camera X and Z.
@@ -158,6 +166,9 @@ public class LodBufferBuilderFactory { LevelPosUtil.getRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerX), LevelPosUtil.getRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerZ)); ApiShared.LOGGER.info("============Render Regions rebuilt============"); + } else { + renderRegions.move(LevelPosUtil.getRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerX), + LevelPosUtil.getRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerZ), RenderRegion::close); } } diff --git a/src/main/java/com/seibel/lod/core/builders/lodBuilding/LodBuilder.java b/src/main/java/com/seibel/lod/core/builders/lodBuilding/LodBuilder.java index e660d71ea..a50a547a6 100644 --- a/src/main/java/com/seibel/lod/core/builders/lodBuilding/LodBuilder.java +++ b/src/main/java/com/seibel/lod/core/builders/lodBuilding/LodBuilder.java @@ -223,7 +223,6 @@ public class LodBuilder // chunk.getChunkPosX(), chunk.getChunkPosZ(), chunk.getMinX(), chunk.getMinZ(), config.distanceGenerationMode); region.addChunkOfData((byte)0, chunkX*16, chunkZ*16, 16, 16, data, data.length/16/16, override); region.regenerateLodFromArea((byte)0, chunkX*16, chunkZ*16, 16, 16); - lodDim.regenDimensionBuffers = true; if (!region.doesDataExist((byte)0, chunkX*16, chunkZ*16, config.distanceGenerationMode)) throw new RuntimeException("data at detail 0 is still null after writes to it!"); @@ -285,7 +284,6 @@ public class LodBuilder LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, chunkX, targetLevel), LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, chunkZ, targetLevel), lodCount, lodCount); - lodDim.regenDimensionBuffers = true; if (!region.doesDataExist(targetLevel, LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, chunkX, targetLevel), diff --git a/src/main/java/com/seibel/lod/core/objects/lod/LodDimension.java b/src/main/java/com/seibel/lod/core/objects/lod/LodDimension.java index aef36b536..605fd722d 100644 --- a/src/main/java/com/seibel/lod/core/objects/lod/LodDimension.java +++ b/src/main/java/com/seibel/lod/core/objects/lod/LodDimension.java @@ -34,7 +34,6 @@ import com.seibel.lod.core.enums.config.GenerationPriority; import com.seibel.lod.core.enums.config.VerticalQuality; import com.seibel.lod.core.handlers.LodDimensionFileHandler; import com.seibel.lod.core.objects.PosToGenerateContainer; -import com.seibel.lod.core.objects.PosToRenderContainer; import com.seibel.lod.core.util.DataPointUtil; import com.seibel.lod.core.util.DetailDistanceUtil; import com.seibel.lod.core.util.LevelPosUtil; @@ -84,15 +83,6 @@ public class LodDimension //NOTE: This list pos is relative to center private volatile RegionPos[] iteratorList = null; - /** stores if the region at the given x and z index needs to be saved to disk */ - /** stores if the region at the given x and z index needs to be regenerated */ - // Use int because I need Tri state: - - /** - * if true that means there are regions in this dimension - * that need to have their buffers rebuilt. - */ - public volatile boolean regenDimensionBuffers = false; private LodDimensionFileHandler fileHandler; @@ -305,10 +295,14 @@ public class LodDimension if (region.getMinDetailLevel() < detail) { if (region.needSaving) return; // FIXME: A crude attempt at lowering chance of race condition! region.cutTree(detail); - region.needRegenBuffer = 2; - regenDimensionBuffers = true; + region.needSignalToRegenBuffer = true; } } + if (region != null && region.needSignalToRegenBuffer) { + region.needSignalToRegenBuffer = false; + ClientApi.lodBufferBuilderFactory.setRegionNeedRegen(x+minPos.x, z+minPos.y); + } + }); if (totalDirtiedRegions > 8) this.saveDirtyRegionsToFile(false); dirtiedRegionsRoughCount = totalDirtiedRegions; @@ -410,9 +404,12 @@ public class LodDimension updated = true; } if (updated) { - region.needRegenBuffer = 2; + region.needSignalToRegenBuffer = true; region.needRecheckGenPoint = true; - regenDimensionBuffers = true; + } + if (region != null && region.needSignalToRegenBuffer) { + region.needSignalToRegenBuffer = false; + ClientApi.lodBufferBuilderFactory.setRegionNeedRegen(x+minPos.x, z+minPos.y); } }); //ApiShared.LOGGER.info("LodDim expend Region complete: " + playerPosX + "," + playerPosZ); @@ -438,22 +435,9 @@ public class LodDimension return false; boolean nodeAdded = region.addVerticalData(detailLevel, posX, posZ, data, override); - if (nodeAdded) { - regenDimensionBuffers = true; - } return nodeAdded; } - /** marks the region at the given region position to have its buffer rebuilt */ - public void markRegionBufferToRegen(int xRegion, int zRegion) - { - LodRegion r = getRegion(xRegion,zRegion); - if (r!=null) { - r.needRegenBuffer = 2; - regenDimensionBuffers = true; - } - } - /** * Returns every position that need to be generated based on the position of the player */ @@ -568,23 +552,6 @@ public class LodDimension return region.getSingleData(detailLevel, posX, posZ); } - /** - * Returns if the buffer at the given array index needs - * to have its buffer regenerated. Also decrease the state by 1 - */ - public boolean getAndClearRegionNeedBufferRegen(int regionX, int regionZ) - { - //FIXME: Use actual atomics on needRegenBuffer - LodRegion region = getRegion(regionX, regionZ); - if (region == null) return false; - int i = region.needRegenBuffer; - if (i > 0) { - region.needRegenBuffer--; - return true; - } - return false; - } - /** * Get the data point at the given LevelPos * in this dimension. @@ -602,8 +569,6 @@ public class LodDimension LodRegion region = getRegion(xRegion, zRegion); if (region == null) return; region.updateArea(detailLevel, posX, posZ); - region.needRegenBuffer = 2; - regenDimensionBuffers = true; } /** Returns true if a region exists at the given LevelPos */ diff --git a/src/main/java/com/seibel/lod/core/objects/lod/LodRegion.java b/src/main/java/com/seibel/lod/core/objects/lod/LodRegion.java index cce4c68f6..97e2453ba 100644 --- a/src/main/java/com/seibel/lod/core/objects/lod/LodRegion.java +++ b/src/main/java/com/seibel/lod/core/objects/lod/LodRegion.java @@ -73,7 +73,8 @@ public class LodRegion { public final int regionPosZ; public volatile boolean needRecheckGenPoint = true; - public volatile int needRegenBuffer = 2; + //public volatile int needRegenBuffer = 2; MOVED TO RENDERREGION! + public volatile boolean needSignalToRegenBuffer = true; public volatile boolean needSaving = false; public final AtomicInteger isWriting = new AtomicInteger(0); @@ -138,7 +139,7 @@ public class LodRegion { boolean updated = this.dataContainer[detailLevel].addVerticalData(data, posX, posZ, override); if (updated) { - needRegenBuffer = 2; + needSignalToRegenBuffer = true; needSaving = true; } return updated; @@ -166,7 +167,7 @@ public class LodRegion { //ApiShared.LOGGER.info("addChunkOfData(region:{}, level:{}, x:{}, z:{}, wx:{}, wz:{}, override:{}, updated:{})", // getRegionPos(), detailLevel, posX, posZ, widthX, widthZ, override, updated); if (updated) { - needRegenBuffer = 2; + needSignalToRegenBuffer = true; needSaving = true; } else { /*ApiShared.LOGGER.info("addChunkOfData nothing changed. Datapoint: {}\n Upper Datapoint: {}", @@ -453,6 +454,7 @@ public class LodRegion { for (byte up = (byte) (Math.max(detailLevel, minDetailLevel) + 1); up <= LodUtil.REGION_DETAIL_LEVEL; up++) { update(up, LevelPosUtil.convert(detailLevel, posX, up), LevelPosUtil.convert(detailLevel, posZ, up)); } + needSignalToRegenBuffer = true; } public boolean regenerateLodFromArea(byte detailLevel, int posX, int posZ, int widthX, int widthZ) { @@ -482,8 +484,8 @@ public class LodRegion { // ApiShared.LOGGER.info(" - Shink: (level:{}, x:{}, z:{}, wx:{}, wz:{})", detailLevel, modPosX, modPosZ, widthX, widthZ); chunkUpdate(detailLevel, modPosX, modPosZ, widthX, widthZ); } while (detailLevel < LodUtil.REGION_DETAIL_LEVEL); - - needRegenBuffer = 2; + + needSignalToRegenBuffer = true; return true; } diff --git a/src/main/java/com/seibel/lod/core/objects/opengl/RenderBuffer.java b/src/main/java/com/seibel/lod/core/objects/opengl/RenderBuffer.java index d9d791403..c74faa4df 100644 --- a/src/main/java/com/seibel/lod/core/objects/opengl/RenderBuffer.java +++ b/src/main/java/com/seibel/lod/core/objects/opengl/RenderBuffer.java @@ -37,22 +37,29 @@ public abstract class RenderBuffer implements AutoCloseable final public void build(Runnable r) { _lockThread(State.Building); - r.run(); - _unlockThread(State.Building); + try { + r.run(); + } finally { + _unlockThread(State.Building); + } } /* Return false if current renderMethod is not suited for current builder * This will auto close the object if returning false. */ final public boolean tryUploadBuffers(LodQuadBuilder builder, GpuUploadMethod uploadMethod) { _lockThread(State.Uploading); - boolean successful = uploadBuffers(builder, uploadMethod); - if (!successful) { - _unlockThreadTo(State.Uploading, State.Closed); - close(); - return false; + boolean successful = false; + try { + successful = uploadBuffers(builder, uploadMethod); + return successful; + } finally { + if (!successful) { + _unlockThreadTo(State.Uploading, State.Closed); + close(); + } else { + _unlockThread(State.Uploading); + } } - _unlockThread(State.Uploading); - return true; } // ====================================================================== diff --git a/src/main/java/com/seibel/lod/core/objects/opengl/RenderRegion.java b/src/main/java/com/seibel/lod/core/objects/opengl/RenderRegion.java index abee9df48..4db6569ee 100644 --- a/src/main/java/com/seibel/lod/core/objects/opengl/RenderRegion.java +++ b/src/main/java/com/seibel/lod/core/objects/opengl/RenderRegion.java @@ -4,6 +4,7 @@ import java.util.ConcurrentModificationException; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import com.seibel.lod.core.api.ApiShared; @@ -42,6 +43,10 @@ public class RenderRegion implements AutoCloseable public static final boolean ENABLE_VERBOSE_LOGGING = false; private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); + /** stores if the region at the given x and z index needs to be regenerated */ + // Use int because I need Tri state: + private AtomicInteger needRegen = new AtomicInteger(2); + private enum BackState { Unused, Building, @@ -71,19 +76,25 @@ public class RenderRegion implements AutoCloseable return lodDim == this.lodDim && regPos.equals(regionPos); } + public void setNeedRegen() { + needRegen.set(2); + } + public Optional> updateStatus(Executor bufferUploader, Executor bufferBuilder, boolean alwaysRegen, int playerPosX, int playerPosZ) { + if (alwaysRegen) setNeedRegen(); + BackState state = backState.get(); if (state != BackState.Unused) { if (ENABLE_VERBOSE_LOGGING) ApiShared.LOGGER.info("{}: UpdateStatus rejected. Cause: BackState is {}", regionPos, state); return Optional.empty(); } - + LodRegion r = lodDim.getRegion(regionPos.x, regionPos.z); if (r==null) { if (ENABLE_VERBOSE_LOGGING) ApiShared.LOGGER.info("{}: UpdateStatus rejected. Cause: Region is null", regionPos); return Optional.empty(); } - if (!alwaysRegen && r.needRegenBuffer == 0) { + if (needRegen.get() == 0) { if (ENABLE_VERBOSE_LOGGING) ApiShared.LOGGER.info("{}: UpdateStatus rejected. Cause: Region doesn't need regen", regionPos); return Optional.empty(); } @@ -92,7 +103,7 @@ public class RenderRegion implements AutoCloseable if (ENABLE_VERBOSE_LOGGING) ApiShared.LOGGER.info("{}: UpdateStatus rejected. Cause: CAS on BackState failed: ", backState.get()); return Optional.empty(); } - r.needRegenBuffer--; + needRegen.decrementAndGet(); return Optional.of(startBuid(bufferUploader, bufferBuilder, r, lodDim, playerPosX, playerPosZ)); } @@ -150,7 +161,7 @@ public class RenderRegion implements AutoCloseable adjRegions[dir.ordinal() - 2] = lodDim.getRegion(regionPos.x+dir.getNormal().x, regionPos.z+dir.getNormal().z); } } catch (Throwable t) { - region.needRegenBuffer = 2; + setNeedRegen(); if (!backState.compareAndSet(BackState.Building, BackState.Unused)) { ApiShared.LOGGER.error("\"Lod Builder Starter\"" + " encountered error on catching exceptions and fallback on starting build task: ", @@ -175,7 +186,9 @@ public class RenderRegion implements AutoCloseable ApiShared.LOGGER.error("\"LodNodeBufferBuilder\" was unable to build quads: ", e3); throw e3; } - }, bufferBuilder).thenAcceptAsync((builder) -> { + }, bufferBuilder) + + .thenAcceptAsync((builder) -> { try { if (ENABLE_EVENT_STEP_LOGGING) ApiShared.LOGGER.info("RenderRegion start Upload @ {}", regionPos); GLProxy glProxy = GLProxy.getInstance(); @@ -203,7 +216,7 @@ public class RenderRegion implements AutoCloseable ApiShared.LOGGER.error("\"LodNodeBufferBuilder\" was unable to upload buffer: ", e3); } }, bufferUploader).exceptionallyCompose((e) -> { - region.needRegenBuffer = 2; + setNeedRegen(); if (!backState.compareAndSet(BackState.Building, BackState.Unused)) { ApiShared.LOGGER.error("\"LodNodeBufferBuilder\"" + " encountered error on exit: ", @@ -305,66 +318,71 @@ public class RenderRegion implements AutoCloseable // not // to always have a wall underwater for (LodDirection lodDirection : LodDirection.ADJ_DIRECTIONS) { - int xAdj = posX + lodDirection.getNormal().x; - int zAdj = posZ + lodDirection.getNormal().z; - byte adjDetail = detailLevel; - int chunkXAdj = LevelPosUtil.getChunkPos(detailLevel, xAdj); - int chunkZAdj = LevelPosUtil.getChunkPos(detailLevel, zAdj); - Boolean isRenderedAdj = chunkGrid.get(chunkXAdj, chunkZAdj); - boolean adjSkip = isRenderedAdj!=null && isRenderedAdj; - - //We check if the adjPos is to be rendered - boolean renderAdjPos = posToRender.contains(detailLevel, xAdj, zAdj); - boolean doesAdjLowerPosExist = detailLevel==0 ? false : posToRender.contains((byte) (detailLevel-1), xAdj*2, zAdj*2); - boolean renderLowerAdjPos = doesAdjLowerPosExist; - LodRegion adjRegion = region; - - //since he system doesn't work for region border we need to check with another system - if(!renderAdjPos && (!doesAdjLowerPosExist || detailLevel==0)) - { - //we compute the distance from the adjPos - double minDistance = LevelPosUtil.minDistance(detailLevel, xAdj, zAdj, playerX, playerZ) - 1.4142*(2 << detailLevel); - //we compute at which detail that position should be rendered - adjRegion = adjRegions[lodDirection.ordinal()-2]; - byte minLevel; - if(adjRegion != null) - { - minLevel = (byte) Math.max(adjRegion.getMinDetailLevel(), - DetailDistanceUtil.getDetailLevelFromDistance(minDistance)); - } else{ - minLevel = DetailDistanceUtil.getDetailLevelFromDistance(minDistance); - } + try { + int xAdj = posX + lodDirection.getNormal().x; + int zAdj = posZ + lodDirection.getNormal().z; + byte adjDetail = detailLevel; + int chunkXAdj = LevelPosUtil.getChunkPos(detailLevel, xAdj); + int chunkZAdj = LevelPosUtil.getChunkPos(detailLevel, zAdj); + Boolean isRenderedAdj = chunkGrid.get(chunkXAdj, chunkZAdj); + boolean adjSkip = isRenderedAdj!=null && isRenderedAdj; - //we check if the detail of the adjPos is equal to the correct one (region border fix) - //or if the detail is wrong by 1 value (region+circle border fix) - renderAdjPos = detailLevel == minLevel; - renderLowerAdjPos = detailLevel==0 ? false : detailLevel-1 == minLevel; - } - if (adjRegion == null) continue; - if (renderAdjPos && !adjSkip) { - //The adj data is at same detail and is extracted - adjData[lodDirection.ordinal() - 2][0] = adjRegion.getAllData(adjDetail, xAdj, zAdj); - } else if (renderLowerAdjPos) - { - //The adj data is at lower detail and is extracted in two steps - xAdj *= 2; - zAdj *= 2; - adjDetail = (byte) (detailLevel - 1); - adjData[lodDirection.ordinal() - 2] = new long[2][]; - isRenderedAdj = chunkGrid.get(chunkXAdj, chunkZAdj); - adjSkip = isRenderedAdj!=null && isRenderedAdj; - if (!adjSkip) { + //We check if the adjPos is to be rendered + boolean renderAdjPos = posToRender.contains(detailLevel, xAdj, zAdj); + boolean doesAdjLowerPosExist = detailLevel==0 ? false : posToRender.contains((byte) (detailLevel-1), xAdj*2, zAdj*2); + boolean renderLowerAdjPos = doesAdjLowerPosExist; + LodRegion adjRegion = region; + + //since he system doesn't work for region border we need to check with another system + if(!renderAdjPos && (!doesAdjLowerPosExist || detailLevel==0)) + { + //we compute the distance from the adjPos + double minDistance = LevelPosUtil.minDistance(detailLevel, xAdj, zAdj, playerX, playerZ) - 1.4142*(2 << detailLevel); + //we compute at which detail that position should be rendered + adjRegion = adjRegions[lodDirection.ordinal()-2]; + byte minLevel; + if(adjRegion != null) + { + minLevel = (byte) Math.max(adjRegion.getMinDetailLevel(), + DetailDistanceUtil.getDetailLevelFromDistance(minDistance)); + } else{ + minLevel = DetailDistanceUtil.getDetailLevelFromDistance(minDistance); + } + + //we check if the detail of the adjPos is equal to the correct one (region border fix) + //or if the detail is wrong by 1 value (region+circle border fix) + renderAdjPos = detailLevel == minLevel; + renderLowerAdjPos = detailLevel==0 ? false : detailLevel-1 == minLevel; + } + if (adjRegion == null) continue; + if (renderAdjPos && !adjSkip) { + //The adj data is at same detail and is extracted adjData[lodDirection.ordinal() - 2][0] = adjRegion.getAllData(adjDetail, xAdj, zAdj); - } - - xAdj += Math.abs(lodDirection.getNormal().x); - zAdj += Math.abs(lodDirection.getNormal().z); - isRenderedAdj = chunkGrid.get(chunkXAdj, chunkZAdj); - adjSkip = isRenderedAdj!=null && isRenderedAdj; - if (!adjSkip) + } else if (renderLowerAdjPos) { - adjData[lodDirection.ordinal() - 2][1] = adjRegion.getAllData(adjDetail, xAdj, zAdj); + //The adj data is at lower detail and is extracted in two steps + xAdj *= 2; + zAdj *= 2; + adjDetail = (byte) (detailLevel - 1); + adjData[lodDirection.ordinal() - 2] = new long[2][]; + isRenderedAdj = chunkGrid.get(chunkXAdj, chunkZAdj); + adjSkip = isRenderedAdj!=null && isRenderedAdj; + if (!adjSkip) { + adjData[lodDirection.ordinal() - 2][0] = adjRegion.getAllData(adjDetail, xAdj, zAdj); + } + + xAdj += Math.abs(lodDirection.getNormal().x); + zAdj += Math.abs(lodDirection.getNormal().z); + isRenderedAdj = chunkGrid.get(chunkXAdj, chunkZAdj); + adjSkip = isRenderedAdj!=null && isRenderedAdj; + if (!adjSkip) + { + adjData[lodDirection.ordinal() - 2][1] = adjRegion.getAllData(adjDetail, xAdj, zAdj); + } } + } catch (RuntimeException e) { + ApiShared.LOGGER.warn("Failed to get adj data for [{}:{},{}] at [{}]", detailLevel, posX, posZ, lodDirection); + ApiShared.LOGGER.warn("Detail exception: ", e); } } diff --git a/src/main/java/com/seibel/lod/core/render/LodRenderer.java b/src/main/java/com/seibel/lod/core/render/LodRenderer.java index 96ff23384..663bba29a 100644 --- a/src/main/java/com/seibel/lod/core/render/LodRenderer.java +++ b/src/main/java/com/seibel/lod/core/render/LodRenderer.java @@ -28,7 +28,6 @@ import org.lwjgl.opengl.GL32; import com.seibel.lod.core.api.ApiShared; import com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory; -import com.seibel.lod.core.builders.lodBuilding.LodBuilder; import com.seibel.lod.core.enums.rendering.DebugMode; import com.seibel.lod.core.enums.rendering.FogColorMode; import com.seibel.lod.core.enums.rendering.FogDistance; @@ -517,7 +516,7 @@ public class LodRenderer if (oldBool == null || !oldBool) { anyChanged = true; - lodDim.markRegionBufferToRegen(pos.getRegionX(), pos.getRegionZ()); + lodBufferBuilderFactory.setRegionNeedRegen(pos.getRegionX(), pos.getRegionZ()); } } if (anyChanged) vanillaRenderedChunks = chunkList; @@ -580,13 +579,8 @@ public class LodRenderer if (tryFullGen) { fullRegen = true; - lodDim.regenDimensionBuffers = false; } else if (tryPartialGen) { - if (lodDim.regenDimensionBuffers) - { - partialRegen = true; - lodDim.regenDimensionBuffers = false; - } + partialRegen = true; } }