Fix beacons not reloading (and improve logic)

This commit is contained in:
James Seibel
2026-02-14 21:32:30 -06:00
parent 66a1d0296e
commit df52f41b87
4 changed files with 146 additions and 117 deletions
@@ -629,10 +629,10 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
continue; continue;
} }
// the section only needs to be updated if a buffer is currently present
LodRenderSection renderSection = this.tryGetValue(pos); LodRenderSection renderSection = this.tryGetValue(pos);
if (renderSection != null) if (renderSection != null)
{ {
// the section only needs to be updated if a buffer is currently present
if (renderSection.gpuUploadComplete()) if (renderSection.gpuUploadComplete())
{ {
if (renderSection.gpuUploadInProgress() if (renderSection.gpuUploadInProgress()
@@ -50,9 +50,9 @@ import org.jetbrains.annotations.Nullable;
import javax.annotation.WillNotClose; import javax.annotation.WillNotClose;
import java.awt.*; import java.awt.*;
import java.util.*; import java.util.*;
import java.util.List;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
/** /**
* A render section represents an area that could be rendered. * A render section represents an area that could be rendered.
@@ -82,6 +82,11 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
public final BeaconRenderHandler beaconRenderHandler; public final BeaconRenderHandler beaconRenderHandler;
@Nullable @Nullable
public final BeaconBeamRepo beaconBeamRepo; 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; 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 */ /** gets the active beacon list and stops/starts beacon rendering as necessary */
private void refreshActiveBeaconList() private void refreshActiveBeaconList()
{ {
// do nothing if beacon rendering or repos are unavailable try
if (this.beaconBeamRepo == null
|| this.beaconRenderHandler == null)
{ {
return; this.beaconRenderHandlingLock.lock();
}
// Synchronized to prevent two threads for accessing the array at once
synchronized (this.activeBeaconList)
{
List<BeaconBeamDTO> activeBeacons = this.beaconBeamRepo.getAllBeamsForPos(this.pos);
// swap old and new active beacon list // do nothing if beacon rendering or repos are unavailable
this.activeBeaconList.clear(); if (this.beaconBeamRepo == null
this.activeBeaconList.addAll(activeBeacons); || this.beaconRenderHandler == null)
{
return;
}
// Synchronized to prevent two threads for accessing the array at once
synchronized (this.activeBeaconList)
{
ArrayList<BeaconBeamDTO> 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() public void tryDisableBeacons()
{ {
// do nothing if beacon rendering is unavailable try
if (this.beaconRenderHandler == null)
{ {
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);
}
} }
finally
if (!this.beaconsRendering)
{ {
return; this.beaconRenderHandlingLock.unlock();
}
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);
} }
} }
public void tryEnableBeacons() public void tryEnableBeacons()
{ {
// do nothing if beacon rendering is unavailable try
if (this.beaconRenderHandler == null)
{ {
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);
}
} }
finally
if (this.beaconsRendering)
{ {
return; this.beaconRenderHandlingLock.unlock();
}
this.beaconsRendering = true;
synchronized (this.activeBeaconList)
{
byte absoluteDetailLevel = (byte)(DhSectionPos.getDetailLevel(this.pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
this.beaconRenderHandler.startRenderingBeacons(this.activeBeaconList, absoluteDetailLevel);
} }
} }
@@ -60,9 +60,12 @@ public class BeaconRenderHandler
private final ReentrantLock updateLock = new ReentrantLock(); 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<DhApiRenderableBox> fullBeaconBoxList = new ArrayList<>(); private final ArrayList<DhApiRenderableBox> fullBeaconBoxList = new ArrayList<>();
private final HashSet<DhBlockPos> beaconBlockPosSet = new HashSet<>(); /** contains all beacons that could be rendered */
private final HashSet<DhBlockPos> fullBeaconBlockPosSet = new HashSet<>();
private boolean cullingThreadRunning = false; private boolean cullingThreadRunning = false;
private boolean updateRenderDataNextFrame = false; private boolean updateRenderDataNextFrame = false;
@@ -72,24 +75,28 @@ public class BeaconRenderHandler
//=============// //=============//
// constructor // // constructor //
//=============// //=============//
//region
public BeaconRenderHandler(@NotNull GenericObjectRenderer renderer) public BeaconRenderHandler(@NotNull GenericObjectRenderer renderer)
{ {
this.beaconBoxGroup = GenericRenderObjectFactory.INSTANCE.createAbsolutePositionedGroup(ModInfo.NAME+":Beacons", new ArrayList<>(0)); this.activeBeaconBoxRenderGroup = GenericRenderObjectFactory.INSTANCE.createAbsolutePositionedGroup(ModInfo.NAME+":Beacons", new ArrayList<>(0));
this.beaconBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT); this.activeBeaconBoxRenderGroup.setBlockLight(LodUtil.MAX_MC_LIGHT);
this.beaconBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT); this.activeBeaconBoxRenderGroup.setSkyLight(LodUtil.MAX_MC_LIGHT);
this.beaconBoxGroup.setSsaoEnabled(false); this.activeBeaconBoxRenderGroup.setSsaoEnabled(false);
this.beaconBoxGroup.setShading(DhApiRenderableBoxGroupShading.getUnshaded()); this.activeBeaconBoxRenderGroup.setShading(DhApiRenderableBoxGroupShading.getUnshaded());
this.beaconBoxGroup.setPreRenderFunc(this::beforeRender); this.activeBeaconBoxRenderGroup.setPreRenderFunc(this::beforeRender);
renderer.add(this.beaconBoxGroup); renderer.add(this.activeBeaconBoxRenderGroup);
} }
//endregion
//=================// //=================//
// render handling // // render handling //
//=================// //=================//
//region
public void startRenderingBeacons(ArrayList<BeaconBeamDTO> beaconList, byte detailLevel) public void startRenderingBeacons(ArrayList<BeaconBeamDTO> beaconList, byte detailLevel)
{ {
@@ -143,12 +150,15 @@ public class BeaconRenderHandler
} }
//LOGGER.info("startRenderingBeacons ["+sortedBeaconList+"]");
// add each beacon to the renderer // add each beacon to the renderer
for (int i = 0; i < sortedBeaconList.size(); i++) for (int i = 0; i < sortedBeaconList.size(); i++)
{ {
BeaconBeamDTO beacon = sortedBeaconList.get(i); BeaconBeamDTO beacon = sortedBeaconList.get(i);
if (!this.beaconBlockPosSet.add(beacon.blockPos)) if (!this.fullBeaconBlockPosSet.add(beacon.blockPos))
{ {
// skip already present beacons
continue; continue;
} }
@@ -161,9 +171,9 @@ public class BeaconRenderHandler
EDhApiBlockMaterial.ILLUMINATED EDhApiBlockMaterial.ILLUMINATED
); );
this.beaconBoxGroup.add(beaconBox); this.activeBeaconBoxRenderGroup.add(beaconBox);
this.fullBeaconBoxList.add(beaconBox); this.fullBeaconBoxList.add(beaconBox);
this.beaconBoxGroup.triggerBoxChange(); this.activeBeaconBoxRenderGroup.triggerBoxChange();
} }
} }
finally finally
@@ -172,58 +182,28 @@ public class BeaconRenderHandler
} }
} }
public void stopRenderingBeacons(ArrayList<BeaconBeamDTO> beaconList) public void stopRenderingBeaconsInRange(long pos)
{ {
try try
{ {
this.updateLock.lock(); this.updateLock.lock();
for (int i = 0; i < beaconList.size(); i++) Predicate<DhApiRenderableBox> removeBoxPredicate = (DhApiRenderableBox box) ->
{ {
BeaconBeamDTO beacon = beaconList.get(i); DhBlockPos blockPos = new DhBlockPos((int)box.minPos.x, (int)box.minPos.y, (int)box.minPos.z);
DhBlockPos beaconPos = beacon.blockPos; boolean contains = DhSectionPos.contains(pos, blockPos);
if (!this.beaconBlockPosSet.remove(beaconPos)) //if (contains)
{ //{
continue; // LOGGER.info("stopRenderingBeaconsInRange ["+DhSectionPos.toString(pos)+"] ["+blockPos+"]");
} //}
return contains;
Predicate<DhApiRenderableBox> removePredicate = (DhApiRenderableBox box) -> };
{ this.activeBeaconBoxRenderGroup.removeIf(removeBoxPredicate);
return box.minPos.x == beaconPos.getX() this.fullBeaconBoxList.removeIf(removeBoxPredicate);
&& 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 pos = newBeam.blockPos; this.fullBeaconBlockPosSet.removeIf((DhBlockPos blockPos) -> DhSectionPos.contains(pos, blockPos));
for (int i = 0; i < this.fullBeaconBoxList.size(); i++)
{ this.activeBeaconBoxRenderGroup.triggerBoxChange();
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;
}
}
} }
finally finally
{ {
@@ -245,10 +225,10 @@ public class BeaconRenderHandler
// this must be called on the render thread to prevent concurrency issues // this must be called on the render thread to prevent concurrency issues
if (this.updateRenderDataNextFrame) if (this.updateRenderDataNextFrame)
{ {
this.beaconBoxGroup.triggerBoxChange(); this.activeBeaconBoxRenderGroup.triggerBoxChange();
this.updateRenderDataNextFrame = false; 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 */ /** does nothing if the culling thread is already running */
private void tryUpdateBeaconCullingAsync() private void tryUpdateBeaconCullingAsync()
@@ -284,7 +264,7 @@ public class BeaconRenderHandler
// Clear the existing box group so we can re-populate it. // 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 // Since the box group is only used when we trigger an update, clearing it here
// and repopulating it is fine. // and repopulating it is fine.
this.beaconBoxGroup.clear(); this.activeBeaconBoxRenderGroup.clear();
// While iterating over every beacon isn't a great way of doing this, // 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 // 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); double distance = Vec3d.getHorizontalDistance(cameraPos, box.minPos);
if (distance > dhFadeDistance) if (distance > dhFadeDistance)
{ {
this.beaconBoxGroup.add(box); this.activeBeaconBoxRenderGroup.add(box);
} }
} }
@@ -317,11 +297,14 @@ public class BeaconRenderHandler
} }
} }
//endregion
//================// //================//
// helper classes // // helper classes //
//================// //================//
//region
private static class NegativeInfiniteBlockPosComparator implements Comparator<BeaconBeamDTO> private static class NegativeInfiniteBlockPosComparator implements Comparator<BeaconBeamDTO>
{ {
@@ -340,6 +323,8 @@ public class BeaconRenderHandler
} }
} }
//endregion
} }
@@ -181,7 +181,7 @@ public class BeaconBeamRepo extends AbstractDhRepo<DhBlockPos, BeaconBeamDTO>
); );
} }
public List<BeaconBeamDTO> getAllBeamsForPos(long pos) public ArrayList<BeaconBeamDTO> getAllBeamsForPos(long pos)
{ {
int minBlockX = DhSectionPos.getMinCornerBlockX(pos); int minBlockX = DhSectionPos.getMinCornerBlockX(pos);
int minBlockZ = DhSectionPos.getMinCornerBlockZ(pos); int minBlockZ = DhSectionPos.getMinCornerBlockZ(pos);
@@ -200,7 +200,7 @@ public class BeaconBeamRepo extends AbstractDhRepo<DhBlockPos, BeaconBeamDTO>
"WHERE " + "WHERE " +
"? <= BlockPosX AND BlockPosX <= ? AND " + "? <= BlockPosX AND BlockPosX <= ? AND " +
"? <= BlockPosZ AND BlockPosZ <= ?"; "? <= BlockPosZ AND BlockPosZ <= ?";
public List<BeaconBeamDTO> getAllBeamsInBlockPosRange( public ArrayList<BeaconBeamDTO> getAllBeamsInBlockPosRange(
int minBlockX, int maxBlockX, int minBlockX, int maxBlockX,
int minBlockZ, int maxBlockZ int minBlockZ, int maxBlockZ
) )