Fix debug render leaking memories, clean up lod render section, fix concurrency bugs with sectionToReload

This commit is contained in:
TomTheFurry
2023-06-11 18:30:55 +08:00
parent c9e2864c88
commit 9902fbb424
5 changed files with 116 additions and 37 deletions
@@ -3,6 +3,7 @@ package com.seibel.lod.core.dataObjects.render.bufferBuilding;
import com.seibel.lod.core.pos.DhBlockPos2D;
import com.seibel.lod.core.pos.DhSectionPos;
import com.seibel.lod.core.render.renderer.DebugRenderer;
import com.seibel.lod.core.render.renderer.IDebugRenderable;
import com.seibel.lod.core.render.renderer.LodRenderer;
import com.seibel.lod.core.render.AbstractRenderBuffer;
import com.seibel.lod.core.config.Config;
@@ -27,7 +28,7 @@ import static com.seibel.lod.core.render.glObject.GLProxy.GL_LOGGER;
*
* @see ColumnRenderBufferBuilder
*/
public class ColumnRenderBuffer extends AbstractRenderBuffer
public class ColumnRenderBuffer extends AbstractRenderBuffer implements IDebugRenderable
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
@@ -55,14 +56,16 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer
this.debugPos = debugPos;
vbos = new GLVertexBuffer[0];
vbosTransparent = new GLVertexBuffer[0];
DebugRenderer.register(this, (r) -> {
if (closed || this.vbos == null) {
return;
}
DebugRenderer.register(this);
}
Color c = Color.green;
r.renderBox(debugPos, -32, 32, c);
});
public void debugRender(DebugRenderer r)
{
if (closed || vbos == null) {
return;
}
Color c = Color.green;
r.renderBox(debugPos, 128, 128, 0.05f, c);
}
@@ -15,6 +15,7 @@ import org.apache.logging.log4j.Logger;
import java.util.HashSet;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* This quadTree structure is our core data structure and holds
@@ -31,9 +32,9 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
/**
* This holds every {@link DhSectionPos} that should be reloaded next tick. <br>
* This is a {@link ConcurrentHashMap} because new sections can be added to this list via the world generator threads.
* This is a {@link ConcurrentLinkedQueue} because new sections can be added to this list via the world generator threads.
*/
private final ConcurrentHashMap<DhSectionPos, Boolean> sectionsToReload = new ConcurrentHashMap<>();
private final ConcurrentLinkedQueue<DhSectionPos> sectionsToReload = new ConcurrentLinkedQueue<>();
private final IDhClientLevel level; //FIXME: Proper hierarchy to remove this reference!
@@ -76,7 +77,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
try
{
// recenter if necessary, removing out of bounds sections
this.setCenterBlockPos(playerPos, LodRenderSection::disposeRenderData);
this.setCenterBlockPos(playerPos, LodRenderSection::dispose);
updateAllRenderSections(playerPos);
}
@@ -88,12 +89,11 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
private void updateAllRenderSections(DhBlockPos2D playerPos)
{
// reload any sections that need it
DhSectionPos[] reloadSectionArray = this.sectionsToReload.keySet().toArray(new DhSectionPos[0]);
this.sectionsToReload.clear();
for (DhSectionPos pos : reloadSectionArray)
DhSectionPos pos;
while ((pos = this.sectionsToReload.poll()) != null)
{
// walk up the tree until we hit the root node
// this is done so any high detail changes flow up to the lower detail render sections as well
// this is done so any high detail changes flow up to the lower detail render sections as well
while (pos.sectionDetailLevel <= this.treeMaxDetailLevel)
{
try
@@ -106,12 +106,11 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
}
catch (IndexOutOfBoundsException e)
{ /* the section is now out of bounds, it doesn't need to be reloaded */ }
pos = pos.getParentPos();
}
}
// walk through each root node
Iterator<DhSectionPos> rootPosIterator = this.rootNodePosIterator();
while (rootPosIterator.hasNext())
@@ -348,7 +347,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
}
//LOGGER.info("LodQuadTree reloadPos ["+pos+"].");
this.sectionsToReload.put(pos, true);
this.sectionsToReload.add(pos);
}
@@ -6,8 +6,12 @@ import com.seibel.lod.core.level.IDhClientLevel;
import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.lod.core.pos.DhSectionPos;
import com.seibel.lod.core.file.renderfile.ILodRenderSourceProvider;
import com.seibel.lod.core.render.renderer.DebugRenderer;
import com.seibel.lod.core.render.renderer.IDebugRenderable;
import org.apache.logging.log4j.Logger;
import java.awt.*;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
@@ -15,7 +19,7 @@ import java.util.concurrent.atomic.AtomicReference;
* A render section represents an area that could be rendered.
* For more information see {@link LodQuadTree}.
*/
public class LodRenderSection
public class LodRenderSection implements IDebugRenderable
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
@@ -38,7 +42,30 @@ public class LodRenderSection
public LodRenderSection(DhSectionPos pos) { this.pos = pos; }
public LodRenderSection(DhSectionPos pos) {
this.pos = pos;
DebugRenderer.register(this);
}
public void debugRender(DebugRenderer r)
{
Color color = Color.red;
if (this.renderSourceProvider == null) color = Color.black;
if (this.renderSourceLoadFuture != null) color = Color.yellow;
if (renderSource != null) {
color = Color.blue;
if (isRenderingEnabled) color = Color.cyan;
if (isRenderingEnabled && isRenderDataLoaded()) color = Color.green;
}
float yOffset = Objects.hashCode(this) / (float) Integer.MAX_VALUE * 16f;
r.renderBox(this.pos, yOffset, yOffset, 0.1f, color);
}
@@ -160,6 +187,8 @@ public class LodRenderSection
this.renderSourceLoadFuture.cancel(true);
this.renderSourceLoadFuture = null;
}
this.renderSourceProvider = null;
}
@@ -208,5 +237,9 @@ public class LodRenderSection
", isRenderEnabled=" + this.isRenderingEnabled +
'}';
}
public void dispose() {
DebugRenderer.unregister(this);
disposeRenderData();
}
}
@@ -8,6 +8,7 @@ import com.seibel.lod.core.logging.ConfigBasedLogger;
import com.seibel.lod.core.logging.ConfigBasedSpamLogger;
import com.seibel.lod.core.pos.DhBlockPos2D;
import com.seibel.lod.core.pos.DhSectionPos;
import com.seibel.lod.core.render.LodRenderSection;
import com.seibel.lod.core.render.glObject.GLProxy;
import com.seibel.lod.core.render.glObject.GLState;
import com.seibel.lod.core.render.glObject.buffer.GLElementBuffer;
@@ -22,11 +23,10 @@ import org.apache.logging.log4j.LogManager;
import org.lwjgl.opengl.GL32;
import java.awt.*;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.*;
public class DebugRenderer {
public static DebugRenderer INSTANCE = new DebugRenderer();
@@ -74,12 +74,34 @@ public class DebugRenderer {
VertexAttribute va;
boolean init = false;
public static void unregister(IDebugRenderable r) {
if (INSTANCE == null) return;
INSTANCE.removeRenderer(r);
}
private void removeRenderer(IDebugRenderable r) {
synchronized (renderers) {
Iterator<WeakReference<IDebugRenderable>> it = renderers.iterator();
while (it.hasNext()) {
WeakReference<IDebugRenderable> ref = it.next();
if (ref.get() == null) {
it.remove();
continue;
}
if (ref.get() == r) {
it.remove();
return;
}
}
}
}
public void init() {
if (init) return;
init = true;
va = VertexAttribute.create();
va.bind();
// Pos
// Pos\
va.setVertexAttribute(0, 0, VertexAttribute.VertexPointer.addVec3Pointer(false));
va.completeAndCheck(Float.BYTES * 3);
basicShader = new ShaderProgram("shaders/debug/vert.vert", "shaders/debug/frag.frag",
@@ -105,21 +127,18 @@ public class DebugRenderer {
boxOutlineBuffer.uploadBuffer(buffer, EGpuUploadMethod.DATA, box_outline_indices.length * Integer.BYTES, GL32.GL_STATIC_DRAW);
}
@FunctionalInterface
public interface IDebugRender {
void render(DebugRenderer r);
}
private final LinkedList<WeakReference<IDebugRenderable>> renderers = new LinkedList<>();
private final Map<Object, IDebugRender> renderers = Collections.synchronizedMap(new WeakHashMap<>());
public void addRenderer(Object o, IDebugRender r) {
public void addRenderer(IDebugRenderable r) {
if (!Config.Client.Advanced.Debugging.debugWireframeRendering.get()) return;
renderers.put(o, r);
synchronized (renderers) {
renderers.add(new WeakReference<>(r));
}
}
public static void register(Object o, IDebugRender r) {
public static void register(IDebugRenderable r) {
if (INSTANCE == null) return;
INSTANCE.addRenderer(o, r);
INSTANCE.addRenderer(r);
}
private Mat4f transform_this_frame;
@@ -131,6 +150,13 @@ public class DebugRenderer {
renderBox(blockMin.x, minY, blockMin.z, blockMax.x, maxY, blockMax.z, color);
}
public void renderBox(DhSectionPos sectPos, float minY, float maxY, float marginPercent, Color color) {
DhBlockPos2D blockMin = sectPos.getCorner().getCornerBlockPos();
DhBlockPos2D blockMax = blockMin.add(sectPos.getWidth().toBlockWidth(), sectPos.getWidth().toBlockWidth());
float edge = sectPos.getWidth().toBlockWidth() * marginPercent;
renderBox(blockMin.x + edge, minY, blockMin.z + edge, blockMax.x - edge, maxY, blockMax.z - edge, color);
}
public void renderBox(float x, float y, float z, float x2, float y2, float z2, Color color) {
Vec3f boxPos = new Vec3f(x - camf.x, y - camf.y, z - camf.z);
Mat4f boxTransform = Mat4f.createTranslateMatrix(boxPos.x, boxPos.y, boxPos.z);
@@ -165,9 +191,22 @@ public class DebugRenderer {
basicShader.bind();
va.bind();
va.bindBufferToAllBindingPoint(boxBuffer.getId());
boxOutlineBuffer.bind();
renderers.forEach((o, r) -> r.render(this));
synchronized (renderers)
{
Iterator<WeakReference<IDebugRenderable>> it = renderers.iterator();
while (it.hasNext()) {
WeakReference<IDebugRenderable> ref = it.next();
IDebugRenderable r = ref.get();
if (r == null) {
it.remove();
continue;
}
r.debugRender(this);
}
}
state.restore();
}
@@ -0,0 +1,5 @@
package com.seibel.lod.core.render.renderer;
public interface IDebugRenderable {
void debugRender(DebugRenderer r);
}