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

Commit 39d7f30e authored by Riley Andrews's avatar Riley Andrews
Browse files

Use ashmem to optimize all bitmap copies.

Bug 21037890
Change-Id: Ie32ca3a0c527755f1a1b77db7548cb9629e2001b
parent 3edbdd84
Loading
Loading
Loading
Loading
+109 −35
Original line number Original line Diff line number Diff line
#define LOG_TAG "Bitmap"
#define LOG_TAG "Bitmap"

#include "Bitmap.h"
#include "Bitmap.h"


#include "Paint.h"
#include "Paint.h"
@@ -23,6 +22,10 @@
#include "core_jni_helpers.h"
#include "core_jni_helpers.h"


#include <jni.h>
#include <jni.h>
#include <memory>
#include <string>
#include <sys/mman.h>
#include <cutils/ashmem.h>


namespace android {
namespace android {


@@ -135,6 +138,17 @@ Bitmap::Bitmap(void* address, void* context, FreeFunc freeFunc,
    mPixelRef->unref();
    mPixelRef->unref();
}
}


Bitmap::Bitmap(void* address, int fd,
            const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
        : mPixelStorageType(PixelStorageType::Ashmem) {
    mPixelStorage.ashmem.address = address;
    mPixelStorage.ashmem.fd = fd;
    mPixelStorage.ashmem.size = ashmem_get_size_region(fd);
    mPixelRef.reset(new WrappedPixelRef(this, address, info, rowBytes, ctable));
    // Note: this will trigger a call to onStrongRefDestroyed(), but
    // we want the pixel ref to have a ref count of 0 at this point
    mPixelRef->unref();
}
Bitmap::~Bitmap() {
Bitmap::~Bitmap() {
    doFreePixels();
    doFreePixels();
}
}
@@ -156,6 +170,10 @@ void Bitmap::doFreePixels() {
        mPixelStorage.external.freeFunc(mPixelStorage.external.address,
        mPixelStorage.external.freeFunc(mPixelStorage.external.address,
                mPixelStorage.external.context);
                mPixelStorage.external.context);
        break;
        break;
    case PixelStorageType::Ashmem:
        munmap(mPixelStorage.ashmem.address, mPixelStorage.ashmem.size);
        close(mPixelStorage.ashmem.fd);
        break;
    case PixelStorageType::Java:
    case PixelStorageType::Java:
        JNIEnv* env = jniEnv();
        JNIEnv* env = jniEnv();
        LOG_ALWAYS_FATAL_IF(mPixelStorage.java.jstrongRef,
        LOG_ALWAYS_FATAL_IF(mPixelStorage.java.jstrongRef,
@@ -179,6 +197,15 @@ void Bitmap::setHasHardwareMipMap(bool hasMipMap) {
    mPixelRef->setHasHardwareMipMap(hasMipMap);
    mPixelRef->setHasHardwareMipMap(hasMipMap);
}
}


int Bitmap::getAshmemFd() const {
    switch (mPixelStorageType) {
    case PixelStorageType::Ashmem:
        return mPixelStorage.ashmem.fd;
    default:
        return -1;
    }
}

const SkImageInfo& Bitmap::info() const {
const SkImageInfo& Bitmap::info() const {
    assertValid();
    assertValid();
    return mPixelRef->info();
    return mPixelRef->info();
@@ -274,6 +301,7 @@ void Bitmap::pinPixelsLocked() {
        LOG_ALWAYS_FATAL("Cannot pin invalid pixels!");
        LOG_ALWAYS_FATAL("Cannot pin invalid pixels!");
        break;
        break;
    case PixelStorageType::External:
    case PixelStorageType::External:
    case PixelStorageType::Ashmem:
        // Nothing to do
        // Nothing to do
        break;
        break;
    case PixelStorageType::Java: {
    case PixelStorageType::Java: {
@@ -296,6 +324,7 @@ void Bitmap::unpinPixelsLocked() {
        LOG_ALWAYS_FATAL("Cannot unpin invalid pixels!");
        LOG_ALWAYS_FATAL("Cannot unpin invalid pixels!");
        break;
        break;
    case PixelStorageType::External:
    case PixelStorageType::External:
    case PixelStorageType::Ashmem:
        // Don't need to do anything
        // Don't need to do anything
        break;
        break;
    case PixelStorageType::Java: {
    case PixelStorageType::Java: {
@@ -898,34 +927,76 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
        }
        }
    }
    }


    android::Bitmap* nativeBitmap = GraphicsJNI::allocateJavaPixelRef(env, bitmap.get(), ctable);
    int fd = p->readFileDescriptor();
    if (!nativeBitmap) {
    int dupFd = dup(fd);
    if (dupFd < 0) {
        SkSafeUnref(ctable);
        SkSafeUnref(ctable);
        doThrowRE(env, "Could not dup parcel fd.");
        return NULL;
        return NULL;
    }
    }


    bool readOnlyMapping = !isMutable;
    Bitmap* nativeBitmap = GraphicsJNI::mapAshmemPixelRef(env, bitmap.get(),
        ctable, dupFd, readOnlyMapping);
    SkSafeUnref(ctable);
    SkSafeUnref(ctable);

    if (!nativeBitmap) {
    size_t size = bitmap->getSize();
        close(dupFd);

        doThrowRE(env, "Could not allocate ashmem pixel ref.");
    android::Parcel::ReadableBlob blob;
    android::status_t status = p->readBlob(size, &blob);
    if (status) {
        nativeBitmap->detachFromJava();
        doThrowRE(env, "Could not read bitmap from parcel blob.");
        return NULL;
        return NULL;
    }
    }

    bitmap->pixelRef()->setImmutable();
    bitmap->lockPixels();
    memcpy(bitmap->getPixels(), blob.data(), size);
    bitmap->unlockPixels();

    blob.release();


    return GraphicsJNI::createBitmap(env, nativeBitmap,
    return GraphicsJNI::createBitmap(env, nativeBitmap,
            getPremulBitmapCreateFlags(isMutable), NULL, NULL, density);
            getPremulBitmapCreateFlags(isMutable), NULL, NULL, density);
}
}


class Ashmem {
public:
    Ashmem(size_t sz, bool removeWritePerm) : mSize(sz) {
        int fd = -1;
        void *addr = nullptr;

        // Create new ashmem region with read/write priv
        fd = ashmem_create_region("bitmap", sz);
        if (fd < 0) {
            goto error;
        }
        addr = mmap(nullptr, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        if (addr == MAP_FAILED) {
            goto error;
        }
        // If requested, remove the ability to make additional writeable to
        // this memory.
        if (removeWritePerm) {
            if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
                goto error;
            }
        }
        mFd = fd;
        mPtr = addr;
        return;
error:
        if (fd >= 0) {
            close(fd);
        }
        if (addr) {
            munmap(addr, sz);
        }
    }
    ~Ashmem() {
        if (mPtr) {
            close(mFd);
            munmap(mPtr, mSize);
        }
    }
    void *getPtr() const { return mPtr; }
    int getFd() const { return mFd; }
private:
    int mFd = -1;
    int mSize;
    void* mPtr = nullptr;
};

static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
                                     jlong bitmapHandle,
                                     jlong bitmapHandle,
                                     jboolean isMutable, jint density,
                                     jboolean isMutable, jint density,
@@ -937,7 +1008,9 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,


    android::Parcel* p = android::parcelForJavaObject(env, parcel);
    android::Parcel* p = android::parcelForJavaObject(env, parcel);
    SkBitmap bitmap;
    SkBitmap bitmap;
    reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap);

    android::Bitmap* androidBitmap = reinterpret_cast<Bitmap*>(bitmapHandle);
    androidBitmap->getSkBitmap(&bitmap);


    p->writeInt32(isMutable);
    p->writeInt32(isMutable);
    p->writeInt32(bitmap.colorType());
    p->writeInt32(bitmap.colorType());
@@ -959,25 +1032,26 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
        }
        }
    }
    }


