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