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