Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve GL resource clean up #67

Open
hageldave opened this issue Sep 12, 2024 · 0 comments
Open

Improve GL resource clean up #67

hageldave opened this issue Sep 12, 2024 · 0 comments

Comments

@hageldave
Copy link
Owner

hageldave commented Sep 12, 2024

GL resources are non-heap objects that are allocated through the application's GL context and are somewhere in memory but are not destroyed via Java's garbage collection. Examples of such objects are FBO, Shader, and VertexArray.

Current state

Currently the classes that are responsible for the GL resource implement the AutoClosable so the compiler occasionally reminds the developer to close such an object, where close() takes care of destruction of the GL resources.

/**
* Disposes of this {@link VertexArray} GL resources, i.e.
* deletes GL buffer and vertex array objects.
*/
@Override
@GLContextRequired
public void close() {
for(int i = 0; i < vbos.length; i++){
glDeleteBuffers(vbos[i]);
vbos[i] = 0;
}
glDeleteBuffers(ibo);
glDeleteVertexArrays(va);
ibo = va = 0;
}

However, the delete calls in close() only work when the GL context that created the resources is active, hence the @GLContextRequired annotation. In order for close() to work, it needs to be called during rendering of the FBOCanvas (or parent AWTGLCanvas) or using the canvas' runInContext(Runnable) method.

Since this is all a lot to remember there is a mechanic to automatically call close() on the various GL resource holding objects when the window that displays the FBOCanvas closes.

/**
* Adds a {@link WindowListener} to the specified window that takes care of
* cleanup (GL resources) when the window is about to close.
* <p>
* This method only has an effect when this {@link JPlotterCanvas} is an instance of {@link FBOCanvas}
* which uses GL resources (see {@link FBOCanvas#createCleanupOnWindowClosingListener()}).
* Otherwise no WindowListener is created or added.
*
* @param window the window to add the listener to (should be the window containing this canvas)
* @return the added listener when this is an FBOCanvas, else null
*/
public default WindowListener addCleanupOnWindowClosingListener(Window window) {
if(this instanceof FBOCanvas) {
WindowListener l = ((FBOCanvas)this).createCleanupOnWindowClosingListener();
window.addWindowListener(l);
return l;
}
return null;
}

public WindowListener createCleanupOnWindowClosingListener() {
return new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
FBOCanvas.this.runInContext(()->FBOCanvas.this.close());
}
};
}

The close() call on FBOCanvas implies close() on subclass BlankCanvas which starts a close() cascade into its Renderer which in turn closes it's attached Renderable items (such as Linse or Points).

Problem

While this works for the specific case that all GL resources are somehow connected to the render tree of BlankCanvas, i.e., all created renderers are reachable from BlankCanvas (e.g. through ChainedRenderer or CompleteRenderer) and all of the created Renderables are also reachable from their Renderers.
If for example a Renderer was replaced through BlankCanvas.setRenderer(Renderer), it is no longer reachable and needs to be closed() manually. Same goes for Renderable objects that are removed through removeItemToRender(Renderable).

Such objects are floating around and garbage collected eventually if not used anymore, but without successful GL resource deletion due to missing the active GL context. This can potentially pollute graphics memory.

Possible solution

There are 2 parts to the solution

  1. The object that hosts the GL context (FBOCanvas) keeps track of all the resources that are allocated. Classes that create GL resources need to report to the canvas in this case. When FBOCanvas closes, it deletes all the resources that it kept track of.
  2. when GL resource holding objects are garbage collected (without the canvas being closed), they tell FBOCanvas to delete their resources later.

There is one caveat though: In order to implement 2. the object needs to know that it is now about to be garbage collected. Prior to Java 9 this would be done with Object.finalize(). However, finalize() has been deprecated since and replaced with the Cleaner mechanic. JPlotter is currently compatible with Java 8, and would need to be updated to a newer version of Java like 11 LTS.
Since the support for Java 8 has long been discontinued, there is no reason to keep compatibility. So the first step towards this issue is to migrate to Java 11.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant