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
This commit is contained in:
halcanary@google.com 2013-12-10 18:33:07 +00:00
parent c9a8a7e23d
commit bc55eec80e
7 changed files with 163 additions and 5 deletions

View File

@ -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',

View File

@ -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',

View File

@ -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 <unistd.h>
#include <sys/mman.h>
#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));
}

View File

@ -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);

View File

@ -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).
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -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<SkDiscardableMemory> 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();
}

View File

@ -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);
}