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