JPlotter icon indicating copy to clipboard operation
JPlotter copied to clipboard

Improve GL resource clean up

Open hageldave opened this issue 5 months ago • 0 comments

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 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.

hageldave avatar Sep 12 '24 12:09 hageldave