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