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