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 #define LOG_TAG "OpenGLRenderer"
     18 #define ATRACE_TAG ATRACE_TAG_VIEW
     19 
     20 #include <SkBitmap.h>
     21 #include <SkCanvas.h>
     22 #include <SkColor.h>
     23 #include <SkPaint.h>
     24 #include <SkPath.h>
     25 #include <SkRect.h>
     26 
     27 #include <utils/JenkinsHash.h>
     28 #include <utils/Trace.h>
     29 
     30 #include "Caches.h"
     31 #include "PathCache.h"
     32 
     33 #include "thread/Signal.h"
     34 #include "thread/TaskProcessor.h"
     35 
     36 namespace android {
     37 namespace uirenderer {
     38 
     39 ///////////////////////////////////////////////////////////////////////////////
     40 // Cache entries
     41 ///////////////////////////////////////////////////////////////////////////////
     42 
     43 PathDescription::PathDescription()
     44         : type(kShapeNone)
     45         , join(SkPaint::kDefault_Join)
     46         , cap(SkPaint::kDefault_Cap)
     47         , style(SkPaint::kFill_Style)
     48         , miter(4.0f)
     49         , strokeWidth(1.0f)
     50         , pathEffect(nullptr) {
     51     memset(&shape, 0, sizeof(Shape));
     52 }
     53 
     54 PathDescription::PathDescription(ShapeType type, const SkPaint* paint)
     55         : type(type)
     56         , join(paint->getStrokeJoin())
     57         , cap(paint->getStrokeCap())
     58         , style(paint->getStyle())
     59         , miter(paint->getStrokeMiter())
     60         , strokeWidth(paint->getStrokeWidth())
     61         , pathEffect(paint->getPathEffect()) {
     62     memset(&shape, 0, sizeof(Shape));
     63 }
     64 
     65 hash_t PathDescription::hash() const {
     66     uint32_t hash = JenkinsHashMix(0, type);
     67     hash = JenkinsHashMix(hash, join);
     68     hash = JenkinsHashMix(hash, cap);
     69     hash = JenkinsHashMix(hash, style);
     70     hash = JenkinsHashMix(hash, android::hash_type(miter));
     71     hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
     72     hash = JenkinsHashMix(hash, android::hash_type(pathEffect));
     73     hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape));
     74     return JenkinsHashWhiten(hash);
     75 }
     76 
     77 ///////////////////////////////////////////////////////////////////////////////
     78 // Utilities
     79 ///////////////////////////////////////////////////////////////////////////////
     80 
     81 bool PathCache::canDrawAsConvexPath(SkPath* path, const SkPaint* paint) {
     82     // NOTE: This should only be used after PathTessellator handles joins properly
     83     return paint->getPathEffect() == nullptr && path->getConvexity() == SkPath::kConvex_Convexity;
     84 }
     85 
     86 void PathCache::computePathBounds(const SkPath* path, const SkPaint* paint,
     87         float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
     88     const SkRect& bounds = path->getBounds();
     89     PathCache::computeBounds(bounds, paint, left, top, offset, width, height);
     90 }
     91 
     92 void PathCache::computeBounds(const SkRect& bounds, const SkPaint* paint,
     93         float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
     94     const float pathWidth = 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 
    106 static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
    107     bitmap.allocPixels(SkImageInfo::MakeA8(width, height));
    108     bitmap.eraseColor(0);
    109 }
    110 
    111 static void initPaint(SkPaint& paint) {
    112     // Make sure the paint is opaque, color, alpha, filter, etc.
    113     // will be applied later when compositing the alpha8 texture
    114     paint.setColor(SK_ColorBLACK);
    115     paint.setAlpha(255);
    116     paint.setColorFilter(nullptr);
    117     paint.setMaskFilter(nullptr);
    118     paint.setShader(nullptr);
    119     SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
    120     SkSafeUnref(paint.setXfermode(mode));
    121 }
    122 
    123 static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap,
    124         float left, float top, float offset, uint32_t width, uint32_t height) {
    125     initBitmap(bitmap, width, height);
    126 
    127     SkPaint pathPaint(*paint);
    128     initPaint(pathPaint);
    129 
    130     SkCanvas canvas(bitmap);
    131     canvas.translate(-left + offset, -top + offset);
    132     canvas.drawPath(*path, pathPaint);
    133 }
    134 
    135 ///////////////////////////////////////////////////////////////////////////////
    136 // Cache constructor/destructor
    137 ///////////////////////////////////////////////////////////////////////////////
    138 
    139 PathCache::PathCache():
    140         mCache(LruCache<PathDescription, PathTexture*>::kUnlimitedCapacity),
    141         mSize(0), mMaxSize(MB(DEFAULT_PATH_CACHE_SIZE)) {
    142     char property[PROPERTY_VALUE_MAX];
    143     if (property_get(PROPERTY_PATH_CACHE_SIZE, property, nullptr) > 0) {
    144         INIT_LOGD("  Setting %s cache size to %sMB", name, property);
    145         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 
    159 PathCache::~PathCache() {
    160     mCache.clear();
    161 }
    162 
    163 ///////////////////////////////////////////////////////////////////////////////
    164 // Size management
    165 ///////////////////////////////////////////////////////////////////////////////
    166 
    167 uint32_t PathCache::getSize() {
    168     return mSize;
    169 }
    170 
    171 uint32_t PathCache::getMaxSize() {
    172     return mMaxSize;
    173 }
    174 
    175 ///////////////////////////////////////////////////////////////////////////////
    176 // Callbacks
    177 ///////////////////////////////////////////////////////////////////////////////
    178 
    179 void PathCache::operator()(PathDescription& entry, PathTexture*& texture) {
    180     removeTexture(texture);
    181 }
    182 
    183 ///////////////////////////////////////////////////////////////////////////////
    184 // Caching
    185 ///////////////////////////////////////////////////////////////////////////////
    186 
    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 
    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 
    230 void PathCache::trim() {
    231     while (mSize > mMaxSize) {
    232         mCache.removeOldest();
    233     }
    234 }
    235 
    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 
    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 
    278 void PathCache::clear() {
    279     mCache.clear();
    280 }
    281 
    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 
    308 PathCache::PathProcessor::PathProcessor(Caches& caches):
    309         TaskProcessor<SkBitmap*>(&caches.tasks), mMaxTextureSize(caches.maxTextureSize) {
    310 }
    311 
    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 
    342 void PathCache::removeDeferred(const SkPath* path) {
    343     Mutex::Autolock l(mLock);
    344     mGarbage.push(path->getGenerationID());
    345 }
    346 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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