diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLBuffer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLBuffer.java index 6aca3891e..4ba33cfce 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLBuffer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLBuffer.java @@ -69,6 +69,17 @@ public class GLBuffer implements AutoCloseable protected boolean bufferStorage; protected boolean isMapped = false; + /** + * Locking on the render thread isn't great, but is needed due to an inconsistent + * race condition where VBOs can be marked as deleted outside the render thread.

+ * + * But, due to being a read-write lock the chance of freezing + * the render thread is very low + * and since this is a stamped lock, the optimistic read time is basically zero. + * (The optimistic lock time doesn't even appear in the profiler). + */ + public final StampedLock renderStampLock = new StampedLock(); + //==============// @@ -142,14 +153,27 @@ public class GLBuffer implements AutoCloseable return; } - final int idToDelete = this.id; // saving the ID to a separate variable is necessary so it can be captured by the lambda - // mark the old data is invalid before deleting to prevent a rare race condition - // where the queued on render thread task runs before the ID is cleared - this.id = 0; - this.size = 0; - - RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("GLBuffer destroyAsync", () -> { destroyBufferIdNow(idToDelete); }); + long writeStamp = 0; + try + { + // lock to prevent the render thread from accessing the buffer's ID + // while we are removing it + writeStamp = renderStampLock.writeLock(); + + final int idToDelete = this.id; // saving the ID to a separate variable is necessary so it can be captured by the lambda + + // mark the old data is invalid before deleting to prevent a rare race condition + // where the queued on render thread task runs before the ID is cleared + this.id = 0; + this.size = 0; + + RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("GLBuffer destroyAsync", () -> { destroyBufferIdNow(idToDelete); }); + } + finally + { + renderStampLock.unlock(writeStamp); + } } private static void destroyBufferIdNow(int id) { diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/terrain/GlDhTerrainShaderProgram.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/terrain/GlDhTerrainShaderProgram.java index ff8b9b6cf..df547a171 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/terrain/GlDhTerrainShaderProgram.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/terrain/GlDhTerrainShaderProgram.java @@ -341,33 +341,45 @@ public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiS continue; } - // don't render empty sections - if (vbo.getVertexCount() == 0) + + // for lock information please view the lock's javadocs + long vboReadStamp = vbo.renderStampLock.readLock(); + long iboReadStamp = vbo.getQuadIBO().renderStampLock.readLock(); + try { - continue; + // don't render empty sections + if (vbo.getVertexCount() == 0) + { + continue; + } + + // don't render deleted VBOs (this will crash the driver/game) + if (vbo.getId() == 0 + || vbo.getQuadIBO().getId() == 0) + { + continue; + } + + // 4 vertices per face, but 6 indices (IE 2 triangles) per face, aka need to multiply by 1.5 + int indexCount = (int) (vbo.getVertexCount() * 1.5); + + vbo.bind(); + vbo.getQuadIBO().bind(); + + GlDhMetaRenderer.INSTANCE.shaderProgramForThisFrame.bindVertexBuffer(vbo.getId()); + GL32.glDrawElements( + GL32.GL_TRIANGLES, + indexCount, + vbo.getQuadIBO().getGlType(), 0); + + vbo.unbind(); + vbo.getQuadIBO().unbind(); } - - // don't render deleted VBOs (this will crash the driver/game) - if (vbo.getId() == 0 - || vbo.getQuadIBO().getId() == 0) + finally { - continue; + vbo.renderStampLock.unlock(vboReadStamp); + vbo.getQuadIBO().renderStampLock.unlock(iboReadStamp); } - - // 4 vertices per face, but 6 indices (IE 2 triangles) per face, aka need to multiply by 1.5 - int indexCount = (int)(vbo.getVertexCount() * 1.5); - - vbo.bind(); - vbo.getQuadIBO().bind(); - - GlDhMetaRenderer.INSTANCE.shaderProgramForThisFrame.bindVertexBuffer(vbo.getId()); - GL32.glDrawElements( - GL32.GL_TRIANGLES, - indexCount, - vbo.getQuadIBO().getGlType(), 0); - - vbo.unbind(); - vbo.getQuadIBO().unbind(); } } } diff --git a/coreSubProjects b/coreSubProjects index e465ef532..d9f3b31cc 160000 --- a/coreSubProjects +++ b/coreSubProjects @@ -1 +1 @@ -Subproject commit e465ef5325479fa67e8227d574f145f2a07f842a +Subproject commit d9f3b31cc56dc463283a68ab89139562f4c851e9