From aebbeb6ade0130302ca01e0c8645be2c20eb88a0 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Tue, 14 Sep 2021 22:49:08 -0500 Subject: [PATCH] Simplify the multi-context uploading, improve stability, and refactor --- .../seibel/lod/builders/LodBufferBuilder.java | 24 +++----- .../com/seibel/lod/proxy/ClientProxy.java | 56 ++++++++++++------ .../java/com/seibel/lod/proxy/GlProxy.java | 59 +++++++++++-------- .../com/seibel/lod/render/LodRenderer.java | 37 ++---------- 4 files changed, 84 insertions(+), 92 deletions(-) diff --git a/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java b/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java index 1cf15ddf7..ad24f4f68 100644 --- a/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java +++ b/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java @@ -82,9 +82,6 @@ public class LodBufferBuilder */ public volatile VertexBuffer[][] drawableVbos; - public GlProxyContext buildingContext = GlProxyContext.ALPHA; - public GlProxyContext renderContext = GlProxyContext.BETA; - /** * if this is true the LOD buffers are currently being * regenerated. @@ -459,8 +456,11 @@ public class LodBufferBuilder */ private void uploadBuffers(boolean fullRegen, LodDimension lodDim) { - GlProxy.getInstance().setGlContext(buildingContext); - // used to prevent debug printing multiple times per upload cycle + GlProxy glProxy = GlProxy.getInstance(); + // make sure we are uploading to a different OpenGL context, + // to prevent interference (IE stuttering) with the Minecraft context. + glProxy.setGlContext(GlProxyContext.LOD_BUILDER); + // only print console debugging for vboUpload once per upload cycle boolean bufferMapFail = false; @@ -481,7 +481,7 @@ public class LodBufferBuilder // make sure all the buffers have been uploaded. // this probably is necessary, but it makes me feel good :) GL11.glFlush(); - GlProxy.getInstance().setGlContext(GlProxyContext.NONE); + glProxy.setGlContext(GlProxyContext.NONE); } /** @@ -520,7 +520,7 @@ public class LodBufferBuilder // only print to console once per upload cycle if (!bufferMapFail) - ClientProxy.LOGGER.debug("LOD buffer upload glMapBuffer failed, using slower glBufferData."); + ClientProxy.LOGGER.debug("LOD buffer upload: glMapBuffer failed, used slower glBufferData."); bufferMapFail = true; } @@ -547,10 +547,6 @@ public class LodBufferBuilder drawableVbos = buildableVbos; buildableVbos = tmpVbo; - GlProxyContext tmpContext = renderContext; - renderContext = buildingContext; - buildingContext = tmpContext; - drawableCenterChunkPos = buildableCenterChunkPos; // the vbos have been swapped @@ -558,7 +554,7 @@ public class LodBufferBuilder bufferLock.unlock(); } - return new VertexBuffersAndOffset(drawableVbos, drawableCenterChunkPos, renderContext); + return new VertexBuffersAndOffset(drawableVbos, drawableCenterChunkPos); } /** @@ -568,13 +564,11 @@ public class LodBufferBuilder { public VertexBuffer[][] vbos; public ChunkPos drawableCenterChunkPos; - public GlProxyContext drawingContext; - public VertexBuffersAndOffset(VertexBuffer[][] newVbos, ChunkPos newDrawableCenterChunkPos, GlProxyContext newDrawingContext) + public VertexBuffersAndOffset(VertexBuffer[][] newVbos, ChunkPos newDrawableCenterChunkPos) { vbos = newVbos; drawableCenterChunkPos = newDrawableCenterChunkPos; - drawingContext = newDrawingContext; } } diff --git a/src/main/java/com/seibel/lod/proxy/ClientProxy.java b/src/main/java/com/seibel/lod/proxy/ClientProxy.java index 6a908b308..68f2918a1 100644 --- a/src/main/java/com/seibel/lod/proxy/ClientProxy.java +++ b/src/main/java/com/seibel/lod/proxy/ClientProxy.java @@ -27,7 +27,6 @@ import com.seibel.lod.builders.worldGeneration.LodNodeGenWorker; import com.seibel.lod.builders.worldGeneration.LodWorldGenerator; import com.seibel.lod.config.LodConfig; import com.seibel.lod.enums.DistanceGenerationMode; -import com.seibel.lod.enums.LodDetail; import com.seibel.lod.objects.LodDimension; import com.seibel.lod.objects.LodWorld; import com.seibel.lod.objects.RegionPos; @@ -47,15 +46,17 @@ import net.minecraftforge.eventbus.api.SubscribeEvent; /** * This handles all events sent to the client, - * and is the starting point for most of this program. + * and is the starting point for most of the mod. * * @author James_Seibel - * @version 8-24-2021 + * @version 9-14-2021 */ public class ClientProxy { public static final Logger LOGGER = LogManager.getLogger("LOD"); + private boolean firstTimeSetupComplete = false; + private static LodWorld lodWorld = new LodWorld(); private static LodBuilder lodBuilder = new LodBuilder(); private static LodBufferBuilder lodBufferBuilder = new LodBufferBuilder(); @@ -64,7 +65,7 @@ public class ClientProxy private boolean configOverrideReminderPrinted = false; - MinecraftWrapper mc = MinecraftWrapper.INSTANCE; + private MinecraftWrapper mc = MinecraftWrapper.INSTANCE; /** @@ -99,7 +100,13 @@ public class ClientProxy */ public void renderLods(float partialTicks) { - GlProxy.getInstance(); + // only run the first time setup once + if (firstTimeSetupComplete) + { + firstFrameSetup(); + } + + DetailDistanceUtil.updateSettings(); if (mc == null || mc.getPlayer() == null || !lodWorld.getIsWorldLoaded()) @@ -113,8 +120,6 @@ public class ClientProxy playerMoveEvent(lodDim); - //System.out.println("memory needed " + lodDim.getMinMemoryNeeded() + " byte"); - //System.out.println(lodDim); lodDim.treeCutter((int) mc.getPlayer().getX(), (int) mc.getPlayer().getZ()); lodDim.treeGenerator((int) mc.getPlayer().getX(), (int) mc.getPlayer().getZ()); @@ -135,7 +140,7 @@ public class ClientProxy renderer.drawLODs(lodDim, partialTicks, mc.getProfiler()); profiler.pop(); // end LOD - profiler.push("terrain"); // restart "terrain" + profiler.push("terrain"); // go back into "terrain" // these can't be set until after the buffers are built (in renderer.drawLODs) @@ -157,8 +162,8 @@ public class ClientProxy //LodConfig.CLIENT.drawLODs.set(true); //LodConfig.CLIENT.debugMode.set(true); - LodConfig.CLIENT.graphics.maxDrawDetail.set(LodDetail.FULL); - LodConfig.CLIENT.worldGenerator.maxGenerationDetail.set(LodDetail.FULL); +// LodConfig.CLIENT.graphics.maxDrawDetail.set(LodDetail.FULL); +// LodConfig.CLIENT.worldGenerator.maxGenerationDetail.set(LodDetail.FULL); // LodConfig.CLIENT.graphics.fogDistance.set(FogDistance.FAR); // LodConfig.CLIENT.graphics.fogDrawOverride.set(FogDrawOverride.ALWAYS_DRAW_FOG_FANCY); @@ -167,14 +172,14 @@ public class ClientProxy // LodConfig.CLIENT.graphics.saturationMultiplier.set(1.0); // LodConfig.CLIENT.worldGenerator.distanceGenerationMode.set(DistanceGenerationMode.SURFACE); - LodConfig.CLIENT.graphics.lodChunkRenderDistance.set(256); +// LodConfig.CLIENT.graphics.lodChunkRenderDistance.set(64); // LodConfig.CLIENT.worldGenerator.lodDistanceCalculatorType.set(DistanceCalculatorType.LINEAR); - LodConfig.CLIENT.graphics.lodQuality.set(3); +// LodConfig.CLIENT.graphics.lodQuality.set(2); // LodConfig.CLIENT.worldGenerator.allowUnstableFeatureGeneration.set(false); - LodConfig.CLIENT.buffers.bufferRebuildPlayerMoveTimeout.set(2000); // 2000 - LodConfig.CLIENT.buffers.bufferRebuildChunkChangeTimeout.set(1000); // 1000 - LodConfig.CLIENT.buffers.bufferRebuildLodChangeTimeout.set(5000); // 5000 +// LodConfig.CLIENT.buffers.bufferRebuildPlayerMoveTimeout.set(2000); // 2000 +// LodConfig.CLIENT.buffers.bufferRebuildChunkChangeTimeout.set(1000); // 1000 +// LodConfig.CLIENT.buffers.bufferRebuildLodChangeTimeout.set(5000); // 5000 LodConfig.CLIENT.debugging.enableDebugKeybinding.set(true); } @@ -267,10 +272,9 @@ public class ClientProxy } - - //==================// - // frame LOD events // - //==================// + //============// + // LOD events // + //============// /** * Re-centers the given LodDimension if it needs to be. @@ -315,6 +319,20 @@ public class ClientProxy DetailDistanceUtil.updateSettings(); } + + /** + * This event is called once during the first frame Minecraft renders in the world. + */ + public void firstFrameSetup() + { + // make sure the GlProxy is created before the LodBufferBuilder + GlProxy.getInstance(); + + + + firstTimeSetupComplete = true; + } + //================// diff --git a/src/main/java/com/seibel/lod/proxy/GlProxy.java b/src/main/java/com/seibel/lod/proxy/GlProxy.java index 8a9ffddf8..bc529943c 100644 --- a/src/main/java/com/seibel/lod/proxy/GlProxy.java +++ b/src/main/java/com/seibel/lod/proxy/GlProxy.java @@ -7,7 +7,8 @@ import org.lwjgl.opengl.WGL; import com.mojang.blaze3d.systems.RenderSystem; /** - * A singleton that holds references to different openGL contexts. + * A singleton that holds references to different openGL contexts + * and GPU capabilities. * *

* Helpful OpenGL resources:

@@ -31,8 +32,13 @@ public class GlProxy public long lodBuilderGlContext; public GLCapabilities lodBuilderGlCapabilities; - public long lodRenderGlContext; - public GLCapabilities lodRenderGlCapabilities; + + /** + * Does this computer's GPU support fancy fog? + */ + public final boolean fancyFogAvailable; + + private GlProxy() { @@ -42,6 +48,12 @@ public class GlProxy throw new IllegalStateException(GlProxy.class.getSimpleName() + " was created outside the render thread!"); + + + //============================// + // create the builder context // + //============================// + minecraftGlContext = WGL.wglGetCurrentContext(); minecraftGlCapabilities = GL.getCapabilities(); deviceContext = WGL.wglGetCurrentDC(); @@ -50,22 +62,28 @@ public class GlProxy if (!WGL.wglShareLists(minecraftGlContext, lodBuilderGlContext)) throw new IllegalStateException("Unable to share lists between Minecraft and builder contexts."); if (!WGL.wglMakeCurrent(deviceContext, lodBuilderGlContext)) - throw new IllegalStateException("Unable to change OpenGL contexts! tried to change to [" + GlProxyContext.ALPHA.toString() + "] from [" + GlProxyContext.MINECRAFT.toString() + "]"); + throw new IllegalStateException("Unable to change OpenGL contexts! tried to change to [" + GlProxyContext.LOD_BUILDER.toString() + "] from [" + GlProxyContext.MINECRAFT.toString() + "]"); lodBuilderGlCapabilities = GL.createCapabilities(); WGL.wglMakeCurrent(deviceContext, 0L); - - - - lodRenderGlContext = WGL.wglCreateContext(deviceContext); - if (!WGL.wglShareLists(minecraftGlContext, lodRenderGlContext)) - throw new IllegalStateException("Unable to share lists between builder and render contexts."); - if (!WGL.wglMakeCurrent(deviceContext, lodRenderGlContext)) - throw new IllegalStateException("Unable to change OpenGL contexts! tried to change to [" + GlProxyContext.ALPHA.toString() + "] from [" + GlProxyContext.BETA.toString() + "]"); - lodRenderGlCapabilities = GL.createCapabilities(); // Since this is called on the render thread, make sure the Minecraft context is used in the end WGL.wglMakeCurrent(deviceContext, minecraftGlContext); + + + + + //==================================// + // get any GPU related capabilities // + //==================================// + + // see if this GPU can run fancy fog + fancyFogAvailable = GL.getCapabilities().GL_NV_fog_distance; + + if (!fancyFogAvailable) + { + ClientProxy.LOGGER.info("This GPU does not support GL_NV_fog_distance. This means that the fancy fog option will not be available."); + } } @@ -80,14 +98,10 @@ public class GlProxy GLCapabilities newGlCapabilities = null; switch(context) { - case ALPHA: + case LOD_BUILDER: contextPointer = lodBuilderGlContext; newGlCapabilities = lodBuilderGlCapabilities; break; - case BETA: - contextPointer = lodRenderGlContext; - newGlCapabilities = lodRenderGlCapabilities; - break; case MINECRAFT: contextPointer = minecraftGlContext; newGlCapabilities = minecraftGlCapabilities; @@ -111,11 +125,7 @@ public class GlProxy long currentContext = WGL.wglGetCurrentContext(); if(currentContext == lodBuilderGlContext) { - return GlProxyContext.ALPHA; - } - else if(currentContext == lodRenderGlContext) - { - return GlProxyContext.BETA; + return GlProxyContext.LOD_BUILDER; } else if(currentContext == minecraftGlContext) { @@ -130,8 +140,7 @@ public class GlProxy public enum GlProxyContext { MINECRAFT, - ALPHA, - BETA, + LOD_BUILDER, /** used to un-bind threads */ NONE, diff --git a/src/main/java/com/seibel/lod/render/LodRenderer.java b/src/main/java/com/seibel/lod/render/LodRenderer.java index 6b80091fd..918281b1f 100644 --- a/src/main/java/com/seibel/lod/render/LodRenderer.java +++ b/src/main/java/com/seibel/lod/render/LodRenderer.java @@ -23,7 +23,6 @@ import java.nio.FloatBuffer; import java.util.HashSet; import java.util.Iterator; -import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL15C; @@ -95,13 +94,7 @@ public class LodRenderer * https://stackoverflow.com/questions/50499238/bytebuffer-allocatedirect-and-xmx */ public static final int MAX_ALOCATEABLE_DIRECT_MEMORY = 64 * 1024 * 1024; - - /** - * Does this computer's GPU support fancy fog? - */ - private static Boolean fancyFogAvailable = null; - private static GlProxy glProxy; - + /** * If true the LODs colors will be replaced with * a checkerboard, this can be used for debugging. @@ -189,25 +182,8 @@ public class LodRenderer profiler = newProfiler; profiler.push("LOD setup"); - - - // only check the GPU capability's once - if (fancyFogAvailable == null) - { - //TODO add this to the GlProxy - // see if this GPU can run fancy fog - fancyFogAvailable = GL.getCapabilities().GL_NV_fog_distance; - - if (!fancyFogAvailable) - { - ClientProxy.LOGGER.info("This GPU does not support GL_NV_fog_distance. This means that fancy fog options will not be available."); - } - - // create the GlProxy TODO this should probably be done somewhere else - glProxy = GlProxy.getInstance(); - } - - + + // TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead) // starting here... determineIfLodsShouldRegenerate(lodDim); @@ -243,11 +219,6 @@ public class LodRenderer swapBuffers(); } - if (renderContext == null) - { - return; - } - //===========================// @@ -738,7 +709,7 @@ public class LodRenderer // only use fancy fog if the user's GPU can deliver - if (!fancyFogAvailable && quality == FogQuality.FANCY) + if (!GlProxy.getInstance().fancyFogAvailable && quality == FogQuality.FANCY) { quality = FogQuality.FAST; }