    size_t size = bitmap.getSize();
    bool ashmemSrc = androidBitmap->getAshmemFd() >= 0;

    if (ashmemSrc && !isMutable) {
    android::Parcel::WritableBlob blob;
        p->writeDupFileDescriptor(androidBitmap->getAshmemFd());
    android::status_t status = p->writeBlob(size, &blob);
    } else {
    if (status) {
        Ashmem dstAshmem(bitmap.getSize(), !isMutable);
        doThrowRE(env, "Could not write bitmap to parcel blob.");
        if (!dstAshmem.getPtr()) {
            doThrowRE(env, "Could not allocate ashmem for new bitmap.");
            return JNI_FALSE;
            return JNI_FALSE;
        }
        }


        bitmap.lockPixels();
        bitmap.lockPixels();
        const void* pSrc = bitmap.getPixels();
        const void* pSrc = bitmap.getPixels();
        if (pSrc == NULL) {
        if (pSrc == NULL) {
        memset(blob.data(), 0, size);
            memset(dstAshmem.getPtr(), 0, bitmap.getSize());
        } else {
        } else {
        memcpy(blob.data(), pSrc, size);
            memcpy(dstAshmem.getPtr(), pSrc, bitmap.getSize());
        }
        }
        bitmap.unlockPixels();
        bitmap.unlockPixels();

        p->writeDupFileDescriptor(dstAshmem.getFd());
    blob.release();
    }
    return JNI_TRUE;
    return JNI_TRUE;
}
}


+9 −0
Original line number Original line Diff line number Diff line
@@ -29,6 +29,7 @@ enum class PixelStorageType {
    Invalid,
    Invalid,
    External,
    External,
    Java,
    Java,
    Ashmem,
};
};


