[SOLVED] Anything that I can do to increase performance?

Trying to port my game to android, but everyone I tested my game with was getting 2-8 fps. Are there any common practices to increase speed?
Does the fact that I have a thread set up to count time between updates cause a serious impact? (Can provide code)
Is there a minimum ram/processor requirement for JME on android?

1 Like

why you use thread to count time between updates? tpf variable is already time between updates and you can just use it. (1tpf = 1second)

Please provide code if its small, or part of code that you checked that make your fps lower.

I’d guess it’s not so much a matter of best practices at this point so much as either an outright bug or a design issue that’s crippling your FPS. Without more information it’s hard to say much more though - you could be hitting draw call/vertex count limits (hardware limits, essentially), or (more likely) there’s a design issue in your code that’s crippling your framerate (a separate time monitoring thread most likely is not to blame unless there’s lock contention with the main thread, although it may very well be unneeded or an antipattern depending on how you’re using it. Give us more details about your project and what’s happening each frame and we can probably at least give you some ideas about what to look for (you might be running an algorithm over a large number of objects each frame, for example).

Well, we don’t even really know what the game is doing that you are trying to port.

“It’s just a game using terrain with shadows and reflections and 20 animated characters…” You will definitely have a bad time.

“It’s a flat shaded game with a bouncing ball…” there may be a bug somewhere.

1 Like

@pspeed I have a low and high graphics mode. The high mode uses Directional lighting and more vertices in models. The low graphics mode uses flat colors and less vertices. I have an average of 150000-350000 vertices in high graphics mode, and an average of 7000-10000 vertices in low graphics mode.

@danielp First, I use this code for updating my game loop at a steady rate:
In appstate initialize:

new Thread(() -> {
            while (counter > -1 && !killAll) {
            try {
                Thread.sleep(1000/30);
                counter++;
            } catch (InterruptedException e) {}
            }}).start();

In update method:

public int updateCounter() {
        int c = counter;
        counter = 0;
        return c;
    }
    @Override
    public void update(float tpf) {
        int uc = updateCounter();
        //System.out.println("UC" + uc);
        Vector2f mp = Main.inputManager_.getCursorPosition();
        double xDiff = (Main.settings_.getWidth() / 2) - mp.x;
        double yDiff = mp.y - (Main.settings_.getHeight() / 2);
        Main.gl.player.slerpNewRot((int) (Math.atan2(xDiff, yDiff) * FastMath.RAD_TO_DEG));
        for (int i = 0; i < uc; i++) {
            Main.gl.update();
        }
        
        remaining.setText("Players Remaining: " + Main.gl.players.size());
        kills.setText(String.valueOf(Main.gl.player.killed));
        coins.setText(String.valueOf(Main.gl.collectedCoins));
    }

This is what happens in my game logic class(Main.gl)(Comments about whats going on):

public void update() {
        for (Player p : players) { // Update players
            p.update();
            
        }
        for (Crusher c : crushers) { // Update crushers
            c.update();
        }
        for (Coin c : coins) { // Update coins
            c.update();
        }
        while (toDie.size() != 0) { // Kill players who were set to die this update(avoiding concurrent modification exception)
            Player p = toDie.get(0);
            players.remove(p);
            toDie.remove(p);
            p.nonRotNode.detachAllChildren();
            
            p.nonRotNode.attachChild(p.skin.getSplat());
            deadplayers.add(p);
            //toDie.remove(p);
        }
        while (toRemove.size() != 0) { // Kill kill coins that were picked up this update(avoiding concurrent modification exception)
            Coin c = toRemove.get(0);
            removeCoin(c);
            toRemove.remove(c);
        }
        if (players.size() == 1 && players.get(0) == player) {
            Main.stateManager_.detach(Running.instance);
            Main.stateManager_.attach(new YouWin());
        }
        //System.out.println(toDie.size());
    }

Player update & killplayers():

public void killPlayers() {
        for (Player p : logic.players) {
            if (p == this) continue; // This would be bad...
            if (Main.colliding(spear, p.body)) { //Shorthand for checking bounding box colisions on spatials
                //p.update(); //A Cause of StackOverflow
                p.die(); //Adds player to toDie list in game logic
                for (int i = 0; i < new Random().nextInt(3); i++) logic.addCoin(p.position, true); // extra coins are given out based on who/what killed the player.
                if (p == logic.player) logic.killer = this; // So you know who killed you when the game ends
            }
        }
    }
    public void update() {
        this.nonRotNode.setLocalTranslation(position); // Causes problems if this is still set to (0, 0, 0) on first update
        this.node.setLocalRotation(rotation); // Same thing
        while (rot > 360) {
            rot -= 360;
        }
        if (newRot != rot) {
            int jump = 13;
            float pos_rot = (360 + newRot - rot) % 360;
            
            /*if (this == logic.player) {System.out.println(pos_rot + " Rot: " + rot + " NEW:" + newRot);
            //if (rot < 0) System.exit(1);
            }*/
            if (pos_rot < 180)  {
                rot += jump;
                pos_rot = (360 + newRot - rot) % 360;
                if (pos_rot > 180) rot = newRot;
            }
            else {
                rot -= jump;
                pos_rot = (360 + newRot - rot) % 360;
                if (pos_rot < 180) rot = newRot;
            }
            this.rotation = new Quaternion().fromAngleAxis(rot*FastMath.DEG_TO_RAD, Vector3f.UNIT_Y);
        }
        this.moveFwd();
        this.nonRotNode.setLocalTranslation(position);
        this.node.setLocalRotation(rotation);
        killPlayers();
    }

Crusher update:

public void update() {
        if (dropping) { // Code to check if the the thing is dropping, and update
            if (position.y <= 0.2f) {
                time = 30;
                dropping = false;
            } else {
                position.y -= 0.8;
                if (position.y < 0) position.y = 0;
                time--;
            }
            if (position.y <= 0.3f) {
                for (Player p : logic.players) {
                    if (Main.colliding(shape, p.body)) {
                        p.die();
                        if (p == logic.player)System.out.println("Killed Player MPOS:" + position + " PPOS:" + p.position);
                    }
                }
            }
        } else if (isDropped()) {
            if (time > 0) {
                time--;
            } else {
                position.y += 0.1;
            }
        }
        Player nearest = findNearestPlayer(logic.players); // Mostly self explanitory
        if (nearest != null) {
            if (!isDropped())moveTowards(nearest.getNextLoc(STEPS_POS));
            if (shouldDrop(nearest.getNextLoc(STEPS_POS)) || shouldDrop(nearest.getNextLoc(1))) drop();
        }
        shape.setLocalTranslation(position);
        shadow.setLocalTranslation(position.x, 0.0001f * shdOff, position.z);
    }

Coin update: //Really dont think this is the cause…

public void update() {
        if (rot > 358) rot = 0;
        else rot+= 0.25f;
        for (Player p : logic.players) {
            if (Main.colliding(node, p.innerNode)) {
                p.collectCoin();
                logic.toRemove.add(this);
            }
        }
        node.setLocalTranslation(position);
        node.setLocalRotation(new Quaternion().fromAngleNormalAxis(rot, Vector3f.UNIT_Y));
    }

I can help here.

  1. No lighting. Sorry but unshaded textures are they way to go for Android. Tell me hypothetically all day about single pass lighting and what not, doesn’t work in my experience.

  2. Small textures, 64x64 average 256x256 for mega detail and necessary only.

  3. My measure is usually 30k vertices per scene. Any more and you’ll start to lag.

Good luck with Android.

  1. Like I said, unshaded mode…
  2. Big textures are only shown in menus. The floor(100x100(need to switch to pow of 2)) and crushers(128x128) are the only other things that uses them.

P.S. When talking about fps/performance I mean low graphics mode.

You could try raising the Heap and Direct memory settings by a few mb and see if that miraculously fixes the problem.

I’ve had a handful of times where my application frame rate dropped for no apparent reason, and then after I turned up the Heap Space memory up, it went away and the framerate returned to normal.
I’m not what the reasoning is, but it seems like the application will always begin to lag for a while prior to throwing an OOM error, but in some cases I’ve found one of my applications caught in a state where the FPS slows to a crawl but doesn’t crash.

This is 100% unnecessary code that at best will be inefficient and at worst will randomly fail. I mean, unless you’ve properly synchronized the variables and/or made them volatile, one thread might randomly see really stale versions of this data.

And I repeat that it’s 100% totally completely absolutely unnecessary.

Any particle emitters?

How’s the object count?

Hi~ I am just a new bee in programming.
But have you check the CPU usage and GPU usage difference ?
1 CPU:high GPU:high
2 CPU:low GPU:high
3 CPU:high GPU:low
for 1: I guess that need a redesign in basic data structure and game logic
for 2: I haven’t meet, sorry
for 3: try to combine objects with same material* into a single mesh.

You can have light, but probably only one or two plus ambient (which is virtually free). I just had one directional plus ambient. That seemed to work fine for me.

It’s best to see your stats in game. Object count hurts more than vertex count on mobile devices. The threshold is waaaay lower than on desktop. You tend to hit the object count wall before the vertex limit.

When I wrote an Android game the whole point was to suffer. Become super efficient. It’s not going to bode well if you don’t scrutinise every line of code you copy over from desktop. You can’t just throw it over and expect it to work. You will probably find it’s your code and not the GPU if you’re sub 100 objects and 100k verts.

Look at the stats in the video below. They are rock bottom. The cause of low FPS was using desktop code on a mobile device, garbage collection… stuff like that.

And I think this was the last video I made on Android. Notice the difference in everything once you get a grip on how mobile dev works.

Imma have to stop you right there, android can handle more than that. The tank game I made in webgl ran nearly at 60fps in mobile chrome and it had 3 2048x2048 shadowmaps for terrain and more than 100k vertices for sure. I suppose it’s possible jme3 adds a lot of overhead and I did keep the number of drawcalls under something like 20 at most so that probably helped. Mobile chrome probably didn’t.

Can confirm the lighting part though, best to use some fast shadow approximation. I ran drop shadows and baked lightmaps which performed fine in this case.

That was testing on my LG G3 which was pretty great for its time but is about 5 years old now.

i would rather say: maybe on android JVM have default much memory usage limit or something.

im not android programmer, but often Java slow issues comes from wrong JVM/run configuration, nothing else.
i dont think JME add too much overhead. but some add for sure, anyway it should not impact much.

its like in my previous work people said “hey, dont use netbeans, its slow”. i said “yes, it is because you run it using 25% of your memory”

anyway more probably is that topic author have something wrong in code. because if i hear that using thread to calculate frame time instead of just use tfp variable. The thread need to synchronize(if he really need update frame time) and synchronize = wait = slow, anyway its not full code he provided as i see. but thats my guess

so in short i guess counter variable is some synchronized variable and it might cause slow, because require wait update method(thread) for user thread or otherwise.

1000/30 = 30 waits per second that could really make 2-8 fps

The issue with the Thread.sleep() is that:

  1. it’s totally and completely 10000000% totally completely unnecessary. Absolutely not needed and looks silly, also.
  2. it could sleep a LOT longer than you’ve requested depending on how busy other threads are.

I would be a little surprised if it wasn’t the complete issue, really.

Edit: well, I guess nothing is using that value from the counter? So maybe it’s just wasted CPU to do nothing. Might as well add 40 more busy loops while we’re at it. :slight_smile:

2 Likes

@pspeed but thats what i said in beginning, but if he need know problem, that it is. unnecessary code that cause issue :smiley:

You will spend 80% of your time doing the last 20% of the game (it’s pretty standard especially android development). When I made my android game a few years ago, the gui was actually a silent killer as it was all separate objects even though very simple quads, as someone else said it’s the object count which matters more. Batching them together gave a big improvement for me. jME has hardware skinning as well for animated models, which produced less memory allocations (check that is on) and try reduce allocations as much as possible so that the garbage collector runs as little as possible (pools can help). It’s been a while since I used JME so thinga may have changed. There is a profiler you can use when debugging games on an android devices (somewhere in the android sdk) which will be enormously helpful in getting to the bottom of things.

Imma have to stop you right there, android can handle more than that. The tank game I made in webgl

Imma have to stop you right there. Yes clearly Android can run the entirety of GTA San Andreas.

I suppose it’s possible jme3 adds a lot of overhead and I did keep the number of drawcalls under something like 20 at most so that probably helped. Mobile chrome probably didn’t.

Clearly I am giving advice on JME forums about JMonkey Engine.

@Jayfella If you want to stop the camera shaking/skipping like in the video, update the camera’s position/rotation in the app state render method. (This works on low fps too)

I found the problem.
My really REALLY REALLY bad NPC update method when I tried lowering the count to two other players.

public int aimForNearestPlayer(List<Player> players) {
        Vector3f clC = null;
        float cDist = Float.MAX_VALUE;
        for(Player p : players) {
            float cD = p.position.distance(this.spear.getWorldTransform().getTranslation());
            if (cD < cDist && p != this) {
                cDist = cD;
                clC = p.position;
            }
        }
        System.out.println(clC);
        if (clC == null) return this.rot;
        int degNearest = 0;
        float distNearest = Float.MAX_VALUE;
        Node newN = new Node();
        newN.setLocalTranslation(this.node.getLocalTranslation());
        newN.setLocalRotation(this.node.getLocalRotation());
        Node newIn = new Node();
        newIn.setLocalTranslation(this.innerNode.getLocalTranslation());
        newIn.setLocalRotation(this.innerNode.getLocalRotation());
        newIn.setLocalTranslation(0.1f,0,0);
        newN.attachChild(newIn);
        for (int i = 0; i < 360; i++) {
            Quaternion nr = new Quaternion().fromAngleAxis(i, Vector3f.UNIT_Y);
            newN.setLocalRotation(nr);
            
            //newIn.setLocalRotation(nr);
            Vector3f tran = newIn.getWorldTranslation();
            float dist = tran.distance(clC);
            //if (tran.x)
            if (dist < distNearest) {
                distNearest = dist;
                degNearest = i;
                
            }
        }
        
       
        return degNearest;
    }

This was the memory hog. It checks 365 degrees for 20 players 30 times a second… the loop runs 219000 times a second at full performance. Then i changed i++ to i += 8, and saw some improvement.
I finally noticed JME’s Spatial.lookAt and changed my code to:

public int aimForNearestPlayer(List<Player> players) {
        Vector3f clC = null;
        float cDist = Float.MAX_VALUE;
        for(Player p : players) {
            float cD = p.position.distance(this.spear.getWorldTransform().getTranslation());
            if (cD < cDist && p != this) {
                cDist = cD;
                clC = p.position;
            }
        }
        System.out.println(clC);
        if (clC == null) return this.rot;
        
        
        Node newN = new Node();
        newN.setLocalTranslation(this.node.getLocalTranslation());
        newN.lookAt(clC, Vector3f.UNIT_Y);
        float[] angles = new float[3];
        newN.getLocalRotation().toAngles(angles);
        float ang = angles[1] * FastMath.RAD_TO_DEG;
        while (ang > 360) {
            ang -= 360;
        }
        if (ang < 0) ang = 360 + ang;
        System.out.println(ang);
        return (int) (ang);
    }

My new problem is that all the players slowly turn in a circle around the center… But at least there’s some performance improvement…