diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java index b3d0dde3f..d61394ad9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java @@ -19,9 +19,7 @@ package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding; -import com.seibel.distanthorizons.api.DhApi; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; import com.seibel.distanthorizons.core.render.glObject.GLProxy; @@ -30,7 +28,6 @@ import com.seibel.distanthorizons.core.render.renderer.LodRenderer; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.objects.StatsMap; import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import org.apache.logging.log4j.Logger; import org.lwjgl.system.MemoryUtil; @@ -46,9 +43,6 @@ import java.util.concurrent.*; public class ColumnRenderBuffer implements AutoCloseable { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - - private static final long MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS = 1_000_000; /** number of bytes a single quad takes */ public static final int QUADS_BYTE_SIZE = LodUtil.LOD_VERTEX_FORMAT.getByteSize() * 4; @@ -59,63 +53,81 @@ public class ColumnRenderBuffer implements AutoCloseable - - public final DhBlockPos pos; + public final DhBlockPos blockPos; public boolean buffersUploaded = false; private GLVertexBuffer[] vbos; private GLVertexBuffer[] vbosTransparent; + private CompletableFuture uploadFuture = null; + //==============// // constructors // //==============// - public ColumnRenderBuffer(DhBlockPos pos) + public ColumnRenderBuffer(DhBlockPos blockPos) { - this.pos = pos; + this.blockPos = blockPos; this.vbos = new GLVertexBuffer[0]; this.vbosTransparent = new GLVertexBuffer[0]; } - - //==================// // buffer uploading // //==================// /** Should be run on a DH thread. */ - public void makeAndUploadBuffers(LodQuadBuilder builder, EDhApiGpuUploadMethod gpuUploadMethod) throws InterruptedException + public synchronized CompletableFuture makeAndUploadBuffersAsync(LodQuadBuilder builder, EDhApiGpuUploadMethod gpuUploadMethod) { - LodUtil.assertTrue(DhApi.isDhThread(), "Buffer uploading needs to be done on a DH thread to prevent locking up any MC threads."); + if (this.uploadFuture != null) + { + return this.uploadFuture; + } + this.uploadFuture = new CompletableFuture<>(); + // make the buffers ArrayList opaqueBuffers = builder.makeOpaqueVertexBuffers(); ArrayList transparentBuffers = builder.makeTransparentVertexBuffers(); - vbos = resizeBuffer(vbos, opaqueBuffers.size()); - vbosTransparent = resizeBuffer(vbosTransparent, transparentBuffers.size()); + this.vbos = resizeBuffer(this.vbos, opaqueBuffers.size()); + this.vbosTransparent = resizeBuffer(this.vbosTransparent, transparentBuffers.size()); // upload on MC's render thread - CompletableFuture uploadFuture = new CompletableFuture<>(); GLProxy.getInstance().queueRunningOnRenderThread(() -> { try { - uploadBuffersDirect(vbos, opaqueBuffers, gpuUploadMethod); - uploadBuffersDirect(vbosTransparent, transparentBuffers, gpuUploadMethod); + if (Thread.interrupted()) + { + throw new InterruptedException(); + } + + uploadBuffersDirect(this.vbos, opaqueBuffers, gpuUploadMethod); + uploadBuffersDirect(this.vbosTransparent, transparentBuffers, gpuUploadMethod); this.buffersUploaded = true; - uploadFuture.complete(null); + + this.uploadFuture.complete(this); + this.uploadFuture = null; } - catch (InterruptedException e) + catch (InterruptedException ignore) { - throw new CompletionException(e); + this.uploadFuture.complete(this); + this.uploadFuture = null; + } + catch (Exception e) + { + LOGGER.error("Unexpected issue uploading buffer ["+this.blockPos +"], error: ["+e.getMessage()+"].", e); + + this.uploadFuture.completeExceptionally(e); + this.uploadFuture = null; } finally { @@ -133,22 +145,7 @@ public class ColumnRenderBuffer implements AutoCloseable } }); - - try - { - // wait for the upload to finish - uploadFuture.get(5_000, TimeUnit.MILLISECONDS); - } - catch (ExecutionException e) - { - LOGGER.warn("Error uploading builder ["+builder+"] synchronously. Error: "+e.getMessage(), e); - } - catch (TimeoutException e) - { - // timeouts can be ignored because it generally means the - // MC Render thread executor was closed - //LOGGER.warn("Error uploading builder ["+builder+"] synchronously. Error: "+e.getMessage(), e); - } + return this.uploadFuture; } private static GLVertexBuffer[] resizeBuffer(GLVertexBuffer[] vbos, int newSize) { @@ -224,7 +221,7 @@ public class ColumnRenderBuffer implements AutoCloseable public boolean renderOpaque(LodRenderer renderContext, DhApiRenderParam renderEventParam) { boolean hasRendered = false; - renderContext.setModelViewMatrixOffset(this.pos, renderEventParam); + renderContext.setModelViewMatrixOffset(this.blockPos, renderEventParam); for (GLVertexBuffer vbo : this.vbos) { if (vbo == null) @@ -252,7 +249,7 @@ public class ColumnRenderBuffer implements AutoCloseable try { // can throw an IllegalStateException if the GL program was freed before it should've been - renderContext.setModelViewMatrixOffset(this.pos, renderEventParam); + renderContext.setModelViewMatrixOffset(this.blockPos, renderEventParam); for (GLVertexBuffer vbo : this.vbosTransparent) { @@ -273,7 +270,7 @@ public class ColumnRenderBuffer implements AutoCloseable } catch (IllegalStateException e) { - LOGGER.error("renderContext program doesn't exist for pos: "+this.pos, e); + LOGGER.error("renderContext program doesn't exist for pos: "+this.blockPos, e); } return hasRendered; 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 e4a661c26..17f37ad5e 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 @@ -115,62 +115,17 @@ public class ColumnRenderBufferBuilder LodQuadBuilder quadBuilder ) { - // TODO put into a single future/thread so it can be easily canceled - ThreadPoolExecutor bufferUploaderExecutor = ThreadPoolUtil.getBufferUploaderExecutor(); - if (bufferUploaderExecutor == null || bufferUploaderExecutor.isTerminated()) + ColumnRenderBuffer buffer = new ColumnRenderBuffer(new DhBlockPos(DhSectionPos.getMinCornerBlockX(pos), clientLevel.getMinY(), DhSectionPos.getMinCornerBlockZ(pos))); + CompletableFuture uploadFuture = buffer.makeAndUploadBuffersAsync(quadBuilder, GLProxy.getInstance().getGpuUploadMethod()); + uploadFuture.whenComplete((uploadedBuffer, exception) -> { - // one or more of the thread pools has been shut down - CompletableFuture future = new CompletableFuture<>(); - future.cancel(true); - return future; - } - - - try - { - return CompletableFuture.supplyAsync(() -> - { - try - { - ColumnRenderBuffer buffer = new ColumnRenderBuffer(new DhBlockPos(DhSectionPos.getMinCornerBlockX(pos), clientLevel.getMinY(), DhSectionPos.getMinCornerBlockZ(pos))); - try - { - buffer.makeAndUploadBuffers(quadBuilder, GLProxy.getInstance().getGpuUploadMethod()); - if (buffer.buffersUploaded) - { - return buffer; - } - else - { - buffer.close(); - return null; - } - } - catch (Exception e) - { - buffer.close(); - throw e; - } - } - catch (InterruptedException e) - { - throw UncheckedInterruptedException.convert(e); - } - catch (Throwable e3) - { - LOGGER.error("LodNodeBufferBuilder was unable to upload buffer for pos ["+DhSectionPos.toString(pos)+"], error: [" + e3.getMessage() + "].", e3); - throw e3; - } - }, bufferUploaderExecutor); - } - catch (RejectedExecutionException ignore) - { - // shouldn't happen, but just in case - - CompletableFuture future = new CompletableFuture<>(); - future.cancel(true); - return future; - } + // clean up if not uploaded + if (!uploadedBuffer.buffersUploaded) + { + uploadedBuffer.close(); + } + }); + return uploadFuture; } private static void makeLodRenderData( LodQuadBuilder quadBuilder, ColumnRenderSource renderSource, IDhClientLevel clientLevel, diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java index c2510e5a0..05fefd901 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java @@ -26,7 +26,11 @@ import com.seibel.distanthorizons.api.objects.data.DhApiChunk; import com.seibel.distanthorizons.api.objects.data.IDhApiFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.generation.tasks.*; +import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker; +import com.seibel.distanthorizons.core.generation.tasks.InProgressWorldGenTaskGroup; +import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult; +import com.seibel.distanthorizons.core.generation.tasks.WorldGenTask; +import com.seibel.distanthorizons.core.generation.tasks.WorldGenTaskGroup; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhChunkPos; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java index 4246ef68f..8c1011974 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java @@ -77,7 +77,6 @@ public class F3Screen ThreadPoolExecutor updatePool = ThreadPoolUtil.getUpdatePropagatorExecutor(); ThreadPoolExecutor lodBuilderPool = ThreadPoolUtil.getChunkToLodBuilderExecutor(); ThreadPoolExecutor bufferBuilderPool = ThreadPoolUtil.getBufferBuilderExecutor(); - ThreadPoolExecutor bufferUploaderPool = ThreadPoolUtil.getBufferUploaderExecutor(); AbstractDhWorld world = SharedApi.getAbstractDhWorld(); Iterable levelIterator = world.getAllLoadedLevels(); @@ -96,7 +95,6 @@ public class F3Screen messageList.add(getThreadPoolStatString("Update Propagator", updatePool)); messageList.add(getThreadPoolStatString("LOD Builder", lodBuilderPool)); messageList.add(getThreadPoolStatString("Buffer Builder", bufferBuilderPool)); - messageList.add(getThreadPoolStatString("Buffer Uploader", bufferUploaderPool)); messageList.add(""); // chunk updates messageList.add(SharedApi.INSTANCE.getDebugMenuString()); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java index 334cba2cc..7e4bb84fc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java @@ -274,10 +274,11 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable this.bufferUploadFuture = ColumnRenderBufferBuilder.uploadBuffersAsync(this.level, this.pos, lodQuadBuilder); return this.bufferUploadFuture.thenCompose((buffer) -> { + // needed to clean up the old data ColumnRenderBuffer previousBuffer = this.renderBuffer; - // upload complete, clean up the old data if - this.renderBuffer = buffer; + // upload complete + this.renderBuffer = buffer.buffersUploaded ? buffer : null; this.buildAndUploadRenderDataToGpuFuture = null; this.bufferBuildFuture = null; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java index f83ecf7bc..93b0db3f5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java @@ -243,7 +243,7 @@ public class GLProxy // only try running for 4ms at a time to (hopefully) prevent random lag spikes if (runDuration > 4_000_000) { - //break; + break; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java index 39151eb93..c78dbefd7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java @@ -56,11 +56,6 @@ public class ThreadPoolUtil @Nullable public static ThreadPoolExecutor getWorldGenExecutor() { return worldGenThreadPool.executor; } - public static final String BUFFER_UPLOADER_THREAD_NAME = "Buffer Uploader"; - private static ThreadPoolExecutor bufferUploaderThreadPool; - @Nullable - public static ThreadPoolExecutor getBufferUploaderExecutor() { return bufferUploaderThreadPool; } - public static final String CLEANUP_THREAD_NAME = "Cleanup"; private static ThreadPoolExecutor cleanupThreadPool; @Nullable @@ -118,7 +113,6 @@ public class ThreadPoolUtil updatePropagatorThreadPool = new ConfigThreadPool(UPDATE_PROPAGATOR_THREAD_FACTORY, Config.Common.MultiThreading.numberOfUpdatePropagatorThreads, Config.Common.MultiThreading.runTimeRatioForUpdatePropagatorThreads, null); worldGenThreadPool = new ConfigThreadPool(WORLD_GEN_THREAD_FACTORY, Config.Common.MultiThreading.numberOfWorldGenerationThreads, Config.Common.MultiThreading.runTimeRatioForWorldGenerationThreads, null); networkCompressionThreadPool = new ConfigThreadPool(NETWORK_COMPRESSION_THREAD_FACTORY, Config.Common.MultiThreading.numberOfNetworkCompressionThreads, Config.Common.MultiThreading.runTimeRatioForNetworkCompressionThreads, null); - bufferUploaderThreadPool = ThreadUtil.makeSingleThreadPool(BUFFER_UPLOADER_THREAD_NAME); cleanupThreadPool = ThreadUtil.makeSingleThreadPool(CLEANUP_THREAD_NAME); beaconCullingThreadPool = ThreadUtil.makeSingleThreadPool(BEACON_CULLING_THREAD_NAME); @@ -160,7 +154,6 @@ public class ThreadPoolUtil updatePropagatorThreadPool.shutdownExecutorService(); worldGenThreadPool.shutdownExecutorService(); networkCompressionThreadPool.shutdownExecutorService(); - bufferUploaderThreadPool.shutdown(); cleanupThreadPool.shutdown(); beaconCullingThreadPool.shutdown();