Battery consumption consideration

Hi folks,



I am testing jmonkeyengine in Nexus S, and notice that jmonkeyengine consume a lots of battery. I think the battery got drained so much (at least partly) because the updateLoop is running constantly (I am assuming that the GL rendering thread only runs when there is a change in the scene - have not check the code yet, so forgive me if i’m wrong). This behavior makes sense for interactive games where AI elements has to constantly calculate their move. However, for apps with a lot of idle time, such as turn based games, a constant loop like this seems to be wasteful (particularly, of battery resource).



Is there a way to config the app so that the loop will not run constantly? I went through the source code last night (starting from SimpleApplication, to Application, to some JmeContext classes) and it looks like there might be a way to slow down the loop (by setting up a mode so that there is some sleeping time in each iteration basically). But that might not be enough for me. Ideally, I would like to run the update pass only when there is an update. Could you guys let me know if there is a way I can control the update routine? Or generally, is there a way to avoid draining battery?



Bellows are how I measure the battery consumption. I use the tutorial number 3 as the test app and measure the battery consumption in my Nexus S. Here are the results:


  • In 1 hour the app (tutorial #3 https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:beginner:hello_asset) drained 25% of the battery.
  • I modified the app a bit using the simpleUpdate method to make the teapot and the ninja rotate constantly and the app drained about the same amount of battery.
  • I measured the battery level using a separate app that listens to ACTION_BATTERY_CHANGED broadcast. Basically I read the battery level before launching the test app, ran the test app for 1 hour (without letting the screen to shut off) and read the battery level again.
  • For a reference, I wrote a simple app using openGL to display about 20 geometries (each with about a few hundred vertexes), without animation. If I let the rendering loops to run constantly, the app drains about 20% of battery after 1 hour. if I set GLSurfaceView.setRenderingMode(RENDERMODE_WHEN_DIRTY) then the app drain only 10% of the battery in 1 hour.
  • For further reference, I also tested the stock android 2.3.3 contact app (no interaction, just displaying the list of contacts). The app drained 10% of the battery in 1 hour.
1 Like

I don’t think this kind of optimization will do much as normally a game lives from visual updates, so you’d maximum improve the battery time in pause mode.

You still need to get input when paused which means you can’t just pause the update loop. jME3 isn’t designed to handle input in any other thread so you are pretty much left with this limitation unless you have some way to get input elsewhere and then “wakeup” the jME3 update loop. Another option would be to just put a Thread.sleep(1000) inside your update callback, then once you get some input you can stop sleeping for a while until there’s no more input coming.

@Momoko_Fan, unless you’ve integrated the Android UI and are able to grab input from that…

While I might agree that most games lives from visual updates, I don’t think all of them do. I think turn based games, such as chess, bowling, pools, … do not need a visual update all the time. And they would still definitely use a great framework such as jmonkeyengine.



I am not sure how it is implemented yet, but i would imagine the interface to be close to how android handle the openGL rendering thread. From any thread you just make a call to “wake up” the rendering thread (and in this case, you would make some call to wake the update thread up). I am not so sure if sleep(1000) would be a good solution, though. The reason is that while your app/game might not need to interact all the time with a user, it should still be very responsive when the user choose to interact with the app.

So just block the update loop until the user touches the screen or smth., you can wait()/notify() yourself.

@normen said:
So just block the update loop until the user touches the screen or smth., you can wait()/notify() yourself.


I tried exactly what you suggested. Turn out I do not have to block and unblock the thread, because the update loop actually runs on the openGL rendering thread. Therefore to stop it from keep running, I just need to set the rendering mode of the GLSurfaceView to RENDERMODE_WHEN_DIRTY. To activate it again I just need to call requestRender. I can get the reference to the GLSurfaceView from the AndroidHarness.

The problem though, is to use JME input management and wake the update thread when there is an input from users, because essentially, the input manager also runs on the update thread. To be exact, this is what happen:

- When an user touch the screen, the event is registered in the android main thread (an instance of the AndroidInput class, which is a subclass of GLSuefaceView, receive this event). The AndroidInput then creates an appropriate JME event (i.e., JME TouchEvent or MouseEvent) and queue it up to be processed in the update thread. However, the update thread is currently not running (to conserve the battery), hence I need a way to wake it up so that I can receive the JME event. Basically, whenever there is an android input event, I need to call requestRender to activate the update loop.

My first thought is to find something in the AndroidHarness class that I can use to detect if an user interact with the device. The closest thing that I have found is onUserInteraction() - however, this method is only guaranteed to be called on touchDown, not touchMove or touchUp events. So it looks to me that I will need to find a way to handle the android events by myself (and sort of bypassing the JME InputManager).

Please let me know if you guys see any other approach or if I miss anything.

Thanks.

Why is that onUserInteraction behavior an issue? You really expect the user to put his finger on the screen and then leave it there while the game is paused, then expecting the game to wake up when he moves his finger?

No, the problem is that I have not found a way to consistently keeping the update thread running when the user is still performing his gesture. I expect the game to be paused until the user touch the screen, let say to swipe it. Then the game have to be awake all the time at least until the user finish his swipe gesture, then depending on the logic of the game, it will stay awake for a while apter that until it finish all the UI changes and then it would pause again.



Note that calling requestRender only make the rendering thread to run its method once (which means the update() method runs once only - I have to find some condition to call requestRender again).



Also I have thought about actually blocking the rendering thread, but since it is an android thread I really hesitate to do so. Some important code (that would have been called in the same iteration should I not blocking it mid way) might not run because I block it.

Why don’t you just count the time theres no input from the user (in the update loop) and only freeze it again when theres been no input for a while?

That should probably work most of the time for a touch event, because in my experiments, I see that as long as the user keep his finger on the screen, some event will be triggered (first we would have a TouchDown, then several TouchMove until the user lift his finger and we receive TouchUp). However I am not so sure if that is because a user (which is me in the experiment :slight_smile: just cannot keep his finger absolutely still all the time or not).



But what if we have a key down event? I am not sure what happen if the user hold a key on a hard keyboard for a long time? Then I do not know how long I should keep the thread run.



Anyway, the approach does not seem to be systematic enough for me.

Yes you know, just keep it running while the key is pressed and only stop it when theres no key pressed. (rather only count the time then)

jME3 supports a custom context mechanism, so you can wrap the existing context and then handle the whole pause/unpause on input thing on the lower level

It’s been a while before I could get back to this issue. I took the approach suggested by Momoko_Fan, trying to put in my own input handling while still utilizing the existing engine as much as I could. So this is what I have done:


  • I extended 4 classes: JmeAndroidSystem, OGLESContext, AndroidInput, and AndroidHarness. My ultimate goal is to change the behavior of AndroidInput and OGLESCOntext. So first I extend AnroidInput, and to put it in the system, I changed the behavior of OGLESContext.createView() to return my own version of the AndroidInput. Then to get my own version of OGLESContext, I have to change the behavior of JmeAndroidSystem.newContext() to return my own version of the Context. Finally, I have to set my own version of JmeAndroidSystem as the JmeSystem delegate.


  • Here is my code:



    [java]

    //

public class MyJmeAndroidSystem extends JmeAndroidSystem {
@Override
public JmeContext newContext(AppSettings settings, Type contextType) {
initialize(settings);
return new MyOGLESContext();
}
}

//
public class MyOGLESContext extends OGLESContext {
@Override
public GLSurfaceView createView(AndroidInput view, ConfigType configType, boolean eglConfigVerboseLogging) {
MyAndroidInput myView = new MyAndroidInput(view.getContext());
myView.setMouseEventsInvertX(view.mouseEventsInvertX);
myView.setMouseEventsInvertY(view.mouseEventsInvertY);
myView.setMouseEventsEnabled(view.mouseEventsEnabled);

System.out.println("Creating MyAndoidInput and replace the AndroidInput");

GLSurfaceView surfaceView = super.createView(myView, configType, eglConfigVerboseLogging);
surfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
return surfaceView;
}

protected void waitFor(boolean createdVal)
{
while (renderable.get() != createdVal){
try {
view.requestRender();
Thread.sleep(10);
} catch (InterruptedException ex) {
}
}
}
}


//
public class MyAndroidInput extends AndroidInput {
public MyAndroidInput(Context ctx) {
super(ctx);
}

public MyAndroidInput(Context ctx, AttributeSet attribs) {
super(ctx, attribs);
}

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
boolean onKeyDown = super.onKeyDown(keyCode, event);
requestRender();
return onKeyDown;
}

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
boolean onKeyUp = super.onKeyUp(keyCode, event);
requestRender();
return onKeyUp;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
boolean result = super.onTouchEvent(event);
requestRender();
return result;
}
}


//
public class MyJMonkeyActivity extends AndroidHarness implements UpdateLooper {
public MyJMonkeyActivity(){
//This should override the effect of the static call in AndoirdHarness. Wish I had a better way to do it.
JmeSystem.setSystemDelegate(new MyJmeAndroidSystem());
}
}

[/java]

- As can be seen in the code above, I make a requestRender() call whenever the main thread expect the renderer thread to execute something. That includes the input handling (the calls in MyAndroidInput), and also the waitFor method in OSLESContext().

So far this approach seems to work nicely. Note that for animation, the Application might need a mechanism to requestRender also. Of course if in the future the interface (or even the implementation) of any of the above classes changes, I might be in some troubles.

Please feel free to give feedback to my code. Did I do anything unnecessary? Is there a better way to "wrap the context"? Did I miss any place that I might have to call requestRender? Thanks in advance.
1 Like