JOGL - without GLEventListener (GLCanvas or GLJPanel)

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

JOGL - without GLEventListener (GLCanvas or GLJPanel)

Andrzej
Hi,

So I have a need to NOT use GLEventListener (native c++ dll doing the rendering, asynchronously on demand, and requires it to be on the same calling thread - all I need it a context, make it current on the same thread, and the native C++ code does the rest, there is no option to rewrite this code in Java)

I was able to get it to work with GLWindow, but I need to be able to have the OpenGL embeded in a JPanel (I couldn't get it to work with NewtCanvasAWT as some people here suggested), and I can't get the same code to work with GLCanvas or GLJpanel (when I set everything up the getContext() method always returns null).

Here is the working GLWindow code, just a proof of concept (I have it working also with my native c++ renderer), rendering a triangle:

package test;


import com.jogamp.newt.opengl.GLWindow;
import com.jogamp.opengl.*;

public final class RenderJOGL
{  
   private GLContext glContext;
   private GLWindow glWindow;
   private GL gl;
   public RenderJOGL() throws Throwable
   {
          GLProfile glProfile = GLProfile.get("GL2");
      GLCapabilities glCaps = new GLCapabilities(glProfile);  
      glCaps.setDoubleBuffered(false);
      glCaps.setHardwareAccelerated(true);

      glWindow = GLWindow.create(glCaps);
      glWindow.setSize(800, 600);
      glWindow.setVisible(true);
     
      glContext = glWindow.getContext();
      this.gl = glContext.getGL();
   }
   
   public GLWindow getWindow() {
           return glWindow;
   }
   
   public void setSize(int viewPixelWidth,int viewPixelHeight)//,int maxAntialias)
   {
      glWindow.setSize(viewPixelWidth,viewPixelHeight);
   }      
   
   public void render()
   {  
      this.makeContentCurrent();

      GL2 gl2 = gl.getGL2();
     
      gl2.glDisable(GL.GL_DEPTH_TEST);
      gl2.glClearColor(0.0f,1.0f,1.0f,0.0f);   //Background blue
      gl2.glClear(GL.GL_COLOR_BUFFER_BIT);
      gl2.glLoadIdentity();  
      gl2.glColor3f(1.0f, 0.0f, 0.0f);   //Color red
      gl2.glBegin(GL.GL_TRIANGLES);
      gl2.glVertex3f(0,0,0.0f);
      gl2.glVertex3f(1,0,0.0f);
      gl2.glVertex3f(0,1,0.0f);        
      gl2.glEnd();
     
      glWindow.display();
   }
   
   private void makeContentCurrent()
   {  
      try
      {
         while (glContext.makeCurrent() == GLContext.CONTEXT_NOT_CURRENT)
         {
            System.out.println("Context not yet current...");
            Thread.sleep(100);
         }
      }
      catch (InterruptedException e)
      {
          e.printStackTrace();
      }
   }          
}

and a simple runner for it:


public class RenderJOGLRunner {
       
   public static void main(String[] args) {
           try {
                RenderJOGL render = new RenderJOGL();
               
                while(true) {
                        render.render();
                }
        } catch (Throwable e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
        }  
   }
}

But when I try something similar with GLJPanel, when I want to render the GLContext is null, this is my GLJPanel implementation:

package test;

import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLContext;
import com.jogamp.opengl.awt.GLJPanel;

public class OpenGLPanel extends GLJPanel {

    public OpenGLPanel(GLCapabilities caps) {
                super(caps);
    }

    public void render() {

                this.setSize(800, 600);
               
        this.setVisible(true);
        this.display();
   
//THIS IS ALWAYS NULL
        while (this.getContext() == null)
        {
           System.out.println("Context null...");
                   try {
                                Thread.sleep(1000);
                        } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                        }
        }
   
        while (this.getContext().makeCurrent() == GLContext.CONTEXT_NOT_CURRENT)
        {
           System.out.println("Context not yet current...");
                   try {
                                Thread.sleep(100);
                        } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                        }
        }
   
        GL2 gl2 = this.getContext().getGL().getGL2();
   
        gl2.glDisable(GL.GL_DEPTH_TEST);
        gl2.glClearColor(0.0f,1.0f,1.0f,0.0f);   //Background blue
        gl2.glClear(GL.GL_COLOR_BUFFER_BIT);
        gl2.glLoadIdentity();  
        gl2.glColor3f(1.0f, 0.0f, 0.0f);   //Color red
        gl2.glBegin(GL.GL_TRIANGLES);
        gl2.glVertex3f(0,0,0.0f);
        gl2.glVertex3f(1,0,0.0f);
        gl2.glVertex3f(0,1,0.0f);        
        gl2.glEnd();
       
        this.display();
}
}

