LwjglCanvas not triggering end of render loop on endNotify

I have an application running in windowed mode.
It used to be the case that calling dispose() on the containing JFrame would also stop the rendering thread. I’m not sure what JME update changed this, but the LWJGL renderer thread isn’t stopping anymore, preventing the JVM from shutting down.
(This scenario happens if the canvas is embedded into a JFrame with DESTROY_ON_CLOSE set, and closing the window by clicking on the window’s close button.)

As far as I can tell, LwjglCanvas.GLCanvas should call destroy() somewhere, but it doesn’t.

You have to call destroy() on the Application object manually. The way the canvas works is that jME3 will remain active even if the canvas is not attached to anything or invisible. This allows editors like the jME3 SDK to work correctly.

Hm… it did work about two months ago, with unmodified code.

Are we talking about the same Component state? I’m talking about a removed one (i.e. after the Window is disposed); you seem to be talking about a closed or invisiblel window, which can still have all kinds of hardware resources.

Ah… I see the last sentence should have been:
As far as I can tell, LwjglCanvas.GLCanvas<i>.removeNotify()</i> should call <i>or trigger</i> destroy() somewhere, but it doesn’t.
removeNotify() is supposed to release all screen resources associated with the Component (i.e. GLCanvas).

The SDK can keep a window around that’s closed, or a HeadlessContext, you don’t run into removeNotify either way.

BTW if my theories are correct (which isn’t necessarily the case :-)), you’ll see undesired behaviour in fullscreen mode, too.
Steps to reproduce:

  • start Task Manager
  • run a full-screen JME application
  • Alt-Tab to Task Manager
  • tell Task Manager to close the window
    If my theory is correct, this runs through the JDK’s Window.dispose() method, which shuts down the AWT event loop as the last top-level Window is gone, but does not shut down the render loop, which prevents the JVM from exiting. Windows should wait for a while, then ask whether it should forcibly shut down the application.
@toolforger said: Hm... it did work about two months ago, with unmodified code.

Are we talking about the same Component state? I’m talking about a removed one (i.e. after the Window is disposed); you seem to be talking about a closed or invisiblel window, which can still have all kinds of hardware resources.

Ah… I see the last sentence should have been:
As far as I can tell, LwjglCanvas.GLCanvas<i>.removeNotify()</i> should call <i>or trigger</i> destroy() somewhere, but it doesn’t.
removeNotify() is supposed to release all screen resources associated with the Component (i.e. GLCanvas).

The SDK can keep a window around that’s closed, or a HeadlessContext, you don’t run into removeNotify either way.

BTW if my theories are correct (which isn’t necessarily the case :-)), you’ll see undesired behaviour in fullscreen mode, too.
Steps to reproduce:

  • start Task Manager
  • run a full-screen JME application
  • Alt-Tab to Task Manager
  • tell Task Manager to close the window
    If my theory is correct, this runs through the JDK’s Window.dispose() method, which shuts down the AWT event loop as the last top-level Window is gone, but does not shut down the render loop, which prevents the JVM from exiting. Windows should wait for a while, then ask whether it should forcibly shut down the application.

Nah, somethings off in your thinking. As momoko said, the update loop should not be dependent on the AWT lifecycle and isn’t meant to. Second, the normal jme window and fullscreen mode are not using AWT at all, its a native window.

Depends on windows its calling the normla shutdown, if you are in the windows tab, if you are in processes tab it will just forcefully kill all threadas and release all native stuff on the os level ^^:)

Anyway i guess you could just add a own windowlistener to that frame or whatever itw as called and use the custom closemode stuff.

<cite>@normen said:</cite> As momoko said, the update loop should not be dependent on the AWT lifecycle and isn't meant to.

That begs the question why it worked two months ago.

<cite>@normen said:</cite> Second, the normal jme window and fullscreen mode are not using AWT at all, its a native window.

“it’s a native windows” doesn’t make sense, everything in AWT is inside a native window.
With the possible exception of fullscreen mode, I haven’t checked how Windows handles it (AWT can wrap fullscreen mode in a Window object it seems, I found cleanup code for that case while debugging this issue.)
Then there’s lightweight Components that don’t have a native peer, but even these draw on the native window of a parent.

If you mean JME isn’t using AWT for fullscreen mode - that’s certainly possible, I haven’t checked that case.
Anything else, however, needs to be AWT, if only to get a native window handle so that OpenGL has a destination for drawing.

<cite>@Empire Phoenix said:</cite> Anyway i guess you could just add a own windowlistener to that frame or whatever itw as called and use the custom closemode stuff.

Yeah, that’s what I added.
Had to fiddle with the wait flag on context.destroy() though. With true, the thread wouldn’t die - I suspect a deadlock, but setting the flag to false worked and I didn’t bother to analyze this fully.
Anyway, I hate to have to add that listener in every new project. It’s another fiddly detail to get right.

I know it worked before. I’ll try to find the responsible commit tonight, and post the SSCCE if there’s interest.

