From 5ae2731008a78aca4c98d6d46a83d19a5132216c Mon Sep 17 00:00:00 2001 From: alex-huff Date: Tue, 19 Sep 2023 17:44:39 -0500 Subject: [PATCH] Fixed deadlock when drawLODs returns early because Iris is rendering shadow pass --- .../core/render/renderer/LodRenderer.java | 928 +++++++++--------- 1 file changed, 463 insertions(+), 465 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java index 4a1c2da33..3e4cc3d81 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java @@ -1,465 +1,463 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.render.renderer; - -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector; -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger; -import com.seibel.distanthorizons.core.pos.DhBlockPos; -import com.seibel.distanthorizons.core.render.AbstractRenderBuffer; -import com.seibel.distanthorizons.core.render.RenderBufferHandler; -import com.seibel.distanthorizons.core.render.glObject.GLProxy; -import com.seibel.distanthorizons.core.render.glObject.GLState; -import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer; -import com.seibel.distanthorizons.core.render.glObject.buffer.QuadElementBuffer; -import com.seibel.distanthorizons.core.render.renderer.shaders.*; -import com.seibel.distanthorizons.core.util.LodUtil; -import com.seibel.distanthorizons.core.util.RenderUtil; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; -import com.seibel.distanthorizons.api.enums.rendering.EDebugRendering; -import com.seibel.distanthorizons.api.enums.rendering.EFogColorMode; -import com.seibel.distanthorizons.core.render.fog.LodFogConfig; -import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor; -import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor; -import com.seibel.distanthorizons.coreapi.util.math.Mat4f; -import com.seibel.distanthorizons.coreapi.util.math.Vec3d; -import com.seibel.distanthorizons.coreapi.util.math.Vec3f; -import org.apache.logging.log4j.LogManager; -import org.lwjgl.opengl.GL32; - -import java.awt.*; -import java.time.Duration; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReentrantLock; - -/** - * This is where all the magic happens.
- * This is where LODs are draw to the world. - */ -public class LodRenderer -{ - public static final ConfigBasedLogger EVENT_LOGGER = new ConfigBasedLogger(LogManager.getLogger(LodRenderer.class), - () -> Config.Client.Advanced.Logging.logRendererBufferEvent.get()); - public static ConfigBasedSpamLogger tickLogger = new ConfigBasedSpamLogger(LogManager.getLogger(LodRenderer.class), - () -> Config.Client.Advanced.Logging.logRendererBufferEvent.get(), 1); - - private static final IIrisAccessor IRIS_ACCESSOR = ModAccessorInjector.INSTANCE.get(IIrisAccessor.class); - - public static final boolean ENABLE_DRAW_LAG_SPIKE_LOGGING = false; - public static final boolean ENABLE_DUMP_GL_STATE = true; - public static final long DRAW_LAG_SPIKE_THRESHOLD_NS = TimeUnit.NANOSECONDS.convert(20, TimeUnit.MILLISECONDS); - - public static final boolean ENABLE_IBO = true; - - // TODO make these private, the LOD Builder can get these variables from the config itself - public static boolean transparencyEnabled = true; - public static boolean fakeOceanFloor = true; - - /** used to prevent cleaning up render resources while they are being used */ - private static final ReentrantLock renderLock = new ReentrantLock(); - - - - public void setupOffset(DhBlockPos pos) throws IllegalStateException - { - Vec3d cam = MC_RENDER.getCameraExactPosition(); - Vec3f modelPos = new Vec3f((float) (pos.x - cam.x), (float) (pos.y - cam.y), (float) (pos.z - cam.z)); - - if (!GL32.glIsProgram(this.shaderProgram.id)) - { - throw new IllegalStateException("No GL program exists with the ID: ["+this.shaderProgram.id+"]. This either means a shader program was freed while it was still in use or was never created."); - } - - this.shaderProgram.bind(); - this.shaderProgram.setModelPos(modelPos); - } - - public void drawVbo(GLVertexBuffer vbo) - { - vbo.bind(); - this.shaderProgram.bindVertexBuffer(vbo.getId()); - GL32.glDrawElements(GL32.GL_TRIANGLES, (vbo.getVertexCount() / 4) * 6, // TODO what does the 4 and 6 here represent? - this.quadIBO.getType(), 0); - vbo.unbind(); - } - public Vec3f getLookVector() { return MC_RENDER.getLookAtVector(); } - - - public static class LagSpikeCatcher - { - long timer = System.nanoTime(); - - public LagSpikeCatcher() { } - - public void end(String source) - { - if (!ENABLE_DRAW_LAG_SPIKE_LOGGING) - { - return; - } - - this.timer = System.nanoTime() - this.timer; - if (this.timer > DRAW_LAG_SPIKE_THRESHOLD_NS) - { - //4 ms - EVENT_LOGGER.debug("NOTE: " + source + " took " + Duration.ofNanos(this.timer) + "!"); - } - - } - - } - - private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); - - public EDebugRendering previousDebugMode = null; - public final RenderBufferHandler bufferHandler; - - // The shader program - LodRenderProgram shaderProgram = null; - public QuadElementBuffer quadIBO = null; - public boolean isSetupComplete = false; - - public LodRenderer(RenderBufferHandler bufferHandler) { this.bufferHandler = bufferHandler; } - - private boolean rendererClosed = false; - public void close() - { - if (this.rendererClosed) - { - EVENT_LOGGER.warn("close() called twice!"); - return; - } - - - this.rendererClosed = true; - - // wait for the renderer to finish before closing (to prevent closing resources that are currently in use) - renderLock.lock(); - try - { - EVENT_LOGGER.info("Shutting down " + LodRenderer.class.getSimpleName() + "..."); - - this.cleanup(); - this.bufferHandler.close(); - - EVENT_LOGGER.info("Finished shutting down " + LodRenderer.class.getSimpleName()); - } - finally - { - renderLock.unlock(); - } - } - - public void drawLODs(Mat4f baseModelViewMatrix, Mat4f baseProjectionMatrix, float partialTicks, IProfilerWrapper profiler) - { - if (this.rendererClosed) - { - EVENT_LOGGER.error("drawLODs() called after close()!"); - return; - } - - if (!renderLock.tryLock()) - { - // never lock the render thread, if the lock isn't available don't wait for it - return; - } - - if (IRIS_ACCESSOR != null && IRIS_ACCESSOR.isRenderingShadowPass()) - { - // We do not have a wy to properly render shader shadow pass, since they can - // and often do change the projection entirely, as well as the output usage. - - //EVENT_LOGGER.debug("Skipping shadow pass render."); - return; - } - - - - try - { - // get MC's shader program and save MC's render state so we can restore it later - LagSpikeCatcher drawSaveGLState = new LagSpikeCatcher(); - GLState minecraftGlState = new GLState(); - if (ENABLE_DUMP_GL_STATE) - { - tickLogger.debug("Saving GL state: " + minecraftGlState); - } - drawSaveGLState.end("drawSaveGLState"); - - // make sure everything has been initialized - GLProxy glProxy = GLProxy.getInstance(); - - - - //===================// - // draw params setup // - //===================// - - profiler.push("LOD draw setup"); - /*---------Set GL State--------*/ - GL32.glViewport(0, 0, MC_RENDER.getTargetFrameBufferViewportWidth(), MC_RENDER.getTargetFrameBufferViewportHeight()); - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, 0); - boolean renderWireframe = Config.Client.Advanced.Debugging.renderWireframe.get(); - if (renderWireframe) - { - GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE); - //GL32.glDisable(GL32.GL_CULL_FACE); - } - else - { - GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); - GL32.glEnable(GL32.GL_CULL_FACE); - } - GL32.glEnable(GL32.GL_DEPTH_TEST); - GL32.glDepthFunc(GL32.GL_LESS); - - - - transparencyEnabled = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled; - fakeOceanFloor = Config.Client.Advanced.Graphics.Quality.transparency.get().fakeTransparencyEnabled; - - GL32.glDisable(GL32.GL_BLEND); // We render opaque first, then transparent - GL32.glDepthMask(true); - GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT); - - /*---------Bind required objects--------*/ - // Setup LodRenderProgram and the LightmapTexture if it has not yet been done - // also binds LightmapTexture, VAO, and ShaderProgram - if (!this.isSetupComplete) - { - this.setup(); - } - else - { - LodFogConfig newFogConfig = this.shaderProgram.isShaderUsable(); - if (newFogConfig != null) - { - this.shaderProgram.free(); - this.shaderProgram = new LodRenderProgram(newFogConfig); - - FogShader.INSTANCE.free(); - FogShader.INSTANCE = new FogShader(newFogConfig); - } - this.shaderProgram.bind(); - } - - GL32.glActiveTexture(GL32.GL_TEXTURE0); - - /*---------Get required data--------*/ - int vanillaBlockRenderedDistance = MC_RENDER.getRenderDistance() * LodUtil.CHUNK_WIDTH; - //Mat4f modelViewProjectionMatrix = RenderUtil.createCombinedModelViewProjectionMatrix(baseProjectionMatrix, baseModelViewMatrix, partialTicks); - - Mat4f projectionMatrix = RenderUtil.createLodProjectionMatrix(baseProjectionMatrix, partialTicks); - - Mat4f modelViewProjectionMatrix = new Mat4f(projectionMatrix); - modelViewProjectionMatrix.multiply(RenderUtil.createLodModelViewMatrix(baseModelViewMatrix)); - - /*---------Fill uniform data--------*/ - this.shaderProgram.fillUniformData(modelViewProjectionMatrix, /*Light map = GL_TEXTURE0*/ 0, - MC.getWrappedClientWorld().getMinHeight(), vanillaBlockRenderedDistance); - - // Note: Since lightmapTexture is changing every frame, it's faster to recreate it than to reuse the old one. - ILightMapWrapper lightmap = MC_RENDER.getLightmapWrapper(); - lightmap.bind(); - if (ENABLE_IBO) - { - this.quadIBO.bind(); - } - - this.bufferHandler.buildRenderListAndUpdateSections(this.getLookVector()); - - - - //===========// - // rendering // - //===========// - - LagSpikeCatcher drawLagSpikeCatcher = new LagSpikeCatcher(); - - profiler.popPush("LOD Opaque"); - // TODO: Directional culling - this.bufferHandler.renderOpaque(this); - - if (Config.Client.Advanced.Graphics.Ssao.enabled.get()) - { - profiler.popPush("LOD SSAO"); - SSAOShader.INSTANCE.setProjectionMatrix(projectionMatrix); - SSAORenderer.INSTANCE.render(minecraftGlState, partialTicks); - } - - - profiler.popPush("LOD Fog"); - FogShader.INSTANCE.setModelViewProjectionMatrix(modelViewProjectionMatrix); - FogShader.INSTANCE.render(partialTicks); - - // DarkShader.INSTANCE.render(partialTicks); // A test shader to make the world darker - - - if (Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled) - { - profiler.popPush("LOD Transparent"); - - GL32.glEnable(GL32.GL_BLEND); - GL32.glBlendFunc(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA); - this.bufferHandler.renderTransparent(this); - GL32.glDepthMask(true); // Apparently the depth mask state is stored in the FBO, so glState fails to restore it... - - FogShader.INSTANCE.render(partialTicks); - } - - drawLagSpikeCatcher.end("LodDraw"); - - - - //================// - // render cleanup // - //================// - - profiler.popPush("LOD cleanup"); - LagSpikeCatcher drawCleanup = new LagSpikeCatcher(); - lightmap.unbind(); - if (ENABLE_IBO) - { - this.quadIBO.unbind(); - } - - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, 0); - - this.shaderProgram.unbind(); - - if (Config.Client.Advanced.Debugging.debugWireframeRendering.get()) - { - profiler.popPush("Debug wireframes"); - // Note: this can be very slow if a lot of boxes are being rendered - DebugRenderer.INSTANCE.render(modelViewProjectionMatrix); - profiler.popPush("LOD cleanup"); - } - - GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT); - - minecraftGlState.restore(); - drawCleanup.end("LodDrawCleanup"); - - // end of internal LOD profiling - profiler.pop(); - tickLogger.incLogTries(); - - } - finally - { - renderLock.unlock(); - } - } - - - - //=================// - // Setup Functions // - //=================// - - /** Setup all render objects - REQUIRES to be in render thread */ - private void setup() - { - if (this.isSetupComplete) - { - EVENT_LOGGER.warn("Renderer setup called but it has already completed setup!"); - return; - } - if (!GLProxy.hasInstance()) - { - EVENT_LOGGER.warn("Renderer setup called but GLProxy has not yet been setup!"); - return; - } - - EVENT_LOGGER.info("Setting up renderer"); - this.isSetupComplete = true; - this.shaderProgram = new LodRenderProgram(LodFogConfig.generateFogConfig()); // TODO this doesn't actually use the fog config - if (ENABLE_IBO) - { - this.quadIBO = new QuadElementBuffer(); - this.quadIBO.reserve(AbstractRenderBuffer.MAX_QUADS_PER_BUFFER); - } - EVENT_LOGGER.info("Renderer setup complete"); - } - - private Color getFogColor(float partialTicks) - { - Color fogColor; - - if (Config.Client.Advanced.Graphics.Fog.colorMode.get() == EFogColorMode.USE_SKY_COLOR) - { - fogColor = MC_RENDER.getSkyColor(); - } - else - { - fogColor = MC_RENDER.getFogColor(partialTicks); - } - - return fogColor; - } - private Color getSpecialFogColor(float partialTicks) { return MC_RENDER.getSpecialFogColor(partialTicks); } - - - - //======================// - // Cleanup Functions // - //======================// - - /** - * cleanup and free all render objects. MUST be on the render thread - * (Many objects are Native, outside of JVM, and need manual cleanup) - */ - private void cleanup() - { - if (!this.isSetupComplete) - { - EVENT_LOGGER.warn("Renderer cleanup called but Renderer has not completed setup!"); - return; - } - if (!GLProxy.hasInstance()) - { - EVENT_LOGGER.warn("Renderer Cleanup called but the GLProxy has never been initalized!"); - return; - } - - this.isSetupComplete = false; - - GLProxy.getInstance().recordOpenGlCall(() -> - { - EVENT_LOGGER.info("Renderer Cleanup Started"); - - this.shaderProgram.free(); - this.shaderProgram = null; - if (this.quadIBO != null) - { - this.quadIBO.destroy(false); - } - - EVENT_LOGGER.info("Renderer Cleanup Complete"); - }); - } - -} +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.render.renderer; + +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; +import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger; +import com.seibel.distanthorizons.core.pos.DhBlockPos; +import com.seibel.distanthorizons.core.render.AbstractRenderBuffer; +import com.seibel.distanthorizons.core.render.RenderBufferHandler; +import com.seibel.distanthorizons.core.render.glObject.GLProxy; +import com.seibel.distanthorizons.core.render.glObject.GLState; +import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer; +import com.seibel.distanthorizons.core.render.glObject.buffer.QuadElementBuffer; +import com.seibel.distanthorizons.core.render.renderer.shaders.*; +import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.util.RenderUtil; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; +import com.seibel.distanthorizons.api.enums.rendering.EDebugRendering; +import com.seibel.distanthorizons.api.enums.rendering.EFogColorMode; +import com.seibel.distanthorizons.core.render.fog.LodFogConfig; +import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor; +import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor; +import com.seibel.distanthorizons.coreapi.util.math.Mat4f; +import com.seibel.distanthorizons.coreapi.util.math.Vec3d; +import com.seibel.distanthorizons.coreapi.util.math.Vec3f; +import org.apache.logging.log4j.LogManager; +import org.lwjgl.opengl.GL32; + +import java.awt.*; +import java.time.Duration; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; + +/** + * This is where all the magic happens.
+ * This is where LODs are draw to the world. + */ +public class LodRenderer +{ + public static final ConfigBasedLogger EVENT_LOGGER = new ConfigBasedLogger(LogManager.getLogger(LodRenderer.class), + () -> Config.Client.Advanced.Logging.logRendererBufferEvent.get()); + public static ConfigBasedSpamLogger tickLogger = new ConfigBasedSpamLogger(LogManager.getLogger(LodRenderer.class), + () -> Config.Client.Advanced.Logging.logRendererBufferEvent.get(), 1); + + private static final IIrisAccessor IRIS_ACCESSOR = ModAccessorInjector.INSTANCE.get(IIrisAccessor.class); + + public static final boolean ENABLE_DRAW_LAG_SPIKE_LOGGING = false; + public static final boolean ENABLE_DUMP_GL_STATE = true; + public static final long DRAW_LAG_SPIKE_THRESHOLD_NS = TimeUnit.NANOSECONDS.convert(20, TimeUnit.MILLISECONDS); + + public static final boolean ENABLE_IBO = true; + + // TODO make these private, the LOD Builder can get these variables from the config itself + public static boolean transparencyEnabled = true; + public static boolean fakeOceanFloor = true; + + /** used to prevent cleaning up render resources while they are being used */ + private static final ReentrantLock renderLock = new ReentrantLock(); + + + + public void setupOffset(DhBlockPos pos) throws IllegalStateException + { + Vec3d cam = MC_RENDER.getCameraExactPosition(); + Vec3f modelPos = new Vec3f((float) (pos.x - cam.x), (float) (pos.y - cam.y), (float) (pos.z - cam.z)); + + if (!GL32.glIsProgram(this.shaderProgram.id)) + { + throw new IllegalStateException("No GL program exists with the ID: ["+this.shaderProgram.id+"]. This either means a shader program was freed while it was still in use or was never created."); + } + + this.shaderProgram.bind(); + this.shaderProgram.setModelPos(modelPos); + } + + public void drawVbo(GLVertexBuffer vbo) + { + vbo.bind(); + this.shaderProgram.bindVertexBuffer(vbo.getId()); + GL32.glDrawElements(GL32.GL_TRIANGLES, (vbo.getVertexCount() / 4) * 6, // TODO what does the 4 and 6 here represent? + this.quadIBO.getType(), 0); + vbo.unbind(); + } + public Vec3f getLookVector() { return MC_RENDER.getLookAtVector(); } + + + public static class LagSpikeCatcher + { + long timer = System.nanoTime(); + + public LagSpikeCatcher() { } + + public void end(String source) + { + if (!ENABLE_DRAW_LAG_SPIKE_LOGGING) + { + return; + } + + this.timer = System.nanoTime() - this.timer; + if (this.timer > DRAW_LAG_SPIKE_THRESHOLD_NS) + { + //4 ms + EVENT_LOGGER.debug("NOTE: " + source + " took " + Duration.ofNanos(this.timer) + "!"); + } + + } + + } + + private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + + public EDebugRendering previousDebugMode = null; + public final RenderBufferHandler bufferHandler; + + // The shader program + LodRenderProgram shaderProgram = null; + public QuadElementBuffer quadIBO = null; + public boolean isSetupComplete = false; + + public LodRenderer(RenderBufferHandler bufferHandler) { this.bufferHandler = bufferHandler; } + + private boolean rendererClosed = false; + public void close() + { + if (this.rendererClosed) + { + EVENT_LOGGER.warn("close() called twice!"); + return; + } + + + this.rendererClosed = true; + + // wait for the renderer to finish before closing (to prevent closing resources that are currently in use) + renderLock.lock(); + try + { + EVENT_LOGGER.info("Shutting down " + LodRenderer.class.getSimpleName() + "..."); + + this.cleanup(); + this.bufferHandler.close(); + + EVENT_LOGGER.info("Finished shutting down " + LodRenderer.class.getSimpleName()); + } + finally + { + renderLock.unlock(); + } + } + + public void drawLODs(Mat4f baseModelViewMatrix, Mat4f baseProjectionMatrix, float partialTicks, IProfilerWrapper profiler) + { + if (this.rendererClosed) + { + EVENT_LOGGER.error("drawLODs() called after close()!"); + return; + } + + if (!renderLock.tryLock()) + { + // never lock the render thread, if the lock isn't available don't wait for it + return; + } + + try + { + if (IRIS_ACCESSOR != null && IRIS_ACCESSOR.isRenderingShadowPass()) + { + // We do not have a wy to properly render shader shadow pass, since they can + // and often do change the projection entirely, as well as the output usage. + + //EVENT_LOGGER.debug("Skipping shadow pass render."); + return; + } + + // get MC's shader program and save MC's render state so we can restore it later + LagSpikeCatcher drawSaveGLState = new LagSpikeCatcher(); + GLState minecraftGlState = new GLState(); + if (ENABLE_DUMP_GL_STATE) + { + tickLogger.debug("Saving GL state: " + minecraftGlState); + } + drawSaveGLState.end("drawSaveGLState"); + + // make sure everything has been initialized + GLProxy glProxy = GLProxy.getInstance(); + + + + //===================// + // draw params setup // + //===================// + + profiler.push("LOD draw setup"); + /*---------Set GL State--------*/ + GL32.glViewport(0, 0, MC_RENDER.getTargetFrameBufferViewportWidth(), MC_RENDER.getTargetFrameBufferViewportHeight()); + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, 0); + boolean renderWireframe = Config.Client.Advanced.Debugging.renderWireframe.get(); + if (renderWireframe) + { + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE); + //GL32.glDisable(GL32.GL_CULL_FACE); + } + else + { + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); + GL32.glEnable(GL32.GL_CULL_FACE); + } + GL32.glEnable(GL32.GL_DEPTH_TEST); + GL32.glDepthFunc(GL32.GL_LESS); + + + + transparencyEnabled = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled; + fakeOceanFloor = Config.Client.Advanced.Graphics.Quality.transparency.get().fakeTransparencyEnabled; + + GL32.glDisable(GL32.GL_BLEND); // We render opaque first, then transparent + GL32.glDepthMask(true); + GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT); + + /*---------Bind required objects--------*/ + // Setup LodRenderProgram and the LightmapTexture if it has not yet been done + // also binds LightmapTexture, VAO, and ShaderProgram + if (!this.isSetupComplete) + { + this.setup(); + } + else + { + LodFogConfig newFogConfig = this.shaderProgram.isShaderUsable(); + if (newFogConfig != null) + { + this.shaderProgram.free(); + this.shaderProgram = new LodRenderProgram(newFogConfig); + + FogShader.INSTANCE.free(); + FogShader.INSTANCE = new FogShader(newFogConfig); + } + this.shaderProgram.bind(); + } + + GL32.glActiveTexture(GL32.GL_TEXTURE0); + + /*---------Get required data--------*/ + int vanillaBlockRenderedDistance = MC_RENDER.getRenderDistance() * LodUtil.CHUNK_WIDTH; + //Mat4f modelViewProjectionMatrix = RenderUtil.createCombinedModelViewProjectionMatrix(baseProjectionMatrix, baseModelViewMatrix, partialTicks); + + Mat4f projectionMatrix = RenderUtil.createLodProjectionMatrix(baseProjectionMatrix, partialTicks); + + Mat4f modelViewProjectionMatrix = new Mat4f(projectionMatrix); + modelViewProjectionMatrix.multiply(RenderUtil.createLodModelViewMatrix(baseModelViewMatrix)); + + /*---------Fill uniform data--------*/ + this.shaderProgram.fillUniformData(modelViewProjectionMatrix, /*Light map = GL_TEXTURE0*/ 0, + MC.getWrappedClientWorld().getMinHeight(), vanillaBlockRenderedDistance); + + // Note: Since lightmapTexture is changing every frame, it's faster to recreate it than to reuse the old one. + ILightMapWrapper lightmap = MC_RENDER.getLightmapWrapper(); + lightmap.bind(); + if (ENABLE_IBO) + { + this.quadIBO.bind(); + } + + this.bufferHandler.buildRenderListAndUpdateSections(this.getLookVector()); + + + + //===========// + // rendering // + //===========// + + LagSpikeCatcher drawLagSpikeCatcher = new LagSpikeCatcher(); + + profiler.popPush("LOD Opaque"); + // TODO: Directional culling + this.bufferHandler.renderOpaque(this); + + if (Config.Client.Advanced.Graphics.Ssao.enabled.get()) + { + profiler.popPush("LOD SSAO"); + SSAOShader.INSTANCE.setProjectionMatrix(projectionMatrix); + SSAORenderer.INSTANCE.render(minecraftGlState, partialTicks); + } + + + profiler.popPush("LOD Fog"); + FogShader.INSTANCE.setModelViewProjectionMatrix(modelViewProjectionMatrix); + FogShader.INSTANCE.render(partialTicks); + + // DarkShader.INSTANCE.render(partialTicks); // A test shader to make the world darker + + + if (Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled) + { + profiler.popPush("LOD Transparent"); + + GL32.glEnable(GL32.GL_BLEND); + GL32.glBlendFunc(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA); + this.bufferHandler.renderTransparent(this); + GL32.glDepthMask(true); // Apparently the depth mask state is stored in the FBO, so glState fails to restore it... + + FogShader.INSTANCE.render(partialTicks); + } + + drawLagSpikeCatcher.end("LodDraw"); + + + + //================// + // render cleanup // + //================// + + profiler.popPush("LOD cleanup"); + LagSpikeCatcher drawCleanup = new LagSpikeCatcher(); + lightmap.unbind(); + if (ENABLE_IBO) + { + this.quadIBO.unbind(); + } + + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, 0); + + this.shaderProgram.unbind(); + + if (Config.Client.Advanced.Debugging.debugWireframeRendering.get()) + { + profiler.popPush("Debug wireframes"); + // Note: this can be very slow if a lot of boxes are being rendered + DebugRenderer.INSTANCE.render(modelViewProjectionMatrix); + profiler.popPush("LOD cleanup"); + } + + GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT); + + minecraftGlState.restore(); + drawCleanup.end("LodDrawCleanup"); + + // end of internal LOD profiling + profiler.pop(); + tickLogger.incLogTries(); + + } + finally + { + renderLock.unlock(); + } + } + + + + //=================// + // Setup Functions // + //=================// + + /** Setup all render objects - REQUIRES to be in render thread */ + private void setup() + { + if (this.isSetupComplete) + { + EVENT_LOGGER.warn("Renderer setup called but it has already completed setup!"); + return; + } + if (!GLProxy.hasInstance()) + { + EVENT_LOGGER.warn("Renderer setup called but GLProxy has not yet been setup!"); + return; + } + + EVENT_LOGGER.info("Setting up renderer"); + this.isSetupComplete = true; + this.shaderProgram = new LodRenderProgram(LodFogConfig.generateFogConfig()); // TODO this doesn't actually use the fog config + if (ENABLE_IBO) + { + this.quadIBO = new QuadElementBuffer(); + this.quadIBO.reserve(AbstractRenderBuffer.MAX_QUADS_PER_BUFFER); + } + EVENT_LOGGER.info("Renderer setup complete"); + } + + private Color getFogColor(float partialTicks) + { + Color fogColor; + + if (Config.Client.Advanced.Graphics.Fog.colorMode.get() == EFogColorMode.USE_SKY_COLOR) + { + fogColor = MC_RENDER.getSkyColor(); + } + else + { + fogColor = MC_RENDER.getFogColor(partialTicks); + } + + return fogColor; + } + private Color getSpecialFogColor(float partialTicks) { return MC_RENDER.getSpecialFogColor(partialTicks); } + + + + //======================// + // Cleanup Functions // + //======================// + + /** + * cleanup and free all render objects. MUST be on the render thread + * (Many objects are Native, outside of JVM, and need manual cleanup) + */ + private void cleanup() + { + if (!this.isSetupComplete) + { + EVENT_LOGGER.warn("Renderer cleanup called but Renderer has not completed setup!"); + return; + } + if (!GLProxy.hasInstance()) + { + EVENT_LOGGER.warn("Renderer Cleanup called but the GLProxy has never been initalized!"); + return; + } + + this.isSetupComplete = false; + + GLProxy.getInstance().recordOpenGlCall(() -> + { + EVENT_LOGGER.info("Renderer Cleanup Started"); + + this.shaderProgram.free(); + this.shaderProgram = null; + if (this.quadIBO != null) + { + this.quadIBO.destroy(false); + } + + EVENT_LOGGER.info("Renderer Cleanup Complete"); + }); + } + +} \ No newline at end of file