OSDN Git Service

Use ashmem to optimize all bitmap copies.
authorRiley Andrews <riandrews@google.com>
Fri, 14 Nov 2014 01:43:25 +0000 (17:43 -0800)
committerRiley Andrews <riandrews@google.com>
Mon, 18 May 2015 19:55:21 +0000 (19:55 +0000)
Bug 21037890
Change-Id: Ie32ca3a0c527755f1a1b77db7548cb9629e2001b

core/jni/android/graphics/Bitmap.cpp
core/jni/android/graphics/Bitmap.h
core/jni/android/graphics/Graphics.cpp
core/jni/android/graphics/GraphicsJNI.h

index 2785c48..7bd5af1 100755 (executable)
@@ -1,5 +1,4 @@
 #define LOG_TAG "Bitmap"
-
 #include "Bitmap.h"
 
 #include "Paint.h"
 #include "core_jni_helpers.h"
 
 #include <jni.h>
+#include <memory>
+#include <string>
+#include <sys/mman.h>
+#include <cutils/ashmem.h>
 
 namespace android {
 
@@ -135,6 +138,17 @@ Bitmap::Bitmap(void* address, void* context, FreeFunc freeFunc,
     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() {
     doFreePixels();
 }
@@ -156,6 +170,10 @@ void Bitmap::doFreePixels() {
         mPixelStorage.external.freeFunc(mPixelStorage.external.address,
                 mPixelStorage.external.context);
         break;
+    case PixelStorageType::Ashmem:
+        munmap(mPixelStorage.ashmem.address, mPixelStorage.ashmem.size);
+        close(mPixelStorage.ashmem.fd);
+        break;
     case PixelStorageType::Java:
         JNIEnv* env = jniEnv();
         LOG_ALWAYS_FATAL_IF(mPixelStorage.java.jstrongRef,
@@ -179,6 +197,15 @@ void Bitmap::setHasHardwareMipMap(bool 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 {
     assertValid();
     return mPixelRef->info();
@@ -274,6 +301,7 @@ void Bitmap::pinPixelsLocked() {
         LOG_ALWAYS_FATAL("Cannot pin invalid pixels!");
         break;
     case PixelStorageType::External:
+    case PixelStorageType::Ashmem:
         // Nothing to do
         break;
     case PixelStorageType::Java: {
@@ -296,6 +324,7 @@ void Bitmap::unpinPixelsLocked() {
         LOG_ALWAYS_FATAL("Cannot unpin invalid pixels!");
         break;
     case PixelStorageType::External:
+    case PixelStorageType::Ashmem:
         // Don't need to do anything
         break;
     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);
-    if (!nativeBitmap) {
+    int fd = p->readFileDescriptor();
+    int dupFd = dup(fd);
+    if (dupFd < 0) {
         SkSafeUnref(ctable);
+        doThrowRE(env, "Could not dup parcel fd.");
         return NULL;
     }
 
+    bool readOnlyMapping = !isMutable;
+    Bitmap* nativeBitmap = GraphicsJNI::mapAshmemPixelRef(env, bitmap.get(),
+        ctable, dupFd, readOnlyMapping);
     SkSafeUnref(ctable);
-
-    size_t size = bitmap->getSize();
-
-    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.");
+    if (!nativeBitmap) {
+        close(dupFd);
+        doThrowRE(env, "Could not allocate ashmem pixel ref.");
         return NULL;
     }
-
-    bitmap->lockPixels();
-    memcpy(bitmap->getPixels(), blob.data(), size);
-    bitmap->unlockPixels();
-
-    blob.release();
+    bitmap->pixelRef()->setImmutable();
 
     return GraphicsJNI::createBitmap(env, nativeBitmap,
             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,
                                      jlong bitmapHandle,
                                      jboolean isMutable, jint density,
@@ -937,7 +1008,9 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
 
     android::Parcel* p = android::parcelForJavaObject(env, parcel);
     SkBitmap bitmap;
-    reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap);
+
+    android::Bitmap* androidBitmap = reinterpret_cast<Bitmap*>(bitmapHandle);
+    androidBitmap->getSkBitmap(&bitmap);
 
     p->writeInt32(isMutable);
     p->writeInt32(bitmap.colorType());
@@ -959,25 +1032,26 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
         }
     }
 
-    size_t size = bitmap.getSize();
-
-    android::Parcel::WritableBlob blob;
-    android::status_t status = p->writeBlob(size, &blob);
-    if (status) {
-        doThrowRE(env, "Could not write bitmap to parcel blob.");
-        return JNI_FALSE;
-    }
-
-    bitmap.lockPixels();
-    const void* pSrc =  bitmap.getPixels();
-    if (pSrc == NULL) {
-        memset(blob.data(), 0, size);
+    bool ashmemSrc = androidBitmap->getAshmemFd() >= 0;
+    if (ashmemSrc && !isMutable) {
+        p->writeDupFileDescriptor(androidBitmap->getAshmemFd());
     } else {
-        memcpy(blob.data(), pSrc, size);
-    }
-    bitmap.unlockPixels();
+        Ashmem dstAshmem(bitmap.getSize(), !isMutable);
+        if (!dstAshmem.getPtr()) {
+            doThrowRE(env, "Could not allocate ashmem for new bitmap.");
+            return JNI_FALSE;
+        }
 
-    blob.release();
+        bitmap.lockPixels();
+        const void* pSrc = bitmap.getPixels();
+        if (pSrc == NULL) {
+            memset(dstAshmem.getPtr(), 0, bitmap.getSize());
+        } else {
+            memcpy(dstAshmem.getPtr(), pSrc, bitmap.getSize());
+        }
+        bitmap.unlockPixels();
+        p->writeDupFileDescriptor(dstAshmem.getFd());
+    }
     return JNI_TRUE;
 }
 
index efeb898..95b5fae 100644 (file)
@@ -29,6 +29,7 @@ enum class PixelStorageType {
     Invalid,
     External,
     Java,
+    Ashmem,
 };
 
 class WrappedPixelRef;
@@ -50,6 +51,8 @@ public:
             const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
     Bitmap(void* address, void* context, FreeFunc freeFunc,
             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;
 
@@ -76,6 +79,7 @@ public:
 
     bool hasHardwareMipMap();
     void setHasHardwareMipMap(bool hasMipMap);
+    int getAshmemFd() const;
 
 private:
     friend class WrappedPixelRef;
@@ -104,6 +108,11 @@ private:
             FreeFunc freeFunc;
         } external;
         struct {
+            void* address;
+            int fd;
+            size_t size;
+        } ashmem;
+        struct {
             JavaVM* jvm;
             jweak jweakRef;
             jbyteArray jstrongRef;
index 1c6f7de..028a385 100644 (file)
@@ -1,5 +1,8 @@
 #define LOG_TAG "GraphicsJNI"
 
+#include <unistd.h>
+#include <sys/mman.h>
+
 #include "jni.h"
 #include "JNIHelp.h"
 #include "GraphicsJNI.h"
@@ -10,6 +13,7 @@
 #include "SkMath.h"
 #include "SkRegion.h"
 #include <android_runtime/AndroidRuntime.h>
+#include <cutils/ashmem.h>
 
 #include <Caches.h>
 #include <TextureCache.h>
@@ -572,6 +576,82 @@ bool GraphicsJNI::allocatePixels(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ct
     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) {
index ef9c2a9..4f72118 100644 (file)
@@ -95,6 +95,12 @@ public:
     static android::Bitmap* allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
             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
      * of that bitmap.  The memory is then attached to the bitmap via an