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