Added OpenGL buffers cleanup

- Now OpenGL buffers are being cleaned when GL objects are disposed
- Fixed a bug when garbage collector ignore OpenGL objects
This commit is contained in:
Евгений Смирнов 2020-12-16 18:09:56 +03:00
parent bfd5c02b0c
commit bfdb22f357
6 changed files with 80 additions and 38 deletions

View File

@ -83,6 +83,8 @@ class LWJGLInitializer {
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
RenderTaskQueue.schedule(OpenGLObjectTracker::deleteEnqueuedObjects);
} }
private static void setupWindowCallbacks() { private static void setupWindowCallbacks() {

View File

@ -17,29 +17,80 @@
*******************************************************************************/ *******************************************************************************/
package ru.windcorp.progressia.client.graphics.backend; package ru.windcorp.progressia.client.graphics.backend;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.function.IntConsumer;
/*
* FIXME deal with client invocations of .delete() when properly disposing of
* objects mid-execution
*/
public class OpenGLObjectTracker { public class OpenGLObjectTracker {
public static interface OpenGLDeletable { public interface OpenGLDeletable {
void delete(); int getHandle();
} }
private static final Collection<OpenGLDeletable> TO_DELETE = new ArrayList<>(); private static final Collection<GLPhantomReference<OpenGLDeletable>> TO_DELETE = new ArrayList<>();
private static final ReferenceQueue<OpenGLDeletable> DELETE_QUEUE = new ReferenceQueue<>();
public synchronized static void register(OpenGLDeletable object) { public synchronized static void register(OpenGLDeletable object, IntConsumer glDeleter) {
TO_DELETE.add(object); GLPhantomReference<OpenGLDeletable> glRef =
new GLPhantomReference<>(object, DELETE_QUEUE, object.getHandle(), glDeleter);
TO_DELETE.add(glRef);
} }
public synchronized static void deleteAllObjects() { public static void deleteAllObjects() {
TO_DELETE.forEach(OpenGLDeletable::delete); for (GLPhantomReference<OpenGLDeletable> glRef
TO_DELETE.clear(); : 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<T> extends PhantomReference<T> {
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.
*
* <p> 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<? super T> 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);
}
}
} }

View File

@ -21,6 +21,7 @@ import static org.lwjgl.opengl.GL20.*;
import java.nio.*; import java.nio.*;
import org.lwjgl.opengl.GL20;
import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable; import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable;
public class VertexBufferObject implements OpenGLDeletable { public class VertexBufferObject implements OpenGLDeletable {
@ -47,7 +48,7 @@ public class VertexBufferObject implements OpenGLDeletable {
public VertexBufferObject(Usage usage) { public VertexBufferObject(Usage usage) {
handle = glGenBuffers(); handle = glGenBuffers();
OpenGLObjectTracker.register(this); OpenGLObjectTracker.register(this, GL20::glDeleteBuffers);
this.usage = usage; this.usage = usage;
} }
@ -194,10 +195,4 @@ public class VertexBufferObject implements OpenGLDeletable {
public int getHandle() { public int getHandle() {
return handle; return handle;
} }
@Override
public void delete() {
glDeleteBuffers(handle);
}
} }

View File

@ -20,6 +20,7 @@ package ru.windcorp.progressia.client.graphics.backend.shaders;
import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL20.*; 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;
import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable; import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable;
import ru.windcorp.progressia.client.graphics.backend.shaders.attributes.Attribute; 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) { public Program(Shader vertexShader, Shader fragmentShader) {
handle = glCreateProgram(); handle = glCreateProgram();
OpenGLObjectTracker.register(this); OpenGLObjectTracker.register(this, GL20::glDeleteProgram);
glAttachShader(handle, vertexShader.getHandle()); glAttachShader(handle, vertexShader.getHandle());
glAttachShader(handle, fragmentShader.getHandle()); glAttachShader(handle, fragmentShader.getHandle());
@ -57,8 +58,6 @@ public class Program implements OpenGLDeletable {
} }
@Override @Override
public void delete() { public int getHandle() { return handle; }
glDeleteProgram(handle);
}
} }

View File

@ -22,6 +22,7 @@ import static org.lwjgl.opengl.GL20.*;
import java.util.Locale; 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;
import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable; import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable;
import ru.windcorp.progressia.common.resource.Resource; import ru.windcorp.progressia.common.resource.Resource;
@ -69,7 +70,7 @@ public class Shader implements OpenGLDeletable {
public Shader(ShaderType type, String source) { public Shader(ShaderType type, String source) {
handle = glCreateShader(type.getGlCode()); handle = glCreateShader(type.getGlCode());
OpenGLObjectTracker.register(this); OpenGLObjectTracker.register(this, GL20::glDeleteShader);
this.type = type; this.type = type;
@ -90,11 +91,6 @@ public class Shader implements OpenGLDeletable {
); );
} }
@Override
public void delete() {
glDeleteShader(handle);
}
public int getHandle() { public int getHandle() {
return handle; return handle;
} }

View File

@ -22,6 +22,7 @@ import static org.lwjgl.opengl.GL20.*;
import java.util.Arrays; 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;
import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable; import ru.windcorp.progressia.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable;
import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.util.crash.CrashReports;
@ -87,7 +88,7 @@ public class TexturePrimitive implements OpenGLDeletable {
if (isLoaded()) return; if (isLoaded()) return;
handle = pixels.load(); handle = pixels.load();
OpenGLObjectTracker.register(this); OpenGLObjectTracker.register(this, GL11::glDeleteTextures);
if (handle < 0) { if (handle < 0) {
CrashReports.report(null, "Failed to allocate texture"); CrashReports.report(null, "Failed to allocate texture");
@ -95,9 +96,7 @@ public class TexturePrimitive implements OpenGLDeletable {
} }
@Override @Override
public void delete() { public int getHandle() {
if (isLoaded()) return handle;
glDeleteTextures(handle);
} }
} }