• 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 
~SkImageRef_ashmem()46 SkImageRef_ashmem::~SkImageRef_ashmem() {
47     SkSafeUnref(fCT);
48     this->closeFD();
49 }
50 
closeFD()51 void SkImageRef_ashmem::closeFD() {
52     if (-1 != fRec.fFD) {
53 #ifdef DUMP_ASHMEM_LIFECYCLE
54         SkDebugf("=== ashmem close %d\n", fRec.fFD);
55 #endif
56         SkASSERT(fRec.fAddr);
57         SkASSERT(fRec.fSize);
58         munmap(fRec.fAddr, fRec.fSize);
59         close(fRec.fFD);
60         fRec.fFD = -1;
61     }
62 }
63 
64 ///////////////////////////////////////////////////////////////////////////////
65 
66 class AshmemAllocator : public SkBitmap::Allocator {
67 public:
AshmemAllocator(SkAshmemRec * rec,const char name[])68     AshmemAllocator(SkAshmemRec* rec, const char name[])
69         : fRec(rec), fName(name) {}
70 
allocPixelRef(SkBitmap * bm,SkColorTable * ct)71     virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) {
72         const size_t size = roundToPageSize(bm->getSize());
73         int fd = fRec->fFD;
74         void* addr = fRec->fAddr;
75 
76         SkASSERT(!fRec->fPinned);
77 
78         if (-1 == fd) {
79             SkASSERT(NULL == addr);
80             SkASSERT(0 == fRec->fSize);
81 
82             fd = ashmem_create_region(fName, size);
83 #ifdef DUMP_ASHMEM_LIFECYCLE
84             SkDebugf("=== ashmem_create_region %s size=%d fd=%d\n", fName, size, fd);
85 #endif
86             if (-1 == fd) {
87                 SkDebugf("------- imageref_ashmem create failed <%s> %d\n",
88                          fName, size);
89                 return false;
90             }
91 
92             int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
93             if (err) {
94                 SkDebugf("------ ashmem_set_prot_region(%d) failed %d\n",
95                          fd, err);
96                 close(fd);
97                 return false;
98             }
99 
100             addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
101             if (-1 == (long)addr) {
102                 SkDebugf("---------- mmap failed for imageref_ashmem size=%d\n",
103                          size);
104                 close(fd);
105                 return false;
106             }
107 
108             fRec->fFD = fd;
109             fRec->fAddr = addr;
110             fRec->fSize = size;
111         } else {
112             SkASSERT(addr);
113             SkASSERT(size == fRec->fSize);
114             (void)ashmem_pin_region(fd, 0, 0);
115         }
116 
117         bm->setPixels(addr, ct);
118         fRec->fPinned = true;
119         return true;
120     }
121 
122 private:
123     // we just point to our caller's memory, these are not copies
124     SkAshmemRec* fRec;
125     const char*  fName;
126 };
127 
onDecode(SkImageDecoder * codec,SkStream * stream,SkBitmap * bitmap,SkBitmap::Config config,SkImageDecoder::Mode mode)128 bool SkImageRef_ashmem::onDecode(SkImageDecoder* codec, SkStream* stream,
129                                  SkBitmap* bitmap, SkBitmap::Config config,
130                                  SkImageDecoder::Mode mode) {
131 
132     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
133         return this->INHERITED::onDecode(codec, stream, bitmap, config, mode);
134     }
135 
136     AshmemAllocator alloc(&fRec, this->getURI());
137 
138     codec->setAllocator(&alloc);
139     bool success = this->INHERITED::onDecode(codec, stream, bitmap, config,
140                                              mode);
141     // remove the allocator, since its on the stack
142     codec->setAllocator(NULL);
143 
144     if (success) {
145         // remember the colortable (if any)
146         SkRefCnt_SafeAssign(fCT, bitmap->getColorTable());
147         return true;
148     } else {
149         if (fRec.fPinned) {
150             ashmem_unpin_region(fRec.fFD, 0, 0);
151             fRec.fPinned = false;
152         }
153         this->closeFD();
154         return false;
155     }
156 }
157 
onLockPixels(SkColorTable ** ct)158 void* SkImageRef_ashmem::onLockPixels(SkColorTable** ct) {
159     SkASSERT(fBitmap.getPixels() == NULL);
160     SkASSERT(fBitmap.getColorTable() == NULL);
161 
162     // fast case: check if we can just pin and get the cached data
163     if (-1 != fRec.fFD) {
164         SkASSERT(fRec.fAddr);
165         SkASSERT(!fRec.fPinned);
166         int pin = ashmem_pin_region(fRec.fFD, 0, 0);
167 
168         if (ASHMEM_NOT_PURGED == pin) { // yea, fast case!
169             fBitmap.setPixels(fRec.fAddr, fCT);
170             fRec.fPinned = true;
171         } else if (ASHMEM_WAS_PURGED == pin) {
172             ashmem_unpin_region(fRec.fFD, 0, 0);
173             // let go of our colortable if we lost the pixels. Well get it back
174             // again when we re-decode
175             if (fCT) {
176                 fCT->unref();
177                 fCT = NULL;
178             }
179 #if defined(DUMP_ASHMEM_LIFECYCLE) || defined(TRACE_ASH_PURGE)
180             SkDebugf("===== ashmem purged %d\n", fBitmap.getSize());
181 #endif
182         } else {
183             SkDebugf("===== ashmem pin_region(%d) returned %d\n", fRec.fFD, pin);
184             // return null result for failure
185             if (ct) {
186                 *ct = NULL;
187             }
188             return NULL;
189         }
190     } else {
191         // no FD, will create an ashmem region in allocator
192     }
193 
194     return this->INHERITED::onLockPixels(ct);
195 }
196 
onUnlockPixels()197 void SkImageRef_ashmem::onUnlockPixels() {
198     this->INHERITED::onUnlockPixels();
199 
200     if (-1 != fRec.fFD) {
201         SkASSERT(fRec.fAddr);
202         SkASSERT(fRec.fPinned);
203 
204         ashmem_unpin_region(fRec.fFD, 0, 0);
205         fRec.fPinned = false;
206     }
207 
208     // we clear this with or without an error, since we've either closed or
209     // unpinned the region
210     fBitmap.setPixels(NULL, NULL);
211 }
212 
flatten(SkFlattenableWriteBuffer & buffer) const213 void SkImageRef_ashmem::flatten(SkFlattenableWriteBuffer& buffer) const {
214     this->INHERITED::flatten(buffer);
215     const char* uri = getURI();
216     if (uri) {
217         size_t len = strlen(uri);
218         buffer.write32(len);
219         buffer.writePad(uri, len);
220     } else {
221         buffer.write32(0);
222     }
223 }
224 
SkImageRef_ashmem(SkFlattenableReadBuffer & buffer)225 SkImageRef_ashmem::SkImageRef_ashmem(SkFlattenableReadBuffer& buffer)
226         : INHERITED(buffer) {
227     fRec.fFD = -1;
228     fRec.fAddr = NULL;
229     fRec.fSize = 0;
230     fRec.fPinned = false;
231     fCT = NULL;
232     size_t length = buffer.readU32();
233     if (length) {
234         char* buf = (char*) malloc(length);
235         buffer.read(buf, length);
236         setURI(buf, length);
237     }
238 }
239 
Create(SkFlattenableReadBuffer & buffer)240 SkPixelRef* SkImageRef_ashmem::Create(SkFlattenableReadBuffer& buffer) {
241     return SkNEW_ARGS(SkImageRef_ashmem, (buffer));
242 }
243 
244 SK_DEFINE_PIXEL_REF_REGISTRAR(SkImageRef_ashmem)
245