Fix GC rarely deleting in use GL buffers

This commit is contained in:
James Seibel
2026-04-23 16:57:17 -05:00
parent 7a0fec2c2f
commit 396315bd05
@@ -137,6 +137,8 @@ public class GLBuffer implements AutoCloseable
// is still used somewhere // is still used somewhere
if (oldId != 0) if (oldId != 0)
{ {
// this ID doesn't need to be tracked anymore
tryRemoveBufferIdFromPhantom(oldId);
destroyBufferIdNow(oldId); destroyBufferIdNow(oldId);
} }
@@ -169,6 +171,11 @@ public class GLBuffer implements AutoCloseable
final int idToDelete = this.id; // saving the ID to a separate variable is necessary so it can be captured by the lambda final int idToDelete = this.id; // saving the ID to a separate variable is necessary so it can be captured by the lambda
// remove the phantom tracking now so the phantom doesn't have the chance to
// get garbage collected before the render thread task runs
// (this can happen if MC is running at extremely low framerates like 1 fps via mods)
tryRemoveBufferIdFromPhantom(idToDelete);
// mark the old data is invalid before deleting to prevent a rare race condition // 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 // where the queued on render thread task runs before the ID is cleared
this.id = 0; this.id = 0;
@@ -181,6 +188,7 @@ public class GLBuffer implements AutoCloseable
renderStampLock.unlock(writeStamp); renderStampLock.unlock(writeStamp);
} }
} }
private static void destroyBufferIdNow(int id) private static void destroyBufferIdNow(int id)
{ {
// only delete valid buffers // only delete valid buffers
@@ -190,20 +198,6 @@ public class GLBuffer implements AutoCloseable
return; return;
} }
// remove and clear the phantom reference if present
if (BUFFER_ID_TO_PHANTOM.containsKey(id))
{
Reference<? extends GLBuffer> phantom = BUFFER_ID_TO_PHANTOM.get(id);
// if we are manually closing this buffer, we don't want the phantom reference to accidentally close it again
// this can cause a race condition were we accidentally delete an in-use buffer and cause NVIDIA
// to throw an EXCEPTION_ACCESS_VIOLATION when we attempt to render it
phantom.clear();
PHANTOM_TO_BUFFER_ID.remove(phantom);
BUFFER_ID_TO_PHANTOM.remove(id);
}
bufferCount.decrementAndGet(); bufferCount.decrementAndGet();
// destroy the buffer if it exists, // destroy the buffer if it exists,
@@ -217,6 +211,32 @@ public class GLBuffer implements AutoCloseable
LOGGER.info("destroyed buffer [" + id + "], remaining: [" + BUFFER_ID_TO_PHANTOM.size() + "]"); LOGGER.info("destroyed buffer [" + id + "], remaining: [" + BUFFER_ID_TO_PHANTOM.size() + "]");
} }
} }
else
{
// shouldn't happen, but just in case
LOGGER.warn("Attempted to destroy a non buffer object with ID ["+id+"].");
}
}
/** should be called before {@link GLBuffer#destroyBufferIdNow} */
private static void tryRemoveBufferIdFromPhantom(int id)
{
if (BUFFER_ID_TO_PHANTOM.containsKey(id))
{
Reference<? extends GLBuffer> phantom = BUFFER_ID_TO_PHANTOM.get(id);
// if we are manually closing this buffer, we don't want the phantom reference to accidentally close it again
// this can cause a race condition were we accidentally delete an in-use buffer and cause NVIDIA
// to throw an EXCEPTION_ACCESS_VIOLATION when we attempt to render it
phantom.clear();
PHANTOM_TO_BUFFER_ID.remove(phantom);
BUFFER_ID_TO_PHANTOM.remove(id);
}
else
{
LOGGER.warn("Unable to remove phantom GLBuffer with ID ["+id+"], buffer may have already been deleted.");
}
} }
//endregion //endregion