PathCache.cpp revision ca89e2a68703bd428e8b66547d033a6ed35b3595
1/* 2 * Copyright (C) 2010 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 19#include <utils/Mutex.h> 20 21#include <sys/sysinfo.h> 22 23#include "Caches.h" 24#include "PathCache.h" 25#include "Properties.h" 26 27namespace android { 28namespace uirenderer { 29 30/////////////////////////////////////////////////////////////////////////////// 31// Path precaching 32/////////////////////////////////////////////////////////////////////////////// 33 34bool PathCache::PrecacheThread::threadLoop() { 35 mSignal.wait(); 36 Vector<Task> tasks; 37 { 38 Mutex::Autolock l(mLock); 39 tasks = mTasks; 40 mTasks.clear(); 41 } 42 43 Caches& caches = Caches::getInstance(); 44 uint32_t maxSize = caches.maxTextureSize; 45 46 ATRACE_BEGIN("pathPrecache"); 47 for (size_t i = 0; i < tasks.size(); i++) { 48 const Task& task = tasks.itemAt(i); 49 50 float left, top, offset; 51 uint32_t width, height; 52 PathCache::computePathBounds(task.path, task.paint, left, top, offset, width, height); 53 54 if (width <= maxSize && height <= maxSize) { 55 SkBitmap* bitmap = new SkBitmap(); 56 57 PathTexture* texture = task.texture; 58 texture->left = left; 59 texture->top = top; 60 texture->offset = offset; 61 texture->width = width; 62 texture->height = height; 63 64 PathCache::drawPath(task.path, task.paint, *bitmap, left, top, offset, width, height); 65 66 texture->future()->produce(bitmap); 67 } else { 68 task.texture->future()->produce(NULL); 69 } 70 } 71 ATRACE_END(); 72 return true; 73} 74 75void PathCache::PrecacheThread::addTask(PathTexture* texture, SkPath* path, SkPaint* paint) { 76 if (!isRunning()) { 77 run("libhwui:pathPrecache", PRIORITY_DEFAULT); 78 } 79 80 Task task; 81 task.texture = texture; 82 task.path = path; 83 task.paint = paint; 84 85 Mutex::Autolock l(mLock); 86 mTasks.add(task); 87 mSignal.signal(); 88} 89 90void PathCache::PrecacheThread::exit() { 91 { 92 Mutex::Autolock l(mLock); 93 mTasks.clear(); 94 } 95 requestExit(); 96 mSignal.signal(); 97} 98 99/////////////////////////////////////////////////////////////////////////////// 100// Path cache 101/////////////////////////////////////////////////////////////////////////////// 102 103PathCache::PathCache(): ShapeCache<PathCacheEntry>("path", 104 PROPERTY_PATH_CACHE_SIZE, DEFAULT_PATH_CACHE_SIZE), mThread(new PrecacheThread()) { 105} 106 107PathCache::~PathCache() { 108 mThread->exit(); 109} 110 111void PathCache::remove(SkPath* path) { 112 Vector<PathCacheEntry> pathsToRemove; 113 LruCache<PathCacheEntry, PathTexture*>::Iterator i(mCache); 114 115 while (i.next()) { 116 const PathCacheEntry& key = i.key(); 117 if (key.path == path || key.path == path->getSourcePath()) { 118 pathsToRemove.push(key); 119 } 120 } 121 122 for (size_t i = 0; i < pathsToRemove.size(); i++) { 123 mCache.remove(pathsToRemove.itemAt(i)); 124 } 125} 126 127void PathCache::removeDeferred(SkPath* path) { 128 Mutex::Autolock l(mLock); 129 mGarbage.push(path); 130} 131 132void PathCache::clearGarbage() { 133 Mutex::Autolock l(mLock); 134 size_t count = mGarbage.size(); 135 for (size_t i = 0; i < count; i++) { 136 remove(mGarbage.itemAt(i)); 137 } 138 mGarbage.clear(); 139} 140 141/** 142 * To properly handle path mutations at draw time we always make a copy 143 * of paths objects when recording display lists. The source path points 144 * to the path we originally copied the path from. This ensures we use 145 * the original path as a cache key the first time a path is inserted 146 * in the cache. The source path is also used to reclaim garbage when a 147 * Dalvik Path object is collected. 148 */ 149static SkPath* getSourcePath(SkPath* path) { 150 const SkPath* sourcePath = path->getSourcePath(); 151 if (sourcePath && sourcePath->getGenerationID() == path->getGenerationID()) { 152 return const_cast<SkPath*>(sourcePath); 153 } 154 return path; 155} 156 157PathTexture* PathCache::get(SkPath* path, SkPaint* paint) { 158 path = getSourcePath(path); 159 160 PathCacheEntry entry(path, paint); 161 PathTexture* texture = mCache.get(entry); 162 163 if (!texture) { 164 texture = addTexture(entry, path, paint); 165 } else { 166 // A bitmap is attached to the texture, this means we need to 167 // upload it as a GL texture 168 if (texture->future() != NULL) { 169 // But we must first wait for the worker thread to be done 170 // producing the bitmap, so let's wait 171 SkBitmap* bitmap = texture->future()->get(); 172 if (bitmap) { 173 addTexture(entry, bitmap, texture); 174 texture->clearFuture(); 175 } else { 176 ALOGW("Path too large to be rendered into a texture (%dx%d)", 177 texture->width, texture->height); 178 texture->clearFuture(); 179 texture = NULL; 180 mCache.remove(entry); 181 } 182 } else if (path->getGenerationID() != texture->generation) { 183 mCache.remove(entry); 184 texture = addTexture(entry, path, paint); 185 } 186 } 187 188 return texture; 189} 190 191void PathCache::precache(SkPath* path, SkPaint* paint) { 192 path = getSourcePath(path); 193 194 PathCacheEntry entry(path, paint); 195 PathTexture* texture = mCache.get(entry); 196 197 bool generate = false; 198 if (!texture) { 199 generate = true; 200 } else if (path->getGenerationID() != texture->generation) { 201 mCache.remove(entry); 202 generate = true; 203 } 204 205 if (generate) { 206 // It is important to specify the generation ID so we do not 207 // attempt to precache the same path several times 208 texture = createTexture(0.0f, 0.0f, 0.0f, 0, 0, path->getGenerationID(), true); 209 210 // During the precaching phase we insert path texture objects into 211 // the cache that do not point to any GL texture. They are instead 212 // treated as a task for the precaching worker thread. This is why 213 // we do not check the cache limit when inserting these objects. 214 // The conversion into GL texture will happen in get(), when a client 215 // asks for a path texture. This is also when the cache limit will 216 // be enforced. 217 mCache.put(entry, texture); 218 mThread->addTask(texture, path, paint); 219 } 220} 221 222}; // namespace uirenderer 223}; // namespace android 224