potential nvidia null VBO pointer crash fix

This commit is contained in:
James Seibel
2026-04-22 22:33:17 -05:00
parent 118ef39c30
commit 33e6ce6376
3 changed files with 67 additions and 31 deletions
@@ -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. <br><br>
*
* 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)
{
@@ -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();
}
}
}