Fix holes when moving around the world
This commit is contained in:
@@ -9,7 +9,6 @@ import com.seibel.lod.core.dataObjects.fullData.sources.ChunkSizedFullDataSource
|
||||
import com.seibel.lod.core.dataObjects.transformers.FullToColumnTransformer;
|
||||
import com.seibel.lod.core.level.IDhClientLevel;
|
||||
import com.seibel.lod.core.pos.DhSectionPos;
|
||||
import com.seibel.lod.core.render.AbstractRenderBuffer;
|
||||
import com.seibel.lod.core.enums.ELodDirection;
|
||||
import com.seibel.lod.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.lod.core.level.IDhLevel;
|
||||
@@ -337,6 +336,7 @@ public class ColumnRenderSource
|
||||
// Render Methods //
|
||||
//================//
|
||||
|
||||
// TODO return future?
|
||||
private void tryBuildBuffer(IDhClientLevel level, ColumnRenderSource renderSource)
|
||||
{
|
||||
if (this.buildRenderBufferFuture == null && !ColumnRenderBuffer.isBusy() && !this.isEmpty)
|
||||
@@ -378,10 +378,10 @@ public class ColumnRenderSource
|
||||
/**
|
||||
* Try and swap in new render buffer for this section. Note that before this call, there should be no other
|
||||
* places storing or referencing the render buffer.
|
||||
* @param renderBufferToSwap The slot for swapping in the new buffer.
|
||||
* @param renderBufferRefToSwap The slot for swapping in the new buffer.
|
||||
* @return True if the swap was successful. False if swap is not needed or if it is in progress.
|
||||
*/
|
||||
public boolean trySwapRenderBuffer(ColumnRenderSource renderSource, AtomicReference<AbstractRenderBuffer> renderBufferToSwap)
|
||||
public boolean trySwapInNewlyBuiltRenderBuffer(ColumnRenderSource renderSource, AtomicReference<ColumnRenderBuffer> renderBufferRefToSwap)
|
||||
{
|
||||
// prevent swapping the buffer to quickly
|
||||
if (this.lastNs != -1 && System.nanoTime() - this.lastNs < SWAP_TIMEOUT_IN_NS)
|
||||
@@ -397,18 +397,13 @@ public class ColumnRenderSource
|
||||
this.lastNs = System.nanoTime();
|
||||
//LOGGER.info("Swapping render buffer for {}", sectionPos);
|
||||
|
||||
AbstractRenderBuffer newBuffer = this.buildRenderBufferFuture.join();
|
||||
AbstractRenderBuffer oldBuffer = renderBufferToSwap.getAndSet(newBuffer);
|
||||
if (oldBuffer instanceof ColumnRenderBuffer)
|
||||
{
|
||||
ColumnRenderBuffer swapped = this.columnRenderBufferRef.swap((ColumnRenderBuffer) oldBuffer);
|
||||
LodUtil.assertTrue(swapped == null);
|
||||
}
|
||||
else if (oldBuffer != null)
|
||||
{
|
||||
throw new UnsupportedOperationException("swap buffer fail, Expected "+AbstractRenderBuffer.class.getSimpleName()+" of type: "+ColumnRenderBuffer.class.getSimpleName()+" class given: "+oldBuffer.getClass().getSimpleName());
|
||||
}
|
||||
ColumnRenderBuffer newBuffer = this.buildRenderBufferFuture.join();
|
||||
ColumnRenderBuffer oldBuffer = renderBufferRefToSwap.getAndSet(newBuffer);
|
||||
|
||||
ColumnRenderBuffer swapped = this.columnRenderBufferRef.swap(oldBuffer);
|
||||
LodUtil.assertTrue(swapped == null);
|
||||
|
||||
|
||||
this.buildRenderBufferFuture = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
+59
-44
@@ -36,20 +36,26 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer
|
||||
//TODO: Make the pool change thread count after the config value is changed
|
||||
public static final ExecutorService BUFFER_BUILDER_THREADS = ThreadUtil.makeThreadPool(Config.Client.Advanced.Threading.numberOfBufferBuilderThreads.get(), "BufferBuilder");
|
||||
public static final ExecutorService BUFFER_UPLOADER = ThreadUtil.makeSingleThreadPool("ColumnBufferUploader");
|
||||
public static final int MAX_CONCURRENT_CALL = 8;
|
||||
// TODO this should probably be based on the number of builder threads
|
||||
public static final int MAX_CONCURRENT_CALL = 8;
|
||||
|
||||
public static final ConfigBasedLogger EVENT_LOGGER = new ConfigBasedLogger(LogManager.getLogger(),
|
||||
() -> Config.Client.Advanced.Debugging.DebugSwitch.logRendererBufferEvent.get());
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
|
||||
private static final long MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS = 1_000_000;
|
||||
GLVertexBuffer[] vbos;
|
||||
GLVertexBuffer[] vbosTransparent;
|
||||
public final DhBlockPos pos;
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
private static final long MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS = 1_000_000;
|
||||
|
||||
|
||||
public final DhBlockPos pos;
|
||||
|
||||
private GLVertexBuffer[] vbos;
|
||||
private GLVertexBuffer[] vbosTransparent;
|
||||
private boolean buffersUploaded = false;
|
||||
private boolean closed = false;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public ColumnRenderBuffer(DhBlockPos pos)
|
||||
{
|
||||
this.pos = pos;
|
||||
@@ -63,8 +69,9 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer
|
||||
// vbo building //
|
||||
//==============//
|
||||
|
||||
// TODO this is static, it should be moved to its own class to prevent confusion
|
||||
/** @return null if busy */
|
||||
public static CompletableFuture<ColumnRenderBuffer> buildBuffers(IDhClientLevel clientLevel, Reference<ColumnRenderBuffer> usedBufferSlot, ColumnRenderSource renderSource, ColumnRenderSource[] adjData)
|
||||
public static CompletableFuture<ColumnRenderBuffer> buildBuffers(IDhClientLevel clientLevel, Reference<ColumnRenderBuffer> renderBufferRef, ColumnRenderSource renderSource, ColumnRenderSource[] adjData)
|
||||
{
|
||||
if (isBusy())
|
||||
{
|
||||
@@ -104,56 +111,59 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer
|
||||
|
||||
}, BUFFER_BUILDER_THREADS)
|
||||
.thenApplyAsync((quadBuilder) ->
|
||||
{
|
||||
try
|
||||
{
|
||||
EVENT_LOGGER.trace("RenderRegion start Upload @ "+renderSource.sectionPos);
|
||||
GLProxy glProxy = GLProxy.getInstance();
|
||||
EGpuUploadMethod method = GLProxy.getInstance().getGpuUploadMethod();
|
||||
EGLProxyContext oldContext = glProxy.getGlContext();
|
||||
glProxy.setGlContext(EGLProxyContext.LOD_BUILDER);
|
||||
ColumnRenderBuffer buffer = renderBufferRef.swap(null);
|
||||
|
||||
if (buffer == null)
|
||||
{
|
||||
buffer = new ColumnRenderBuffer(new DhBlockPos(renderSource.sectionPos.getCorner().getCornerBlockPos(), clientLevel.getMinY()));
|
||||
}
|
||||
buffer.buffersUploaded = false;
|
||||
|
||||
try
|
||||
{
|
||||
EVENT_LOGGER.trace("RenderRegion start Upload @ {}", renderSource.sectionPos);
|
||||
GLProxy glProxy = GLProxy.getInstance();
|
||||
EGpuUploadMethod method = GLProxy.getInstance().getGpuUploadMethod();
|
||||
EGLProxyContext oldContext = glProxy.getGlContext();
|
||||
glProxy.setGlContext(EGLProxyContext.LOD_BUILDER);
|
||||
ColumnRenderBuffer buffer = usedBufferSlot.swap(null);
|
||||
|
||||
if (buffer == null)
|
||||
{
|
||||
buffer = new ColumnRenderBuffer(new DhBlockPos(renderSource.sectionPos.getCorner().getCornerBlockPos(), clientLevel.getMinY()));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
buffer.uploadBuffer(quadBuilder, method);
|
||||
EVENT_LOGGER.trace("RenderRegion end Upload @ {}", renderSource.sectionPos);
|
||||
return buffer;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
buffer.close();
|
||||
throw e;
|
||||
}
|
||||
finally
|
||||
{
|
||||
glProxy.setGlContext(oldContext);
|
||||
}
|
||||
buffer.uploadBuffer(quadBuilder, method);
|
||||
EVENT_LOGGER.trace("RenderRegion end Upload @ "+renderSource.sectionPos);
|
||||
return buffer;
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
catch (Exception e)
|
||||
{
|
||||
throw UncheckedInterruptedException.convert(e);
|
||||
buffer.close();
|
||||
throw e;
|
||||
}
|
||||
catch (Throwable e3)
|
||||
finally
|
||||
{
|
||||
LOGGER.error("\"LodNodeBufferBuilder\" was unable to upload buffer: ", e3);
|
||||
throw e3;
|
||||
glProxy.setGlContext(oldContext);
|
||||
}
|
||||
},
|
||||
BUFFER_UPLOADER).handle((columnRenderBuffer, ex) ->
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
throw UncheckedInterruptedException.convert(e);
|
||||
}
|
||||
catch (Throwable e3)
|
||||
{
|
||||
LOGGER.error("\"LodNodeBufferBuilder\" was unable to upload buffer: ", e3);
|
||||
throw e3;
|
||||
}
|
||||
},
|
||||
BUFFER_UPLOADER).handle((columnRenderBuffer, ex) ->
|
||||
{
|
||||
//LOGGER.info("RenderRegion endBuild @ {}", renderSource.sectionPos);
|
||||
if (ex != null)
|
||||
{
|
||||
LOGGER.warn("Buffer building failed: "+ex.getMessage(), ex);
|
||||
|
||||
ColumnRenderBuffer buffer;
|
||||
if (!usedBufferSlot.isEmpty())
|
||||
if (!renderBufferRef.isEmpty())
|
||||
{
|
||||
buffer = usedBufferSlot.swap(null);
|
||||
buffer = renderBufferRef.swap(null);
|
||||
buffer.close();
|
||||
}
|
||||
|
||||
@@ -321,6 +331,8 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer
|
||||
{
|
||||
this.uploadBuffersDirect(builder, method);
|
||||
}
|
||||
|
||||
this.buffersUploaded = true;
|
||||
}
|
||||
|
||||
private void uploadBuffersMapped(LodQuadBuilder builder, EGpuUploadMethod method)
|
||||
@@ -552,6 +564,7 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer
|
||||
return;
|
||||
}
|
||||
this.closed = true;
|
||||
this.buffersUploaded = false;
|
||||
|
||||
GLProxy.getInstance().recordOpenGlCall(() ->
|
||||
{
|
||||
@@ -578,13 +591,15 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer
|
||||
// getters //
|
||||
//=========//
|
||||
|
||||
public boolean areBuffersUploaded() { return this.buffersUploaded; }
|
||||
|
||||
// TODO move static methods to their own class to avoid confusion
|
||||
private static long getCurrentJobsCount()
|
||||
{
|
||||
long jobs = ((ThreadPoolExecutor) BUFFER_BUILDER_THREADS).getQueue().stream().filter(runnable -> !((Future<?>) runnable).isDone()).count();
|
||||
jobs += ((ThreadPoolExecutor) BUFFER_UPLOADER).getQueue().stream().filter(runnable -> !((Future<?>) runnable).isDone()).count();
|
||||
return jobs;
|
||||
}
|
||||
|
||||
public static boolean isBusy() { return getCurrentJobsCount() > MAX_CONCURRENT_CALL; }
|
||||
|
||||
}
|
||||
|
||||
@@ -199,10 +199,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
|
||||
if (nullableRenderSection != null)
|
||||
{
|
||||
// TODO this logic isn't ready
|
||||
// it can cause buffers to render on top of each other and doesn't completely solve the empty section rendering bug.
|
||||
// Some sort of logic to determine if the buffer has been uploaded is probably necessary
|
||||
// if (areChildRenderSectionsLoaded(nullableRenderSection))
|
||||
if (areChildRenderSectionsLoaded(nullableRenderSection))
|
||||
{
|
||||
nullableRenderSection.disableAndDisposeRendering();
|
||||
}
|
||||
@@ -250,21 +247,31 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
|
||||
// enable the render section
|
||||
nullableRenderSection.loadRenderSourceAndEnableRendering(this.renderSourceProvider);
|
||||
|
||||
// determine if the section has loaded yet // TODO rename "tick" to check loading future or something?
|
||||
nullableRenderSection.tick(this.level);
|
||||
|
||||
|
||||
// delete/disable children
|
||||
nullableQuadNode.deleteAllChildren((renderSection) ->
|
||||
if (isSectionLoaded(nullableRenderSection))
|
||||
{
|
||||
if (renderSection != null)
|
||||
nullableQuadNode.deleteAllChildren((renderSection) ->
|
||||
{
|
||||
renderSection.disableAndDisposeRendering();
|
||||
}
|
||||
});
|
||||
if (renderSection != null)
|
||||
{
|
||||
renderSection.disableAndDisposeRendering();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to determine if a section can unload or not.
|
||||
* If this returns true, that means there are child render sections ready to render,
|
||||
* so there won't be any holes in the world by disabling the parent.
|
||||
* <br><Br>
|
||||
* FIXME sometimes sections will render on top of each other
|
||||
*/
|
||||
private boolean areChildRenderSectionsLoaded(LodRenderSection renderSection)
|
||||
{
|
||||
if (renderSection == null)
|
||||
@@ -279,31 +286,40 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
}
|
||||
else
|
||||
{
|
||||
// recursively look for a loaded child
|
||||
// recursively check if all children are loaded
|
||||
|
||||
try
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
LodRenderSection child0 = this.getChildSection(renderSection.pos, 0);
|
||||
LodRenderSection child1 = this.getChildSection(renderSection.pos, 1);
|
||||
LodRenderSection child2 = this.getChildSection(renderSection.pos, 2);
|
||||
LodRenderSection child3 = this.getChildSection(renderSection.pos, 3);
|
||||
|
||||
// either the child section is loaded, or check the next section down
|
||||
return (isSectionLoaded(child0) || areChildRenderSectionsLoaded(child0))
|
||||
&& (isSectionLoaded(child1) || areChildRenderSectionsLoaded(child1))
|
||||
&& (isSectionLoaded(child2) || areChildRenderSectionsLoaded(child2))
|
||||
&& (isSectionLoaded(child3) || areChildRenderSectionsLoaded(child3));
|
||||
}
|
||||
catch (IndexOutOfBoundsException e)
|
||||
{
|
||||
// FIXME will happen if children are outside the render distance
|
||||
return true;
|
||||
DhSectionPos childPos = renderSection.pos.getChildByIndex(i);
|
||||
// if a section is out of bounds, act like it is loaded
|
||||
if (this.isSectionPosInBounds(childPos))
|
||||
{
|
||||
LodRenderSection child = this.getChildSection(renderSection.pos, i);
|
||||
// check if either this child or all of its children are loaded
|
||||
boolean childLoaded = isSectionLoaded(child) || areChildRenderSectionsLoaded(child);
|
||||
if (!childLoaded)
|
||||
{
|
||||
// at least one child isn't loaded
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// all children are loaded
|
||||
return true;
|
||||
}
|
||||
}
|
||||
private static boolean isSectionLoaded(LodRenderSection renderSection)
|
||||
{
|
||||
return renderSection != null && renderSection.isLoaded() && renderSection.isRenderingEnabled() && !renderSection.getRenderSource().isEmpty();
|
||||
return renderSection != null
|
||||
&& renderSection.isLoaded()
|
||||
&& renderSection.isRenderingEnabled()
|
||||
|
||||
&& renderSection.renderBufferRef.get() != null
|
||||
&& renderSection.renderBufferRef.get().areBuffersUploaded()
|
||||
|
||||
&& renderSection.getRenderSource() != null
|
||||
&& !renderSection.getRenderSource().isEmpty();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.seibel.lod.core.render;
|
||||
|
||||
import com.seibel.lod.core.dataObjects.render.ColumnRenderSource;
|
||||
import com.seibel.lod.core.dataObjects.render.bufferBuilding.ColumnRenderBuffer;
|
||||
import com.seibel.lod.core.level.IDhClientLevel;
|
||||
import com.seibel.lod.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.lod.core.pos.DhSectionPos;
|
||||
@@ -22,7 +23,7 @@ public class LodRenderSection
|
||||
private ColumnRenderSource renderSource;
|
||||
private ILodRenderSourceProvider renderSourceProvider = null;
|
||||
|
||||
public final AtomicReference<AbstractRenderBuffer> abstractRenderBufferRef = new AtomicReference<>();
|
||||
public final AtomicReference<ColumnRenderBuffer> renderBufferRef = new AtomicReference<>();
|
||||
|
||||
|
||||
|
||||
@@ -128,10 +129,10 @@ public class LodRenderSection
|
||||
this.renderSource = null;
|
||||
}
|
||||
|
||||
if (this.abstractRenderBufferRef.get() != null)
|
||||
if (this.renderBufferRef.get() != null)
|
||||
{
|
||||
this.abstractRenderBufferRef.get().close();
|
||||
this.abstractRenderBufferRef.set(null);
|
||||
this.renderBufferRef.get().close();
|
||||
this.renderBufferRef.set(null);
|
||||
}
|
||||
|
||||
if (this.loadFuture != null)
|
||||
|
||||
@@ -139,10 +139,9 @@ public class RenderBufferHandler
|
||||
{
|
||||
if (renderSection != null && renderSection.shouldRender())
|
||||
{
|
||||
// this should always be true
|
||||
if (renderSection.abstractRenderBufferRef.get() != null)
|
||||
if (renderSection.renderBufferRef.get() != null && renderSection.renderBufferRef.get().areBuffersUploaded())
|
||||
{
|
||||
this.loadedNearToFarBuffers.add(new LoadedRenderBuffer(renderSection.abstractRenderBufferRef.get(), sectionPos));
|
||||
this.loadedNearToFarBuffers.add(new LoadedRenderBuffer(renderSection.renderBufferRef.get(), sectionPos));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -171,7 +170,7 @@ public class RenderBufferHandler
|
||||
if (!renderSection.shouldRender())
|
||||
{
|
||||
//TODO: Does this really need to force the old buffer to not be rendered?
|
||||
AbstractRenderBuffer previousRenderBuffer = renderSection.abstractRenderBufferRef.getAndSet(null);
|
||||
AbstractRenderBuffer previousRenderBuffer = renderSection.renderBufferRef.getAndSet(null);
|
||||
if (previousRenderBuffer != null)
|
||||
{
|
||||
previousRenderBuffer.close();
|
||||
@@ -180,7 +179,7 @@ public class RenderBufferHandler
|
||||
else
|
||||
{
|
||||
LodUtil.assertTrue(currentRenderSource != null); // section.shouldRender() should have ensured this
|
||||
currentRenderSource.trySwapRenderBuffer(renderSection.getRenderSource(), renderSection.abstractRenderBufferRef);
|
||||
currentRenderSource.trySwapInNewlyBuiltRenderBuffer(renderSection.getRenderSource(), renderSection.renderBufferRef);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -190,10 +189,10 @@ public class RenderBufferHandler
|
||||
{
|
||||
this.quadTree.forEachValue((renderSection) ->
|
||||
{
|
||||
if (renderSection != null && renderSection.abstractRenderBufferRef.get() != null)
|
||||
if (renderSection != null && renderSection.renderBufferRef.get() != null)
|
||||
{
|
||||
renderSection.abstractRenderBufferRef.get().close();
|
||||
renderSection.abstractRenderBufferRef.set(null);
|
||||
renderSection.renderBufferRef.get().close();
|
||||
renderSection.renderBufferRef.set(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user