blob: 9e6ec8488ebf9d9a114633d35076407ab8fbcb0e [file] [log] [blame]
Romain Guy7fbcc042010-08-04 15:40:07 -07001/*
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
Romain Guyca89e2a2013-03-08 17:44:20 -080019#include <utils/Mutex.h>
Romain Guya2341a92010-09-08 18:04:33 -070020
Romain Guyca89e2a2013-03-08 17:44:20 -080021#include <sys/sysinfo.h>
22
23#include "Caches.h"
Romain Guy7fbcc042010-08-04 15:40:07 -070024#include "PathCache.h"
Romain Guyfb8b7632010-08-23 21:05:08 -070025#include "Properties.h"
Romain Guy7fbcc042010-08-04 15:40:07 -070026
27namespace android {
28namespace uirenderer {
29
Romain Guyca89e2a2013-03-08 17:44:20 -080030///////////////////////////////////////////////////////////////////////////////
31// Path precaching
32///////////////////////////////////////////////////////////////////////////////
Romain Guyfdd6fc12012-04-27 11:47:13 -070033
Romain Guyca89e2a2013-03-08 17:44:20 -080034bool 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;
Romain Guyfdd6fc12012-04-27 11:47:13 -070073}
Romain Guy33f6beb2012-02-16 19:24:51 -080074
Romain Guyca89e2a2013-03-08 17:44:20 -080075void PathCache::PrecacheThread::addTask(PathTexture* texture, SkPath* path, SkPaint* paint) {
76 if (!isRunning()) {
77 run("libhwui:pathPrecache", PRIORITY_DEFAULT);
78 }
Romain Guy33f6beb2012-02-16 19:24:51 -080079
Romain Guyca89e2a2013-03-08 17:44:20 -080080 Task task;
81 task.texture = texture;
82 task.path = path;
83 task.paint = paint;
Romain Guy33f6beb2012-02-16 19:24:51 -080084
Romain Guyca89e2a2013-03-08 17:44:20 -080085 Mutex::Autolock l(mLock);
86 mTasks.add(task);
87 mSignal.signal();
88}
Romain Guy33f6beb2012-02-16 19:24:51 -080089
Romain Guyca89e2a2013-03-08 17:44:20 -080090void PathCache::PrecacheThread::exit() {
91 {
92 Mutex::Autolock l(mLock);
93 mTasks.clear();
94 }
95 requestExit();
96 mSignal.signal();
Romain Guy33f6beb2012-02-16 19:24:51 -080097}
98
Romain Guy7fbcc042010-08-04 15:40:07 -070099///////////////////////////////////////////////////////////////////////////////
Romain Guyff26a0c2011-01-20 11:35:46 -0800100// Path cache
Romain Guy7fbcc042010-08-04 15:40:07 -0700101///////////////////////////////////////////////////////////////////////////////
102
Romain Guyff26a0c2011-01-20 11:35:46 -0800103PathCache::PathCache(): ShapeCache<PathCacheEntry>("path",
Romain Guyca89e2a2013-03-08 17:44:20 -0800104 PROPERTY_PATH_CACHE_SIZE, DEFAULT_PATH_CACHE_SIZE), mThread(new PrecacheThread()) {
105}
106
107PathCache::~PathCache() {
108 mThread->exit();
Romain Guy7fbcc042010-08-04 15:40:07 -0700109}
110
Romain Guya2341a92010-09-08 18:04:33 -0700111void PathCache::remove(SkPath* path) {
Romain Guy059e12c2012-11-28 17:35:51 -0800112 Vector<PathCacheEntry> pathsToRemove;
113 LruCache<PathCacheEntry, PathTexture*>::Iterator i(mCache);
114
115 while (i.next()) {
116 const PathCacheEntry& key = i.key();
Romain Guyca89e2a2013-03-08 17:44:20 -0800117 if (key.path == path || key.path == path->getSourcePath()) {
Romain Guy059e12c2012-11-28 17:35:51 -0800118 pathsToRemove.push(key);
Romain Guya2341a92010-09-08 18:04:33 -0700119 }
120 }
Romain Guy9e108412010-11-09 14:35:20 -0800121
122 for (size_t i = 0; i < pathsToRemove.size(); i++) {
Romain Guy059e12c2012-11-28 17:35:51 -0800123 mCache.remove(pathsToRemove.itemAt(i));
Romain Guy9e108412010-11-09 14:35:20 -0800124 }
Romain Guyfe48f652010-11-11 15:36:56 -0800125}
126
127void PathCache::removeDeferred(SkPath* path) {
Romain Guyca89e2a2013-03-08 17:44:20 -0800128 Mutex::Autolock l(mLock);
Romain Guyfe48f652010-11-11 15:36:56 -0800129 mGarbage.push(path);
130}
131
132void PathCache::clearGarbage() {
Romain Guyca89e2a2013-03-08 17:44:20 -0800133 Mutex::Autolock l(mLock);
Romain Guyfe48f652010-11-11 15:36:56 -0800134 size_t count = mGarbage.size();
135 for (size_t i = 0; i < count; i++) {
136 remove(mGarbage.itemAt(i));
137 }
138 mGarbage.clear();
Romain Guya2341a92010-09-08 18:04:33 -0700139}
140
Romain Guyca89e2a2013-03-08 17:44:20 -0800141/**
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) {
Romain Guy4bcb7462012-02-23 17:08:38 -0800150 const SkPath* sourcePath = path->getSourcePath();
151 if (sourcePath && sourcePath->getGenerationID() == path->getGenerationID()) {
Romain Guyca89e2a2013-03-08 17:44:20 -0800152 return const_cast<SkPath*>(sourcePath);
Romain Guy4bcb7462012-02-23 17:08:38 -0800153 }
Romain Guyca89e2a2013-03-08 17:44:20 -0800154 return path;
155}
156
157PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
158 path = getSourcePath(path);
Romain Guy4bcb7462012-02-23 17:08:38 -0800159
Romain Guy7fbcc042010-08-04 15:40:07 -0700160 PathCacheEntry entry(path, paint);
161 PathTexture* texture = mCache.get(entry);
162
163 if (!texture) {
164 texture = addTexture(entry, path, paint);
Romain Guyca89e2a2013-03-08 17:44:20 -0800165 } 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 }
Romain Guy7fbcc042010-08-04 15:40:07 -0700186 }
187
Romain Guy7fbcc042010-08-04 15:40:07 -0700188 return texture;
189}
190
Romain Guyca89e2a2013-03-08 17:44:20 -0800191void 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
Romain Guy7fbcc042010-08-04 15:40:07 -0700222}; // namespace uirenderer
223}; // namespace android