and how I use it:

private void createUI() {
        getContentPane().setBackground(Color.white);

        setMinimumSize(new Dimension(640, 480));

        topBar_ = new JToolBar("Top Bar");
        topBar_.setMinimumSize(new Dimension(100, 30));
        topBar_.setFloatable(false);

        bottomBar_ = new JToolBar("Bottom Bar");
        bottomBar_.setMinimumSize(new Dimension(100, 30));
        bottomBar_.setFloatable(false);
               
        Container pane = getContentPane();
        pane.setLayout(new BorderLayout());

        middlePanel_ = new JPanel(new BorderLayout(), false);
        middlePanel_.setBackground(Color.lightGray);
        middlePanel_.setPreferredSize(new Dimension(800, 600));

        OpenGLPanel panel = this.getTestGLJPanel();
        middlePanel_.add(panel, BorderLayout.CENTER);

        getRootPane().addComponentListener(new ComponentAdapter() {
            public void componentResized(ComponentEvent e) {
                openGLPanel_.setPreferredSize(openGLPanel_.getSize());
            }
        });

        pane.add(topBar_, BorderLayout.NORTH);
        pane.add(middlePanel_, BorderLayout.CENTER);
        pane.add(bottomBar_, BorderLayout.SOUTH);
               
        pack();
        setLocationRelativeTo(null);
               
        panel.render();
}


I'm not sure what I can be doing wrong. I tried with GLCanvas as well, but basically same thing as in GLJPanel. I just don't know how to use it without using the GLEventListener.



Reply | Threaded
Open this post in threaded view
|

Re: JOGL - without GLEventListener (GLCanvas or GLJPanel)

gouessej
Administrator
Hey

It's doable for sure as it's what I do in JogAmp's Ardor3D Continuation.
Julien Gouesse | Personal blog | Website
Reply | Threaded
Open this post in threaded view
|

Re: JOGL - without GLEventListener (GLCanvas or GLJPanel)

Andrzej
Hi,

Ok, I'm having some success with GLCanvas, GLJPanel, and even GLWindow inside the NewtCanvasAWT, at least with my single classes and triangle rendering :)

I can't get my native c++ renderer to work however... I can get it to work in standalong GLWindow only. As soon as I try to put it in a NewtCanvasAWT or if I try using GLCanvas or GLJPanel I have problems.

The rendering in those cases behaves strange, some things get rendered some do not. My native c++ renderer relies on making a callback to Java to make the context current on the current thread to perform the rendering. So call java to make thread current, get back and do a bunch of gl* calls.

So I guess my question is general, why would I see any differences on the how the openGL rendering works depending on whether my GLWindow is standalone or inside a NewtCanvasAWT? I suspect some threading quirks but I really don't know where to look for the answer :(
Reply | Threaded
Open this post in threaded view
|

Re: JOGL - without GLEventListener (GLCanvas or GLJPanel)

gouessej
Administrator
You can use GLAutoDrawable.invoke() to push your runnables, they will be executed when the OpenGL context is current. If you prefer creating the context in the C++ code, you can create an external context in JOGL bound to the OpenGL context created in C++.
Julien Gouesse | Personal blog | Website
Reply | Threaded
Open this post in threaded view
|

Re: JOGL - without GLEventListener (GLCanvas or GLJPanel)

