From bc55eec80ef376208b3c1bfc65d8dc8b672d59f0 Mon Sep 17 00:00:00 2001 From: "halcanary@google.com" Date: Tue, 10 Dec 2013 18:33:07 +0000 Subject: [PATCH] Implement SkAshmemDiscardableMemory - Implement ashmem-backed SkDiscardableMemory subclass: This class in only accesible via the SkDiscardableMemory::Create() function, which replaces the mock implementation in SkDiscardableMemory_none.cpp - Added SkDiscardableMemory_ashmem.cpp to the Android port of Skia Removed SkDiscardableMemory_none.cpp from the Android port. - Added DiscardableMemoryTest. Still needs work. - SkDiscardablePixelRef Bugfix: onLockPixels() now calls SkDELETE on the SkDiscardableMemory pointer when it fails to unlock. - Improved documentation inside ashmem.h BUG= R=scroggo@google.com Review URL: https://codereview.chromium.org/83563002 git-svn-id: http://skia.googlecode.com/svn/trunk@12608 2bbb7eff-a529-9590-31e7-b0007b416f81 --- gyp/ports.gyp | 2 + gyp/tests.gyp | 1 + src/ports/SkDiscardableMemory_ashmem.cpp | 114 +++++++++++++++++++++++ src/utils/android/ashmem.h | 6 ++ tests/CachedDecodingPixelRefTest.cpp | 6 +- tests/DiscardableMemoryTest.cpp | 34 +++++++ tools/LazyDecodeBitmap.cpp | 5 +- 7 files changed, 163 insertions(+), 5 deletions(-) create mode 100644 src/ports/SkDiscardableMemory_ashmem.cpp create mode 100644 tests/DiscardableMemoryTest.cpp diff --git a/gyp/ports.gyp b/gyp/ports.gyp index 068d2e1263..26003dea4f 100644 --- a/gyp/ports.gyp +++ b/gyp/ports.gyp @@ -153,10 +153,12 @@ [ 'skia_os == "android"', { 'sources!': [ '../src/ports/SkDebug_stdio.cpp', + '../src/ports/SkDiscardableMemory_none.cpp', '../src/ports/SkPurgeableMemoryBlock_none.cpp', ], 'sources': [ '../src/ports/SkDebug_android.cpp', + '../src/ports/SkDiscardableMemory_ashmem.cpp', '../src/ports/SkFontConfigInterface_android.cpp', '../src/ports/SkFontConfigParser_android.cpp', '../src/ports/SkFontHost_fontconfig.cpp', diff --git a/gyp/tests.gyp b/gyp/tests.gyp index ed1d07788d..4c8053e6a6 100644 --- a/gyp/tests.gyp +++ b/gyp/tests.gyp @@ -56,6 +56,7 @@ '../tests/DequeTest.cpp', '../tests/DeviceLooperTest.cpp', '../tests/DiscardableMemoryPool.cpp', + '../tests/DiscardableMemoryTest.cpp', '../tests/DocumentTest.cpp', '../tests/DrawBitmapRectTest.cpp', '../tests/DrawPathTest.cpp', diff --git a/src/ports/SkDiscardableMemory_ashmem.cpp b/src/ports/SkDiscardableMemory_ashmem.cpp new file mode 100644 index 0000000000..6f8684e31c --- /dev/null +++ b/src/ports/SkDiscardableMemory_ashmem.cpp @@ -0,0 +1,114 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include +#include +#include "SkDiscardableMemory.h" +#include "SkTypes.h" +#include "android/ashmem.h" + +//////////////////////////////////////////////////////////////////////////////// +namespace { +/** + * DiscardableMemory implementation that uses the Android kernel's + * ashmem (Android shared memory). + */ +class SkAshmemDiscardableMemory : public SkDiscardableMemory { +public: + SkAshmemDiscardableMemory(int fd, void* address, size_t size); + virtual ~SkAshmemDiscardableMemory(); + virtual bool lock() SK_OVERRIDE; + virtual void* data() SK_OVERRIDE; + virtual void unlock() SK_OVERRIDE; +private: + bool fLocked; + int fFd; + void* fMemory; + const size_t fSize; +}; + +SkAshmemDiscardableMemory::SkAshmemDiscardableMemory(int fd, + void* address, + size_t size) + : fLocked(true) // Ashmem pages are pinned by default. + , fFd(fd) + , fMemory(address) + , fSize(size) { + SkASSERT(fFd >= 0); + SkASSERT(fMemory != NULL); + SkASSERT(fSize > 0); +} + +SkAshmemDiscardableMemory::~SkAshmemDiscardableMemory() { + SkASSERT(!fLocked); + if (NULL != fMemory) { + munmap(fMemory, fSize); + } + if (fFd != -1) { + close(fFd); + } +} + +bool SkAshmemDiscardableMemory::lock() { + SkASSERT(!fLocked); + if (-1 == fFd) { + fLocked = false; + return false; + } + SkASSERT(fMemory != NULL); + if (fLocked || (ASHMEM_NOT_PURGED == ashmem_pin_region(fFd, 0, 0))) { + fLocked = true; + return true; + } else { + munmap(fMemory, fSize); + fMemory = NULL; + + close(fFd); + fFd = -1; + fLocked = false; + return false; + } +} + +void* SkAshmemDiscardableMemory::data() { + SkASSERT(fLocked); + return fLocked ? fMemory : NULL; +} + +void SkAshmemDiscardableMemory::unlock() { + SkASSERT(fLocked); + if (fLocked && (fFd != -1)) { + ashmem_unpin_region(fFd, 0, 0); + } + fLocked = false; +} +} // namespace +//////////////////////////////////////////////////////////////////////////////// + +SkDiscardableMemory* SkDiscardableMemory::Create(size_t bytes) { + // ashmem likes lengths on page boundaries. + const size_t mask = getpagesize() - 1; + size_t size = (bytes + mask) & ~mask; + + static const char name[] = "Skia_Ashmem_Discardable_Memory"; + int fd = ashmem_create_region(name, size); + if (fd < 0) { + return NULL; + } + if (0 != ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE)) { + close(fd); + return NULL; + } + void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + if ((MAP_FAILED == addr) || (NULL == addr)) { + close(fd); + return NULL; + } + + return SkNEW_ARGS(SkAshmemDiscardableMemory, (fd, addr, size)); +} + diff --git a/src/utils/android/ashmem.h b/src/utils/android/ashmem.h index 278192b439..94ffe1a33b 100644 --- a/src/utils/android/ashmem.h +++ b/src/utils/android/ashmem.h @@ -16,6 +16,12 @@ extern "C" { int ashmem_create_region(const char *name, size_t size); int ashmem_set_prot_region(int fd, int prot); + +/** + * @return ASHMEM_NOT_PURGED if the memory was not purged. + * ASHMEM_WAS_PURGED if the memory was purged. + * -1 on error. + */ int ashmem_pin_region(int fd, size_t offset, size_t len); int ashmem_unpin_region(int fd, size_t offset, size_t len); int ashmem_get_size_region(int fd); diff --git a/tests/CachedDecodingPixelRefTest.cpp b/tests/CachedDecodingPixelRefTest.cpp index 6abcf3d301..1e4ab3f35d 100644 --- a/tests/CachedDecodingPixelRefTest.cpp +++ b/tests/CachedDecodingPixelRefTest.cpp @@ -300,12 +300,12 @@ DEF_TEST(DiscardableAndCachingPixelRef, reporter) { REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed()); SkDiscardableMemoryPool* globalPool = SkGetGlobalDiscardableMemoryPool(); + // Only acts differently from NULL on a platform that has a + // default discardable memory implementation that differs from the + // global DM pool. CheckPixelRef(TestImageGenerator::kFailGetPixels_TestType, reporter, kSkDiscardable_PixelRefType, globalPool); CheckPixelRef(TestImageGenerator::kSucceedGetPixels_TestType, reporter, kSkDiscardable_PixelRefType, globalPool); - - // TODO(halcanary): When ashmem-backed SkDiscardableMemory lands, - // test that here (on platforms where it is availible). } //////////////////////////////////////////////////////////////////////////////// diff --git a/tests/DiscardableMemoryTest.cpp b/tests/DiscardableMemoryTest.cpp new file mode 100644 index 0000000000..1fbc28478f --- /dev/null +++ b/tests/DiscardableMemoryTest.cpp @@ -0,0 +1,34 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkDiscardableMemory.h" + +#include "Test.h" +#include "TestClassDef.h" + +DEF_TEST(DiscardableMemory, reporter) { + const char testString[] = "HELLO, WORLD!"; + const size_t len = sizeof(testString); + SkAutoTDelete dm(SkDiscardableMemory::Create(len)); + REPORTER_ASSERT(reporter, dm.get() != NULL); + if (NULL == dm.get()) { + return; + } + void* ptr = dm->data(); + REPORTER_ASSERT(reporter, ptr != NULL); + memcpy(ptr, testString, sizeof(testString)); + dm->unlock(); + bool success = dm->lock(); + REPORTER_ASSERT(reporter, success); + if (!success) { + return; + } + ptr = dm->data(); + REPORTER_ASSERT(reporter, 0 == memcmp(ptr, testString, len)); + dm->unlock(); +} + diff --git a/tools/LazyDecodeBitmap.cpp b/tools/LazyDecodeBitmap.cpp index 9e850e5753..f5ff1477d3 100644 --- a/tools/LazyDecodeBitmap.cpp +++ b/tools/LazyDecodeBitmap.cpp @@ -17,7 +17,6 @@ __SK_FORCE_IMAGE_DECODER_LINKING; -// TODO(halcanary) Use this flag when ashmem-backed discardable memory lands. DEFINE_bool(useVolatileCache, false, "Use a volatile cache for deferred image decoding pixels. " "Only meaningful if --deferImageDecoding is set to true and the platform has an " "implementation."); @@ -39,9 +38,11 @@ bool sk_tools::LazyDecodeBitmap(const void* src, return false; } SkDiscardableMemory::Factory* pool = NULL; - if (info.fWidth * info.fHeight > 32 * 1024) { + if ((!FLAGS_useVolatileCache) || (info.fWidth * info.fHeight < 32 * 1024)) { // how to do switching with SkDiscardableMemory. pool = SkGetGlobalDiscardableMemoryPool(); + // Only meaningful if platform has a default discardable + // memory implementation that differs from the global DM pool. } return SkDiscardablePixelRef::Install(gen.detach(), dst, pool); }