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