I'm having issues getting JOGL canvas/panel in JavaFX scene. My assumption was, that if JOGL GLCanvas adheres to contract for awt.Canvas, there should be no issue in placing it in JFX scene.
I got together following code (all snippets are inside JFX Application::start).
First creation of JOGL "hello world" (pulsating background just to see if things render and move):
GLCanvas glCanvas = new GLCanvas();
glCanvas.setSize(size-10, size-10);
glCanvas.addGLEventListener(new GLEventListener() {
private final long t0 = System.currentTimeMillis();
@Override
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {}
@Override
public void init(GLAutoDrawable drawable) {
new Thread(() -> {while (true) glCanvas.display();}).start();
}
@Override
public void dispose(GLAutoDrawable drawable) {}
@Override
public void display(GLAutoDrawable drawable) {
GL4 gl = drawable.getGL().getGL4();
float t = (float)(Math.sin((System.currentTimeMillis()-t0)/1000f)+1)/2f;
gl.glClearColor(t, 1-t, 0, 1);
gl.glClear(GL4.GL_COLOR_BUFFER_BIT);
}
});
GLJPanel glPanel = new GLJPanel();
glPanel.add(glCanvas);
glPanel.setBackground(Color.BLUE);
Alternative hello world (no moving, this clearly works):
Canvas awtCanvas = new Canvas() {
@Override
public void paint(Graphics g) {g.fillRect(0, 0, getWidth(), getHeight());}
};
awtCanvas.setSize(size-10, size-10);
JPanel swingPanel = new JPanel();
swingPanel.add(awtCanvas);
swingPanel.setBackground(Color.BLUE);
And test code putting things together:
if (jfx) {
SwingNode swingNode = new SwingNode();
swingNode.setContent(swingPanel);
primaryStage.setScene(new Scene(new Group(swingNode), size, size+30));
primaryStage.show();
} else {
JFrame frame = new JFrame();
frame.setSize(size, size+30);
frame.add(swingPanel);
frame.setVisible(true);
}
Reference solutions JFrame(Panel(Canvas())), JFrame(JPanel(GLCanvas())), and JFrame(GLJPanel(GLCanvas())) work as expected (sort of, GLPanel seems to ignore background color setting).
When I try the non-JOGL wrapper Stage(Scene(Group(JPanel(Canvas()))), works as expected (JFX works).
Now for JOGL, wrapper Stage(Scene(Group(JPanel(GLJPanel(GLCanvas())))) produces black where canvas content should be. Wrapper Stage(Scene(Group(JPanel(GLCanvas()))) (skipping use of GLJPanel) will not produce black square for canvas content, and I only get blue square for JPanel.
The only mentions I found on the internet are from 2012/2013 where it seems it does not work (but it also seems SwingNode did not exist at the time), and 2016 on JOGL forums that links to dead link in bugzzila.
Out of sheer desperation tried to use GLJPanel instead of GLCanvas, and it works. Sort of.
There is issue with SwingNode resize - it has a bug that does not allow easy resize (OracleJDK has the bug for quite some time, OpenJDK seems to have it solved)
Suggested solution to the resize issue is to put width/height property listener on Scene and reinsert the canvas on each size change:
Dimension dim = new Dimension((int)scene.getWidth(), (int)scene.getHeight());
ChangeListener<Number> stageSizeListener = (obs, prev, next) -> {
dim.setSize(scene.getWidth(), scene.getHeight());
component.setSize(dim);
swingNode.setContent(swingPanel);
};
scene.heightProperty().addListener(stageSizeListener);
scene.widthProperty().addListener(stageSizeListener);
This works with code in the question, but each reinsert triggers dispose(GL) and init(GL) cycle which might be ok for some applications, not in my case.
Decided to do a little hack, making the GLJPanel in size of some maximum allowed resolution (say 4K, I render to off screen buffer first anyway), rendering in desired resolution, blitting the result in top-left corner of the panel, and clipping the panel within JFX scene to that very same resolution.
As tomsontom mentioned, the performance is not exactly great, GLJPanel is much slower than GLCanvas by itself (from 800FPS to 40FPS at 4K), and when in SwingNode it gets even worse (17FPS at 4K). Will postpone use of JFX as far as possible, and hopefully they either get SwingNode fixed in upcoming separation from JDK, or even get the DirectRenderingNode.