michael@0: /* michael@0: * Copyright 2011 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "SkImageRef_ashmem.h" michael@0: #include "SkImageDecoder.h" michael@0: #include "SkReadBuffer.h" michael@0: #include "SkWriteBuffer.h" michael@0: #include "SkThread.h" michael@0: michael@0: #include "android/ashmem.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: //#define TRACE_ASH_PURGE // just trace purges michael@0: michael@0: #ifdef DUMP_IMAGEREF_LIFECYCLE michael@0: #define DUMP_ASHMEM_LIFECYCLE michael@0: #else michael@0: // #define DUMP_ASHMEM_LIFECYCLE michael@0: #endif michael@0: michael@0: // ashmem likes lengths on page boundaries michael@0: static size_t roundToPageSize(size_t size) { michael@0: const size_t mask = getpagesize() - 1; michael@0: size_t newsize = (size + mask) & ~mask; michael@0: // SkDebugf("---- oldsize %d newsize %d\n", size, newsize); michael@0: return newsize; michael@0: } michael@0: michael@0: SkImageRef_ashmem::SkImageRef_ashmem(const SkImageInfo& info, michael@0: SkStreamRewindable* stream, michael@0: int sampleSize) michael@0: : SkImageRef(info, stream, sampleSize) michael@0: { michael@0: fRec.fFD = -1; michael@0: fRec.fAddr = NULL; michael@0: fRec.fSize = 0; michael@0: fRec.fPinned = false; michael@0: michael@0: fCT = NULL; michael@0: } michael@0: michael@0: SkImageRef_ashmem::~SkImageRef_ashmem() { michael@0: SkSafeUnref(fCT); michael@0: this->closeFD(); michael@0: } michael@0: michael@0: void SkImageRef_ashmem::closeFD() { michael@0: if (-1 != fRec.fFD) { michael@0: #ifdef DUMP_ASHMEM_LIFECYCLE michael@0: SkDebugf("=== ashmem close %d\n", fRec.fFD); michael@0: #endif michael@0: SkASSERT(fRec.fAddr); michael@0: SkASSERT(fRec.fSize); michael@0: munmap(fRec.fAddr, fRec.fSize); michael@0: close(fRec.fFD); michael@0: fRec.fFD = -1; michael@0: } michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: class AshmemAllocator : public SkBitmap::Allocator { michael@0: public: michael@0: AshmemAllocator(SkAshmemRec* rec, const char name[]) michael@0: : fRec(rec), fName(name) {} michael@0: michael@0: virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) { michael@0: const size_t size = roundToPageSize(bm->getSize()); michael@0: int fd = fRec->fFD; michael@0: void* addr = fRec->fAddr; michael@0: michael@0: SkASSERT(!fRec->fPinned); michael@0: michael@0: if (-1 == fd) { michael@0: SkASSERT(NULL == addr); michael@0: SkASSERT(0 == fRec->fSize); michael@0: michael@0: fd = ashmem_create_region(fName, size); michael@0: #ifdef DUMP_ASHMEM_LIFECYCLE michael@0: SkDebugf("=== ashmem_create_region %s size=%d fd=%d\n", fName, size, fd); michael@0: #endif michael@0: if (-1 == fd) { michael@0: SkDebugf("------- imageref_ashmem create failed <%s> %d\n", michael@0: fName, size); michael@0: return false; michael@0: } michael@0: michael@0: int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE); michael@0: if (err) { michael@0: SkDebugf("------ ashmem_set_prot_region(%d) failed %d\n", michael@0: fd, err); michael@0: close(fd); michael@0: return false; michael@0: } michael@0: michael@0: addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); michael@0: if (-1 == (long)addr) { michael@0: SkDebugf("---------- mmap failed for imageref_ashmem size=%d\n", michael@0: size); michael@0: close(fd); michael@0: return false; michael@0: } michael@0: michael@0: fRec->fFD = fd; michael@0: fRec->fAddr = addr; michael@0: fRec->fSize = size; michael@0: } else { michael@0: SkASSERT(addr); michael@0: SkASSERT(size == fRec->fSize); michael@0: (void)ashmem_pin_region(fd, 0, 0); michael@0: } michael@0: michael@0: bm->setPixels(addr, ct); michael@0: fRec->fPinned = true; michael@0: return true; michael@0: } michael@0: michael@0: private: michael@0: // we just point to our caller's memory, these are not copies michael@0: SkAshmemRec* fRec; michael@0: const char* fName; michael@0: }; michael@0: michael@0: bool SkImageRef_ashmem::onDecode(SkImageDecoder* codec, SkStreamRewindable* stream, michael@0: SkBitmap* bitmap, SkBitmap::Config config, michael@0: SkImageDecoder::Mode mode) { michael@0: michael@0: if (SkImageDecoder::kDecodeBounds_Mode == mode) { michael@0: return this->INHERITED::onDecode(codec, stream, bitmap, config, mode); michael@0: } michael@0: michael@0: // Ashmem memory is guaranteed to be initialized to 0. michael@0: codec->setSkipWritingZeroes(true); michael@0: michael@0: AshmemAllocator alloc(&fRec, this->getURI()); michael@0: michael@0: codec->setAllocator(&alloc); michael@0: bool success = this->INHERITED::onDecode(codec, stream, bitmap, config, michael@0: mode); michael@0: // remove the allocator, since its on the stack michael@0: codec->setAllocator(NULL); michael@0: michael@0: if (success) { michael@0: // remember the colortable (if any) michael@0: SkRefCnt_SafeAssign(fCT, bitmap->getColorTable()); michael@0: return true; michael@0: } else { michael@0: if (fRec.fPinned) { michael@0: ashmem_unpin_region(fRec.fFD, 0, 0); michael@0: fRec.fPinned = false; michael@0: } michael@0: this->closeFD(); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: bool SkImageRef_ashmem::onNewLockPixels(LockRec* rec) { michael@0: SkASSERT(fBitmap.getPixels() == NULL); michael@0: SkASSERT(fBitmap.getColorTable() == NULL); michael@0: michael@0: // fast case: check if we can just pin and get the cached data michael@0: if (-1 != fRec.fFD) { michael@0: SkASSERT(fRec.fAddr); michael@0: SkASSERT(!fRec.fPinned); michael@0: int pin = ashmem_pin_region(fRec.fFD, 0, 0); michael@0: michael@0: if (ASHMEM_NOT_PURGED == pin) { // yea, fast case! michael@0: fBitmap.setPixels(fRec.fAddr, fCT); michael@0: fRec.fPinned = true; michael@0: } else if (ASHMEM_WAS_PURGED == pin) { michael@0: ashmem_unpin_region(fRec.fFD, 0, 0); michael@0: // let go of our colortable if we lost the pixels. Well get it back michael@0: // again when we re-decode michael@0: if (fCT) { michael@0: fCT->unref(); michael@0: fCT = NULL; michael@0: } michael@0: #if defined(DUMP_ASHMEM_LIFECYCLE) || defined(TRACE_ASH_PURGE) michael@0: SkDebugf("===== ashmem purged %d\n", fBitmap.getSize()); michael@0: #endif michael@0: } else { michael@0: SkDebugf("===== ashmem pin_region(%d) returned %d\n", fRec.fFD, pin); michael@0: return false; michael@0: } michael@0: } else { michael@0: // no FD, will create an ashmem region in allocator michael@0: } michael@0: michael@0: return this->INHERITED::onNewLockPixels(rec); michael@0: } michael@0: michael@0: void SkImageRef_ashmem::onUnlockPixels() { michael@0: this->INHERITED::onUnlockPixels(); michael@0: michael@0: if (-1 != fRec.fFD) { michael@0: SkASSERT(fRec.fAddr); michael@0: SkASSERT(fRec.fPinned); michael@0: michael@0: ashmem_unpin_region(fRec.fFD, 0, 0); michael@0: fRec.fPinned = false; michael@0: } michael@0: michael@0: // we clear this with or without an error, since we've either closed or michael@0: // unpinned the region michael@0: fBitmap.setPixels(NULL, NULL); michael@0: } michael@0: michael@0: void SkImageRef_ashmem::flatten(SkWriteBuffer& buffer) const { michael@0: this->INHERITED::flatten(buffer); michael@0: buffer.writeString(getURI()); michael@0: } michael@0: michael@0: SkImageRef_ashmem::SkImageRef_ashmem(SkReadBuffer& buffer) michael@0: : INHERITED(buffer) { michael@0: fRec.fFD = -1; michael@0: fRec.fAddr = NULL; michael@0: fRec.fSize = 0; michael@0: fRec.fPinned = false; michael@0: fCT = NULL; michael@0: michael@0: SkString uri; michael@0: buffer.readString(&uri); michael@0: this->setURI(uri); michael@0: }