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
- 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.
- 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.
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, andVertexArray.Current state
Currently the classes that are responsible for the GL resource implement the
AutoClosableso the compiler occasionally reminds the developer to close such an object, whereclose()takes care of destruction of the GL resources.JPlotter/jplotter/src/main/java/hageldave/jplotter/gl/VertexArray.java
Lines 210 to 224 in 82d78a8
However, the delete calls in
close()only work when the GL context that created the resources is active, hence the@GLContextRequiredannotation. In order forclose()to work, it needs to be called during rendering of theFBOCanvas(or parentAWTGLCanvas) 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 theFBOCanvascloses.JPlotter/jplotter/src/main/java/hageldave/jplotter/canvas/JPlotterCanvas.java
Lines 353 to 371 in 82d78a8
JPlotter/jplotter/src/main/java/hageldave/jplotter/canvas/FBOCanvas.java
Lines 748 to 755 in 82d78a8
The
close()call onFBOCanvasimpliesclose()on subclassBlankCanvaswhich starts aclose()cascade into itsRendererwhich in turn closes it's attachedRenderableitems (such asLinseorPoints).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 fromBlankCanvas(e.g. throughChainedRendererorCompleteRenderer) and all of the createdRenderables are also reachable from theirRenderers.If for example a Renderer was replaced through
BlankCanvas.setRenderer(Renderer), it is no longer reachable and needs to be closed() manually. Same goes forRenderableobjects that are removed throughremoveItemToRender(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
FBOCanvas) keeps track of all the resources that are allocated. Classes that create GL resources need to report to the canvas in this case. WhenFBOCanvascloses, it deletes all the resources that it kept track of.FBOCanvasto 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 theCleanermechanic. 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.