Illegal State Exception when detaching app state

Hi fellows,
I am getting this error when I detach my game run app state :

java.lang.IllegalStateException: Scene graph is not properly updated for rendering.
State was changed after rootNode.updateGeometricState() call.
Make sure you do not modify the scene from another thread!
Problem spatial name: renderNode
at com.jme3.scene.Spatial.checkCulling(Spatial.java:260)

My game uses heavily the render to texture solution, and I guess this node/error has to something to do with that.
Anyway, I always thought the render cycle (appstate rendering first) should prevent this from happening…
How can I fix this problem ?

Could be one of two things for me:

  • The detachment part is being made on code invoked from another thread
  • The app state is not cleaning up properly.

can you provide some code for us to see?

Hi blood ! Thanks for helping me again !

This is my cleanup ( all objects are attached to this nodes ):

appStateGUINode.removeFromParent(); appStateNode.removeFromParent();

This is the function that detach the app state :

public void onMouseClickUp_BtnYes(String par) {
        Main.ref_stateManager.detach( Main.appState_gameRun );
        Main.ref_stateManager.attach( Main.appState_menu ) ;
}

I did some tests, and found an solution, but I am not sure why this fix it.

  1. I moved the detach to the next update tick :

    if(prepareToDetach) {
    Main.ref_stateManager.detach( Main.appState_gameRun );
    Main.ref_stateManager.attach( Main.appState_menu ) ;
    }

  2. Added the flag on the method :

    public void onMouseClickUp_BtnYes(String par) {
    Main.ref_stateManager.getState(AppStateGamePhaseRun.class).prepareToDetach=true;
    }

This method is called by an new UI I am building, and its an callback… There is no multiple threads anywhere thought…
I guess I cant remove main nodes outside the update loop ?

is onMouseClickUp_BtnYes an event/callback that can be triggered anytime?

This is another possibility: the event is fired when the rendering has not finished processing, and it’s still looking for the detached nodes (and maybe appstates). By setting the flag you give the control of detachment of nodes back to the main loop when you process it on the update kick.

I think you can also synchronize but flags are easier to manage in most cases

Its fired by mouse click event.
I am thinking on another possibility :

This onMouseClickUp_BtnYes method is in my app state, you may wander why I am using this :

Main.ref_stateManager.getState(AppStateGamePhaseRun.class).prepareToDetach=true;

instead of this :

 this.prepareToDetach=true;

Thats is because I am using reflection, the variables off the appstate are fake even for “this”.
I wander if this is the reason, because I did some debug and found out that the node.removeFromParent is getting crazy with this and its not removing all the parents…

Hi, need to read this.
http://wiki.jmonkeyengine.org/doku.php/jme3:advanced:multithreading?s[]=queue&s[]=multithreading

You think you didnt use multiple threads, but the UI IS multipethreading.

Anything about reflection there ? Note that I am not using threads in this case…

Yeah, most likely your viewport still exists but you’ve skipped updating the spatial that is rendered there. Note: cleanup() is called at the beginning of the next update cycle… so chances are you are doing your updateGeometricState() in a strange place.

We’d have to see your app state to be sure.

I guess you are right… How can I remove it then ?
This is my cleanup :

public void cleanUp() { 
   offView.setEnabled(false); 
   for(int t=0;t<=renderNode.getChildren().size()-1;t++) 
      renderNode.getChild(t).removeFromParent(); 
   renderNode.removeFromParent(); 
   enabled=false; 
   offView.detachScene(renderNode);
}

Anything else should I have here , like offView.destroy or something ?

Show the whole app state. I can’t see where you are calling updateGeometricState()… for some reason you stop calling it before you should be.

Also, you should really be extending BaseAppState so that you don’t have to (incorrectly) handle enabled state yourself.

I guess you are right about that :blush:

if(phaseOverTimer!=null&&phaseOverTimer.getExpired()) {
    phaseOverTimer=null;
    processGameOver("win");
}

if(paused) {
    if(mazeBackGround!=null) mazeBackGround.tickUpdate(tpf);
    if(playerzoomControl!=null) playerzoomControl.tickUpdate(tpf);
    return;
}

if(prepareToDetach) {
    //exitpaneldlg.cleanup();
    //this.setEnabled(true);
    prepareToDetach=false;
    //appStateGUINode.removeFromParent();
    //appStateNode.removeFromParent();
    cleanup(); // I dont know why this bugs and dont call sometimes
    Main.ref_stateManager.detach( Main.appState_gameRun );
    Main.ref_stateManager.attach( Main.appState_menu ) ;
    return;
}

if(gameOverTimer!=null&&gameOverTimer.getExpired()) {
    gameOverTimer=null;
    redscreen.getSpatial().removeFromParent();
    redscreen = new ScreenController(Main.ref_guiNode, Main.ref_settings, Main.ref_assetManager);
    redscreen.setScreenColor(new ColorRGBA(0.5f, 0, 0, 0.4f));
    processGameOver("death");
}

Vector3f zoomCamPos = playerzoomControl.getViewPort().getCamera().getLocation();
geom_playerzoom.setLocalTranslation(
        (float)zoomCamPos.x*(screenReduction*1.5f) - (sizeWidth*screenReduction)/2f,
        (float)zoomCamPos.y*(screenReduction*1.5f) - (sizeHeight*screenReduction)/2f,
        geom_playerzoom.getLocalTranslation().z
        );

if(mazeBackGround!=null) mazeBackGround.tickUpdate(tpf);
if(playerzoomControl!=null) playerzoomControl.tickUpdate(tpf);

So to fix it, I should clean up only after the geometric updates right ?
Obs: the geometric updates are being done in the last two lines, the tickupdates methods.

You should have an app state that manages the offscreen viewport. It will update the node during render() and render the frame there, too. The viewport will get cleaned up and removed on cleanup(), etc…

That’s what I wanted you to paste… since I need to see where you call updateGeometricState()… which should be in that app state. So if you don’t have such an app state then you need one. It will make all of this a thousand times easier to manage.

This is the tickUpdate implementation for both renderes I have, its currently in an normal class, I guess I should convert it to an app state :

protected void tickUpdate(float tpf) {
    if(updatesneeded!=0) renderTickCounter++;
    if( updatesneeded==0 || (renderTickCounter < updatesneeded && offTex!=null) ) {
        update(tpf);
        renderNode.updateLogicalState(tpf);
        renderNode.updateGeometricState();
    } else {
        if(renderTickCounter==updatesneeded) { cleanUp(); renderTickCounter++; }
    }
}

Typical pattern is something like:

psuedo code:

class MyAppState extends BaseAppState {
    protected void initialize( Application app ) {
        create the viewport and stuff
    }
    
    protected void cleanup( Application app ) {
        remove the viewport
    }

    protected void onEnable() {
        viewport.setEnabled(true);
    }

    protected void onDisable() {
        viewport.setEnabled(true);
    }

    public void update( float tpf ) {
        node.updateLogicalState(tpf);
    }

    public void render() {
        node.updateGeometricState();
    }
}

…something like that.

Nhumm …
I didnt know I should do it on render() …
I will convert it to an app state and do that then :smile:
Thanks friend !

Doing it on render() makes sure two things:
-all controls have been run, all other app states that might have modified something have also been update()ed.
-if the viewport will be rendered then geometric state will be updated

Learning new stuff every day :slight_smile: