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