diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/LWJGLInitializer.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/LWJGLInitializer.java index 17e36f6..30f72b4 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/LWJGLInitializer.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/LWJGLInitializer.java @@ -83,6 +83,8 @@ class LWJGLInitializer { glEnable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + RenderTaskQueue.schedule(OpenGLObjectTracker::deleteEnqueuedObjects); } private static void setupWindowCallbacks() { diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/OpenGLObjectTracker.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/OpenGLObjectTracker.java index 93931fc..decf3ca 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/OpenGLObjectTracker.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/OpenGLObjectTracker.java @@ -17,29 +17,80 @@ *******************************************************************************/ package ru.windcorp.progressia.client.graphics.backend; +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; import java.util.ArrayList; import java.util.Collection; - -/* - * FIXME deal with client invocations of .delete() when properly disposing of - * objects mid-execution - */ +import java.util.function.IntConsumer; public class OpenGLObjectTracker { - - public static interface OpenGLDeletable { - void delete(); + + public interface OpenGLDeletable { + int getHandle(); } - private static final Collection TO_DELETE = new ArrayList<>(); + private static final Collection> TO_DELETE = new ArrayList<>(); + private static final ReferenceQueue DELETE_QUEUE = new ReferenceQueue<>(); - public synchronized static void register(OpenGLDeletable object) { - TO_DELETE.add(object); - } - - public synchronized static void deleteAllObjects() { - TO_DELETE.forEach(OpenGLDeletable::delete); - TO_DELETE.clear(); + public synchronized static void register(OpenGLDeletable object, IntConsumer glDeleter) { + GLPhantomReference glRef = + new GLPhantomReference<>(object, DELETE_QUEUE, object.getHandle(), glDeleter); + TO_DELETE.add(glRef); } + public static void deleteAllObjects() { + for (GLPhantomReference glRef + : TO_DELETE + ) { + glRef.clear(); + } + } + + public static void deleteEnqueuedObjects() { + while (true) { + GLPhantomReference glRef; + glRef = (GLPhantomReference) DELETE_QUEUE.poll(); + if (glRef == null) { + break; + } else { + glRef.delete(); + } + } + } + + private static class GLPhantomReference extends PhantomReference { + + private final int referentGLhandle; + private final IntConsumer GLDeleter; + + /** + * Creates a new phantom reference that refers to the given object and + * is registered with the given queue. + * + *

It is possible to create a phantom reference with a {@code null} + * queue, but such a reference is completely useless: Its {@code get} + * method will always return {@code null} and, since it does not have a queue, + * it will never be enqueued. + * + * @param referent the object the new phantom reference will refer to + * @param q the queue with which the reference is to be registered, + * or {@code null} if registration is not required + */ + public GLPhantomReference(T referent, + ReferenceQueue q, + int referentGLhandle, + IntConsumer GLDeleter) { + super(referent, q); + this.referentGLhandle = referentGLhandle; + this.GLDeleter = GLDeleter; + } + + public int getHandle() { + return referentGLhandle; + } + + public void delete() { + GLDeleter.accept(referentGLhandle); + } + } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/VertexBufferObject.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/VertexBufferObject.java index 4631038..1039cde 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/VertexBufferObject.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/VertexBufferObject.java @@ -21,6 +21,7 @@ import static org.lwjgl.opengl.GL20.*; import java.nio.*; +import org.lwjgl.opengl.GL20; import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable; public class VertexBufferObject implements OpenGLDeletable { @@ -47,7 +48,7 @@ public class VertexBufferObject implements OpenGLDeletable { public VertexBufferObject(Usage usage) { handle = glGenBuffers(); - OpenGLObjectTracker.register(this); + OpenGLObjectTracker.register(this, GL20::glDeleteBuffers); this.usage = usage; } @@ -194,10 +195,4 @@ public class VertexBufferObject implements OpenGLDeletable { public int getHandle() { return handle; } - - @Override - public void delete() { - glDeleteBuffers(handle); - } - } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Program.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Program.java index 2b63885..970fa10 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Program.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Program.java @@ -20,6 +20,7 @@ package ru.windcorp.progressia.client.graphics.backend.shaders; import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL20.*; +import org.lwjgl.opengl.GL20; import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker; import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable; import ru.windcorp.progressia.client.graphics.backend.shaders.attributes.Attribute; @@ -32,7 +33,7 @@ public class Program implements OpenGLDeletable { public Program(Shader vertexShader, Shader fragmentShader) { handle = glCreateProgram(); - OpenGLObjectTracker.register(this); + OpenGLObjectTracker.register(this, GL20::glDeleteProgram); glAttachShader(handle, vertexShader.getHandle()); glAttachShader(handle, fragmentShader.getHandle()); @@ -57,8 +58,6 @@ public class Program implements OpenGLDeletable { } @Override - public void delete() { - glDeleteProgram(handle); - } + public int getHandle() { return handle; } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Shader.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Shader.java index 27567e7..1f8f6d3 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Shader.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Shader.java @@ -22,6 +22,7 @@ import static org.lwjgl.opengl.GL20.*; import java.util.Locale; +import org.lwjgl.opengl.GL20; import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker; import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable; import ru.windcorp.progressia.common.resource.Resource; @@ -69,7 +70,7 @@ public class Shader implements OpenGLDeletable { public Shader(ShaderType type, String source) { handle = glCreateShader(type.getGlCode()); - OpenGLObjectTracker.register(this); + OpenGLObjectTracker.register(this, GL20::glDeleteShader); this.type = type; @@ -90,11 +91,6 @@ public class Shader implements OpenGLDeletable { ); } - @Override - public void delete() { - glDeleteShader(handle); - } - public int getHandle() { return handle; } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/texture/TexturePrimitive.java b/src/main/java/ru/windcorp/progressia/client/graphics/texture/TexturePrimitive.java index b4df4fb..bf42fa7 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/texture/TexturePrimitive.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/texture/TexturePrimitive.java @@ -22,6 +22,7 @@ import static org.lwjgl.opengl.GL20.*; import java.util.Arrays; +import org.lwjgl.opengl.GL11; import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker; import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable; import ru.windcorp.progressia.common.util.crash.CrashReports; @@ -87,7 +88,7 @@ public class TexturePrimitive implements OpenGLDeletable { if (isLoaded()) return; handle = pixels.load(); - OpenGLObjectTracker.register(this); + OpenGLObjectTracker.register(this, GL11::glDeleteTextures); if (handle < 0) { CrashReports.report(null, "Failed to allocate texture"); @@ -95,9 +96,7 @@ public class TexturePrimitive implements OpenGLDeletable { } @Override - public void delete() { - if (isLoaded()) - glDeleteTextures(handle); + public int getHandle() { + return handle; } - }