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