[Solved] How to properly remove appstates?

Hi all,

In my game, I have a main menu that launches different levels based on what the player clicks. The game loads successfully, but when I try to remove the game and exit to the menu and reload the game, it results in a glitchy game screen and the RAM usage goes up. Is this how I’m supposed to use appstates? Am I cleaning up the resources properly? Also, is there a way to delete the scene model like in OpenGL? Any help is appreciated. Also, by “glitchy” I mean the game loads and everything works properly, but the objects are all transparent and the game is slow. Maybe it has something to do with a shadow renderer I attached to the viewport?

    this.onDisable();
    sound.cleanup();
    bullet.cleanup();
    Main.root.detachAllChildren();
    app.getGuiViewPort().clearProcessors();
    this.niftyDisplay.cleanup();
    for(SceneProcessor p: view.getProcessors()){
        if(p instanceof FilterPostProcessor){
            ((FilterPostProcessor)p).removeAllFilters();
        }
        p.cleanup();
        view.removeProcessor(p);
    }
    //getApplication().getInputManager().clearMappings();
    getApplication().getInputManager().removeListener(this);
    view.detachScene(scene);

Detaching states should do the trick. If you view is glitchy this means your onDisable and cleanup methods don’t work correctly.

You can do a few things, though it’s probably not your problem, however I feel like not many people know these tricks. They can be useful on occasion.

 /**
     * Completely destroy a mesh.
     * @param mesh The mesh to destroy.
     */
    public void destroyMesh(Mesh mesh) {

        if (mesh != null) {
            for( VertexBuffer vb : mesh.getBufferList() ) {
                BufferUtils.destroyDirectBuffer( vb.getData() );
            }

            mesh = null;
        }
    }

// removes a texture from the assetManager cache.
public boolean unloadTexture(AssetKey assetKey) {
        return assetManager.deleteFromCache(assetKey);
    }

This is how to pre-load a scene: (please read it) https://javadoc.jmonkeyengine.org/com/jme3/renderer/RenderManager.html#preloadScene-com.jme3.scene.Spatial-

I’d probably leave jme to manage itself for the most part, however. It does seem to know what it’s doing :stuck_out_tongue:

1 Like

Hi,

Just to debug the issue, I deleted the scene mesh in the cleanup method. Like I expected, it threw a fatal error. Maybe I’m not removing the scene properly? I’ll follow up with some of my findings from the error log, but here is my new code to clean up the appstate:

    this.onDisable();
    sound.cleanup();
    bullet.cleanup();
    Main.root.detachAllChildren();
    Main.root.removeLight(sun);
    app.getGuiViewPort().clearProcessors();
    this.niftyDisplay.cleanup();
    for(SceneProcessor p: view.getProcessors()){
        if(p instanceof FilterPostProcessor){
            ((FilterPostProcessor)p).removeAllFilters();
        }
        p.cleanup();
        view.removeProcessor(p);
    }
    //getApplication().getInputManager().clearMappings();
    getApplication().getInputManager().removeListener(this);
    view.detachScene(scene);
    scene.depthFirstTraversal(new SceneGraphVisitor() {
        @Override
        public void visit(Spatial spatial){
            if(spatial instanceof Geometry){
                System.out.println(spatial.getName());
                ResourceHandler.destroyMesh(((Geometry) spatial).getMesh());
            }
        }
    });
    
    input.clearMappings();

Key findings:

