Posted: 01 October 2012
Motivation
Before describing how to enable non-continuous rendering in libGDX , I need to explain libGDX's default rendering mode: "continuous" rendering.
In libGDX the render thread on Android will, by default, invoke your
ApplicationListener
's render
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
ApplicationListener
's create
method:
Gdx.graphics.setContinuousRendering(false); Gdx.graphics.requestRendering();
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 render
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
Gdx.graphics.requestRendering()
- A call to
Gdx.app.postRunnable()
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:
Gdx.graphics.requestRendering();
at the end of your ApplicationListener
's 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
call to requestRendering()
.
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.
Extras
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
enabled.
Open Issues
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 scene2d
or box2d
stuff is impacted by
non-continuous rendering.