I'm making an android application that uses GLSurfaceView and android media effects to apply filters to images.The implementation is working fine except it appears as if there's a memory leak in the app.With each addition of an effect the graphics memory used by the app increases significantly and this memory is never released.
Does any one know how to reduce the memory footprint from this class or at least knows what is causing the high usage of memory and point me in the right direction to solving this?
I've tried to call GLES20.glDeleteTextures(2, mTextures, 0)
just before GLES20.glGenTextures(2, mTextures, 0)
but it didn't help.I've also experiment with GLES20.glFinish()
and GLES20.glFlush()
and mEffectContext.release()
with no success or any observable change.
I've attached a screenshot from the profiling I've performed and also relevant pieces of the class itself below.
This is my first stackoverflow question so please feel free to correct me incase i've broken any community guidelines.
class ImageFilterView extends GLSurfaceView implements GLSurfaceView.Renderer {
private int[] mTextures = new int[2];
private void init() {
setEGLContextClientVersion(2);
setRenderer(this);
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
setFilterEffect(NONE);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
if (mTexRenderer != null) {
mTexRenderer.updateViewSize(width, height);
}
}
@Override
public void onDrawFrame(GL10 gl) {
if (!mInitialized) {
//Only need to do this once
mEffectContext = EffectContext.createWithCurrentGlContext();
mTexRenderer.init();
loadTextures();
mInitialized = true;
}
if (mCurrentEffect != NONE || mCustomEffect != null) {
//if an effect is chosen initialize it and apply it to the texture
initEffect();
applyEffect();
}
renderResult();
if (isSaveImage) {
final Bitmap mFilterBitmap = BitmapUtil.createBitmapFromGLSurface(this, gl);
Log.e(TAG, "onDrawFrame: " + mFilterBitmap);
isSaveImage = false;
if (mOnSaveBitmap != null) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
mOnSaveBitmap.onBitmapReady(mFilterBitmap);
}
});
}
}
}
void setFilterEffect(PhotoFilter effect) {
mCurrentEffect = effect;
mCustomEffect = null;
requestRender();
}
void saveBitmap(OnSaveBitmap onSaveBitmap) {
mOnSaveBitmap = onSaveBitmap;
isSaveImage = true;
requestRender();
}
private void loadTextures() {
GLES20.glGenTextures(2, mTextures, 0);
// Load input bitmap
if (mSourceBitmap != null) {
mImageWidth = mSourceBitmap.getWidth();
mImageHeight = mSourceBitmap.getHeight();
mTexRenderer.updateTextureSize(mImageWidth, mImageHeight);
// Upload to texture
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextures[0]);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mSourceBitmap, 0);
// Set texture parameters
GLToolbox.initTexParams();
}
}
private void initEffect() {
EffectFactory effectFactory = mEffectContext.getFactory();
if (mEffect != null) {
mEffect.release();
}
if (mCustomEffect != null) {
mEffect = effectFactory.createEffect(mCustomEffect.getEffectName());
Map<String, Object> parameters = mCustomEffect.getParameters();
for (Map.Entry<String, Object> param : parameters.entrySet()) {
mEffect.setParameter(param.getKey(), param.getValue());
}
} else {
// Initialize the correct effect based on the selected menu/action item
switch (mCurrentEffect) {
case VIGNETTE:
mEffect = effectFactory.createEffect(EFFECT_VIGNETTE);
mEffect.setParameter("scale", .5f);
break;
}
}
}
private void applyEffect() {
mEffect.apply(mTextures[0], mImageWidth, mImageHeight, mTextures[1]);
}
private void renderResult() {
if (mCurrentEffect != NONE || mCustomEffect != null) {
// if no effect is chosen, just render the original bitmap
mTexRenderer.renderTexture(mTextures[1]);
} else {
// render the result of applyEffect()
mTexRenderer.renderTexture(mTextures[0]);
}
}
}
Screenshot of profiling:Memory increases every time a filter is added