diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java index 3f4fe08a2..b73573c7c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java @@ -55,26 +55,6 @@ public class ColumnRenderBufferBuilder // vbo building // //==============// - /** @link adjData should be null for adjacent sections that cross detail level boundaries */ - public static CompletableFuture uploadBuffersAsync( - IDhClientLevel clientLevel, - long pos, - LodQuadBuilder quadBuilder - ) - { - DhBlockPos minBlockPos = new DhBlockPos(DhSectionPos.getMinCornerBlockX(pos), clientLevel.getLevelWrapper().getMinHeight(), DhSectionPos.getMinCornerBlockZ(pos)); - LodBufferContainer bufferContainer = new LodBufferContainer(pos, minBlockPos); - CompletableFuture uploadFuture = bufferContainer.tryMakeAndUploadBuffersAsync(quadBuilder); - uploadFuture.whenComplete((uploadedBuffer, exception) -> - { - // clean up if not uploaded - if (uploadedBuffer != null && !uploadedBuffer.buffersUploaded) - { - uploadedBuffer.close(); - } - }); - return uploadFuture; - } public static void makeLodRenderData( LodQuadBuilder quadBuilder, ColumnRenderSource renderSource, IDhClientLevel clientLevel, ColumnRenderSource[] adjRegions, boolean[] isSameDetailLevel) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodBufferContainer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodBufferContainer.java index f18137349..4519b12d5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodBufferContainer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodBufferContainer.java @@ -20,24 +20,24 @@ package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.level.IDhClientLevel; import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler; import com.seibel.distanthorizons.core.util.ExceptionUtil; -import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.render.AbstractDhRenderApiDefinition; import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.ILodContainerUniformBufferWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import org.jetbrains.annotations.Nullable; import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicReference; /** * Java representation of one or more OpenGL buffers for rendering. @@ -63,8 +63,6 @@ public class LodBufferContainer implements AutoCloseable public ILodContainerUniformBufferWrapper uniformContainer = WRAPPER_FACTORY.createLodContainerUniformWrapper(); - private final AtomicReference> uploadFutureRef = new AtomicReference<>(null); - //==============// @@ -72,7 +70,7 @@ public class LodBufferContainer implements AutoCloseable //==============// //region - public LodBufferContainer(long pos, DhBlockPos minCornerBlockPos) + private LodBufferContainer(long pos, DhBlockPos minCornerBlockPos) { this.pos = pos; this.minCornerBlockPos = minCornerBlockPos; @@ -92,41 +90,12 @@ public class LodBufferContainer implements AutoCloseable //region /** Should be run on a DH thread. */ - public synchronized CompletableFuture tryMakeAndUploadBuffersAsync(LodQuadBuilder builder) + public static CompletableFuture tryMakeAndUploadBuffersAsync( + long pos, IDhClientLevel clientLevel, + LodQuadBuilder builder) { - //================// - // handle futures // - //================// - //region - - // separate variable to prevent race condition when checking null - CompletableFuture oldFuture = this.uploadFutureRef.get(); - if (oldFuture != null) - { - // upload already in process - return oldFuture; - } - // new upload needed CompletableFuture future = new CompletableFuture<>(); - future.handle((lodBufferContainer, throwable) -> - { - if (!this.uploadFutureRef.compareAndSet(future, null)) - { - LOGGER.warn("upload future ref changed for pos ["+DhSectionPos.toString(this.pos)+"]."); - } - - return null; - }); - - if (!this.uploadFutureRef.compareAndSet(null, future)) - { - oldFuture = this.uploadFutureRef.get(); - LodUtil.assertTrue(oldFuture != null, "Concurrency error"); - return oldFuture; - } - - //endregion @@ -135,91 +104,122 @@ public class LodBufferContainer implements AutoCloseable //================// //region + DhBlockPos minCornerBlockPos = new DhBlockPos( + DhSectionPos.getMinCornerBlockX(pos), + clientLevel.getLevelWrapper().getMinHeight(), + DhSectionPos.getMinCornerBlockZ(pos)); + LodBufferContainer bufferContainer = new LodBufferContainer(pos, minCornerBlockPos); + + // create CPU vertex buffers ArrayList opaqueBuffers = builder.makeOpaqueVertexBuffers(); ArrayList transparentBuffers = builder.makeTransparentVertexBuffers(); - this.vboOpaqueWrappers = resizeWrapperArray(this.vboOpaqueWrappers, opaqueBuffers.size()); - this.vboTransparentWrappers = resizeWrapperArray(this.vboTransparentWrappers, transparentBuffers.size()); + // update arrays to contain buffers + bufferContainer.vboOpaqueWrappers = resizeWrapperArray(bufferContainer.vboOpaqueWrappers, opaqueBuffers.size()); + bufferContainer.vboTransparentWrappers = resizeWrapperArray(bufferContainer.vboTransparentWrappers, transparentBuffers.size()); - // mac requires separate IBO objects for each VBO when using OpenGL, + // create CPU index buffers if needed. + // Mac requires separate IBO objects for each VBO when using OpenGL, // all other OS's can share a single IBO for quicker loading times boolean useSingleIbo = RENDER_DEF.useSingleIbo(); - @Nullable ArrayList opaqueIndexBuffers = useSingleIbo ? null : this.createIndexBuffers(opaqueBuffers); - @Nullable ArrayList transparentIndexBuffers = useSingleIbo ? null : this.createIndexBuffers(transparentBuffers); + @Nullable ArrayList opaqueIndexBuffers = useSingleIbo ? null : bufferContainer.createIndexBuffers(opaqueBuffers); + @Nullable ArrayList transparentIndexBuffers = useSingleIbo ? null : bufferContainer.createIndexBuffers(transparentBuffers); //endregion - //================// - // upload buffers // - //================// - //region + //=============// + // create VBOs // + //=============// + //region - try + StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); + + CompletableFuture createFuture = new CompletableFuture(); + RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("LodBufferContainer Setup", () -> { - //=============// - // create VBOs // - //=============// - - CompletableFuture createOpaqueFuture = createBufferWrappersAsync(future, this.vboOpaqueWrappers, opaqueBuffers); - CompletableFuture createTransparentFuture = createBufferWrappersAsync(future, this.vboTransparentWrappers, transparentBuffers); - - CompletableFuture createFuture = CompletableFuture.allOf(createOpaqueFuture, createTransparentFuture); - createFuture.exceptionally((Throwable e) -> + try { - // create VBOs failed // + // skip this event if requested + if (Thread.interrupted() + || future.isCancelled()) + { + throw new InterruptedException(); + } + + + createBufferWrappers(bufferContainer.vboOpaqueWrappers, opaqueBuffers, stackTraceElements); + createBufferWrappers(bufferContainer.vboTransparentWrappers, transparentBuffers, stackTraceElements); + createFuture.complete(null); + } + catch (Exception e) + { if (!ExceptionUtil.isShutdownException(e)) { - LOGGER.error("Unexpected issue creating buffer [" + this.minCornerBlockPos + "], error: [" + e.getMessage() + "].", e); + LOGGER.error("Unexpected issue creating buffers for pos: ["+DhSectionPos.toString(bufferContainer.pos)+"], error: ["+e.getMessage()+"].", e); } + + bufferContainer.close(); + createFuture.completeExceptionally(e); + } + }); + + //endregion + + + + //====================// + // upload VBOs to GPU // + //====================// + //region + + createFuture.exceptionally((Throwable e) -> + { + // create VBOs failed // + if (!ExceptionUtil.isShutdownException(e)) + { + LOGGER.error("Unexpected issue creating buffer [" + bufferContainer.minCornerBlockPos + "], error: [" + e.getMessage() + "].", e); + } + + bufferContainer.close(); + future.completeExceptionally(e); + return null; + }); + createFuture.thenRun(() -> + { + CompletableFuture opaqueFuture = uploadBuffersAsync(future, bufferContainer.vboOpaqueWrappers, opaqueBuffers, opaqueIndexBuffers); + CompletableFuture transparentFuture = uploadBuffersAsync(future, bufferContainer.vboTransparentWrappers, transparentBuffers, transparentIndexBuffers); + CompletableFuture uploadFuture = CompletableFuture.allOf(opaqueFuture, transparentFuture); + uploadFuture.exceptionally((Throwable e) -> + { + // upload failed // + if (!ExceptionUtil.isShutdownException(e)) + { + LOGGER.error("Unexpected issue uploading buffer [" + bufferContainer.minCornerBlockPos + "], error: [" + e.getMessage() + "].", e); + } + + bufferContainer.close(); future.completeExceptionally(e); return null; }); - createFuture.thenRun(() -> + uploadFuture.thenRun(() -> { - //=============// - // upload VBOs // - //=============// - - CompletableFuture opaqueFuture = uploadBuffersAsync(future, this.vboOpaqueWrappers, opaqueBuffers, opaqueIndexBuffers); - CompletableFuture transparentFuture = uploadBuffersAsync(future, this.vboTransparentWrappers, transparentBuffers, transparentIndexBuffers); - - CompletableFuture uploadFuture = CompletableFuture.allOf(opaqueFuture, transparentFuture); - uploadFuture.exceptionally((Throwable e) -> - { - // upload failed // - - if (!ExceptionUtil.isShutdownException(e)) - { - LOGGER.error("Unexpected issue uploading buffer [" + this.minCornerBlockPos + "], error: [" + e.getMessage() + "].", e); - } - future.completeExceptionally(e); - return null; - }); - uploadFuture.thenRun(() -> - { - // upload success / - - this.buffersUploaded = true; - future.complete(this); - }); + // upload success // + bufferContainer.buffersUploaded = true; + future.complete(bufferContainer); }); - } - catch (Exception e) - { - if (!ExceptionUtil.isShutdownException(e)) - { - LOGGER.error("Unexpected issue prepping buffer uploading [" + this.minCornerBlockPos + "], error: [" + e.getMessage() + "].", e); - } - future.completeExceptionally(e); - } + }); + + //endregion - //================// - // buffer cleanup // - //================// + + //====================// + // CPU Buffer cleanup // + //====================// + //region future.whenComplete((LodBufferContainer lodBufferContainer, Throwable throwable) -> { @@ -290,11 +290,10 @@ public class LodBufferContainer implements AutoCloseable return newVbos; } - private static CompletableFuture createBufferWrappersAsync( - CompletableFuture parentFuture, - IVertexBufferWrapper[] vboWrappers, ArrayList vertexBuffers) + private static void createBufferWrappers( + IVertexBufferWrapper[] vboWrappers, ArrayList vertexBuffers, + @Nullable StackTraceElement[] callerStackTrace) { - ArrayList> createVboFutureList = new ArrayList<>(); for (int i = 0; i < vertexBuffers.size(); i++) { if (i >= vboWrappers.length) @@ -304,45 +303,9 @@ public class LodBufferContainer implements AutoCloseable if (vboWrappers[i] == null) { - final int finalVboIndex = i; - - CompletableFuture future = new CompletableFuture<>(); - createVboFutureList.add(future); - - RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("LodBufferContainer Setup", () -> - { - try - { - // skip this event if requested - if (Thread.interrupted() - || parentFuture.isCancelled()) - { - throw new InterruptedException(); - } - - - vboWrappers[finalVboIndex] = WRAPPER_FACTORY.createVboWrapper("distantHorizons:McLodRenderer"); - future.complete(null); - } - catch (Exception e) - { - future.completeExceptionally(e); - } - }); + vboWrappers[i] = WRAPPER_FACTORY.createVboWrapper("distantHorizons:McLodRenderer", callerStackTrace); } } - - if (createVboFutureList.size() == 0) - { - return CompletableFuture.completedFuture(null); - } - - CompletableFuture[] futureArray = new CompletableFuture[createVboFutureList.size()]; - for (int i = 0; i < createVboFutureList.size(); i++) - { - futureArray[i] = createVboFutureList.get(i); - } - return CompletableFuture.allOf(futureArray); } /** Index buffers should be null if {@link AbstractDhRenderApiDefinition#useSingleIbo()} returns true. */ @@ -365,8 +328,6 @@ public class LodBufferContainer implements AutoCloseable // final variables for use in lambdas // - final int finalVboIndex = vboIndex; - final IVertexBufferWrapper finalVboWrapper = vboWrappers[vboIndex]; final ByteBuffer finalVertexBuffer = vertexBuffers.get(vboIndex); @@ -385,6 +346,8 @@ public class LodBufferContainer implements AutoCloseable CompletableFuture vertexUploadFuture = new CompletableFuture<>(); uploadFutureList.add(vertexUploadFuture); + + final StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("LodBufferContainer VBO Upload", () -> { try @@ -396,21 +359,12 @@ public class LodBufferContainer implements AutoCloseable throw new InterruptedException(); } - - try - { - finalVboWrapper.uploadVertexBuffer(finalVertexBuffer, finalVertexCount); - vertexUploadFuture.complete(null); - } - catch (Exception e) - { - vboWrappers[finalVboIndex] = null; - finalVboWrapper.close(); - LOGGER.error("Failed to upload buffer. Error: [" + e.getMessage() + "].", e); - } + finalVboWrapper.uploadVertexBuffer(finalVertexBuffer, finalVertexCount, stackTraceElements); + vertexUploadFuture.complete(null); } catch (Exception e) { + LOGGER.error("Failed to upload buffer. Error: [" + e.getMessage() + "].", e); vertexUploadFuture.completeExceptionally(e); } }); @@ -445,6 +399,7 @@ public class LodBufferContainer implements AutoCloseable } catch (Exception e) { + finalVboWrapper.close(); indexUploadFuture.completeExceptionally(e); } }); 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 1f870ebd4..fea3d51da 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 @@ -36,6 +36,7 @@ import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer; +import com.seibel.distanthorizons.core.util.ExceptionUtil; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.threading.PriorityTaskPicker; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; @@ -62,7 +63,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable public final long pos; - private final IDhClientLevel level; + private final IDhClientLevel clientLevel; private final IClientLevelWrapper levelWrapper; @WillNotClose private final FullDataSourceProviderV2 fullDataSourceProvider; @@ -97,13 +98,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable */ private Runnable getAndBuildRenderDataRunnable = null; - /** - * Represents just uploading the {@link LodQuadBuilder} to the GPU.
- * Separate from {@link LodRenderSection#getAndBuildRenderDataFutureRef} because they run on - * different threads (buffer uploading is on the MC render thread) and need to be canceled separately. - */ - private final AtomicReference> bufferUploadFutureRef = new AtomicReference<>(null); - //=============// @@ -114,12 +108,12 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable public LodRenderSection( long pos, LodQuadTree quadTree, - IDhClientLevel level, FullDataSourceProviderV2 fullDataSourceProvider) + IDhClientLevel clientLevel, FullDataSourceProviderV2 fullDataSourceProvider) { this.pos = pos; this.quadTree = quadTree; - this.level = level; - this.levelWrapper = level.getClientLevelWrapper(); + this.clientLevel = clientLevel; + this.levelWrapper = clientLevel.getClientLevelWrapper(); this.fullDataSourceProvider = fullDataSourceProvider; DEBUG_RENDERER.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showRenderSectionStatus); @@ -161,6 +155,8 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable try { + // shouldn't happen since this method is synchronized, but just in case + // make sure we only ever start one upload task if (!this.getAndBuildRenderDataFutureRef.compareAndSet(null, future)) { CompletableFuture oldFuture = this.getAndBuildRenderDataFutureRef.get(); @@ -173,6 +169,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable { try { + // build LOD data on a DH thread LodQuadBuilder lodQuadBuilder = this.getAndBuildRenderData(); if (lodQuadBuilder == null) { @@ -180,7 +177,8 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable return; } - this.uploadToGpuAsync(lodQuadBuilder) + // uploading will primarily happen on the render thread + this.uploadToGpuAsync(future, lodQuadBuilder) .thenRun(() -> { // the future is passed in separately (IE not using the local var) to prevent any possible race condition null pointers @@ -190,7 +188,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable catch (Exception e) { LOGGER.error("Unexpected issue creating render data for pos: ["+DhSectionPos.toString(this.pos)+"], error: ["+e.getMessage()+"].", e); - future.complete(null); + future.completeExceptionally(e); } }; executor.execute(this.getAndBuildRenderDataRunnable); @@ -205,6 +203,14 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable return false; } } + + + //=======================// + // Get LOD ID data // + // and build render data // + //=======================// + //region + @Nullable private synchronized LodQuadBuilder getAndBuildRenderData() { @@ -218,7 +224,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable boolean enableTransparency = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled; - LodQuadBuilder lodQuadBuilder = new LodQuadBuilder(enableTransparency, this.level.getClientLevelWrapper()); + LodQuadBuilder lodQuadBuilder = new LodQuadBuilder(enableTransparency, this.clientLevel.getClientLevelWrapper()); // get the adjacent positions @@ -241,7 +247,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable // the render sources are only needed by this synchronous method, // then they can be closed - ColumnRenderBufferBuilder.makeLodRenderData(lodQuadBuilder, thisRenderSource, this.level, adjacentRenderSections, adjIsSameDetailLevel); + ColumnRenderBufferBuilder.makeLodRenderData(lodQuadBuilder, thisRenderSource, this.clientLevel, adjacentRenderSections, adjIsSameDetailLevel); return lodQuadBuilder; } catch (Exception e) @@ -291,53 +297,63 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable detailLevel += DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL; return detailLevel == DhSectionPos.getDetailLevel(this.pos); } - private synchronized CompletableFuture uploadToGpuAsync(LodQuadBuilder lodQuadBuilder) + + //endregion + + + private synchronized CompletableFuture uploadToGpuAsync( + CompletableFuture parentFuture, + LodQuadBuilder lodQuadBuilder) { - CompletableFuture oldFuture = this.bufferUploadFutureRef.getAndSet(null); - if (oldFuture != null) + CompletableFuture uploadFuture = LodBufferContainer.tryMakeAndUploadBuffersAsync(this.pos, this.clientLevel, lodQuadBuilder); + uploadFuture.whenComplete((bufferContainer, e) -> { - // canceling the previous future - // prevents the CPU from working on something that won't be used - oldFuture.cancel(true); - } - - CompletableFuture future = ColumnRenderBufferBuilder.uploadBuffersAsync(this.level, this.pos, lodQuadBuilder); - future.handle((lodBufferContainer, throwable) -> - { - if (!this.bufferUploadFutureRef.compareAndSet(future, null) - // if the old future is canceled then the future ref will be different and that's expected - && !future.isCancelled() - // if the old future is already done, then we don't care about the ref being swapped - && !future.isDone()) + try { - LOGGER.warn("Buffer upload future ref changed for pos: ["+DhSectionPos.toString(this.pos)+"]."); + // handle errors and early shutdown + if (e != null) + { + if (!ExceptionUtil.isShutdownException(e)) + { + LOGGER.error("Unexpected issue uploading buffers for pos: [" + DhSectionPos.toString(this.pos) + "], error: [" + e.getMessage() + "].", e); + } + + if (bufferContainer != null) + { + // shouldn't happen, but just in case + bufferContainer.close(); + } + return; + } + + // close the old container + LodBufferContainer oldContainer = this.renderBufferContainer; + this.renderBufferContainer = bufferContainer.buffersUploaded ? bufferContainer : null; + if (oldContainer != null) + { + oldContainer.close(); + } + + // upload complete + this.renderDataDirty = false; + + + if (parentFuture.isCancelled()) + { + // if the parent future was canceled that likely means + // this LodRenderSection was closed before this point, + // meaning this buffer will become homeless, + // so we need to clean it up here + bufferContainer.close(); + } } - - return null; - }); - - future.thenAccept((LodBufferContainer buffer) -> - { - // needed to clean up the old data - LodBufferContainer previousContainer = this.renderBufferContainer; - - // upload complete - this.renderBufferContainer = buffer.buffersUploaded ? buffer : null; - this.renderDataDirty = false; - - if (previousContainer != null) + catch (Exception finishEx) { - previousContainer.close(); + LOGGER.error("Unexpected buffer finish exception: ["+finishEx.getMessage()+"]", finishEx); } }); - - if (!this.bufferUploadFutureRef.compareAndSet(null, future)) - { - LodUtil.assertNotReach("Buffer upload future ref couldn't be set due to concurrency error, pos: ["+DhSectionPos.toString(this.pos)+"]."); - } - - return future; + return uploadFuture; } //endregion render data uploading @@ -391,8 +407,8 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable return; } - int levelMinY = this.level.getLevelWrapper().getMinHeight(); - int levelMaxY = this.level.getLevelWrapper().getMaxHeight(); + int levelMinY = this.clientLevel.getLevelWrapper().getMinHeight(); + int levelMaxY = this.clientLevel.getLevelWrapper().getMaxHeight(); // show the wireframe a bit lower than world max height, // since most worlds don't render all the way up to the max height @@ -429,13 +445,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable } - this.setRenderingEnabled(false); - if (this.renderBufferContainer != null) - { - this.renderBufferContainer.close(); - } - - // removes any in-progress futures since they aren't needed any more + // render loading is no longer needed CompletableFuture buildFuture = this.getAndBuildRenderDataFutureRef.get(); if (buildFuture != null) { @@ -451,12 +461,17 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable renderLoaderExecutor.remove(runnable); } } + + // cancel the future after removing the runnable + // to make sure the runnable is properly removed + buildFuture.cancel(true); } - CompletableFuture uploadFuture = this.bufferUploadFutureRef.get(); - if (uploadFuture != null) + + this.setRenderingEnabled(false); + if (this.renderBufferContainer != null) { - uploadFuture.cancel(true); + this.renderBufferContainer.close(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/ExceptionUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/ExceptionUtil.java index 916961af6..a6f8f4b08 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/ExceptionUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/ExceptionUtil.java @@ -25,6 +25,7 @@ public class ExceptionUtil return throwable instanceof InterruptedException || throwable instanceof UncheckedInterruptedException || throwable instanceof RejectedExecutionException + || throwable instanceof CancellationException || throwable instanceof ClosedByInterruptException; } @@ -37,8 +38,8 @@ public class ExceptionUtil unwrapped instanceof CancellationException; } public static Throwable ensureUnwrap(Throwable t) - { - return t instanceof CompletionException ? ensureUnwrap(t.getCause()) : t; - } + { return t instanceof CompletionException ? ensureUnwrap(t.getCause()) : t; } + + }