Posted: 01 October 2012
In libGDX the render thread on Android will, by default, invoke your
callback as frequently as makes sense. In practice, this means once per
screen refresh, or 60 (or 30 or 50 or 80) times per second (depending on
your hardware). This makes sense for games that are updating the majority
of the screen the majority of the time they're running.
The major downside of this approach is battery usage. A render thread actively running 60 times a second, even when your game is relatively idle (e.g., its showing a static menu or high-score list) will be a big CPU user and a drain on the battery. If you re-draw the entire screen on each render call (which is pretty typical), the battery drain will be even worse.
Note that I'm mostly interested in the behavior on Android so the details and motivations may not apply directly to desktop, GWT, or other libGDX back ends (though all the code should work fine). Also, note that continuous rendering in no way guarantees a render rate, there are hiccups when other applications run, or when your own app takes too long in executing its render. Independent of non-continuous rendering, you should not depend on the framerate in your code.
The solution for the battery drain and high CPU usage of continuous rendering is non-continuous (or "on demand") rendering.
Enabling Non-continuous Rendering
Enabling non-continuous rendering is straight-forward. The only complexity comes from knowing when to request a render.
To enable non-continuous rendering, add this to your
You can enable or disable continuous rendering at any point, not just at create-time. For example, you could leave your app in continuous rendering mode during the core part of your game, and only switch to non-continuous mode when displaying a menu or other relatively static content.
Once continuous rendering is disabled, the render thread will wait before
rendering a frame (technically, before invoking your app's
callback). There are currently three different ways the render thread
will be awoken if non-continuous rendering is enabled:
- If there are input events (touch screen, keyboard, mouse, etc) to process
- A call to
- A call to
The first is case is handled internally by libGDX. So you just need to
sprinkle calls to
Gdx.graphics.requestRendering() where (and if)
necessary in your application.
Kicking the Render Thread
As a first pass, just invoke:
at the end of your
render method. This should
be exactly equivalent to the continuous rendering approach. The next step
is to have logic in your
render method that will occasionally skip this
In my app, I request another render in the following situations:
- Right after enabling non-continuous rendering.
- If any animations are active (to keep the animation smooth).
- If any internal event is triggered, or if any internal state machines are active. This is coarse and conservative, but its safer to occasionally render an extra frame or two.
- On resize (this shouldn't be necessary, but currently seems to be necessary on the desktop).
Consequences of Sporadic Rendering
Assuming you don't miss any places where you need to kick the renderer, there are a couple implications of non-continuous rendering.
If your render method is also the method that updates your game's state, those updates can now become more sporadic, and you may see larger gaps between updates than you would have.
The built-in FPS tracker will be knocked out of whack. I changed tack and
record the time each call to
render() takes (i.e., tracking
seconds-per-frame) and store a histogram of the results. This gives me
the same information, just in a different form.
If you're switching between continuous and non-continuous rendering,
Gdx.graphics.isContinuousRendering() might be useful to figure out if
you need to request a render or not. Though it probably costs exactly the
same to invoke
requestRendering as to check which mode is currently
I'm not sure if any of this applies to the GWT backend.
Non-continuous rendering was broken in the JOGL backend. The JOGL backend was executed in mid-2012 (though for other transgressions).
I'm not sure if any of the
box2d stuff is impacted by