1

我目前在 android GLSurfaceView() 中进行以下 openGL 调用 onDrawFrame():

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);

我需要在每一帧都执行此操作(或类似的操作),因为我正在使用setPreviewCallback()相机内的 android 功能使用自定义回调方法处理来自 android 相机预览的实时提要,但我的垃圾收集绝对是疯狂的(以下重复关于每秒 10 次!):

....

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是一个字节数组,我将它包装到一个缓冲区中。我已经使用 DDMS 完成了分析,实际上大多数分配都是字节数组,从我对 wrap 函数所做的阅读来看,它似乎可能会在调用 wrap 时创建一个底层 byte[],这将然后在用作纹理后由 GC 收集。

如何减少分配次数?我应该以某种方式通过 GL 调用进行更改吗?似乎我可以只是重用相同的字节数组,但我不确定如何。

任何帮助将不胜感激!这么多垃圾吓死我了!

4

2 回答 2

4

有一个有据可查的 android 错误setPreviewCallback()导致过度垃圾收集的方法有关。此问题的解决方案是使用较新的方法,在该方法setPreviewCallbackWithBuffer中,您预先分配一个缓冲区,该缓冲区使用以下方法在每个帧上添加和重新添加addCallbackBuffer

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
...
  mCamera.addCallbackBuffer(callbackBuffer);
  mCamera.setPreviewCallbackWithBuffer(class that implements PreviewCallback);
...
}

并且在 onPreviewFrame 函数中必须再次添加缓冲区:

public void onPreviewFrame(byte[] yuvArray, Camera camera) {
...
  camera.addCallbackBuffer(callbackBuffer);
...
}

解决在 GL 调用中分配内存的最初怀疑ByteBuffer.wrap():它不是。它实际上将使用底层数组(yArray在提供的代码中)并且不会分配新的内存来进行垃圾收集。

于 2012-07-10T18:10:48.323 回答
0

ByteBuffer在每次调用时都会创建一个新实例onDrawFrame,这会立即成为垃圾并被收集。只有当数据的大小发生变化时才需要这个,因此纹理大小尺寸本身也发生了变化。我认为这不太可能。

预先分配ByteBuffer一次并通过批量方法更新其内容。

于 2012-07-10T05:25:25.257 回答