class WrappedPixelRef;
class WrappedPixelRef;
@@ -50,6 +51,8 @@ public:
            const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
            const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
    Bitmap(void* address, void* context, FreeFunc freeFunc,
    Bitmap(void* address, void* context, FreeFunc freeFunc,
            const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
            const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
    Bitmap(void* address, int fd, const SkImageInfo& info, size_t rowBytes,
            SkColorTable* ctable);


    const SkImageInfo& info() const;
    const SkImageInfo& info() const;


@@ -76,6 +79,7 @@ public:


    bool hasHardwareMipMap();
    bool hasHardwareMipMap();
    void setHasHardwareMipMap(bool hasMipMap);
    void setHasHardwareMipMap(bool hasMipMap);
    int getAshmemFd() const;


private:
private:
    friend class WrappedPixelRef;
    friend class WrappedPixelRef;
@@ -103,6 +107,11 @@ private:
            void* context;
            void* context;
            FreeFunc freeFunc;
            FreeFunc freeFunc;
        } external;
        } external;
        struct {
            void* address;
            int fd;
            size_t size;
        } ashmem;
        struct {
        struct {
            JavaVM* jvm;
            JavaVM* jvm;
            jweak jweakRef;
            jweak jweakRef;
+80 −0
Original line number Original line Diff line number Diff line
#define LOG_TAG "GraphicsJNI"
#define LOG_TAG "GraphicsJNI"


#include <unistd.h>
#include <sys/mman.h>

#include "jni.h"
#include "jni.h"
#include "JNIHelp.h"
#include "JNIHelp.h"
#include "GraphicsJNI.h"
#include "GraphicsJNI.h"
@@ -10,6 +13,7 @@
#include "SkMath.h"
#include "SkMath.h"
#include "SkRegion.h"
#include "SkRegion.h"
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/AndroidRuntime.h>
#include <cutils/ashmem.h>


#include <Caches.h>
#include <Caches.h>
#include <TextureCache.h>
#include <TextureCache.h>
@@ -572,6 +576,82 @@ bool GraphicsJNI::allocatePixels(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ct
    return true;
    return true;
}
}


android::Bitmap* GraphicsJNI::allocateAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
                                                     SkColorTable* ctable) {
    int fd;

    const SkImageInfo& info = bitmap->info();
    if (info.fColorType == kUnknown_SkColorType) {
        doThrowIAE(env, "unknown bitmap configuration");
        return nullptr;
    }

    size_t size;
    if (!computeAllocationSize(*bitmap, &size)) {
        return nullptr;
    }

    // we must respect the rowBytes value already set on the bitmap instead of
    // attempting to compute our own.
    const size_t rowBytes = bitmap->rowBytes();

    // Create new ashmem region with read/write priv
    fd = ashmem_create_region("bitmap", size);
    if (fd < 0) {
        return nullptr;
    }

    void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED) {
        close(fd);
        return nullptr;
    }

    if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
        munmap(addr, size);
        close(fd);
        return nullptr;
    }

    android::Bitmap* wrapper = new android::Bitmap(addr, fd, info, rowBytes, ctable);
    wrapper->getSkBitmap(bitmap);
    // since we're already allocated, we lockPixels right away
    // HeapAllocator behaves this way too
    bitmap->lockPixels();

    return wrapper;
}

android::Bitmap* GraphicsJNI::mapAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
                                                SkColorTable* ctable, int fd, bool readOnly) {
    int flags;

    const SkImageInfo& info = bitmap->info();
    if (info.fColorType == kUnknown_SkColorType) {
        doThrowIAE(env, "unknown bitmap configuration");
        return nullptr;
    }

    // Create new ashmem region with read/write priv
    flags = readOnly ? (PROT_READ) : (PROT_READ | PROT_WRITE);
    void* addr = mmap(NULL, ashmem_get_size_region(fd), flags, MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED) {
        return nullptr;
    }

    // we must respect the rowBytes value already set on the bitmap instead of
    // attempting to compute our own.
    const size_t rowBytes = bitmap->rowBytes();

    android::Bitmap* wrapper = new android::Bitmap(addr, fd, info, rowBytes, ctable);
    wrapper->getSkBitmap(bitmap);
    // since we're already allocated, we lockPixels right away
    // HeapAllocator behaves this way too
    bitmap->lockPixels();

    return wrapper;
}

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


JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env) {
JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env) {
+6 −0
Original line number Original line Diff line number Diff line
@@ -95,6 +95,12 @@ public:
    static android::Bitmap* allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
    static android::Bitmap* allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
            SkColorTable* ctable);
            SkColorTable* ctable);


    static android::Bitmap* allocateAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
            SkColorTable* ctable);

    static android::Bitmap* mapAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
            SkColorTable* ctable, int fd, bool readOnly);

    /**
    /**
     * Given a bitmap we natively allocate a memory block to store the contents
     * Given a bitmap we natively allocate a memory block to store the contents
     * of that bitmap.  The memory is then attached to the bitmap via an
     * of that bitmap.  The memory is then attached to the bitmap via an