SceneViewer tab failure

Here's the problem.



OS Ubuntu 10.4 32 bit



Step to reproduce.



launch jmonkeyplatform

create a new project

open Main.java and focus it in the editor panel.

close jmonkeyplatform

launch jmonkeyplatform



jmp fails creating SceneViewer with message "Failed to parent canvas to display"



It has to do with the lack of a root for an invisible component.



Solutions.


  1. do not open SceneViewer on startup (a thing that can make jme perceived performance a lot better because it takes ages to realize the SceneViewer, at least on my - pretty old - pc).
  2. do not realize SceneViewer canvas on creation but the first time it has been made visible.

A simple workaround coudl be to ensure that the scene viewer window is the active tab when the platform starts, via component.requestActive() or some TopComponent specific property.

pgi said:

A simple workaround coudl be to ensure that the scene viewer window is the active tab when the platform starts, via component.requestActive() or some TopComponent specific property.

Doesnt work, its very hard to get called after the other window opens and before lwjgl needs the window. The "simplest" solution I saw was tweaking every other window type so it does not hide the SceneViewer on startup :/

I tested a tricky solution. Well, not so tricky after all. Here's the logic.



repeat the "requestActive" request until SceneViewerTopComponent becomes visible.



Being a permanent windows, SceneViewer will always be able to honor that request, it is just a matter of waiting for other windows to pop up.



To implement it just move the initialization in the componentShowing method, using a boolean to avoid multiple initializations. In the componentOpened method create a timer that sends the request multiple time if the ogl panel has not been initialized.



It is a kind of spin loop, not a very elegant solution, but it should work. Here's the modified part of SceneViewerTopComponent:


    private boolean initialized = false;
    private Timer initTimer = null;
   
    @Override
    public void componentOpened() {
        super.componentOpened();
        initTimer = new Timer(100, new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                if(initialized) initTimer.stop();
                else requestActive();
            }
        });
        initTimer.start();
    }

    @Override
    protected void componentShowing() {
        super.componentShowing();
        if(!initialized) {
            initialized = true;
            app.setSceneActive(true);
            oGLPanel.add(((JmeCanvasContext) app.getContext()).getCanvas());
        }
    }



Here's the diff:

# This patch file was generated by NetBeans IDE
# Following Index: paths are relative to: /home/pgi/progetti/jme3-gde/jme3-core/src/com/jme3/gde/core/sceneviewer
# This patch can be applied using context Tools: Patch action on respective folder.
# It uses platform neutral UTF-8 encoding and n newlines.
# Above lines and this line are ignored by the patching process.
Index: SceneViewerTopComponent.java
--- SceneViewerTopComponent.java Base (BASE)
+++ SceneViewerTopComponent.java Locally Modified (Based On LOCAL)
@@ -38,15 +38,17 @@
 import com.jme3.gde.core.scene.nodes.JmeSpatial;
 import com.jme3.system.JmeCanvasContext;
 import com.jme3.system.SystemListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
 import java.util.Collection;
 import java.util.logging.Logger;
+import javax.swing.Timer;
 import org.openide.util.NbBundle;
 import org.openide.windows.TopComponent;
 import org.openide.windows.WindowManager;
 import org.openide.util.ImageUtilities;
 import org.netbeans.api.settings.ConvertAsProperties;
 import org.openide.util.Lookup;
-import org.openide.util.lookup.AbstractLookup;
 import org.openide.util.lookup.InstanceContent;
 
 /**
@@ -181,17 +183,31 @@
         return TopComponent.PERSISTENCE_ALWAYS;
     }
 
+    private boolean initialized = false;
+    private Timer initTimer = null;
+
     @Override
     public void componentOpened() {
         super.componentOpened();
-        app.setSceneActive(true);
-        oGLPanel.add(((JmeCanvasContext) app.getContext()).getCanvas());
+        initTimer = new Timer(100, new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                if(initialized) initTimer.stop();
+                else requestActive();
     }
+        });
+        initTimer.start();
+    }
 
     @Override
     protected void componentShowing() {
         super.componentShowing();
+        if(!initialized) {
+            initialized = true;
+            app.setSceneActive(true);
+            oGLPanel.add(((JmeCanvasContext) app.getContext()).getCanvas());
     }
+    }
 
     @Override
     protected void componentHidden() {

Did you try this? Because the SceneViewer gets opened before the other windows, thats the problem. Its the topmost component shortly and initializes, then becomes hidden.

As a workaround, reconfiguring the windows after the SceneViewer has loaded (like hiding the properties or smth) should trigger a reload and it should be visible again w/o restart.

Cheers,

Normen

Well, that's not possible if things are done right. The jme3 canvas is a AWT component and it must be realized in the edt thread, which is the same thread that runs the open/show methods of the netbeans tabs.



So if the sceneviewer tab is not only opend but also showed it should block the initialization of other tabs until the underlying canvas is completely built.


I think its got more to do with the workaround of Kirill. It delivers a "ready" component and then fails to "really" attach the canvas when the other windows have hidden the window.

Bug has been fixed by the amazing momoko_fan! :smiley:

Yeah the bug was that the "authorization" to reinit the canvas was sent before it was ready (due to a bug), causing a LWJGL exception (bug). But now its fixed (the bug is), and if you manage to crash/bug Scene Viewer ever again… Please contact us :stuck_out_tongue:

Momoko_Fan said:

Yeah the bug was that the "authorization" to reinit the canvas was sent before it was ready (due to a bug)

Cool, you explain a bug with a bug  XD

Now i got spurious "Failed to parent canvas to display" even when the scene viewer is the first tab displayed. Unfortunately i cannot find a procedure to reproduce it, a thing that might denote a race condition.

Seems to work fine on Windows and Mac, so I guess it has something to do with Linux?

I don't think its a race condition in the workaround, since everything there is "synchronized".

Maybe its because I have a wait() inside the addNotify and removeNotify methods?

From a wait I'd expect a dead lock, the "failed to parent canvas to display" seems to say that the control flow isn't blocked so i think that isn't the source of the problem (albeit putting a wait inside a method that is executed by the edt is a bit dangerous because if the edt waits too much the interface will look sluggish and folks will start complaining about java being slow).



Actually the random tab failure isn't the only problem i have with jmp after the last update (or maybe the previous one). When i start the project, everything goes fine, when i stop it the netbeans ui reports the process as stopped (in the run tab) as always BUT when i exit the IDE it says "hey you got ten processes running in background".



This might be caused by the wait in the remove notify, I'll have to check with jvisualvm to be sure if the background processes are kept alive by the edt waiting for something.

The zombie process thing turned out to be garbage collector related. If I remove the concurrent gc option from the config file of jmp the problem disappears. I still have the SceneViewer tab failure but that's a minor issue for me.