This library adds a new element to the JavaFX for rendering OpenGL graphics using LWJGL, JOGL, LWJGL2 or LibGDX. It is optimized for each platform and includes some auxiliary functions for working with OpenGL from JavaFX.
NOTE: All examples are written in Kotlin + Gradle + LWJGL. If you want to use Java/JOGL/Maven, you can use example code generator.
dependencies {
// implementation JavaFX
// implementation LWJGL
// implementation ...
implementation 'com.huskerdev:openglfx-lwjgl:4.1.14'
}
Available modules:
openglfx-lwjgl
openglfx-lwjgl2
openglfx-jogl
openglfx-libgdx
If you are using JPMS (Java 9+), then you need to add the following module in module-info.java
:
requires openglfx.lwjgl;
openglfx
adds exports for JavaFX at runtime, but if you experience issues with package access, then add following arguments to JVM:
-Dopenglfx.disable.exports=true
Then add --add-exports
by hand:
--add-exports=javafx.graphics/com.sun.prism=ALL-UNNAMED
--add-exports=javafx.graphics/com.sun.javafx.scene.layout=ALL-UNNAMED
--add-exports=javafx.graphics/com.sun.javafx.scene=ALL-UNNAMED
--add-exports=javafx.graphics/com.sun.javafx.sg.prism=ALL-UNNAMED
--add-exports=javafx.graphics/com.sun.scenario=ALL-UNNAMED
--add-exports=javafx.graphics/com.sun.javafx.tk=ALL-UNNAMED
--add-exports=javafx.graphics/com.sun.glass.ui=ALL-UNNAMED
https://github.com/husker-dev/openglfx-example
This library adds only one component - GLCanvas
, that can be used like a regular element in JavaFX.
After canvas is not needed anymore, call dispose()
to free the allocated memory.
import com.huskerdev.openglfx.canvas.GLCanvas
import com.huskerdev.openglfx.lwjgl.LWJGLExecutor.Companion.LWJGL_MODULE
val canvas = GLCanvas(LWJGL_MODULE)
Available modules:
LWJGL_MODULE
LWJGL2_MODULE
JOGL_MODULE
LIBGDX_MODULE
GLCanvas
uses a logic similar to JOGL. The component has events where you can render the content.
canvas.addOnInitEvent { event ->
// Init some gl properties only once
}
canvas.addOnRenderEvent { event ->
// Render some content every frame
}
canvas.addOnReshapeEvent { event ->
// Change viewport matrices at resizing
}
canvas.addOnDisposeEvent { event ->
// Clear native data at disposing
}
GLCanvas
supports multi-sampling anti-aliasing. It can be enabled during creation.
For maximum possible MSAA level, specify -1.
GLCanvas(.., msaa = 4)
GLCanvas
based on the swap chain system - you can think of it as "double buffering".
The best UI performance is achieved with 2
(default). The most responsive to resizing is 1
.
GLCanvas(.., swapBuffers = 1)
By default, OpenGL draws the image upside down. GLCanvas
has the ability to flip it without loss of performance. To do this, specify the parameter at startup.
GLCanvas(.., flipY = true)
In GLCanvas you can specify the desired profile. I advise you to always choose Core. To do this, you need to specify a parameter in constructor.
GLCanvas(.., profile = GLProfile.CORE)
GLCanvas(.., profile = GLProfile.COMPATIBILITY)
If you need to update content with a certain FPS, then you should use property fps
. Keep in mind that JavaFX can limits the refresh rate.
Value | Behavior |
---|---|
< 0 | Monitor refresh rate |
0 | Do not updates automatically (Keep in mind that the update may be triggered by resizing or other commands from JavaFX) |
> 0 | Update with desired FPS |
// In constructor
val canvas = GLCanvas(..., fps = 30)
// Or at runtime
canvas.fps = 40
Don't forget to disable VSync before JavaFX initialization if you want to get FPS more than monitor's frequency.
System.setProperty("prism.vsync", "false")
openglfx
has the ability to move images from JavaFX to OpenGL textures and vice versa. A special class is used for this:
val fbo = GLImageManager.toGL(image)
val image = GLImageManager.fromGL(fbo, width, height)
openglfx
supports RenderDoc integration.
To use it, you need to set externalWindow
in GLCanvas constructor to true
.
This will create a separate window with the rendered image, which you can connect to via RenderDoc or NSight.
To use in LibGDX project, you should create new module.
Minimal build.gradle
example:
plugins {
id("org.jetbrains.kotlin.jvm")
}
sourceSets.main.resources.srcDirs += [ rootProject.file('assets').path ]
dependencies {
implementation project(':core')
implementation "com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion"
implementation "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
// openglfx
api("com.huskerdev:openglfx-libgdx:4.1.0")
// implementation(/* JavaFX */)
// implementation(/* LWJGL */)
}
Instead of using GLCanvas, you should use LibGDXCanvas
:
val canvas = LibGDXCanvas(Main()) // Main is ApplicationAdapter
-
husker-dev/grapl is used to create offscreen thread-independent GL context on Windows, MacOS and Linux.
-
Executors are the bridges from OpenGLFX inner logic to outer libraries like LWJGL or JOGL.
LWJGL JOGL Class LWJGLExecutor.kt JOGLFXExecutor.kt Instance LWJGL_MODULE JOGL_MODULE If you want to add new OpenGL library, just create your implementation of GLExecutor and use it as existing one:
GLCanvas.create(YOUR_EXECUTOR_INSTANCE)
. -
To efficiently connect OpenGL and JavaFX, OpenGLFX uses some techniques based on OS and supported extensions.
Description Implementation Windows
(that supports EXT_external_objects)EXT_external_objects_win32 is used to create shared DXGI texture between DirectX 9 and OpenGL. ExternalObjectsCanvasWinD3D.kt Windows
(that supports WGL_NV_DX_interop)NV_DX_interop is used to synchronize textures between DirectX 9 and OpenGL. WGLDXInteropCanvas.kt Windows
(es2 pipeline)EXT_external_objects_win32 is used to create chain with shared texture:
OpenGL ⟷ Vulkan ⟷ OpenGLExternalObjectsCanvasWinES2.kt Linux EXT_external_objects_fd is used to create chain with shared texture:
OpenGL ⟷ Vulkan ⟷ OpenGLExternalObjectsCanvasFd.kt macOS IOSurface is used to create memory block in VRAM that can be used in different OpenGL contexts. IOSurfaceCanvas.kt Other Copies ByteBuffer from glReadPixels
to JavaFX textureBlitCanvas.kt
- streamingdv - big donation, debugging
- James H Ball - macOS tester
- Andrew Hamilton - macOS tester, suggested new additions