Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f890fab5 authored by Patrick Dubroy's avatar Patrick Dubroy
Browse files

Ensure bitmaps aren't freed while referenced from a display list

Also removes the reference queue finalizers. They aren't necessary
anymore now that Bitmaps are allocated in the heap.
parent 2a1cc5ac
Loading
Loading
Loading
Loading
+0 −127
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.util;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;

/**
 * This class can be used to implement reliable finalizers.
 * 
 * @hide
 */
public final class Finalizers {
    private static final String LOG_TAG = "Finalizers";
    
    private static final Object[] sLock = new Object[0];
    private static boolean sInit;
    private static Reclaimer sReclaimer;

    /**
     * Subclass of PhantomReference used to reclaim resources.
     */
    public static abstract class ReclaimableReference<T> extends PhantomReference<T> {
        public ReclaimableReference(T r, ReferenceQueue<Object> q) {
            super(r, q);
        }
        
        public abstract void reclaim();
    }

    /**
     * Returns the queue used to reclaim ReclaimableReferences.
     * 
     * @return A reference queue or null before initialization
     */
    public static ReferenceQueue<Object> getQueue() {
        synchronized (sLock) {
            if (!sInit) {
                return null;
            }
            if (!sReclaimer.isRunning()) {
                sReclaimer = new Reclaimer(sReclaimer.mQueue);
                sReclaimer.start();
            }
            return sReclaimer.mQueue;
        }
    }

    /**
     * Invoked by Zygote. Don't touch!
     */
    public static void init() {
        synchronized (sLock) {
            if (!sInit && sReclaimer == null) {
                sReclaimer = new Reclaimer();
                sReclaimer.start();
                sInit = true;
            }
        }
    }
    
    private static class Reclaimer extends Thread {
        ReferenceQueue<Object> mQueue;

        private volatile boolean mRunning = false;

        Reclaimer() {
            this(new ReferenceQueue<Object>());
        }

        Reclaimer(ReferenceQueue<Object> queue) {
            super("Reclaimer");
            setDaemon(true);
            mQueue = queue;            
        }

        @Override
        public void start() {
            mRunning = true;
            super.start();
        }

        boolean isRunning() {
            return mRunning;
        }

        @SuppressWarnings({"InfiniteLoopStatement"})
        @Override
        public void run() {
            try {
                while (true) {
                    try {
                        cleanUp(mQueue.remove());
                    } catch (InterruptedException e) {
                        // Ignore
                    }
                }
            } catch (Exception e) {
                Log.e(LOG_TAG, "Reclaimer thread exiting: ", e);
            } finally {
                mRunning = false;
            }
        }