Event: 13.788 Thread 0x000000001ac75000 Uncommon trap: reason=array_check action=maybe_recompile pc=0x0000000004e65e00 method=com.jme3.util.ListSort.binaryInsertionSort([Ljava/lang/Object;IIILjava/util/Comparator;)V @ 123

Java Threads: ( => current thread )
=>0x000000001ac75000 JavaThread “jME3 Main” [_thread_in_Java, id=9444, stack(0x0000000026d80000,0x0000000026e80000)]

Problematic frame:
C [bulletjme.dll+0x620d9]

JRE version: Java™ SE Runtime Environment (8.0_161-b12) (build 1.8.0_161-b12)
Java VM: Java HotSpot™ 64-Bit Server VM (25.161-b12 mixed mode windows-amd64 compressed oops)
Problematic frame:
J 2817 C2 java.nio.DirectFloatBufferU.get()F (15 bytes) @ 0x0000000004c0d2ce [0x0000000004c0d280+0x4e]

Are these appstates? Because if they are, I believe you’re not doing this in a clean way. The way I do for removing appstates is to simply to call:

stateManager.detach(this);
stateManager.attach(new GameplayAppState()); // Whatever appstate to replace the one being currently being deleted

When you call detach, the cleanup() method (maybe the onDisable() too, not sure though) is called. Then I put all the cleanup code there then.

“sound” is not an AppState. I changed the bullet cleanup code to just stateManager.detach(bullet) but
the error seems to occur with the mesh because the java vm error says

com.jme3.bullet.collision.shapes.MeshCollisionShape::createCollisionMesh (170 bytes) 

is the problem. Does the assetmanager cache assets somewhere for future use?

The assetmanager problem was that it was loading from the cache. I fixed this by calling

assets.clearCache();

The first issue still persists, though. I think it may be a rendering glitch.

You are only showing little snippets of code… but it is enough to know that maybe you don’t know how app states are supposed to work.

Extend BaseAppState
Do everything you create in the initialize() or onEnable() depending on how ‘resident’ they should be.
Clean up everything you create in the cleanup() or onDisable() depending on where you created them.

Never ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever ever call AppState.cleanup()/AppState.initialize(), etc. yourself. Just don’t.

attach app states when you want to use them, disable them when you want to not use them… very rarely, detach them when you will never ever use them again.

There are plenty of examples around, too.

1 Like

Ok, I think I just need to manually remove the shadow renderer and the other post processing filters. This solved the rendering issues. The RAM issues still persist, though, but I don’t think they’ll matter for my game. Also, input.clearMappings() seems to get rid of the mouse mappings. Is there a way to add them back?
Thanks.

Why? Do you plan to never ever use them again? Just add them once and leave them.

Don’t call clearMappings(). Remove what you added and nothing more.

Else, System.exit(0) is a pretty easy way to clean everything up.

In reality, you want to be a good developer and clean up the stuff you create… and not create this, create that then clean up with a flamethrower.

1 Like

Ok nevermind, I think the issue has to do with the depth buffer not being cleared properly during the appstate change. I tried calling

this.getApplication().getRenderer().clearBuffers(true, true, true);

at the end of the cleanup method but I get this error:

java.lang.RuntimeException: No OpenGL context found in the current thread.

i think the problem has to do with the FilterPostProcessor.

As a side note, the colors seem to change slightly (not gamma (not/double gamma corrected?) when running the game appstate the second time.
I just added multiple ambient lights because I forgot to remove the old one.

Thank you so much pspeed!
Your advice of adding the post processing filters and shadow renderer once worked! I guess the post processing filters just don’t like to be added and removed from the jme renderer!

Finished code to clean up appstate (RAM is still an issue but I think jme will handle it):

    sound.cleanup();
    bullet.getPhysicsSpace().remove(player);
    bullet.getPhysicsSpace().removeAll(characterNode);
    bullet.getPhysicsSpace().remove(this.sceneControl);
    for(PhysicsRigidBody rb: bullet.getPhysicsSpace().getRigidBodyList()){
        bullet.getPhysicsSpace().remove(rb);
    }
    bullet.getPhysicsSpace().removeTickListener(this);
    bullet.getPhysicsSpace().removeCollisionListener(this);
    bullet.cleanup();
    Main.root.detachAllChildren();
    Main.root.detachChild(scene);
    
    Main.root.removeLight(sun);
    Main.root.removeLight(ambient);
    
    app.getGuiViewPort().clearProcessors();
    this.niftyDisplay.cleanup();
    
    for(SceneProcessor sp: view.getProcessors()){
        System.out.println(sp.getClass());
    }
    
    if(fpp != null){
        /*for(Filter f: fpp.getFilterList()){
            f.setEnabled(false);
        }
        fpp.removeAllFilters();*/
        
        //view.removeProcessor(fpp);
        //fpp.cleanup();
    }
    
    view.removeProcessor(dlsr);
    
    //getApplication().getInputManager().clearMappings();
    getApplication().getInputManager().removeListener(this);
    view.detachScene(scene);
    scene.depthFirstTraversal(new SceneGraphVisitor() {
        @Override
        public void visit(Spatial spatial){
            if(spatial instanceof Geometry){
                //System.out.println(spatial.getName());
                ResourceHandler.destroyMesh(((Geometry) spatial).getMesh());
            }
        }
    });
    
    scene.removeControl(sceneControl);
    characterNode.removeControl(player);
    
    for(String mapping: this.mappings){
        input.deleteMapping(mapping);
    }
    
    stateManager.detach(bullet);
    
    this.getApplication().getRenderer().clearBuffers(true, true, false);

Is this the bullet app state?

Probably OT, but this is what I do:
render a full screen quad with “loading…” on it
add a 1-pixel scene that includes every possible texture
detach both of these after the first update loop

Yes. The problem, however, was actually that the asset manager was returning a cached object after it had been manually deleted. I am currently making the bullet app state static and constant throughout the game.

I don’t think you have much of clue about what you’re doing, and while I encourage working things out, I strongly recommend you leave that static keyword alone, along with trying to “fix” the engine because there’s nothing wrong with it. Please spend your time learning, and not waving that massive stabby knife around butchering everything in your path.

1 Like

Well, then you absolutely shouldn’t be calling cleanup() yourself.

Never ever ever call that on an app state. It’s just silly.

After having a quick read of the thread, I think your problem is not the Appstate management.

Appstates are intended to help keep things tidy, but they aren’t a magical silver bullet if you don’t clean stuff properly. As @jayfella said, have a second look at the tutorials.