Game Development Reference
In-Depth Information
Working Around a Bug in FloatBuffer
The reason for the Hero's dismal performance with the new
SpriteBatcher
method isn't obvious.
Our
SpriteBatcher
puts a float array into a direct
ByteBuffer
each frame when we call
Vertices.
setVertices()
. The method boils down to calling
FloatBuffer.put(float[])
, and that's the
culprit for the performance hit. While desktop Java implements that
FloatBuffer
method via a
real bulk memory move, the Harmony version used in older Android versions calls
FloatBuffer.
put(float)
for each element in the array. And that's extremely unfortunate, as that method is a
JNI method, which has a lot of overhead (much like the OpenGL ES methods, which are also
JNI methods).
There are a couple of solutions.
IntBuffer.put(int[])
does not suffer from this problem, for
example. We could replace the
FloatBuffer
in our
Vertices
class with an
IntBuffer
, and modify
Vertices.setVertices()
so that it first transfers the floats from the float array to a temporary
int array and then copies the contents of that int array to the
IntBuffer
. This solution was
proposed by Ryan McNally, a fellow game developer, who also reported the bug on the Android
bug tracker. It produces a five-times performance increase on the Hero, and a little less on other
Android devices.
We modify the
Vertices
class to include this fix. We change the
vertices
member to an
IntBuffer
. We add a new member called
tmpBuffer
, which is an int array. The
tmpBuffer
array is
initialized in the constructor of
Vertices
, as follows:
this.tmpBuffer =
new int
[maxVertices * vertexSize / 4];
We also get an
IntBuffer
view from the
ByteBuffer
in the constructor, instead of a
FloatBuffer
:
vertices = buffer.asIntBuffer();
And the
Vertices.setVertices()
method looks like this now:
public void
setVertices(
float
[] vertices,
int
offset,
int
length) {
this
.vertices.clear();
int
len = offset + length;
for
(
int
i = offset, j = 0; i < len; i++, j++)
tmpBuffer[j] = Float.floatToRawIntBits(vertices[i]);
this
.vertices.put(tmpBuffer, 0, length);
this
.vertices.flip();
}
First, we transfer the contents of the
vertices
parameter to the
tmpBuffer
. The static method
Float.floatToRawIntBits()
reinterprets the bit pattern of a float as an int. We then need to copy
the contents of the int array to the
IntBuffer
, formerly known as a
FloatBuffer
. Does it improve
performance? Running the
SpriteBatcherTest
produces the following output now on the Hero,
Droid, and Nexus One:
Hero (1.5):
12-28 00:24:54.770: DEBUG/FPSCounter(2538): fps: 61
12-28 00:24:54.770: DEBUG/FPSCounter(2538): fps: 61
12-28 00:24:55.790: DEBUG/FPSCounter(2538): fps: 62
12-28 00:24:55.790: DEBUG/FPSCounter(2538): fps: 62