Andrzej
This post was updated on .
My native renderer is a bit complicated, and it does not have one render() function I can call in a Runnable. It kicks off a rendering thread, onto which rendering operations are queued on based on necessity.

Here is what happens:
1. Initially the opengl context is created on Java side (GLAutoDrawable -> GLWindow/GLCanvas/GLJPanel)
2. The rendering thread decides when it need to render, and then calls back into the Java code to make the context current, and once that's done does does all the gl* rendering it needs.
3. Another call back into Java let's opengl know to render (by calling display() on the GLAutoDrawable)

This all works find when the GLAutoDrawable is the GLWindow, and it's stand alone. As soon as I put that inside a NewtCanvasAWT or use GLCanvas, I see nothing rendered, and get timouts, something like this (this specifically is coming from the GLCanvas usage):

Exception in thread "AWT-EventQueue-0" java.lang.RuntimeException: Waited 5000ms for: <11bf8c0e, 77ede76a>[count 2, qsz 1, owner <Thread-4>] - <AWT-EventQueue-0>
        at jogamp.common.util.locks.RecursiveLockImpl01Unfairish.lock(RecursiveLockImpl01Unfairish.java:198)
        at com.jogamp.nativewindow.awt.JAWTWindow.lockSurface(JAWTWindow.java:603)
        at jogamp.opengl.GLDrawableHelper.resizeOffscreenDrawable(GLDrawableHelper.java:344)
        at com.jogamp.opengl.awt.GLCanvas.reshapeImpl(GLCanvas.java:844)
        at com.jogamp.opengl.awt.GLCanvas.reshape(GLCanvas.java:822)
        at java.awt.Component.setBounds(Component.java:2261)
        at java.awt.BorderLayout.layoutContainer(BorderLayout.java:838)
        at java.awt.Container.layout(Container.java:1510)
        at java.awt.Container.doLayout(Container.java:1499)
        at java.awt.Container.validateTree(Container.java:1695)
        at java.awt.Container.validateTree(Container.java:1704)
        at java.awt.Container.validateTree(Container.java:1704)
        at java.awt.Container.validateTree(Container.java:1704)
        at java.awt.Container.validateTree(Container.java:1704)
        at java.awt.Container.validate(Container.java:1630)
        at java.awt.Container.validateUnconditionally(Container.java:1667)
        at java.awt.Window.show(Window.java:1033)
        at java.awt.Component.show(Component.java:1671)
        at java.awt.Component.setVisible(Component.java:1623)
        at java.awt.Window.setVisible(Window.java:1014)
        at com.lhsystems.maps.testbed.presenter.SessionWindowPresenter.present(SessionWindowPresenter.java:65)
        at com.lhsystems.maps.testbed.presenter.DataPackageDetailPresenter.lambda$0(DataPackageDetailPresenter.java:69)
        at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
        at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
        at java.awt.EventQueue.access$500(EventQueue.java:97)
        at java.awt.EventQueue$3.run(EventQueue.java:709)
        at java.awt.EventQueue$3.run(EventQueue.java:703)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
Exception in thread "AWT-EventQueue-0" java.lang.RuntimeException: Waited 5000ms for: <11bf8c0e, 77ede76a>[count 2, qsz 1, owner <Thread-4>] - <AWT-EventQueue-0>
        at jogamp.common.util.locks.RecursiveLockImpl01Unfairish.lock(RecursiveLockImpl01Unfairish.java:198)
        at com.jogamp.nativewindow.awt.JAWTWindow.lockSurface(JAWTWindow.java:603)
        at jogamp.opengl.GLDrawableHelper.resizeOffscreenDrawable(GLDrawableHelper.java:344)
        at com.jogamp.opengl.awt.GLCanvas.reshapeImpl(GLCanvas.java:844)
        at com.jogamp.opengl.awt.GLCanvas.reshape(GLCanvas.java:822)
        at java.awt.Component.setBounds(Component.java:2261)
        at java.awt.BorderLayout.layoutContainer(BorderLayout.java:838)
        at java.awt.Container.layout(Container.java:1510)
        at java.awt.Container.doLayout(Container.java:1499)
        at java.awt.Container.validateTree(Container.java:1695)
        at java.awt.Container.validateTree(Container.java:1704)
        at java.awt.Container.validateTree(Container.java:1704)
        at java.awt.Container.validateTree(Container.java:1704)
        at java.awt.Container.validateTree(Container.java:1704)
        at java.awt.Container.validate(Container.java:1630)
        at java.awt.Window.dispatchEventImpl(Window.java:2744)
        at java.awt.Component.dispatchEvent(Component.java:4711)
        at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
        at java.awt.EventQueue.access$500(EventQueue.java:97)
        at java.awt.EventQueue$3.run(EventQueue.java:709)
        at java.awt.EventQueue$3.run(EventQueue.java:703)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
        at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:90)
        at java.awt.EventQueue$4.run(EventQueue.java:731)
        at java.awt.EventQueue$4.run(EventQueue.java:729)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

