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 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(NULL) { 51 memset(&shape, 0, sizeof(Shape)); 52 } 53 54 PathDescription::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 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 int PathDescription::compare(const PathDescription& rhs) const { 78 return memcmp(this, &rhs, sizeof(PathDescription)); 79 } 80 81 /////////////////////////////////////////////////////////////////////////////// 82 // Utilities 83 /////////////////////////////////////////////////////////////////////////////// 84 85 bool 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 90 void 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 96 void 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 110 static 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 116 static 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 128 static 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 140 static 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 156 PathCache::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 169 PathCache::~PathCache() { 170 mCache.clear(); 171 } 172 173 void 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 187 uint32_t PathCache::getSize() { 188 return mSize; 189 } 190 191 uint32_t PathCache::getMaxSize() { 192 return mMaxSize; 193 } 194 195 void PathCache::setMaxSize(uint32_t maxSize) { 196 mMaxSize = maxSize; 197 while (mSize > mMaxSize) { 198 mCache.removeOldest(); 199 } 200 } 201 202 /////////////////////////////////////////////////////////////////////////////// 203 // Callbacks 204 /////////////////////////////////////////////////////////////////////////////// 205 206 void PathCache::operator()(PathDescription& entry, PathTexture*& texture) { 207 removeTexture(texture); 208 } 209 210 /////////////////////////////////////////////////////////////////////////////// 211 // Caching 212 /////////////////////////////////////////////////////////////////////////////// 213 214 void PathCache::removeTexture(PathTexture* texture) { 215 if (texture) { 216 const uint32_t size = texture->width * texture->height; 217 218 // If there is a pending task we must wait for it to return 219 // before attempting our cleanup 220 const sp<Task<SkBitmap*> >& task = texture->task(); 221 if (task != NULL) { 222 SkBitmap* bitmap = task->getResult(); 223 texture->clearTask(); 224 } else { 225 // If there is a pending task, the path was not added 226 // to the cache and the size wasn't increased 227 if (size > mSize) { 228 ALOGE("Removing path texture of size %d will leave " 229 "the cache in an inconsistent state", size); 230 } 231 mSize -= size; 232 } 233 234 PATH_LOGD("PathCache::delete name, size, mSize = %d, %d, %d", 235 texture->id, size, mSize); 236 if (mDebugEnabled) { 237 ALOGD("Shape deleted, size = %d", size); 238 } 239 240 if (texture->id) { 241 Caches::getInstance().deleteTexture(texture->id); 242 } 243 delete texture; 244 } 245 } 246 247 void PathCache::purgeCache(uint32_t width, uint32_t height) { 248 const uint32_t size = width * height; 249 // Don't even try to cache a bitmap that's bigger than the cache 250 if (size < mMaxSize) { 251 while (mSize + size > mMaxSize) { 252 mCache.removeOldest(); 253 } 254 } 255 } 256 257 void PathCache::trim() { 258 while (mSize > mMaxSize) { 259 mCache.removeOldest(); 260 } 261 } 262 263 PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath *path, 264 const SkPaint* paint) { 265 ATRACE_CALL(); 266 267 float left, top, offset; 268 uint32_t width, height; 269 computePathBounds(path, paint, left, top, offset, width, height); 270 271 if (!checkTextureSize(width, height)) return NULL; 272 273 purgeCache(width, height); 274 275 SkBitmap bitmap; 276 drawPath(path, paint, bitmap, left, top, offset, width, height); 277 278 PathTexture* texture = createTexture(left, top, offset, width, height, 279 path->getGenerationID()); 280 generateTexture(entry, &bitmap, texture); 281 282 return texture; 283 } 284 285 void PathCache::generateTexture(const PathDescription& entry, SkBitmap* bitmap, 286 PathTexture* texture, bool addToCache) { 287 generateTexture(*bitmap, texture); 288 289 uint32_t size = texture->width * texture->height; 290 if (size < mMaxSize) { 291 mSize += size; 292 PATH_LOGD("PathCache::get/create: name, size, mSize = %d, %d, %d", 293 texture->id, size, mSize); 294 if (mDebugEnabled) { 295 ALOGD("Shape created, size = %d", size); 296 } 297 if (addToCache) { 298 mCache.put(entry, texture); 299 } 300 } else { 301 // It's okay to add a texture that's bigger than the cache since 302 // we'll trim the cache later when addToCache is set to false 303 if (!addToCache) { 304 mSize += size; 305 } 306 texture->cleanup = true; 307 } 308 } 309 310 void PathCache::clear() { 311 mCache.clear(); 312 } 313 314 void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) { 315 SkAutoLockPixels alp(bitmap); 316 if (!bitmap.readyToDraw()) { 317 ALOGE("Cannot generate texture from bitmap"); 318 return; 319 } 320 321 glGenTextures(1, &texture->id); 322 323 Caches::getInstance().bindTexture(texture->id); 324 // Textures are Alpha8 325 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 326 327 texture->blend = true; 328 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0, 329 GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels()); 330 331 texture->setFilter(GL_LINEAR); 332 texture->setWrap(GL_CLAMP_TO_EDGE); 333 } 334 335 /////////////////////////////////////////////////////////////////////////////// 336 // Path precaching 337 /////////////////////////////////////////////////////////////////////////////// 338 339 PathCache::PathProcessor::PathProcessor(Caches& caches): 340 TaskProcessor<SkBitmap*>(&caches.tasks), mMaxTextureSize(caches.maxTextureSize) { 341 } 342 343 void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) { 344 sp<PathTask> t = static_cast<PathTask* >(task.get()); 345 ATRACE_NAME("pathPrecache"); 346 347 float left, top, offset; 348 uint32_t width, height; 349 PathCache::computePathBounds(t->path, t->paint, left, top, offset, width, height); 350 351 PathTexture* texture = t->texture; 352 texture->left = left; 353 texture->top = top; 354 texture->offset = offset; 355 texture->width = width; 356 texture->height = height; 357 358 if (width <= mMaxTextureSize && height <= mMaxTextureSize) { 359 SkBitmap* bitmap = new SkBitmap(); 360 drawPath(t->path, t->paint, *bitmap, left, top, offset, width, height); 361 t->setResult(bitmap); 362 } else { 363 texture->width = 0; 364 texture->height = 0; 365 t->setResult(NULL); 366 } 367 } 368 369 /////////////////////////////////////////////////////////////////////////////// 370 // Paths 371 /////////////////////////////////////////////////////////////////////////////// 372 373 void PathCache::remove(Vector<PathDescription>& pathsToRemove, const path_pair_t& pair) { 374 LruCache<PathDescription, PathTexture*>::Iterator i(mCache); 375 376 while (i.next()) { 377 const PathDescription& key = i.key(); 378 if (key.type == kShapePath && 379 (key.shape.path.mPath == pair.getFirst() || 380 key.shape.path.mPath == pair.getSecond())) { 381 pathsToRemove.push(key); 382 } 383 } 384 } 385 386 void PathCache::removeDeferred(SkPath* path) { 387 Mutex::Autolock l(mLock); 388 mGarbage.push(path_pair_t(path, const_cast<SkPath*>(path->getSourcePath()))); 389 } 390 391 void PathCache::clearGarbage() { 392 Vector<PathDescription> pathsToRemove; 393 394 { // scope for the mutex 395 Mutex::Autolock l(mLock); 396 size_t count = mGarbage.size(); 397 for (size_t i = 0; i < count; i++) { 398 remove(pathsToRemove, mGarbage.itemAt(i)); 399 } 400 mGarbage.clear(); 401 } 402 403 for (size_t i = 0; i < pathsToRemove.size(); i++) { 404 mCache.remove(pathsToRemove.itemAt(i)); 405 } 406 } 407 408 /** 409 * To properly handle path mutations at draw time we always make a copy 410 * of paths objects when recording display lists. The source path points 411 * to the path we originally copied the path from. This ensures we use 412 * the original path as a cache key the first time a path is inserted 413 * in the cache. The source path is also used to reclaim garbage when a 414 * Dalvik Path object is collected. 415 */ 416 static SkPath* getSourcePath(SkPath* path) { 417 const SkPath* sourcePath = path->getSourcePath(); 418 if (sourcePath && sourcePath->getGenerationID() == path->getGenerationID()) { 419 return const_cast<SkPath*>(sourcePath); 420 } 421 return path; 422 } 423 424 PathTexture* PathCache::get(SkPath* path, SkPaint* paint) { 425 path = getSourcePath(path); 426 427 PathDescription entry(kShapePath, paint); 428 entry.shape.path.mPath = path; 429 430 PathTexture* texture = mCache.get(entry); 431 432 if (!texture) { 433 texture = addTexture(entry, path, paint); 434 } else { 435 // A bitmap is attached to the texture, this means we need to 436 // upload it as a GL texture 437 const sp<Task<SkBitmap*> >& task = texture->task(); 438 if (task != NULL) { 439 // But we must first wait for the worker thread to be done 440 // producing the bitmap, so let's wait 441 SkBitmap* bitmap = task->getResult(); 442 if (bitmap) { 443 generateTexture(entry, bitmap, texture, false); 444 texture->clearTask(); 445 } else { 446 ALOGW("Path too large to be rendered into a texture"); 447 texture->clearTask(); 448 texture = NULL; 449 mCache.remove(entry); 450 } 451 } else if (path->getGenerationID() != texture->generation) { 452 // The size of the path might have changed so we first 453 // remove the entry from the cache 454 mCache.remove(entry); 455 texture = addTexture(entry, path, paint); 456 } 457 } 458 459 return texture; 460 } 461 462 void PathCache::precache(SkPath* path, SkPaint* paint) { 463 if (!Caches::getInstance().tasks.canRunTasks()) { 464 return; 465 } 466 467 path = getSourcePath(path); 468 469 PathDescription entry(kShapePath, paint); 470 entry.shape.path.mPath = path; 471 472 PathTexture* texture = mCache.get(entry); 473 474 bool generate = false; 475 if (!texture) { 476 generate = true; 477 } else if (path->getGenerationID() != texture->generation) { 478 mCache.remove(entry); 479 generate = true; 480 } 481 482 if (generate) { 483 // It is important to specify the generation ID so we do not 484 // attempt to precache the same path several times 485 texture = createTexture(0.0f, 0.0f, 0.0f, 0, 0, path->getGenerationID()); 486 sp<PathTask> task = new PathTask(path, paint, texture); 487 texture->setTask(task); 488 489 // During the precaching phase we insert path texture objects into 490 // the cache that do not point to any GL texture. They are instead 491 // treated as a task for the precaching worker thread. This is why 492 // we do not check the cache limit when inserting these objects. 493 // The conversion into GL texture will happen in get(), when a client 494 // asks for a path texture. This is also when the cache limit will 495 // be enforced. 496 mCache.put(entry, texture); 497 498 if (mProcessor == NULL) { 499 mProcessor = new PathProcessor(Caches::getInstance()); 500 } 501 mProcessor->add(task); 502 } 503 } 504 505 /////////////////////////////////////////////////////////////////////////////// 506 // Rounded rects 507 /////////////////////////////////////////////////////////////////////////////// 508 509 PathTexture* PathCache::getRoundRect(float width, float height, 510 float rx, float ry, SkPaint* paint) { 511 PathDescription entry(kShapeRoundRect, paint); 512 entry.shape.roundRect.mWidth = width; 513 entry.shape.roundRect.mHeight = height; 514 entry.shape.roundRect.mRx = rx; 515 entry.shape.roundRect.mRy = ry; 516 517 PathTexture* texture = get(entry); 518 519 if (!texture) { 520 SkPath path; 521 SkRect r; 522 r.set(0.0f, 0.0f, width, height); 523 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction); 524 525 texture = addTexture(entry, &path, paint); 526 } 527 528 return texture; 529 } 530 531 /////////////////////////////////////////////////////////////////////////////// 532 // Circles 533 /////////////////////////////////////////////////////////////////////////////// 534 535 PathTexture* PathCache::getCircle(float radius, SkPaint* paint) { 536 PathDescription entry(kShapeCircle, paint); 537 entry.shape.circle.mRadius = radius; 538 539 PathTexture* texture = get(entry); 540 541 if (!texture) { 542 SkPath path; 543 path.addCircle(radius, radius, radius, SkPath::kCW_Direction); 544 545 texture = addTexture(entry, &path, paint); 546 } 547 548 return texture; 549 } 550 551 /////////////////////////////////////////////////////////////////////////////// 552 // Ovals 553 /////////////////////////////////////////////////////////////////////////////// 554 555 PathTexture* PathCache::getOval(float width, float height, SkPaint* paint) { 556 PathDescription entry(kShapeOval, paint); 557 entry.shape.oval.mWidth = width; 558 entry.shape.oval.mHeight = height; 559 560 PathTexture* texture = get(entry); 561 562 if (!texture) { 563 SkPath path; 564 SkRect r; 565 r.set(0.0f, 0.0f, width, height); 566 path.addOval(r, SkPath::kCW_Direction); 567 568 texture = addTexture(entry, &path, paint); 569 } 570 571 return texture; 572 } 573 574 /////////////////////////////////////////////////////////////////////////////// 575 // Rects 576 /////////////////////////////////////////////////////////////////////////////// 577 578 PathTexture* PathCache::getRect(float width, float height, SkPaint* paint) { 579 PathDescription entry(kShapeRect, paint); 580 entry.shape.rect.mWidth = width; 581 entry.shape.rect.mHeight = height; 582 583 PathTexture* texture = get(entry); 584 585 if (!texture) { 586 SkPath path; 587 SkRect r; 588 r.set(0.0f, 0.0f, width, height); 589 path.addRect(r, SkPath::kCW_Direction); 590 591 texture = addTexture(entry, &path, paint); 592 } 593 594 return texture; 595 } 596 597 /////////////////////////////////////////////////////////////////////////////// 598 // Arcs 599 /////////////////////////////////////////////////////////////////////////////// 600 601 PathTexture* PathCache::getArc(float width, float height, 602 float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) { 603 PathDescription entry(kShapeArc, paint); 604 entry.shape.arc.mWidth = width; 605 entry.shape.arc.mHeight = height; 606 entry.shape.arc.mStartAngle = startAngle; 607 entry.shape.arc.mSweepAngle = sweepAngle; 608 entry.shape.arc.mUseCenter = useCenter; 609 610 PathTexture* texture = get(entry); 611 612 if (!texture) { 613 SkPath path; 614 SkRect r; 615 r.set(0.0f, 0.0f, width, height); 616 if (useCenter) { 617 path.moveTo(r.centerX(), r.centerY()); 618 } 619 path.arcTo(r, startAngle, sweepAngle, !useCenter); 620 if (useCenter) { 621 path.close(); 622 } 623 624 texture = addTexture(entry, &path, paint); 625 } 626 627 return texture; 628 } 629 630 }; // namespace uirenderer 631 }; // namespace android 632