Probably a simple multi-threading issue with AWT and Display.setParent()

I have an LWJGL-based application that’s using the new Display.setParent() method to attach the LWJGL display to an AWT canvas. As the code stands everything works nicely: the scene is rendered correctly, AWT keyboard and mouse events are handled properly, etc.

With one exception - picking. This is the only part of the code that invokes any GL methods outside of the initialisation/rendering code (to get the current projection/modelview to project a ray into the scene). When this code is executed in response to an AWT click event, the GL11 ‘global’ is null, which I think means that the GL context is not attached to the current thread (the AWT dispatch thread in this case). If I tried to kludge round this using Display.makeCurrent() I get an exception because the context is attached to the main thread.

One thing to point out, I haven’t used a separate thread for the application or the rendering loop.

The following pseudo-code illustrates how I’m running the application:

create AWT frame and canvas
Display.create()
Display.setParent( canvas )
frame.setVisible()
add mouse listener to frame {
picking code that accesses the GL modelview/projection matrices
}
loop {
render scene
swap buffers
}

So what’s going on? Is this just a daft multi-threading cock-up on my behalf? Should the context be attached to the AWT dispatch thread?

Neither have I used SwingUtilities.invokeLater(), I’m fairly experienced with AWT/SWING and am VERY aware of the issues when attempting to update a SWING GUI in a multi-threaded application. But the above problem strikes me as remarkably similar - maybe this is the problem?

The SWING developers recommend that one invokes the code that realizes a SWING component using invokeLater() which I’m also not doing (indeed I don’t think I every have, whoops). Interestingly enough, if I run the above code using invokeLater() I don’t get any AWT events ???

Cheers for any suggestions or hints from anyone that’s had similar problems or can see what I’m doing wrong.

I have noticed other frameworks/engines implementing a sort of event queue mechanism, is that what I should be using to access the GL context? i.e. add a ‘pick’ event to a queue in the application, and then handle events from within the rendering loop where the context is valid?

Apologies if I’m reiterating an known problem - I’ve had a nose round but can’t find any examples or forum threads that are related.

  • chris

Best is to simply record the ‘picking-event’ and set a flag, now when you are about to render your next frame, you read the flag, and execute the picking code.

if is was like this:


while(mainLoopRunning)
{
   if(Mouse.isButtonDown(0))
   {
      // pick-code
      // store result
   }

   // render code
}

convert it to:


int[] pickMouseAt = null;
Object pickLock = new Object();
onMouseClicked(MouseEvent evt)
{
   synchronized(pickLock)
   {
      pickMouseAt = new int[]{evt.getX(), evt.getY()};
   }
}



while(mainLoopRunning)
{
   int[] at = null;
   synchronized(pickLock)
   {
      if(pickMouseAt != null)
         at = pickMouseAt.clone();
      pickMouseAt = null;
   }

   if(at != null)
   {
      // pick-code
      // store result
   }

   // render code
}

Cheers Riven, yeah it seems that was the problem, nothing to do with SWING/threads.
I just need to execute the picking code within the rendering loop where the OpenGL context is available.

FYI, I’ve gone a step further and implemented a generic solution for any code that needs to be ‘started’ outside of the rendering loop (I can’t think of any other cases at the moment but I’m sure they’ll come up as I learn more about OpenGL/3D stuff).

I have a context object that is globally available (as a singleton), which is generally used to maintain timing information (elapsed time since last frame, etc) and a few other odds and sods at the moment. I’ve added a ‘queue’ of Runnable objects that is invoked within the rendering loop.

So my picking demo looks something like this now (again in pseudo-code):

initialise everything;
add a mouse-click handler {
add a new Runnable to the queue {
picking code;
}
}
rendering loop {
execute and remove all Runnables in the queue;
render scene;
swap buffers;
}

Works like a treat :slight_smile:

Cheers

  • chris