Reputation: 8579
I am currently making the following openGL calls onDrawFrame() in an android GLSurfaceView():
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, cameraTexture[0]);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, camPreviewSize.width, camPreviewSize.height, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, ByteBuffer.wrap(yArray));
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
I need to do this (or something like it) every frame because I am processing a live feed from an android camera preview with a custom callback method using android's setPreviewCallback()
functionality within the camera, but my garbage collection is going absolutely wild (the following repeats about 10x a second!):
....
GC_FOR_ALLOC freed 1530K, 42% free 3671K/6307K, paused 18ms
GC_FOR_ALLOC freed 1530K, 42% free 3671K/6307K, paused 22ms
GC_FOR_ALLOC freed 1530K, 42% free 3671K/6307K, paused 25ms
GC_FOR_ALLOC freed 1530K, 42% free 3671K/6307K, paused 20ms
GC_FOR_ALLOC freed 1530K, 42% free 3671K/6307K, paused 20ms
GC_FOR_ALLOC freed 1530K, 42% free 3671K/6307K, paused 16ms
GC_FOR_ALLOC freed 1530K, 42% free 3671K/6307K, paused 22ms
....
yArray
is a byte array, and I wrap it to a buffer. I've done profiling using DDMS, and indeed the majority of allocations are byte arrays, and from the reading I have done on the wrap function, it seems as though it may create an underlying byte[] on a call to wrap, which will then be collected by the GC after it is used as a texture.
How can I reduce the number of allocations? Should I change by GL calls somehow? It seems like I could just be reusing the same byte array instead, but I'm not sure how.
Any help would be so appreciated! This amount of garbage scares me!
Upvotes: 1
Views: 1275
Reputation: 8579
There is a well documented android bug relating to the setPreviewCallback()
method that causes excessive garbage collection. The solution to this problem is to use the newer method setPreviewCallbackWithBuffer
where you preallocate a buffer that is added and re-added on every fram with the addCallbackBuffer
method:
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
...
mCamera.addCallbackBuffer(callbackBuffer);
mCamera.setPreviewCallbackWithBuffer(class that implements PreviewCallback);
...
}
and in the onPreviewFrame function the buffer must be added again:
public void onPreviewFrame(byte[] yuvArray, Camera camera) {
...
camera.addCallbackBuffer(callbackBuffer);
...
}
to address the initial suspicion that ByteBuffer.wrap()
is allocating memory in the GL calls: it is not. It will actually use the underlying array (yArray
in the provided code) and will not allocate new memory to be garbage collected.
Upvotes: 4
Reputation: 3528
You're creating a new ByteBuffer
instance on every onDrawFrame
call, which is immediately garbage and collected. You'd need this only when the size of the data changed, and thus the texture size dimensions itself. I don't think this's likely.
Preallocate the ByteBuffer
one time and update its content via bulk methods.
Upvotes: 0