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

Commit f29d5a5b authored by Derek Sollenberger's avatar Derek Sollenberger
Browse files

Update AndroidPixelRef to prevent VM from cleaning up memory prematurely.

bug:18306529
Change-Id: I1ea94df1dcaf4fcf248b63dc8b0a13f36412570a
parent 195c4388
Loading
Loading
Loading
Loading
+13 −14
Original line number Diff line number Diff line
@@ -558,7 +558,7 @@ class GLES20Canvas extends HardwareCanvas {
        Bitmap bitmap = patch.getBitmap();
        throwIfCannotDraw(bitmap);
        final long nativePaint = paint == null ? 0 : paint.mNativePaint;
        nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, patch.mNativeChunk,
        nDrawPatch(mRenderer, bitmap.mNativeBitmap, patch.mNativeChunk,
                dst.left, dst.top, dst.right, dst.bottom, nativePaint);
    }

@@ -567,32 +567,31 @@ class GLES20Canvas extends HardwareCanvas {
        Bitmap bitmap = patch.getBitmap();
        throwIfCannotDraw(bitmap);
        final long nativePaint = paint == null ? 0 : paint.mNativePaint;
        nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, patch.mNativeChunk,
        nDrawPatch(mRenderer, bitmap.mNativeBitmap, patch.mNativeChunk,
                dst.left, dst.top, dst.right, dst.bottom, nativePaint);
    }

    private static native void nDrawPatch(long renderer, long bitmap, byte[] buffer, long chunk,
    private static native void nDrawPatch(long renderer, long bitmap, long chunk,
            float left, float top, float right, float bottom, long paint);

    @Override
    public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
        throwIfCannotDraw(bitmap);
        final long nativePaint = paint == null ? 0 : paint.mNativePaint;
        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, nativePaint);
    }

    private static native void nDrawBitmap(long renderer, long bitmap, byte[] buffer,
            float left, float top, long paint);
    private static native void nDrawBitmap(long renderer, long bitmap, float left,
            float top, long paint);

    @Override
    public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
        throwIfCannotDraw(bitmap);
        final long nativePaint = paint == null ? 0 : paint.mNativePaint;
        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer,
                matrix.native_instance, nativePaint);
        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, matrix.native_instance, nativePaint);
    }

    private static native void nDrawBitmap(long renderer, long bitmap, byte[] buffer,
    private static native void nDrawBitmap(long renderer, long bitmap,
            long matrix, long paint);

    @Override
@@ -612,7 +611,7 @@ class GLES20Canvas extends HardwareCanvas {
            bottom = src.bottom;
        }

        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, right, bottom,
                dst.left, dst.top, dst.right, dst.bottom, nativePaint);
    }

