JPlotter
JPlotter copied to clipboard
Improve GL resource clean up
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.
https://github.com/hageldave/JPlotter/blob/82d78a889a365fcda0c98fa27afcd5be39a42cc9/jplotter/src/main/java/hageldave/jplotter/gl/VertexArray.java#L210-L224
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.
https://github.com/hageldave/JPlotter/blob/82d78a889a365fcda0c98fa27afcd5be39a42cc9/jplotter/src/main/java/hageldave/jplotter/canvas/JPlotterCanvas.java#L353-L371
https://github.com/hageldave/JPlotter/blob/82d78a889a365fcda0c98fa27afcd5be39a42cc9/jplotter/src/main/java/hageldave/jplotter/canvas/FBOCanvas.java#L748-L755
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 Renderable
s are also reachable from their Renderer
s.
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. WhenFBOCanvas
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.