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_CALL(); 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