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