• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
roundToPageSize(size_t size)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 
SkImageRef_ashmem(SkStream * stream,SkBitmap::Config config,int sampleSize)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 
~SkImageRef_ashmem()48 SkImageRef_ashmem::~SkImageRef_ashmem() {
49     SkSafeUnref(fCT);
50     this->closeFD();
51 }
52 
closeFD()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:
AshmemAllocator(SkAshmemRec * rec,const char name[])70     AshmemAllocator(SkAshmemRec* rec, const char name[])
71         : fRec(rec), fName(name) {}
72 
allocPixelRef(SkBitmap * bm,SkColorTable * ct)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 
onDecode(SkImageDecoder * codec,SkStream * stream,SkBitmap * bitmap,SkBitmap::Config config,SkImageDecoder::Mode mode)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 
onLockPixels(SkColorTable ** ct)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 
onUnlockPixels()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 
flatten(SkFlattenableWriteBuffer & buffer) const215 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 
SkImageRef_ashmem(SkFlattenableReadBuffer & buffer)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 
Create(SkFlattenableReadBuffer & buffer)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