Capping framerate independently of logic updates in single-threaded Android Gameloop

My game is currently using the below game loop (this is an OpenGL ES 2.0 game, therefore the loop-work takes place on the rendering thread in the provided onDrawFrame method).

Currently my loop provides (and will need to continue providing) the following:

• The loop allows me to cap logic updates to an arbitary value (which I have set to 60) - so 60 updates per second. This allows my game to run at the same speed (ie, not too fast) on all devices I've tested so far. (I'm not concerned with a device not being able to handle 60 ticks per second as I'm yet to find one that can't and I've tested this on some very old devices, they all run perfectly fine).

• If a device can't handle the rendering demands (much more likely), the loop will allow it to drop frames so it can catch up. All the while, allowing the logic/physics tics to carry on running at a smooth 60 ticks per second.

The only remaining problem with the loop is that it renders 'flat out' - so, out of the 20+ devices on which I've tested this, there are 3 main groups:

VSYNC'd at 60 FPS - (for example Google Nexus 10)

VSYNC'd at 30 FPS - (eg, generic Android tablet)

No VSYNC - (Samsung Galaxy Ace Mk1)

So, when running on say, a VSYNC 60FPS device, I generally get 60 logic updates per second and 60FPS.

On a VSYNC 30FPS device, I get 60 Logic updates and 30FPS

On a No VSYNC device, I get 60 Logic updates and 'The Sky is the limit' FPS (on my Ace, it can get up to 90-100 FPS).

What I really want my loop to do, is limit the rendering. In my case I want to run my physics at 60 ticks per second and render at 30FPS.

I want to do this because rendering flat out is eating battery and heating up devices unnecessarily. If my rendering is capped at 30FPS I would think it would help a lot in this case no? I need my physics to keep running at 60 ticks, reducing that isn't really an option (I've tried it and it causes a few glitches with collision and sprites that move very fast over tiny distances) but I digress....

I've been trying all day to figure out how to do this so that:

On all devices (that can handle at least 30FPS or greater) it will always run at 30FPS. (so even if VSYNC'd to 60FPS).

The loop

@Override
public void onDrawFrame(GL10 gl) {

//Grab time
newTime = System.currentTimeMillis()*0.001;
frameTime = newTime - currentTime; //Amount of time this entire frame took
if ( frameTime > (dt*25))
frameTime = (dt*25);
currentTime = newTime;

accumulator += frameTime;

//Update logic while
while (accumulator >= dt){
saveGameState();
updateLogic(dt);
t += dt;
accumulator -= dt;
}

interpolation = (float) (accumulator / dt);

render(interpolation);

}


Sleep time?

As the loop renders whenever it's not updating, I'm thinking I need a way to 'sleep' once it's called and finished render(interpolation) until it's ready to go again.

I tried putting this following code in after render(interpolation) (within onDrawFrame - where newTimeMillis is System.currentTimeMillis() grabbed at the start of onDrawFrame) but I'm not really sure how this would work when onDrawFrame is called every 16.66666666666667MS on devices that have VSYNC capped at 60FPS. I've getting really confused over this and would appreciate if someone could give me a clue as to what kind of code I need to inject into my loop to achieve that which I'm trying to achieve.....(something I incorrectly assumed would a simple thing to do!!)

            render(interpolation);

long sleepTime = (System.currentTimeMillis()-(newTimeMillis));
if (sleepTime<33.33333333333333){
try {
Thread.sleep((long) (33.33333333333333-(sleepTime)));
} catch (InterruptedException e) {
e.printStackTrace();
}
}


1 Answer

Only render when you've done at least 1 logic update.

If you have not done any logic update (too fast) sleep for about 1ms and check again.

I found that on some OSes calculating the sleep time caused the timing to always be off by a good margin. It's not an issue on Android or iOS but I got into the habit of using 1ms sleep for action games and calculating sleep time only for animated applications (audio visualizers, etc) or slow-pace games where high consistent fps isn't critical.

• Hi @StephaneHockenhull- thanks, however, I'm not sure that would help. I'm always running physics at 60 ticks per second (as required), however, I want to render at 30 frames per seconds. Doing what you suggest would render when a logic update has run, so 60 times per second (unless, of cours a render cycle is dropped in the case of a device not being able to keep up). I need devices to never render more that 30fps. – BungleBonce Dec 27 '14 at 16:17
• Then what you need is to render every 2 logic updates, either by running 2 updates @ 30fps, or counting the number of updates and only rendering when you've done 2 updates or more, keep the odd bit in case 3 updates have been done. if(logic_updates_count >= 2){ logic_updates_count &= 1; render(); } – Stephane Hockenhull Dec 27 '14 at 16:25
• Thannks @StephaneHockenhull, I'll give it a go – BungleBonce Dec 27 '14 at 22:03