Home | History | Annotate | Download | only in ports
      1 
      2 /*
      3  * Copyright 2011 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 #include "SkImageRef_ashmem.h"
      9 #include "SkImageDecoder.h"
     10 #include "SkFlattenable.h"
     11 #include "SkThread.h"
     12 
     13 #include <sys/mman.h>
     14 #include <unistd.h>
     15 #include <cutils/ashmem.h>
     16 
     17 //#define TRACE_ASH_PURGE     // just trace purges
     18 
     19 #ifdef DUMP_IMAGEREF_LIFECYCLE
     20     #define DUMP_ASHMEM_LIFECYCLE
     21 #else
     22 //    #define DUMP_ASHMEM_LIFECYCLE
     23 #endif
     24 
     25 // ashmem likes lengths on page boundaries
     26 static size_t roundToPageSize(size_t size) {
     27     const size_t mask = getpagesize() - 1;
     28     size_t newsize = (size + mask) & ~mask;
     29 //    SkDebugf("---- oldsize %d newsize %d\n", size, newsize);
     30     return newsize;
     31 }
     32 
     33 SkImageRef_ashmem::SkImageRef_ashmem(SkStream* stream,
     34                                              SkBitmap::Config config,
     35                                              int sampleSize)
     36         : SkImageRef(stream, config, sampleSize) {
     37 
     38     fRec.fFD = -1;
     39     fRec.fAddr = NULL;
     40     fRec.fSize = 0;
     41     fRec.fPinned = false;
     42 
     43     fCT = NULL;
     44 
     45     this->useDefaultMutex();   // we don't need/want the shared imageref mutex
     46 }
     47 
     48 SkImageRef_ashmem::~SkImageRef_ashmem() {
     49     SkSafeUnref(fCT);
     50     this->closeFD();
     51 }
     52 
     53 void SkImageRef_ashmem::closeFD() {
     54     if (-1 != fRec.fFD) {
     55 #ifdef DUMP_ASHMEM_LIFECYCLE
     56         SkDebugf("=== ashmem close %d\n", fRec.fFD);
     57 #endif
     58         SkASSERT(fRec.fAddr);
     59         SkASSERT(fRec.fSize);
     60         munmap(fRec.fAddr, fRec.fSize);
     61         close(fRec.fFD);
     62         fRec.fFD = -1;
     63     }
     64 }
     65 
     66 ///////////////////////////////////////////////////////////////////////////////
     67 
     68 class AshmemAllocator : public SkBitmap::Allocator {
     69 public:
     70     AshmemAllocator(SkAshmemRec* rec, const char name[])
     71         : fRec(rec), fName(name) {}
     72 
     73     virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) {
     74         const size_t size = roundToPageSize(bm->getSize());
     75         int fd = fRec->fFD;
     76         void* addr = fRec->fAddr;
     77 
     78         SkASSERT(!fRec->fPinned);
     79 
     80         if (-1 == fd) {
     81             SkASSERT(NULL == addr);
     82             SkASSERT(0 == fRec->fSize);
     83 
     84             fd = ashmem_create_region(fName, size);
     85 #ifdef DUMP_ASHMEM_LIFECYCLE
     86             SkDebugf("=== ashmem_create_region %s size=%d fd=%d\n", fName, size, fd);
     87 #endif
     88             if (-1 == fd) {
     89                 SkDebugf("------- imageref_ashmem create failed <%s> %d\n",
     90                          fName, size);
     91                 return false;
     92             }
     93 
     94             int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
     95             if (err) {
     96                 SkDebugf("------ ashmem_set_prot_region(%d) failed %d\n",
     97                          fd, err);
     98                 close(fd);
     99                 return false;
    100             }
    101 
    102             addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
    103             if (-1 == (long)addr) {
    104                 SkDebugf("---------- mmap failed for imageref_ashmem size=%d\n",
    105                          size);
    106                 close(fd);
    107                 return false;
    108             }
    109 
    110             fRec->fFD = fd;
    111             fRec->fAddr = addr;
    112             fRec->fSize = size;
    113         } else {
    114             SkASSERT(addr);
    115             SkASSERT(size == fRec->fSize);
    116             (void)ashmem_pin_region(fd, 0, 0);
    117         }
    118 
    119         bm->setPixels(addr, ct);
    120         fRec->fPinned = true;
    121         return true;
    122     }
    123 
    124 private:
    125     // we just point to our caller's memory, these are not copies
    126     SkAshmemRec* fRec;
    127     const char*  fName;
    128 };
    129 
    130 bool SkImageRef_ashmem::onDecode(SkImageDecoder* codec, SkStream* stream,
    131                                  SkBitmap* bitmap, SkBitmap::Config config,
    132                                  SkImageDecoder::Mode mode) {
    133 
    134     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
    135         return this->INHERITED::onDecode(codec, stream, bitmap, config, mode);
    136     }
    137 
    138     AshmemAllocator alloc(&fRec, this->getURI());
    139 
    140     codec->setAllocator(&alloc);
    141     bool success = this->INHERITED::onDecode(codec, stream, bitmap, config,
    142                                              mode);
    143     // remove the allocator, since its on the stack
    144     codec->setAllocator(NULL);
    145 
    146     if (success) {
    147         // remember the colortable (if any)
    148         SkRefCnt_SafeAssign(fCT, bitmap->getColorTable());
    149         return true;
    150     } else {
    151         if (fRec.fPinned) {
    152             ashmem_unpin_region(fRec.fFD, 0, 0);
    153             fRec.fPinned = false;
    154         }
    155         this->closeFD();
    156         return false;
    157     }
    158 }
    159 
    160 void* SkImageRef_ashmem::onLockPixels(SkColorTable** ct) {
    161     SkASSERT(fBitmap.getPixels() == NULL);
    162     SkASSERT(fBitmap.getColorTable() == NULL);
    163 
    164     // fast case: check if we can just pin and get the cached data
    165     if (-1 != fRec.fFD) {
    166         SkASSERT(fRec.fAddr);
    167         SkASSERT(!fRec.fPinned);
    168         int pin = ashmem_pin_region(fRec.fFD, 0, 0);
    169 
    170         if (ASHMEM_NOT_PURGED == pin) { // yea, fast case!
    171             fBitmap.setPixels(fRec.fAddr, fCT);
    172             fRec.fPinned = true;
    173         } else if (ASHMEM_WAS_PURGED == pin) {
    174             ashmem_unpin_region(fRec.fFD, 0, 0);
    175             // let go of our colortable if we lost the pixels. Well get it back
    176             // again when we re-decode
    177             if (fCT) {
    178                 fCT->unref();
    179                 fCT = NULL;
    180             }
    181 #if defined(DUMP_ASHMEM_LIFECYCLE) || defined(TRACE_ASH_PURGE)
    182             SkDebugf("===== ashmem purged %d\n", fBitmap.getSize());
    183 #endif
    184         } else {
    185             SkDebugf("===== ashmem pin_region(%d) returned %d\n", fRec.fFD, pin);
    186             // return null result for failure
    187             if (ct) {
    188                 *ct = NULL;
    189             }
    190             return NULL;
    191         }
    192     } else {
    193         // no FD, will create an ashmem region in allocator
    194     }
    195 
    196     return this->INHERITED::onLockPixels(ct);
    197 }
    198 
    199 void SkImageRef_ashmem::onUnlockPixels() {
    200     this->INHERITED::onUnlockPixels();
    201 
    202     if (-1 != fRec.fFD) {
    203         SkASSERT(fRec.fAddr);
    204         SkASSERT(fRec.fPinned);
    205 
    206         ashmem_unpin_region(fRec.fFD, 0, 0);
    207         fRec.fPinned = false;
    208     }
    209 
    210     // we clear this with or without an error, since we've either closed or
    211     // unpinned the region
    212     fBitmap.setPixels(NULL, NULL);
    213 }
    214 
    215 void SkImageRef_ashmem::flatten(SkFlattenableWriteBuffer& buffer) const {
    216     this->INHERITED::flatten(buffer);
    217     const char* uri = getURI();
    218     if (uri) {
    219         size_t len = strlen(uri);
    220         buffer.write32(len);
    221         buffer.writePad(uri, len);
    222     } else {
    223         buffer.write32(0);
    224     }
    225 }
    226 
    227 SkImageRef_ashmem::SkImageRef_ashmem(SkFlattenableReadBuffer& buffer)
    228         : INHERITED(buffer) {
    229     fRec.fFD = -1;
    230     fRec.fAddr = NULL;
    231     fRec.fSize = 0;
    232     fRec.fPinned = false;
    233     fCT = NULL;
    234     size_t length = buffer.readU32();
    235     if (length) {
    236         char* buf = (char*) malloc(length);
    237         buffer.read(buf, length);
    238         setURI(buf, length);
    239     }
    240     this->useDefaultMutex();   // we don't need/want the shared imageref mutex
    241 }
    242 
    243 SkPixelRef* SkImageRef_ashmem::Create(SkFlattenableReadBuffer& buffer) {
    244     return SkNEW_ARGS(SkImageRef_ashmem, (buffer));
    245 }
    246 
    247 SK_DEFINE_PIXEL_REF_REGISTRAR(SkImageRef_ashmem)
    248