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 
PathDescription()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 
PathDescription(ShapeType type,const SkPaint * paint)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 
hash() const65 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 
canDrawAsConvexPath(SkPath * path,const SkPaint * paint)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 
computePathBounds(const SkPath * path,const SkPaint * paint,float & left,float & top,float & offset,uint32_t & width,uint32_t & height)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 
computeBounds(const SkRect & bounds,const SkPaint * paint,float & left,float & top,float & offset,uint32_t & width,uint32_t & height)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 = std::max(bounds.width(), 1.0f);
95     const float pathHeight = std::max(bounds.height(), 1.0f);
96 
97     left = bounds.fLeft;
98     top = bounds.fTop;
99 
100     offset = (int) floorf(std::max(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 
initBitmap(SkBitmap & bitmap,uint32_t width,uint32_t height)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 
initPaint(SkPaint & paint)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 
drawPath(const SkPath * path,const SkPaint * paint,SkBitmap & bitmap,float left,float top,float offset,uint32_t width,uint32_t height)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 
PathCache()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         mMaxSize = 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 = Properties::debugLevel & kDebugCaches;
157 }
158 
~PathCache()159 PathCache::~PathCache() {
160     mCache.clear();
161 }
162 
163 ///////////////////////////////////////////////////////////////////////////////
164 // Size management
165 ///////////////////////////////////////////////////////////////////////////////
166 
getSize()167 uint32_t PathCache::getSize() {
168     return mSize;
169 }
170 
getMaxSize()171 uint32_t PathCache::getMaxSize() {
172     return mMaxSize;
173 }
174 
175 ///////////////////////////////////////////////////////////////////////////////
176 // Callbacks
177 ///////////////////////////////////////////////////////////////////////////////
178 
operator ()(PathDescription & entry,PathTexture * & texture)179 void PathCache::operator()(PathDescription& entry, PathTexture*& texture) {
180     removeTexture(texture);
181 }
182 
183 ///////////////////////////////////////////////////////////////////////////////
184 // Caching
185 ///////////////////////////////////////////////////////////////////////////////
186 
removeTexture(PathTexture * texture)187 void PathCache::removeTexture(PathTexture* texture) {
188     if (texture) {
189         const uint32_t size = texture->width * texture->height;
190 
191         // If there is a pending task we must wait for it to return
192         // before attempting our cleanup
193         const sp<Task<SkBitmap*> >& task = texture->task();
194         if (task != nullptr) {
195             task->getResult();
196             texture->clearTask();
197         } else {
198             // If there is a pending task, the path was not added
199             // to the cache and the size wasn't increased
200             if (size > mSize) {
201                 ALOGE("Removing path texture of size %d will leave "
202                         "the cache in an inconsistent state", size);
203             }
204             mSize -= size;
205         }
206 
207         PATH_LOGD("PathCache::delete name, size, mSize = %d, %d, %d",
208                 texture->id, size, mSize);
209         if (mDebugEnabled) {
210             ALOGD("Shape deleted, size = %d", size);
211         }
212 
213         if (texture->id) {
214             Caches::getInstance().textureState().deleteTexture(texture->id);
215         }
216         delete texture;
217     }
218 }
219 
purgeCache(uint32_t width,uint32_t height)220 void PathCache::purgeCache(uint32_t width, uint32_t height) {
221     const uint32_t size = width * height;
222     // Don't even try to cache a bitmap that's bigger than the cache
223     if (size < mMaxSize) {
224         while (mSize + size > mMaxSize) {
225             mCache.removeOldest();
226         }
227     }
228 }
229 
trim()230 void PathCache::trim() {
231     while (mSize > mMaxSize) {
232         mCache.removeOldest();
233     }
234 }
235 
addTexture(const PathDescription & entry,const SkPath * path,const SkPaint * paint)236 PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath *path,
237         const SkPaint* paint) {
238     ATRACE_NAME("Generate Path Texture");
239 
240     float left, top, offset;
241     uint32_t width, height;
242     computePathBounds(path, paint, left, top, offset, width, height);
243 
244     if (!checkTextureSize(width, height)) return nullptr;
245 
246     purgeCache(width, height);
247 
248     SkBitmap bitmap;
249     drawPath(path, paint, bitmap, left, top, offset, width, height);
250 
251     PathTexture* texture = new PathTexture(Caches::getInstance(),
252             left, top, offset, width, height,
253             path->getGenerationID());
254     generateTexture(entry, &bitmap, texture);
255 
256     return texture;
257 }
258 
generateTexture(const PathDescription & entry,SkBitmap * bitmap,PathTexture * texture,bool addToCache)259 void PathCache::generateTexture(const PathDescription& entry, SkBitmap* bitmap,
260         PathTexture* texture, bool addToCache) {
261     generateTexture(*bitmap, texture);
262 
263     // Note here that we upload to a texture even if it's bigger than mMaxSize.
264     // Such an entry in mCache will only be temporary, since it will be evicted
265     // immediately on trim, or on any other Path entering the cache.
266     uint32_t size = texture->width * texture->height;
267     mSize += size;
268     PATH_LOGD("PathCache::get/create: name, size, mSize = %d, %d, %d",
269             texture->id, size, mSize);
270     if (mDebugEnabled) {
271         ALOGD("Shape created, size = %d", size);
272     }
273     if (addToCache) {
274         mCache.put(entry, texture);
275     }
276 }
277 
clear()278 void PathCache::clear() {
279     mCache.clear();
280 }
281 
generateTexture(SkBitmap & bitmap,Texture * texture)282 void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) {
283     ATRACE_NAME("Upload Path Texture");
284     SkAutoLockPixels alp(bitmap);
285     if (!bitmap.readyToDraw()) {
286         ALOGE("Cannot generate texture from bitmap");
287         return;
288     }
289 
290     glGenTextures(1, &texture->id);
291 
292     Caches::getInstance().textureState().bindTexture(texture->id);
293     // Textures are Alpha8
294     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
295 
296     texture->blend = true;
297     glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
298             GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels());
299 
300     texture->setFilter(GL_LINEAR);
301     texture->setWrap(GL_CLAMP_TO_EDGE);
302 }
303 
304 ///////////////////////////////////////////////////////////////////////////////
305 // Path precaching
306 ///////////////////////////////////////////////////////////////////////////////
307 
PathProcessor(Caches & caches)308 PathCache::PathProcessor::PathProcessor(Caches& caches):
309         TaskProcessor<SkBitmap*>(&caches.tasks), mMaxTextureSize(caches.maxTextureSize) {
310 }
311 
onProcess(const sp<Task<SkBitmap * >> & task)312 void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) {
313     PathTask* t = static_cast<PathTask*>(task.get());
314     ATRACE_NAME("pathPrecache");
315 
316     float left, top, offset;
317     uint32_t width, height;
318     PathCache::computePathBounds(&t->path, &t->paint, left, top, offset, width, height);
319 
320     PathTexture* texture = t->texture;
321     texture->left = left;
322     texture->top = top;
323     texture->offset = offset;
324     texture->width = width;
325     texture->height = height;
326 
327     if (width <= mMaxTextureSize && height <= mMaxTextureSize) {
328         SkBitmap* bitmap = new SkBitmap();
329         drawPath(&t->path, &t->paint, *bitmap, left, top, offset, width, height);
330         t->setResult(bitmap);
331     } else {
332         texture->width = 0;
333         texture->height = 0;
334         t->setResult(nullptr);
335     }
336 }
337 
338 ///////////////////////////////////////////////////////////////////////////////
339 // Paths
340 ///////////////////////////////////////////////////////////////////////////////
341 
removeDeferred(const SkPath * path)342 void PathCache::removeDeferred(const SkPath* path) {
343     Mutex::Autolock l(mLock);
344     mGarbage.push(path->getGenerationID());
345 }
346 
clearGarbage()347 void PathCache::clearGarbage() {
348     Vector<PathDescription> pathsToRemove;
349 
350     { // scope for the mutex
351         Mutex::Autolock l(mLock);
352         size_t count = mGarbage.size();
353         for (size_t i = 0; i < count; i++) {
354             const uint32_t generationID = mGarbage.itemAt(i);
355 
356             LruCache<PathDescription, PathTexture*>::Iterator iter(mCache);
357             while (iter.next()) {
358                 const PathDescription& key = iter.key();
359                 if (key.type == kShapePath && key.shape.path.mGenerationID == generationID) {
360                     pathsToRemove.push(key);
361                 }
362             }
363         }
364         mGarbage.clear();
365     }
366 
367     for (size_t i = 0; i < pathsToRemove.size(); i++) {
368         mCache.remove(pathsToRemove.itemAt(i));
369     }
370 }
371 
get(const SkPath * path,const SkPaint * paint)372 PathTexture* PathCache::get(const SkPath* path, const SkPaint* paint) {
373     PathDescription entry(kShapePath, paint);
374     entry.shape.path.mGenerationID = path->getGenerationID();
375 
376     PathTexture* texture = mCache.get(entry);
377 
378     if (!texture) {
379         texture = addTexture(entry, path, paint);
380     } else {
381         // A bitmap is attached to the texture, this means we need to
382         // upload it as a GL texture
383         const sp<Task<SkBitmap*> >& task = texture->task();
384         if (task != nullptr) {
385             // But we must first wait for the worker thread to be done
386             // producing the bitmap, so let's wait
387             SkBitmap* bitmap = task->getResult();
388             if (bitmap) {
389                 generateTexture(entry, bitmap, texture, false);
390                 texture->clearTask();
391             } else {
392                 ALOGW("Path too large to be rendered into a texture");
393                 texture->clearTask();
394                 texture = nullptr;
395                 mCache.remove(entry);
396             }
397         }
398     }
399 
400     return texture;
401 }
402 
precache(const SkPath * path,const SkPaint * paint)403 void PathCache::precache(const SkPath* path, const SkPaint* paint) {
404     if (!Caches::getInstance().tasks.canRunTasks()) {
405         return;
406     }
407 
408     PathDescription entry(kShapePath, paint);
409     entry.shape.path.mGenerationID = path->getGenerationID();
410 
411     PathTexture* texture = mCache.get(entry);
412 
413     bool generate = false;
414     if (!texture) {
415         generate = true;
416     }
417 
418     if (generate) {
419         // It is important to specify the generation ID so we do not
420         // attempt to precache the same path several times
421         texture = new PathTexture(Caches::getInstance(), path->getGenerationID());
422         sp<PathTask> task = new PathTask(path, paint, texture);
423         texture->setTask(task);
424 
425         // During the precaching phase we insert path texture objects into
426         // the cache that do not point to any GL texture. They are instead
427         // treated as a task for the precaching worker thread. This is why
428         // we do not check the cache limit when inserting these objects.
429         // The conversion into GL texture will happen in get(), when a client
430         // asks for a path texture. This is also when the cache limit will
431         // be enforced.
432         mCache.put(entry, texture);
433 
434         if (mProcessor == nullptr) {
435             mProcessor = new PathProcessor(Caches::getInstance());
436         }
437         mProcessor->add(task);
438     }
439 }
440 
441 ///////////////////////////////////////////////////////////////////////////////
442 // Rounded rects
443 ///////////////////////////////////////////////////////////////////////////////
444 
getRoundRect(float width,float height,float rx,float ry,const SkPaint * paint)445 PathTexture* PathCache::getRoundRect(float width, float height,
446         float rx, float ry, const SkPaint* paint) {
447     PathDescription entry(kShapeRoundRect, paint);
448     entry.shape.roundRect.mWidth = width;
449     entry.shape.roundRect.mHeight = height;
450     entry.shape.roundRect.mRx = rx;
451     entry.shape.roundRect.mRy = ry;
452 
453     PathTexture* texture = get(entry);
454 
455     if (!texture) {
456         SkPath path;
457         SkRect r;
458         r.set(0.0f, 0.0f, width, height);
459         path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
460 
461         texture = addTexture(entry, &path, paint);
462     }
463 
464     return texture;
465 }
466 
467 ///////////////////////////////////////////////////////////////////////////////
468 // Circles
469 ///////////////////////////////////////////////////////////////////////////////
470 
getCircle(float radius,const SkPaint * paint)471 PathTexture* PathCache::getCircle(float radius, const SkPaint* paint) {
472     PathDescription entry(kShapeCircle, paint);
473     entry.shape.circle.mRadius = radius;
474 
475     PathTexture* texture = get(entry);
476 
477     if (!texture) {
478         SkPath path;
479         path.addCircle(radius, radius, radius, SkPath::kCW_Direction);
480 
481         texture = addTexture(entry, &path, paint);
482     }
483 
484     return texture;
485 }
486 
487 ///////////////////////////////////////////////////////////////////////////////
488 // Ovals
489 ///////////////////////////////////////////////////////////////////////////////
490 
getOval(float width,float height,const SkPaint * paint)491 PathTexture* PathCache::getOval(float width, float height, const SkPaint* paint) {
492     PathDescription entry(kShapeOval, paint);
493     entry.shape.oval.mWidth = width;
494     entry.shape.oval.mHeight = height;
495 
496     PathTexture* texture = get(entry);
497 
498     if (!texture) {
499         SkPath path;
500         SkRect r;
501         r.set(0.0f, 0.0f, width, height);
502         path.addOval(r, SkPath::kCW_Direction);
503 
504         texture = addTexture(entry, &path, paint);
505     }
506 
507     return texture;
508 }
509 
510 ///////////////////////////////////////////////////////////////////////////////
511 // Rects
512 ///////////////////////////////////////////////////////////////////////////////
513 
getRect(float width,float height,const SkPaint * paint)514 PathTexture* PathCache::getRect(float width, float height, const SkPaint* paint) {
515     PathDescription entry(kShapeRect, paint);
516     entry.shape.rect.mWidth = width;
517     entry.shape.rect.mHeight = height;
518 
519     PathTexture* texture = get(entry);
520 
521     if (!texture) {
522         SkPath path;
523         SkRect r;
524         r.set(0.0f, 0.0f, width, height);
525         path.addRect(r, SkPath::kCW_Direction);
526 
527         texture = addTexture(entry, &path, paint);
528     }
529 
530     return texture;
531 }
532 
533 ///////////////////////////////////////////////////////////////////////////////
534 // Arcs
535 ///////////////////////////////////////////////////////////////////////////////
536 
getArc(float width,float height,float startAngle,float sweepAngle,bool useCenter,const SkPaint * paint)537 PathTexture* PathCache::getArc(float width, float height,
538         float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) {
539     PathDescription entry(kShapeArc, paint);
540     entry.shape.arc.mWidth = width;
541     entry.shape.arc.mHeight = height;
542     entry.shape.arc.mStartAngle = startAngle;
543     entry.shape.arc.mSweepAngle = sweepAngle;
544     entry.shape.arc.mUseCenter = useCenter;
545 
546     PathTexture* texture = get(entry);
547 
548     if (!texture) {
549         SkPath path;
550         SkRect r;
551         r.set(0.0f, 0.0f, width, height);
552         if (useCenter) {
553             path.moveTo(r.centerX(), r.centerY());
554         }
555         path.arcTo(r, startAngle, sweepAngle, !useCenter);
556         if (useCenter) {
557             path.close();
558         }
559 
560         texture = addTexture(entry, &path, paint);
561     }
562 
563     return texture;
564 }
565 
566 }; // namespace uirenderer
567 }; // namespace android
568