OSDN Git Service

am fd395ce7: am d0077829: am fb397cf8: (-s ours) Merge "Frameworks/base: Support...
[android-x86/frameworks-base.git] / libs / hwui / PathCache.cpp
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #define LOG_TAG "OpenGLRenderer"
18 #define ATRACE_TAG ATRACE_TAG_VIEW
19
20 #include <SkBitmap.h>
21 #include <SkCanvas.h>
22 #include <SkColor.h>
23 #include <SkPaint.h>
24 #include <SkPath.h>
25 #include <SkRect.h>
26
27 #include <utils/JenkinsHash.h>
28 #include <utils/Trace.h>
29
30 #include "Caches.h"
31 #include "PathCache.h"
32
33 #include "thread/Signal.h"
34 #include "thread/TaskProcessor.h"
35
36 namespace android {
37 namespace uirenderer {
38
39 ///////////////////////////////////////////////////////////////////////////////
40 // Cache entries
41 ///////////////////////////////////////////////////////////////////////////////
42
43 PathDescription::PathDescription()
44         : type(kShapeNone)
45         , join(SkPaint::kDefault_Join)
46         , cap(SkPaint::kDefault_Cap)
47         , style(SkPaint::kFill_Style)
48         , miter(4.0f)
49         , strokeWidth(1.0f)
50         , pathEffect(nullptr) {
51     memset(&shape, 0, sizeof(Shape));
52 }
53
54 PathDescription::PathDescription(ShapeType type, const SkPaint* paint)
55         : type(type)
56         , join(paint->getStrokeJoin())
57         , cap(paint->getStrokeCap())
58         , style(paint->getStyle())
59         , miter(paint->getStrokeMiter())
60         , strokeWidth(paint->getStrokeWidth())
61         , pathEffect(paint->getPathEffect()) {
62     memset(&shape, 0, sizeof(Shape));
63 }
64
65 hash_t PathDescription::hash() const {
66     uint32_t hash = JenkinsHashMix(0, type);
67     hash = JenkinsHashMix(hash, join);
68     hash = JenkinsHashMix(hash, cap);
69     hash = JenkinsHashMix(hash, style);
70     hash = JenkinsHashMix(hash, android::hash_type(miter));
71     hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
72     hash = JenkinsHashMix(hash, android::hash_type(pathEffect));
73     hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape));
74     return JenkinsHashWhiten(hash);
75 }
76
77 ///////////////////////////////////////////////////////////////////////////////
78 // Utilities
79 ///////////////////////////////////////////////////////////////////////////////
80
81 bool PathCache::canDrawAsConvexPath(SkPath* path, const SkPaint* paint) {
82     // NOTE: This should only be used after PathTessellator handles joins properly
83     return paint->getPathEffect() == nullptr && path->getConvexity() == SkPath::kConvex_Convexity;
84 }
85
86 void PathCache::computePathBounds(const SkPath* path, const SkPaint* paint,
87         float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
88     const SkRect& bounds = path->getBounds();
89     PathCache::computeBounds(bounds, paint, left, top, offset, width, height);
90 }
91
92 void PathCache::computeBounds(const SkRect& bounds, const SkPaint* paint,
93         float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
94     const float pathWidth = fmax(bounds.width(), 1.0f);
95     const float pathHeight = fmax(bounds.height(), 1.0f);
96
97     left = bounds.fLeft;
98     top = bounds.fTop;
99
100     offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
101
102     width = uint32_t(pathWidth + offset * 2.0 + 0.5);
103     height = uint32_t(pathHeight + offset * 2.0 + 0.5);
104 }
105
106 static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
107     bitmap.allocPixels(SkImageInfo::MakeA8(width, height));
108     bitmap.eraseColor(0);
109 }
110
111 static void initPaint(SkPaint& paint) {
112     // Make sure the paint is opaque, color, alpha, filter, etc.
113     // will be applied later when compositing the alpha8 texture
114     paint.setColor(SK_ColorBLACK);
115     paint.setAlpha(255);
116     paint.setColorFilter(nullptr);
117     paint.setMaskFilter(nullptr);
118     paint.setShader(nullptr);
119     SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
120     SkSafeUnref(paint.setXfermode(mode));
121 }
122
123 static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap,
124         float left, float top, float offset, uint32_t width, uint32_t height) {
125     initBitmap(bitmap, width, height);
126
127     SkPaint pathPaint(*paint);
128     initPaint(pathPaint);
129
130     SkCanvas canvas(bitmap);
131     canvas.translate(-left + offset, -top + offset);
132     canvas.drawPath(*path, pathPaint);
133 }
134
135 ///////////////////////////////////////////////////////////////////////////////
136 // Cache constructor/destructor
137 ///////////////////////////////////////////////////////////////////////////////
138
139 PathCache::PathCache():
140         mCache(LruCache<PathDescription, PathTexture*>::kUnlimitedCapacity),
141         mSize(0), mMaxSize(MB(DEFAULT_PATH_CACHE_SIZE)) {
142     char property[PROPERTY_VALUE_MAX];
143     if (property_get(PROPERTY_PATH_CACHE_SIZE, property, nullptr) > 0) {
144         INIT_LOGD("  Setting %s cache size to %sMB", name, property);
145         setMaxSize(MB(atof(property)));
146     } else {
147         INIT_LOGD("  Using default %s cache size of %.2fMB", name, DEFAULT_PATH_CACHE_SIZE);
148     }
149
150     mCache.setOnEntryRemovedListener(this);
151
152     GLint maxTextureSize;
153     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
154     mMaxTextureSize = maxTextureSize;
155
156     mDebugEnabled = readDebugLevel() & kDebugCaches;
157 }
158
159 PathCache::~PathCache() {
160     mCache.clear();
161 }
162
163 ///////////////////////////////////////////////////////////////////////////////
164 // Size management
165 ///////////////////////////////////////////////////////////////////////////////
166
167 uint32_t PathCache::getSize() {
168     return mSize;
169 }
170
171 uint32_t PathCache::getMaxSize() {
172     return mMaxSize;
173 }
174
175 void PathCache::setMaxSize(uint32_t maxSize) {
176     mMaxSize = maxSize;
177     while (mSize > mMaxSize) {
178         mCache.removeOldest();
179     }
180 }
181
182 ///////////////////////////////////////////////////////////////////////////////
183 // Callbacks
184 ///////////////////////////////////////////////////////////////////////////////
185
186 void PathCache::operator()(PathDescription& entry, PathTexture*& texture) {
187     removeTexture(texture);
188 }
189
190 ///////////////////////////////////////////////////////////////////////////////
191 // Caching
192 ///////////////////////////////////////////////////////////////////////////////
193
194 void PathCache::removeTexture(PathTexture* texture) {
195     if (texture) {
196         const uint32_t size = texture->width * texture->height;
197
198         // If there is a pending task we must wait for it to return
199         // before attempting our cleanup
200         const sp<Task<SkBitmap*> >& task = texture->task();
201         if (task != nullptr) {
202             task->getResult();
203             texture->clearTask();
204         } else {
205             // If there is a pending task, the path was not added
206             // to the cache and the size wasn't increased
207             if (size > mSize) {
208                 ALOGE("Removing path texture of size %d will leave "
209                         "the cache in an inconsistent state", size);
210             }
211             mSize -= size;
212         }
213
214         PATH_LOGD("PathCache::delete name, size, mSize = %d, %d, %d",
215                 texture->id, size, mSize);
216         if (mDebugEnabled) {
217             ALOGD("Shape deleted, size = %d", size);
218         }
219
220         if (texture->id) {
221             Caches::getInstance().textureState().deleteTexture(texture->id);
222         }
223         delete texture;
224     }
225 }
226
227 void PathCache::purgeCache(uint32_t width, uint32_t height) {
228     const uint32_t size = width * height;
229     // Don't even try to cache a bitmap that's bigger than the cache
230     if (size < mMaxSize) {
231         while (mSize + size > mMaxSize) {
232             mCache.removeOldest();
233         }
234     }
235 }
236
237 void PathCache::trim() {
238     while (mSize > mMaxSize) {
239         mCache.removeOldest();
240     }
241 }
242
243 PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath *path,
244         const SkPaint* paint) {
245     ATRACE_NAME("Generate Path Texture");
246
247     float left, top, offset;
248     uint32_t width, height;
249     computePathBounds(path, paint, left, top, offset, width, height);
250
251     if (!checkTextureSize(width, height)) return nullptr;
252
253     purgeCache(width, height);
254
255     SkBitmap bitmap;
256     drawPath(path, paint, bitmap, left, top, offset, width, height);
257
258     PathTexture* texture = new PathTexture(Caches::getInstance(),
259             left, top, offset, width, height,
260             path->getGenerationID());
261     generateTexture(entry, &bitmap, texture);
262
263     return texture;
264 }
265
266 void PathCache::generateTexture(const PathDescription& entry, SkBitmap* bitmap,
267         PathTexture* texture, bool addToCache) {
268     generateTexture(*bitmap, texture);
269
270     uint32_t size = texture->width * texture->height;
271     if (size < mMaxSize) {
272         mSize += size;
273         PATH_LOGD("PathCache::get/create: name, size, mSize = %d, %d, %d",
274                 texture->id, size, mSize);
275         if (mDebugEnabled) {
276             ALOGD("Shape created, size = %d", size);
277         }
278         if (addToCache) {
279             mCache.put(entry, texture);
280         }
281     } else {
282         // It's okay to add a texture that's bigger than the cache since
283         // we'll trim the cache later when addToCache is set to false
284         if (!addToCache) {
285             mSize += size;
286         }
287         texture->cleanup = true;
288     }
289 }
290
291 void PathCache::clear() {
292     mCache.clear();
293 }
294
295 void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) {
296     SkAutoLockPixels alp(bitmap);
297     if (!bitmap.readyToDraw()) {
298         ALOGE("Cannot generate texture from bitmap");
299         return;
300     }
301
302     glGenTextures(1, &texture->id);
303
304     Caches::getInstance().textureState().bindTexture(texture->id);
305     // Textures are Alpha8
306     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
307
308     texture->blend = true;
309     glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
310             GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels());
311
312     texture->setFilter(GL_LINEAR);
313     texture->setWrap(GL_CLAMP_TO_EDGE);
314 }
315
316 ///////////////////////////////////////////////////////////////////////////////
317 // Path precaching
318 ///////////////////////////////////////////////////////////////////////////////
319
320 PathCache::PathProcessor::PathProcessor(Caches& caches):
321         TaskProcessor<SkBitmap*>(&caches.tasks), mMaxTextureSize(caches.maxTextureSize) {
322 }
323
324 void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) {
325     PathTask* t = static_cast<PathTask*>(task.get());
326     ATRACE_NAME("pathPrecache");
327
328     float left, top, offset;
329     uint32_t width, height;
330     PathCache::computePathBounds(&t->path, &t->paint, left, top, offset, width, height);
331
332     PathTexture* texture = t->texture;
333     texture->left = left;
334     texture->top = top;
335     texture->offset = offset;
336     texture->width = width;
337     texture->height = height;
338
339     if (width <= mMaxTextureSize && height <= mMaxTextureSize) {
340         SkBitmap* bitmap = new SkBitmap();
341         drawPath(&t->path, &t->paint, *bitmap, left, top, offset, width, height);
342         t->setResult(bitmap);
343     } else {
344         texture->width = 0;
345         texture->height = 0;
346         t->setResult(nullptr);
347     }
348 }
349
350 ///////////////////////////////////////////////////////////////////////////////
351 // Paths
352 ///////////////////////////////////////////////////////////////////////////////
353
354 void PathCache::removeDeferred(const SkPath* path) {
355     Mutex::Autolock l(mLock);
356     mGarbage.push(path->getGenerationID());
357 }
358
359 void PathCache::clearGarbage() {
360     Vector<PathDescription> pathsToRemove;
361
362     { // scope for the mutex
363         Mutex::Autolock l(mLock);
364         size_t count = mGarbage.size();
365         for (size_t i = 0; i < count; i++) {
366             const uint32_t generationID = mGarbage.itemAt(i);
367
368             LruCache<PathDescription, PathTexture*>::Iterator iter(mCache);
369             while (iter.next()) {
370                 const PathDescription& key = iter.key();
371                 if (key.type == kShapePath && key.shape.path.mGenerationID == generationID) {
372                     pathsToRemove.push(key);
373                 }
374             }
375         }
376         mGarbage.clear();
377     }
378
379     for (size_t i = 0; i < pathsToRemove.size(); i++) {
380         mCache.remove(pathsToRemove.itemAt(i));
381     }
382 }
383
384 PathTexture* PathCache::get(const SkPath* path, const SkPaint* paint) {
385     PathDescription entry(kShapePath, paint);
386     entry.shape.path.mGenerationID = path->getGenerationID();
387
388     PathTexture* texture = mCache.get(entry);
389
390     if (!texture) {
391         texture = addTexture(entry, path, paint);
392     } else {
393         // A bitmap is attached to the texture, this means we need to
394         // upload it as a GL texture
395         const sp<Task<SkBitmap*> >& task = texture->task();
396         if (task != nullptr) {
397             // But we must first wait for the worker thread to be done
398             // producing the bitmap, so let's wait
399             SkBitmap* bitmap = task->getResult();
400             if (bitmap) {
401                 generateTexture(entry, bitmap, texture, false);
402                 texture->clearTask();
403             } else {
404                 ALOGW("Path too large to be rendered into a texture");
405                 texture->clearTask();
406                 texture = nullptr;
407                 mCache.remove(entry);
408             }
409         }
410     }
411
412     return texture;
413 }
414
415 void PathCache::precache(const SkPath* path, const SkPaint* paint) {
416     if (!Caches::getInstance().tasks.canRunTasks()) {
417         return;
418     }
419
420     PathDescription entry(kShapePath, paint);
421     entry.shape.path.mGenerationID = path->getGenerationID();
422
423     PathTexture* texture = mCache.get(entry);
424
425     bool generate = false;
426     if (!texture) {
427         generate = true;
428     }
429
430     if (generate) {
431         // It is important to specify the generation ID so we do not
432         // attempt to precache the same path several times
433         texture = new PathTexture(Caches::getInstance(), path->getGenerationID());
434         sp<PathTask> task = new PathTask(path, paint, texture);
435         texture->setTask(task);
436
437         // During the precaching phase we insert path texture objects into
438         // the cache that do not point to any GL texture. They are instead
439         // treated as a task for the precaching worker thread. This is why
440         // we do not check the cache limit when inserting these objects.
441         // The conversion into GL texture will happen in get(), when a client
442         // asks for a path texture. This is also when the cache limit will
443         // be enforced.
444         mCache.put(entry, texture);
445
446         if (mProcessor == nullptr) {
447             mProcessor = new PathProcessor(Caches::getInstance());
448         }
449         if (!mProcessor->add(task)) {
450             mProcessor->process(task);
451         }
452     }
453 }
454
455 ///////////////////////////////////////////////////////////////////////////////
456 // Rounded rects
457 ///////////////////////////////////////////////////////////////////////////////
458
459 PathTexture* PathCache::getRoundRect(float width, float height,
460         float rx, float ry, const SkPaint* paint) {
461     PathDescription entry(kShapeRoundRect, paint);
462     entry.shape.roundRect.mWidth = width;
463     entry.shape.roundRect.mHeight = height;
464     entry.shape.roundRect.mRx = rx;
465     entry.shape.roundRect.mRy = ry;
466
467     PathTexture* texture = get(entry);
468
469     if (!texture) {
470         SkPath path;
471         SkRect r;
472         r.set(0.0f, 0.0f, width, height);
473         path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
474
475         texture = addTexture(entry, &path, paint);
476     }
477
478     return texture;
479 }
480
481 ///////////////////////////////////////////////////////////////////////////////
482 // Circles
483 ///////////////////////////////////////////////////////////////////////////////
484
485 PathTexture* PathCache::getCircle(float radius, const SkPaint* paint) {
486     PathDescription entry(kShapeCircle, paint);
487     entry.shape.circle.mRadius = radius;
488
489     PathTexture* texture = get(entry);
490
491     if (!texture) {
492         SkPath path;
493         path.addCircle(radius, radius, radius, SkPath::kCW_Direction);
494
495         texture = addTexture(entry, &path, paint);
496     }
497
498     return texture;
499 }
500
501 ///////////////////////////////////////////////////////////////////////////////
502 // Ovals
503 ///////////////////////////////////////////////////////////////////////////////
504
505 PathTexture* PathCache::getOval(float width, float height, const SkPaint* paint) {
506     PathDescription entry(kShapeOval, paint);
507     entry.shape.oval.mWidth = width;
508     entry.shape.oval.mHeight = height;
509
510     PathTexture* texture = get(entry);
511
512     if (!texture) {
513         SkPath path;
514         SkRect r;
515         r.set(0.0f, 0.0f, width, height);
516         path.addOval(r, SkPath::kCW_Direction);
517
518         texture = addTexture(entry, &path, paint);
519     }
520
521     return texture;
522 }
523
524 ///////////////////////////////////////////////////////////////////////////////
525 // Rects
526 ///////////////////////////////////////////////////////////////////////////////
527
528 PathTexture* PathCache::getRect(float width, float height, const SkPaint* paint) {
529     PathDescription entry(kShapeRect, paint);
530     entry.shape.rect.mWidth = width;
531     entry.shape.rect.mHeight = height;
532
533     PathTexture* texture = get(entry);
534
535     if (!texture) {
536         SkPath path;
537         SkRect r;
538         r.set(0.0f, 0.0f, width, height);
539         path.addRect(r, SkPath::kCW_Direction);
540
541         texture = addTexture(entry, &path, paint);
542     }
543
544     return texture;
545 }
546
547 ///////////////////////////////////////////////////////////////////////////////
548 // Arcs
549 ///////////////////////////////////////////////////////////////////////////////
550
551 PathTexture* PathCache::getArc(float width, float height,
552         float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) {
553     PathDescription entry(kShapeArc, paint);
554     entry.shape.arc.mWidth = width;
555     entry.shape.arc.mHeight = height;
556     entry.shape.arc.mStartAngle = startAngle;
557     entry.shape.arc.mSweepAngle = sweepAngle;
558     entry.shape.arc.mUseCenter = useCenter;
559
560     PathTexture* texture = get(entry);
561
562     if (!texture) {
563         SkPath path;
564         SkRect r;
565         r.set(0.0f, 0.0f, width, height);
566         if (useCenter) {
567             path.moveTo(r.centerX(), r.centerY());
568         }
569         path.arcTo(r, startAngle, sweepAngle, !useCenter);
570         if (useCenter) {
571             path.close();
572         }
573
574         texture = addTexture(entry, &path, paint);
575     }
576
577     return texture;
578 }
579
580 }; // namespace uirenderer
581 }; // namespace android