Use ashmem to optimize all bitmap copies.
Bug 21037890
Change-Id: Ie32ca3a0c527755f1a1b77db7548cb9629e2001b
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 2785c48..7bd5af1 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1,5 +1,4 @@
#define LOG_TAG "Bitmap"
-
#include "Bitmap.h"
#include "Paint.h"
@@ -23,6 +22,10 @@
#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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
}
}
- 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;
}
diff --git a/core/jni/android/graphics/Bitmap.h b/core/jni/android/graphics/Bitmap.h
index efeb898..95b5fae 100644
--- a/core/jni/android/graphics/Bitmap.h
+++ b/core/jni/android/graphics/Bitmap.h
@@ -29,6 +29,7 @@
Invalid,
External,
Java,
+ Ashmem,
};
class WrappedPixelRef;
@@ -50,6 +51,8 @@
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 @@
bool hasHardwareMipMap();
void setHasHardwareMipMap(bool hasMipMap);
+ int getAshmemFd() const;
private:
friend class WrappedPixelRef;
@@ -104,6 +108,11 @@
FreeFunc freeFunc;
} external;
struct {
+ void* address;
+ int fd;
+ size_t size;
+ } ashmem;
+ struct {
JavaVM* jvm;
jweak jweakRef;
jbyteArray jstrongRef;
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 1c6f7de..028a385 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -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 @@
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) {
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index ef9c2a9..4f72118 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -95,6 +95,12 @@
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