It's not easy to provide a distilled one class example showing this, due to this c++ rendering part that seems to be necessary to reproduce this problem :(

I'm not sure whether this is enough information to point me in the right direction... I am not sure why it is all good in a standalone GLWindow, works beautifully, but fails when put in NewtCanvasAWT or when GLCanvas/GLJPanel is used...

Well that's not entirely true, it works fine inside a NewtCanvasAWT as long as I don't put that NewtCanvasAWT inside anthing else (like a JPanel), as long as its stand alone, it's also fine. When I put it inside a NewtCanvasAWT, and then put that in a JPanel, I get further than with GLCanvas/GLJPanel, I can actually see some of my c++ renders, but not all of it. And I still get a timeout, but a little different:

DefaultEDT.run(): Caught exception occured on thread AWT-EventQueue-0-Display-.macosx_nil-1-EDT-2: RunnableTask[executed true, tTotal 49840 ms, tExec 5000 ms, tQueue 44840 ms, attachment null, throwable java.lang.RuntimeException: Waited 5000ms for: <35a85d1, 783f7363>[count 4, qsz 0, owner <Thread-6>] - <AWT-EventQueue-0-Display-.macosx_nil-1-EDT-2>]
java.lang.RuntimeException: Waited 5000ms for: <35a85d1, 783f7363>[count 4, qsz 0, owner <Thread-6>] - <AWT-EventQueue-0-Display-.macosx_nil-1-EDT-2>
        at jogamp.common.util.locks.RecursiveLockImpl01Unfairish.lock(RecursiveLockImpl01Unfairish.java:198)
        at jogamp.newt.WindowImpl$SetSizeAction.run(WindowImpl.java:1045)
        at jogamp.newt.DisplayImpl.runOnEDTIfAvail(DisplayImpl.java:435)
        at jogamp.newt.WindowImpl.runOnEDTIfAvail(WindowImpl.java:2133)
        at jogamp.newt.WindowImpl.setSize(WindowImpl.java:1087)
        at com.jogamp.newt.opengl.GLWindow.setSize(GLWindow.java:509)
        at jogamp.newt.awt.event.AWTParentWindowAdapter$1.run(AWTParentWindowAdapter.java:128)
        at com.jogamp.common.util.RunnableTask.run(RunnableTask.java:133)
        at jogamp.newt.DefaultEDTUtil$NEDT.run(DefaultEDTUtil.java:372)

I suspect this all has to do with Native/AWT threads, and my c++ renderer expecting things to happen on one thread, but the GLCanvas/GLJpanel/NewtCanvasAWT having a different threading model than the stand alone GLWindow... But I am not sure how achieve what I want...

Oh and this is all on macos 10.13.4
Reply | Threaded
Open this post in threaded view
|

Re: JOGL - without GLEventListener (GLCanvas or GLJPanel)

Andrzej
Ok, so it looks like the problem was on the c++ native renderer part. It looks like I need to rebind the textures on each pass. I'm not sure why this is not needed on any other platform, why this seems to be Java/JOGL specific, I'll investigate more...