@toolforger said: That begs the question why it worked two months ago.
I don't know of any changes within the last two months regarding this except that lwjgl was updated. At any instance this is the behavior we want and will continue to implement so you will have to deal with that fwiw.
"it's a native windows" doesn't make sense, everything in AWT is inside a native window. With the possible exception of fullscreen mode, I haven't checked how Windows handles it (AWT can wrap fullscreen mode in a Window object it seems, I found cleanup code for that case while debugging this issue.) Then there's lightweight Components that don't have a native peer, but even these draw on the native window of a parent.

Yes it does make sense. LWJGL is using JNI to use Win32 / Cocoa / XWindows API to construct windows and display an opengl screen. It has nothing to do whatsoever with AWT, you can even run it on a JVM that has no AWT layer at all. The AWT/Canvas integration is basically just a workaround for people who are used to AWT and it never worked well.

If you mean JME isn't using AWT for fullscreen mode - that's certainly possible, I haven't checked that case. Anything else, however, needs to be AWT, if only to get a native window handle so that OpenGL has a destination for drawing.

No, as said, I mean for a window. The main window you see in a jME app is NOT a swing or AWT window. The settings window is though.

Yeah, that's what I added. Had to fiddle with the wait flag on context.destroy() though. With true, the thread wouldn't die - I suspect a deadlock, but setting the flag to false worked and I didn't bother to analyze this fully. Anyway, I hate to have to add that listener in every new project. It's another fiddly detail to get right.

I know it worked before. I’ll try to find the responsible commit tonight, and post the SSCCE if there’s interest.

Yeah, please do that, maybe the lwjgl layer changed some behavior, lots of things were done around the AWT integration, esp. for the OSX integration. Still I guess you’ll have to make modifications to how your app expects the engine to work.

1 Like

Dammit. I can’t find a revision where the thread would die.
Must be something in my own code that changed. I have no idea what change may have caused this, I haven’t done anything about the startup/teardown code in weeks.

Ah well.
I’d still like to see the render loop to shut down automatically when it’s not needed anymore, but anyway.
Thanks for the attention, and sorry for the false alarm.

1 Like

removeNotify() is actually called when the canvas is removed / detached from the frame. Its even called when the frame resizes or changes in such a way that the canvas is no longer needed. We had cases in the SDK where maximizing the scene viewer caused removeNotify() and then addNotify() to be called in a quick succession. As you can see, it is not appropriate to destroy the jME3 thread on removeNotify().

Hmm… well, the AWT-imposed contract for removeNotify is “release all screen resources”.
I have learned through bitter experience that violating AWT contracts tends to introduce subtle bugs. Probably because AWT does so much caching and can gloss over most breaches of contract, so the bug doesn’t manifest itself right away but weeks or months later when somebody does things in a valid but slightly unusual way.t to
If such a quick shutdown-restart sequence is a problem, it’s probably better to avoid the shutdown. Either keep the Canvas object alive but invisible (this does NOT trigger a removeNotify), or add a mechanism to “reparent” the render loop to the new OpenGL context before Canvas destruction.

That’s just a general recommendation. I don’t like that notification handler I had to write, but (a) it’s working now, and (b) I found that the reasons for which I started to use LwjglCanvas are probably invalid and I’m going to go back to LfjglDisplay.

On a rather wild tangent: Is JME built for supporting multiple OpenGL contexts at the same time?
I’m thinking multiple LwjglCanvases, or maybe multi-monitor setups in fullscreen mode where each monitor shows a different scenegraph. (None of that is relevant for me right now, I’m just curious.)

We can get multiple backbuffer renderers going but support for multiple windows or AWT embeds is wonky / nonexistent in lwjgl, thats why the engine atm also doesn’t consider this an option. The SDK uses multiple render views due to that and only allows one actual scene to be opened (as the main window can be configured to use the heavyweight canvas overlay). We want to change that at some point though but thats probably after we move to JOGL completely.

I did notice a proliferation of globals in LWJGL code, so the wonkiness didn’t come unexpected, but it’s good to know for sure. Thanks.

@toolforger said: Hmm... well, the AWT-imposed contract for removeNotify is "release all screen resources". I have learned through bitter experience that violating AWT contracts tends to introduce subtle bugs. Probably because AWT does so much caching and can gloss over most breaches of contract, so the bug doesn't manifest itself right away but weeks or months later when somebody does things in a valid but slightly unusual way.t to If such a quick shutdown-restart sequence is a problem, it's probably better to avoid the shutdown. Either keep the Canvas object alive but invisible (this does NOT trigger a removeNotify), or add a mechanism to "reparent" the render loop to the new OpenGL context before Canvas destruction.

That’s just a general recommendation. I don’t like that notification handler I had to write, but (a) it’s working now, and (b) I found that the reasons for which I started to use LwjglCanvas are probably invalid and I’m going to go back to LfjglDisplay.

On a rather wild tangent: Is JME built for supporting multiple OpenGL contexts at the same time?
I’m thinking multiple LwjglCanvases, or maybe multi-monitor setups in fullscreen mode where each monitor shows a different scenegraph. (None of that is relevant for me right now, I’m just curious.)


We do destroy the canvas and associated OpenGL resources, but we have another, off-screen context, that is used to keep OpenGL objects alive. This allows us to generate previews of models even when the scene viewer is closed by the user.