        private void cleanUp(Reference<?> reference) {
            do {
                reference.clear();
                ((ReclaimableReference<?>) reference).reclaim();
            } while ((reference = mQueue.poll()) != null);
        }
    }
}
+12 −19
Original line number Diff line number Diff line
@@ -34,11 +34,6 @@ import android.text.GraphicsOperations;
import android.text.SpannableString;
import android.text.SpannedString;
import android.text.TextUtils;
import android.util.Finalizers;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * An implementation of Canvas on top of OpenGL ES 2.0.
@@ -47,6 +42,10 @@ class GLES20Canvas extends HardwareCanvas {
    private final boolean mOpaque;
    private int mRenderer;

    // The native renderer will be destroyed when this object dies.
    // DO NOT overwrite this reference once it is set.
    private CanvasFinalizer mFinalizer;

    private int mWidth;
    private int mHeight;
    
@@ -78,7 +77,7 @@ class GLES20Canvas extends HardwareCanvas {
        this(false, translucent);
    }
    
    GLES20Canvas(boolean record, boolean translucent) {
    protected GLES20Canvas(boolean record, boolean translucent) {
        mOpaque = !translucent;

        if (record) {
@@ -90,7 +89,7 @@ class GLES20Canvas extends HardwareCanvas {
        if (mRenderer == 0) {
            throw new IllegalStateException("Could not create GLES20Canvas renderer");
        } else {
            new CanvasFinalizer(this);
            mFinalizer = new CanvasFinalizer(mRenderer);
        }
    }

@@ -99,22 +98,16 @@ class GLES20Canvas extends HardwareCanvas {

    private static native void nDestroyRenderer(int renderer);

    private static class CanvasFinalizer extends Finalizers.ReclaimableReference<GLES20Canvas> {
        private static final Set<CanvasFinalizer> sFinalizers = Collections.synchronizedSet(
                new HashSet<CanvasFinalizer>());

        private int mRenderer;
    private static class CanvasFinalizer {
        final int mRenderer;

        CanvasFinalizer(GLES20Canvas canvas) {
            super(canvas, Finalizers.getQueue());
            mRenderer = canvas.mRenderer;
            sFinalizers.add(this);
        CanvasFinalizer(int renderer) {
            mRenderer = renderer;
        }

        @Override
        public void reclaim() {
        protected void finalize() throws Throwable {
            nDestroyRenderer(mRenderer);
            sFinalizers.remove(this);
        }
    }

+12 −19
Original line number Diff line number Diff line
@@ -16,12 +16,6 @@

package android.view;

import android.util.Finalizers;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * An implementation of display list for OpenGL ES 2.0.
 */
@@ -33,13 +27,18 @@ class GLES20DisplayList extends DisplayList {

    int mNativeDisplayList;

    // The native display list will be destroyed when this object dies.
    // DO NOT overwrite this reference once it is set.
    @SuppressWarnings("unused")
    private DisplayListFinalizer mFinalizer;

    @Override
    HardwareCanvas start() {
        if (mStarted) {
            throw new IllegalStateException("Recording has already started");
        }

        mCanvas = new GLES20Canvas(true, true);
        mCanvas = new GLES20RecordingCanvas(true);
        mStarted = true;
        mRecorded = false;

@@ -53,7 +52,7 @@ class GLES20DisplayList extends DisplayList {
            mRecorded = true;

            mNativeDisplayList = mCanvas.getDisplayList();
            new DisplayListFinalizer(this);
            mFinalizer = new DisplayListFinalizer(mNativeDisplayList);
        }
    }

@@ -62,22 +61,16 @@ class GLES20DisplayList extends DisplayList {
        return !mStarted && mRecorded;
    }

    private static class DisplayListFinalizer extends Finalizers.ReclaimableReference<DisplayList> {
        private static final Set<DisplayListFinalizer> sFinalizers = Collections.synchronizedSet(
                new HashSet<DisplayListFinalizer>());

        private int mNativeDisplayList;
    private static class DisplayListFinalizer {
        int mNativeDisplayList;

        DisplayListFinalizer(GLES20DisplayList displayList) {
            super(displayList, Finalizers.getQueue());
            mNativeDisplayList = displayList.mNativeDisplayList;
            sFinalizers.add(this);
        DisplayListFinalizer(int nativeDisplayList) {
            mNativeDisplayList = nativeDisplayList;
        }

        @Override
        public void reclaim() {
        protected void finalize() throws Throwable {
            GLES20Canvas.destroyDisplayList(mNativeDisplayList);
            sFinalizers.remove(this);
        }
    }
}
+273 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.view;

import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;

import java.util.HashSet;

/**
 * An implementation of a GL canvas that records drawing operations.
 * This is intended for use with a DisplayList. This class keeps a list of all the Paint and
 * Bitmap objects that it draws, preventing the backing memory of Bitmaps from being freed while
 * the DisplayList is still holding a native reference to the memory.
 */
class GLES20RecordingCanvas extends GLES20Canvas {
    // These lists ensure that any Bitmaps recorded by a DisplayList are kept alive as long
    // as the DisplayList is alive.
    private HashSet<Bitmap> mBitmaps = new HashSet<Bitmap>();

    GLES20RecordingCanvas(boolean translucent) {
        super(true, translucent);
    }

    private void recordShaderBitmap(Paint paint) {
        if (paint != null) {
            final Shader shader = paint.getShader();
            if (shader instanceof BitmapShader) {
                mBitmaps.add(((BitmapShader) shader).mBitmap);
            }
        }
    }

    @Override
    public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
            Paint paint) {
        super.drawArc(oval, startAngle, sweepAngle, useCenter, paint);
    }

    @Override
    public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
        super.drawPatch(bitmap, chunks, dst, paint);
        mBitmaps.add(bitmap);
        // Shaders in the Paint are ignored when drawing a Bitmap
    }

    @Override
    public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
        super.drawBitmap(bitmap, left, top, paint);
        mBitmaps.add(bitmap);
        // Shaders in the Paint are ignored when drawing a Bitmap
    }

    @Override
    public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
        super.drawBitmap(bitmap, matrix, paint);
        mBitmaps.add(bitmap);
        // Shaders in the Paint are ignored when drawing a Bitmap
    }

    @Override
    public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
        super.drawBitmap(bitmap, src, dst, paint);
        mBitmaps.add(bitmap);
        // Shaders in the Paint are ignored when drawing a Bitmap
    }

    @Override
    public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
        super.drawBitmap(bitmap, src, dst, paint);
        mBitmaps.add(bitmap);
        // Shaders in the Paint are ignored when drawing a Bitmap
    }

    @Override
    public void drawBitmap(int[] colors, int offset, int stride, float x, float y, int width,
            int height, boolean hasAlpha, Paint paint) {
        super.drawBitmap(colors, offset, stride, x, y, width, height, hasAlpha, paint);
        // Shaders in the Paint are ignored when drawing a Bitmap
    }

    @Override
    public void drawBitmap(int[] colors, int offset, int stride, int x, int y, int width,
            int height, boolean hasAlpha, Paint paint) {
        super.drawBitmap(colors, offset, stride, x, y, width, height, hasAlpha, paint);
        // Shaders in the Paint are ignored when drawing a Bitmap
    }

    @Override
    public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts,
            int vertOffset, int[] colors, int colorOffset, Paint paint) {
        super.drawBitmapMesh(bitmap, meshWidth, meshHeight, verts, vertOffset, colors, colorOffset,
                paint);
        mBitmaps.add(bitmap);
        // Shaders in the Paint are ignored when drawing a Bitmap
    }

    @Override
    public void drawCircle(float cx, float cy, float radius, Paint paint) {
        super.drawCircle(cx, cy, radius, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
        super.drawLine(startX, startY, stopX, stopY, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawLines(float[] pts, int offset, int count, Paint paint) {
        super.drawLines(pts, offset, count, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawLines(float[] pts, Paint paint) {
        super.drawLines(pts, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawOval(RectF oval, Paint paint) {
        super.drawOval(oval, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawPaint(Paint paint) {
        super.drawPaint(paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawPath(Path path, Paint paint) {
        super.drawPath(path, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawPoint(float x, float y, Paint paint) {
        super.drawPoint(x, y, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawPoints(float[] pts, int offset, int count, Paint paint) {
        super.drawPoints(pts, offset, count, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawPoints(float[] pts, Paint paint) {
        super.drawPoints(pts, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint) {
        super.drawPosText(text, index, count, pos, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawPosText(String text, float[] pos, Paint paint) {
        super.drawPosText(text, pos, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawRect(float left, float top, float right, float bottom, Paint paint) {
        super.drawRect(left, top, right, bottom, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawRect(Rect r, Paint paint) {
        super.drawRect(r, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawRect(RectF r, Paint paint) {
        super.drawRect(r, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) {
        super.drawRoundRect(rect, rx, ry, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawText(char[] text, int index, int count, float x, float y, Paint paint) {
        super.drawText(text, index, count, x, y, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
        super.drawText(text, start, end, x, y, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawText(String text, int start, int end, float x, float y, Paint paint) {
        super.drawText(text, start, end, x, y, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawText(String text, float x, float y, Paint paint) {
        super.drawText(text, x, y, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawTextOnPath(char[] text, int index, int count, Path path, float hOffset,
            float vOffset, Paint paint) {
        super.drawTextOnPath(text, index, count, path, hOffset, vOffset, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) {
        super.drawTextOnPath(text, path, hOffset, vOffset, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount,
            float x, float y, int dir, Paint paint) {
        super.drawTextRun(text, index, count, contextIndex, contextCount, x, y, dir, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawTextRun(CharSequence text, int start, int end, int contextStart,
            int contextEnd, float x, float y, int dir, Paint paint) {
        super.drawTextRun(text, start, end, contextStart, contextEnd, x, y, dir, paint);
        recordShaderBitmap(paint);
    }

    @Override
    public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset,
            float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices,
            int indexOffset, int indexCount, Paint paint) {
        super.drawVertices(mode, vertexCount, verts, vertOffset, texs, texOffset, colors,
                colorOffset, indices, indexOffset, indexCount, paint);
        recordShaderBitmap(paint);
    }
}
+0 −7
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import android.os.IBinder;
import android.os.Process;
import android.os.SystemProperties;
import android.util.Config;
import android.util.Finalizers;
import android.util.Log;
import android.util.Slog;

@@ -142,12 +141,6 @@ public class RuntimeInit {
            Debug.enableEmulatorTraceOutput();
        }

        /**
         * Initialize the thread used to reclaim resources without
         * going through finalizers.
         */
        Finalizers.init();

        initialized = true;
    }

Loading