diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/QuadTree/LodQuadTree.java b/core/src/main/java/com/seibel/distanthorizons/core/render/QuadTree/LodQuadTree.java index e6e5eecd8..db02c5424 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/QuadTree/LodQuadTree.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/QuadTree/LodQuadTree.java @@ -629,10 +629,10 @@ public class LodQuadTree extends QuadTree implements IDebugRen continue; } - // the section only needs to be updated if a buffer is currently present LodRenderSection renderSection = this.tryGetValue(pos); if (renderSection != null) { + // the section only needs to be updated if a buffer is currently present if (renderSection.gpuUploadComplete()) { if (renderSection.gpuUploadInProgress() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/QuadTree/LodRenderSection.java b/core/src/main/java/com/seibel/distanthorizons/core/render/QuadTree/LodRenderSection.java index 8c98dc9ed..b24d8b2d6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/QuadTree/LodRenderSection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/QuadTree/LodRenderSection.java @@ -50,9 +50,9 @@ import org.jetbrains.annotations.Nullable; import javax.annotation.WillNotClose; import java.awt.*; import java.util.*; -import java.util.List; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.ReentrantLock; /** * A render section represents an area that could be rendered. @@ -82,6 +82,11 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable public final BeaconRenderHandler beaconRenderHandler; @Nullable public final BeaconBeamRepo beaconBeamRepo; + /** + * locking is necessary to prevent some weird threading issues + * causing beacons to appear/disappear at the wrong times. + */ + private final ReentrantLock beaconRenderHandlingLock = new ReentrantLock(); private boolean renderingEnabled = false; @@ -386,76 +391,115 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable /** gets the active beacon list and stops/starts beacon rendering as necessary */ private void refreshActiveBeaconList() { - // do nothing if beacon rendering or repos are unavailable - if (this.beaconBeamRepo == null - || this.beaconRenderHandler == null) + try { - return; - } - - - // Synchronized to prevent two threads for accessing the array at once - synchronized (this.activeBeaconList) - { - List activeBeacons = this.beaconBeamRepo.getAllBeamsForPos(this.pos); + this.beaconRenderHandlingLock.lock(); - // swap old and new active beacon list - this.activeBeaconList.clear(); - this.activeBeaconList.addAll(activeBeacons); + // do nothing if beacon rendering or repos are unavailable + if (this.beaconBeamRepo == null + || this.beaconRenderHandler == null) + { + return; + } + + + + // Synchronized to prevent two threads for accessing the array at once + synchronized (this.activeBeaconList) + { + ArrayList activeBeacons = this.beaconBeamRepo.getAllBeamsForPos(this.pos); + + // swap old and new active beacon list + this.activeBeaconList.clear(); + this.activeBeaconList.addAll(activeBeacons); + + // if the beacons are currently rendering, + // re-create them so we can see any potential changes + if (this.beaconsRendering) + { + this.tryDisableBeacons(); + this.tryEnableBeacons(); + } + } + } + finally + { + this.beaconRenderHandlingLock.unlock(); } } public void tryDisableBeacons() { - // do nothing if beacon rendering is unavailable - if (this.beaconRenderHandler == null) + try { - return; + this.beaconRenderHandlingLock.lock(); + + + // do nothing if beacon rendering is unavailable + if (this.beaconRenderHandler == null) + { + return; + } + + if (!this.beaconsRendering) + { + return; + } + this.beaconsRendering = false; + + + + if (Config.Client.Advanced.Debugging.DebugWireframe.showRenderSectionStatus.get()) + { + // show that this position has just been disabled + DebugRenderer.makeParticle( + new DebugRenderer.BoxParticle( + new DebugRenderer.Box(this.pos, 128f, 156f, 0.09f, Color.CYAN.darker()), + 0.2, 32f + ) + ); + } + + synchronized (this.activeBeaconList) + { + this.beaconRenderHandler.stopRenderingBeaconsInRange(this.pos); + } } - - if (!this.beaconsRendering) + finally { - return; - } - this.beaconsRendering = false; - - - if (Config.Client.Advanced.Debugging.DebugWireframe.showRenderSectionStatus.get()) - { - // show that this position has just been disabled - DebugRenderer.makeParticle( - new DebugRenderer.BoxParticle( - new DebugRenderer.Box(this.pos, 128f, 156f, 0.09f, Color.CYAN.darker()), - 0.2, 32f - ) - ); - } - - synchronized (this.activeBeaconList) - { - this.beaconRenderHandler.stopRenderingBeacons(this.activeBeaconList); + this.beaconRenderHandlingLock.unlock(); } } public void tryEnableBeacons() { - // do nothing if beacon rendering is unavailable - if (this.beaconRenderHandler == null) + try { - return; + this.beaconRenderHandlingLock.lock(); + + + // do nothing if beacon rendering is unavailable + if (this.beaconRenderHandler == null) + { + return; + } + + if (this.beaconsRendering) + { + return; + } + this.beaconsRendering = true; + + + synchronized (this.activeBeaconList) + { + byte absoluteDetailLevel = (byte)(DhSectionPos.getDetailLevel(this.pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); + this.beaconRenderHandler.startRenderingBeacons(this.activeBeaconList, absoluteDetailLevel); + } } - - if (this.beaconsRendering) + finally { - return; - } - this.beaconsRendering = true; - - - synchronized (this.activeBeaconList) - { - byte absoluteDetailLevel = (byte)(DhSectionPos.getDetailLevel(this.pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); - this.beaconRenderHandler.startRenderingBeacons(this.activeBeaconList, absoluteDetailLevel); + this.beaconRenderHandlingLock.unlock(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java index e5935fa2b..61c325a9a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java @@ -60,9 +60,12 @@ public class BeaconRenderHandler private final ReentrantLock updateLock = new ReentrantLock(); - private final IDhApiRenderableBoxGroup beaconBoxGroup; + /** only contains the beacons currently being rendered (culled beacons will be missing) */ + private final IDhApiRenderableBoxGroup activeBeaconBoxRenderGroup; + /** contains all beacons that could be rendered (including those that are being culled) */ private final ArrayList fullBeaconBoxList = new ArrayList<>(); - private final HashSet beaconBlockPosSet = new HashSet<>(); + /** contains all beacons that could be rendered */ + private final HashSet fullBeaconBlockPosSet = new HashSet<>(); private boolean cullingThreadRunning = false; private boolean updateRenderDataNextFrame = false; @@ -72,24 +75,28 @@ public class BeaconRenderHandler //=============// // constructor // //=============// + //region public BeaconRenderHandler(@NotNull GenericObjectRenderer renderer) { - this.beaconBoxGroup = GenericRenderObjectFactory.INSTANCE.createAbsolutePositionedGroup(ModInfo.NAME+":Beacons", new ArrayList<>(0)); - this.beaconBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT); - this.beaconBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT); - this.beaconBoxGroup.setSsaoEnabled(false); - this.beaconBoxGroup.setShading(DhApiRenderableBoxGroupShading.getUnshaded()); - this.beaconBoxGroup.setPreRenderFunc(this::beforeRender); + this.activeBeaconBoxRenderGroup = GenericRenderObjectFactory.INSTANCE.createAbsolutePositionedGroup(ModInfo.NAME+":Beacons", new ArrayList<>(0)); + this.activeBeaconBoxRenderGroup.setBlockLight(LodUtil.MAX_MC_LIGHT); + this.activeBeaconBoxRenderGroup.setSkyLight(LodUtil.MAX_MC_LIGHT); + this.activeBeaconBoxRenderGroup.setSsaoEnabled(false); + this.activeBeaconBoxRenderGroup.setShading(DhApiRenderableBoxGroupShading.getUnshaded()); + this.activeBeaconBoxRenderGroup.setPreRenderFunc(this::beforeRender); - renderer.add(this.beaconBoxGroup); + renderer.add(this.activeBeaconBoxRenderGroup); } + //endregion + //=================// // render handling // //=================// + //region public void startRenderingBeacons(ArrayList beaconList, byte detailLevel) { @@ -143,12 +150,15 @@ public class BeaconRenderHandler } + //LOGGER.info("startRenderingBeacons ["+sortedBeaconList+"]"); + // add each beacon to the renderer for (int i = 0; i < sortedBeaconList.size(); i++) { BeaconBeamDTO beacon = sortedBeaconList.get(i); - if (!this.beaconBlockPosSet.add(beacon.blockPos)) + if (!this.fullBeaconBlockPosSet.add(beacon.blockPos)) { + // skip already present beacons continue; } @@ -161,9 +171,9 @@ public class BeaconRenderHandler EDhApiBlockMaterial.ILLUMINATED ); - this.beaconBoxGroup.add(beaconBox); + this.activeBeaconBoxRenderGroup.add(beaconBox); this.fullBeaconBoxList.add(beaconBox); - this.beaconBoxGroup.triggerBoxChange(); + this.activeBeaconBoxRenderGroup.triggerBoxChange(); } } finally @@ -172,58 +182,28 @@ public class BeaconRenderHandler } } - public void stopRenderingBeacons(ArrayList beaconList) + public void stopRenderingBeaconsInRange(long pos) { try { this.updateLock.lock(); - for (int i = 0; i < beaconList.size(); i++) + Predicate removeBoxPredicate = (DhApiRenderableBox box) -> { - BeaconBeamDTO beacon = beaconList.get(i); - DhBlockPos beaconPos = beacon.blockPos; - if (!this.beaconBlockPosSet.remove(beaconPos)) - { - continue; - } - - Predicate removePredicate = (DhApiRenderableBox box) -> - { - return box.minPos.x == beaconPos.getX() - && box.minPos.y == beaconPos.getY() + 1 // plus 1 because the beam starts above the beacon - && box.minPos.z == beaconPos.getZ(); - }; - this.beaconBoxGroup.removeIf(removePredicate); - this.fullBeaconBoxList.removeIf(removePredicate); - - this.beaconBoxGroup.triggerBoxChange(); - } - } - finally - { - this.updateLock.unlock(); - } - } - - public void updateBeaconColor(BeaconBeamDTO newBeam) - { - try - { - this.updateLock.lock(); + DhBlockPos blockPos = new DhBlockPos((int)box.minPos.x, (int)box.minPos.y, (int)box.minPos.z); + boolean contains = DhSectionPos.contains(pos, blockPos); + //if (contains) + //{ + // LOGGER.info("stopRenderingBeaconsInRange ["+DhSectionPos.toString(pos)+"] ["+blockPos+"]"); + //} + return contains; + }; + this.activeBeaconBoxRenderGroup.removeIf(removeBoxPredicate); + this.fullBeaconBoxList.removeIf(removeBoxPredicate); - DhBlockPos pos = newBeam.blockPos; - for (int i = 0; i < this.fullBeaconBoxList.size(); i++) - { - DhApiRenderableBox box = this.fullBeaconBoxList.get(i); - if (box.minPos.x == pos.getX() - && box.minPos.y == pos.getY() + 1 // plus 1 because the beam starts above the beacon - && box.minPos.z == pos.getZ()) - { - box.color = newBeam.color; - this.beaconBoxGroup.triggerBoxChange(); - break; - } - } + this.fullBeaconBlockPosSet.removeIf((DhBlockPos blockPos) -> DhSectionPos.contains(pos, blockPos)); + + this.activeBeaconBoxRenderGroup.triggerBoxChange(); } finally { @@ -245,10 +225,10 @@ public class BeaconRenderHandler // this must be called on the render thread to prevent concurrency issues if (this.updateRenderDataNextFrame) { - this.beaconBoxGroup.triggerBoxChange(); + this.activeBeaconBoxRenderGroup.triggerBoxChange(); this.updateRenderDataNextFrame = false; } - this.beaconBoxGroup.setActive(Config.Client.Advanced.Graphics.GenericRendering.enableBeaconRendering.get()); + this.activeBeaconBoxRenderGroup.setActive(Config.Client.Advanced.Graphics.GenericRendering.enableBeaconRendering.get()); } /** does nothing if the culling thread is already running */ private void tryUpdateBeaconCullingAsync() @@ -284,7 +264,7 @@ public class BeaconRenderHandler // Clear the existing box group so we can re-populate it. // Since the box group is only used when we trigger an update, clearing it here // and repopulating it is fine. - this.beaconBoxGroup.clear(); + this.activeBeaconBoxRenderGroup.clear(); // While iterating over every beacon isn't a great way of doing this, // when 940 beacons were tested this only took ~0.9 Milliseconds, so as long as @@ -295,7 +275,7 @@ public class BeaconRenderHandler double distance = Vec3d.getHorizontalDistance(cameraPos, box.minPos); if (distance > dhFadeDistance) { - this.beaconBoxGroup.add(box); + this.activeBeaconBoxRenderGroup.add(box); } } @@ -317,11 +297,14 @@ public class BeaconRenderHandler } } + //endregion + //================// // helper classes // //================// + //region private static class NegativeInfiniteBlockPosComparator implements Comparator { @@ -340,6 +323,8 @@ public class BeaconRenderHandler } } + //endregion + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/BeaconBeamRepo.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/BeaconBeamRepo.java index e6b0e586e..31f98514c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/BeaconBeamRepo.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/BeaconBeamRepo.java @@ -181,7 +181,7 @@ public class BeaconBeamRepo extends AbstractDhRepo ); } - public List getAllBeamsForPos(long pos) + public ArrayList getAllBeamsForPos(long pos) { int minBlockX = DhSectionPos.getMinCornerBlockX(pos); int minBlockZ = DhSectionPos.getMinCornerBlockZ(pos); @@ -200,7 +200,7 @@ public class BeaconBeamRepo extends AbstractDhRepo "WHERE " + "? <= BlockPosX AND BlockPosX <= ? AND " + "? <= BlockPosZ AND BlockPosZ <= ?"; - public List getAllBeamsInBlockPosRange( + public ArrayList getAllBeamsInBlockPosRange( int minBlockX, int maxBlockX, int minBlockZ, int maxBlockZ )