@@ -633,11 +632,11 @@ class GLES20Canvas extends HardwareCanvas {
            bottom = src.bottom;
        }

        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, right, bottom,
                dst.left, dst.top, dst.right, dst.bottom, nativePaint);
    }

    private static native void nDrawBitmap(long renderer, long bitmap, byte[] buffer,
    private static native void nDrawBitmap(long renderer, long bitmap,
            float srcLeft, float srcTop, float srcRight, float srcBottom,
            float left, float top, float right, float bottom, long paint);

@@ -698,11 +697,11 @@ class GLES20Canvas extends HardwareCanvas {
        }

        final long nativePaint = paint == null ? 0 : paint.mNativePaint;
        nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, meshWidth, meshHeight,
        nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, meshWidth, meshHeight,
                verts, vertOffset, colors, colorOffset, nativePaint);
    }

    private static native void nDrawBitmapMesh(long renderer, long bitmap, byte[] buffer,
    private static native void nDrawBitmapMesh(long renderer, long bitmap,
            int meshWidth, int meshHeight, float[] verts, int vertOffset,
            int[] colors, int colorOffset, long paint);

+5 −101
Original line number Diff line number Diff line
@@ -492,19 +492,15 @@ AndroidPixelRef::AndroidPixelRef(JNIEnv* env, const SkImageInfo& info, void* sto
        SkMallocPixelRef(info, storage, rowBytes, ctable, (storageObj == NULL)),
        fWrappedPixelRef(NULL) {
    SkASSERT(storage);
    SkASSERT(storageObj);
    SkASSERT(env);

    if (env->GetJavaVM(&fVM) != JNI_OK) {
        SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
        sk_throw();
    }
    fStorageObj = storageObj;
    fHasGlobalRef = false;
    fGlobalRefCnt = 0;

    // If storageObj is NULL, the memory was NOT allocated on the Java heap
    fOnJavaHeap = (storageObj != NULL);

    fStorageObj = (jbyteArray) env->NewGlobalRef(storageObj);
}

AndroidPixelRef::AndroidPixelRef(AndroidPixelRef& wrappedPixelRef, const SkImageInfo& info,
@@ -516,91 +512,18 @@ AndroidPixelRef::AndroidPixelRef(AndroidPixelRef& wrappedPixelRef, const SkImage
    SkASSERT(fWrappedPixelRef);
    SkSafeRef(fWrappedPixelRef);

    // don't need to initialize these, as all the relevant logic delegates to the wrapped ref
    // don't need to initialize this, as all the relevant logic delegates to the wrapped ref
    fStorageObj = NULL;
    fHasGlobalRef = false;
    fGlobalRefCnt = 0;
    fOnJavaHeap = false;
}

AndroidPixelRef::~AndroidPixelRef() {
    if (fWrappedPixelRef) {
        SkSafeUnref(fWrappedPixelRef);
    } else if (fOnJavaHeap) {
        JNIEnv* env = vm2env(fVM);

        if (fStorageObj && fHasGlobalRef) {
            env->DeleteGlobalRef(fStorageObj);
        }
        fStorageObj = NULL;
    }
}
jbyteArray AndroidPixelRef::getStorageObj() {
    if (fWrappedPixelRef) {
        return fWrappedPixelRef->fStorageObj;
    }
    return fStorageObj;
}

void AndroidPixelRef::setLocalJNIRef(jbyteArray arr) {
    if (fWrappedPixelRef) {
        // delegate java obj management to the wrapped ref
        fWrappedPixelRef->setLocalJNIRef(arr);
    } else if (!fHasGlobalRef) {
        fStorageObj = arr;
    }
}

void AndroidPixelRef::globalRef(void* localref) {
    if (fWrappedPixelRef) {
        // delegate java obj management to the wrapped ref
        fWrappedPixelRef->globalRef(localref);

        // Note: we only ref and unref the wrapped AndroidPixelRef so that
        // bitmap->pixelRef()->globalRef() and globalUnref() can be used in a pair, even if
        // the bitmap has its underlying AndroidPixelRef swapped out/wrapped
        return;
    }
    if (fOnJavaHeap && sk_atomic_inc(&fGlobalRefCnt) == 0) {
        JNIEnv *env = vm2env(fVM);

        // If JNI ref was passed, it is always used
        if (localref) fStorageObj = (jbyteArray) localref;

        if (fStorageObj == NULL) {
            SkDebugf("No valid local ref to create a JNI global ref\n");
            sk_throw();
        }
        if (fHasGlobalRef) {
            // This should never happen
            SkDebugf("Already holding a JNI global ref");
            sk_throw();
        }

        fStorageObj = (jbyteArray) env->NewGlobalRef(fStorageObj);
        // TODO: Check for failure here
        fHasGlobalRef = true;
    }
    ref();
}

void AndroidPixelRef::globalUnref() {
    if (fWrappedPixelRef) {
        // delegate java obj management to the wrapped ref
        fWrappedPixelRef->globalUnref();
        return;
    }
    if (fOnJavaHeap && sk_atomic_dec(&fGlobalRefCnt) == 1) {
    } else {
        SkASSERT(fStorageObj);
        JNIEnv* env = vm2env(fVM);
        if (!fHasGlobalRef) {
            SkDebugf("We don't have a global ref!");
            sk_throw();
        }
        env->DeleteGlobalRef(fStorageObj);
        fStorageObj = NULL;
        fHasGlobalRef = false;
    }
    unref();
}

///////////////////////////////////////////////////////////////////////////////
@@ -657,25 +580,6 @@ bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {

////////////////////////////////////////////////////////////////////////////////

JavaHeapBitmapRef::JavaHeapBitmapRef(JNIEnv* env, SkBitmap* nativeBitmap, jbyteArray buffer) {
    fEnv = env;
    fNativeBitmap = nativeBitmap;
    fBuffer = buffer;

    // If the buffer is NULL, the backing memory wasn't allocated on the Java heap
    if (fBuffer) {
        ((AndroidPixelRef*) fNativeBitmap->pixelRef())->setLocalJNIRef(fBuffer);
    }
}

JavaHeapBitmapRef::~JavaHeapBitmapRef() {
    if (fBuffer) {
        ((AndroidPixelRef*) fNativeBitmap->pixelRef())->setLocalJNIRef(NULL);
    }
}

////////////////////////////////////////////////////////////////////////////////

static jclass make_globalref(JNIEnv* env, const char classname[])
{
    jclass c = env->FindClass(classname);
+0 −41
Original line number Diff line number Diff line
@@ -123,52 +123,11 @@ public:

    virtual ~AndroidPixelRef();

    jbyteArray getStorageObj();

    void setLocalJNIRef(jbyteArray arr);

    /** Used to hold a ref to the pixels when the Java bitmap may be collected.
     *  If specified, 'localref' is a valid JNI local reference to the byte array
     *  containing the pixel data.
     *
     *  'localref' may only be NULL if setLocalJNIRef() was already called with
     *  a JNI local ref that is still valid.
     */
    virtual void globalRef(void* localref=NULL);

    /** Release a ref that was acquired using globalRef(). */
    virtual void globalUnref();

private:
    AndroidPixelRef* const fWrappedPixelRef; // if set, delegate memory management calls to this

    JavaVM* fVM;
    bool fOnJavaHeap; // If true, the memory was allocated on the Java heap

    jbyteArray fStorageObj; // The Java byte[] object used as the bitmap backing store
    bool fHasGlobalRef; // If true, fStorageObj holds a JNI global ref

    mutable int32_t fGlobalRefCnt;
};

/** A helper class for accessing Java-heap-allocated bitmaps.
 *  This should be used when calling into a JNI method that retains a
 *  reference to the bitmap longer than the lifetime of the Java Bitmap.
 *
 *  After creating an instance of this class, a call to
 *  AndroidPixelRef::globalRef() will allocate a JNI global reference
 *  to the backing buffer object.
 */
class JavaHeapBitmapRef {
public:

    JavaHeapBitmapRef(JNIEnv *env, SkBitmap* nativeBitmap, jbyteArray buffer);
    ~JavaHeapBitmapRef();

private:
    JNIEnv* fEnv;
    SkBitmap* fNativeBitmap;
    jbyteArray fBuffer;
};

/** Allocator which allocates the backing buffer in the Java heap.
+11 −24
Original line number Diff line number Diff line
@@ -347,11 +347,8 @@ static void android_view_GLES20Canvas_concatMatrix(JNIEnv* env, jobject clazz,
// ----------------------------------------------------------------------------

static void android_view_GLES20Canvas_drawBitmap(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jlong bitmapPtr, jbyteArray buffer,
        jfloat left, jfloat top, jlong paintPtr) {
        jlong rendererPtr, jlong bitmapPtr, jfloat left, jfloat top, jlong paintPtr) {
    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
    // This object allows the renderer to allocate a global JNI ref to the buffer object.
    JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);

    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    Paint* paint = reinterpret_cast<Paint*>(paintPtr);
@@ -364,12 +361,10 @@ static void android_view_GLES20Canvas_drawBitmap(JNIEnv* env, jobject clazz,
}

static void android_view_GLES20Canvas_drawBitmapRect(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jlong bitmapPtr, jbyteArray buffer,
        jlong rendererPtr, jlong bitmapPtr,
        float srcLeft, float srcTop, float srcRight, float srcBottom,
        float dstLeft, float dstTop, float dstRight, float dstBottom, jlong paintPtr) {
    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
    // This object allows the renderer to allocate a global JNI ref to the buffer object.
    JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);

    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    Paint* paint = reinterpret_cast<Paint*>(paintPtr);
@@ -378,11 +373,8 @@ static void android_view_GLES20Canvas_drawBitmapRect(JNIEnv* env, jobject clazz,
}

static void android_view_GLES20Canvas_drawBitmapMatrix(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jlong bitmapPtr, jbyteArray buffer,
        jlong matrixPtr, jlong paintPtr) {
        jlong rendererPtr, jlong bitmapPtr, jlong matrixPtr, jlong paintPtr) {
    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
    // This object allows the renderer to allocate a global JNI ref to the buffer object.
    JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);

    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
@@ -427,12 +419,9 @@ static void android_view_GLES20Canvas_drawBitmapData(JNIEnv* env, jobject clazz,
}

static void android_view_GLES20Canvas_drawBitmapMesh(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jlong bitmapPtr, jbyteArray buffer,
        jint meshWidth, jint meshHeight, jfloatArray vertices, jint offset, jintArray colors,
        jint colorOffset, jlong paintPtr) {
        jlong rendererPtr, jlong bitmapPtr, jint meshWidth, jint meshHeight,
        jfloatArray vertices, jint offset, jintArray colors, jint colorOffset, jlong paintPtr) {
    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
    // This object allows the renderer to allocate a global JNI ref to the buffer object.
    JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);

    jfloat* verticesArray = vertices ? env->GetFloatArrayElements(vertices, NULL) + offset : NULL;
    jint* colorsArray = colors ? env->GetIntArrayElements(colors, NULL) + colorOffset : NULL;
@@ -446,11 +435,9 @@ static void android_view_GLES20Canvas_drawBitmapMesh(JNIEnv* env, jobject clazz,
}

static void android_view_GLES20Canvas_drawPatch(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jlong bitmapPtr, jbyteArray buffer, jlong patchPtr,
        jlong rendererPtr, jlong bitmapPtr, jlong patchPtr,
        float left, float top, float right, float bottom, jlong paintPtr) {
    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
    // This object allows the renderer to allocate a global JNI ref to the buffer object.
    JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);

    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    Res_png_9patch* patch = reinterpret_cast<Res_png_9patch*>(patchPtr);
@@ -914,14 +901,14 @@ static JNINativeMethod gMethods[] = {
    { "nGetMatrix",         "(JJ)V",           (void*) android_view_GLES20Canvas_getMatrix },
    { "nConcatMatrix",      "(JJ)V",           (void*) android_view_GLES20Canvas_concatMatrix },

    { "nDrawBitmap",        "(JJ[BFFJ)V",      (void*) android_view_GLES20Canvas_drawBitmap },
    { "nDrawBitmap",        "(JJ[BFFFFFFFFJ)V",(void*) android_view_GLES20Canvas_drawBitmapRect },
    { "nDrawBitmap",        "(JJ[BJJ)V",       (void*) android_view_GLES20Canvas_drawBitmapMatrix },
    { "nDrawBitmap",        "(JJFFJ)V",      (void*) android_view_GLES20Canvas_drawBitmap },
    { "nDrawBitmap",        "(JJFFFFFFFFJ)V",(void*) android_view_GLES20Canvas_drawBitmapRect },
    { "nDrawBitmap",        "(JJJJ)V",       (void*) android_view_GLES20Canvas_drawBitmapMatrix },
    { "nDrawBitmap",        "(J[IIIFFIIZJ)V",  (void*) android_view_GLES20Canvas_drawBitmapData },

    { "nDrawBitmapMesh",    "(JJ[BII[FI[IIJ)V",(void*) android_view_GLES20Canvas_drawBitmapMesh },
    { "nDrawBitmapMesh",    "(JJII[FI[IIJ)V",(void*) android_view_GLES20Canvas_drawBitmapMesh },

    { "nDrawPatch",         "(JJ[BJFFFFJ)V",   (void*) android_view_GLES20Canvas_drawPatch },
    { "nDrawPatch",         "(JJJFFFFJ)V",   (void*) android_view_GLES20Canvas_drawPatch },

    { "nDrawColor",         "(JII)V",          (void*) android_view_GLES20Canvas_drawColor },
    { "nDrawRect",          "(JFFFFJ)V",       (void*) android_view_GLES20Canvas_drawRect },
+1 −6
Original line number Diff line number Diff line
@@ -48,13 +48,8 @@ public final class Bitmap implements Parcelable {

    /**
     * Backing buffer for the Bitmap.
     * Made public for quick access from drawing methods -- do NOT modify
     * from outside this class
     *
     * @hide
     */
    @SuppressWarnings("UnusedDeclaration") // native code only
    public byte[] mBuffer;
    private byte[] mBuffer;

    @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) // Keep to finalize native resources
    private final BitmapFinalizer mFinalizer;