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 #include <GLES2/gl2.h>
18
19 #include <utils/Mutex.h>
20
21 #include "Caches.h"
22 #include "Texture.h"
23 #include "TextureCache.h"
24 #include "Properties.h"
25 #include "utils/TraceUtils.h"
26 #include "hwui/Bitmap.h"
27
28 namespace android {
29 namespace uirenderer {
30
31 ///////////////////////////////////////////////////////////////////////////////
32 // Constructors/destructor
33 ///////////////////////////////////////////////////////////////////////////////
34
TextureCache()35 TextureCache::TextureCache()
36 : mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity)
37 , mSize(0)
38 , mMaxSize(Properties::textureCacheSize)
39 , mFlushRate(Properties::textureCacheFlushRate) {
40 mCache.setOnEntryRemovedListener(this);
41
42 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
43 INIT_LOGD(" Maximum texture dimension is %d pixels", mMaxTextureSize);
44
45 mDebugEnabled = Properties::debugLevel & kDebugCaches;
46 }
47
~TextureCache()48 TextureCache::~TextureCache() {
49 this->clear();
50 }
51
52 ///////////////////////////////////////////////////////////////////////////////
53 // Size management
54 ///////////////////////////////////////////////////////////////////////////////
55
getSize()56 uint32_t TextureCache::getSize() {
57 return mSize;
58 }
59
getMaxSize()60 uint32_t TextureCache::getMaxSize() {
61 return mMaxSize;
62 }
63
64 ///////////////////////////////////////////////////////////////////////////////
65 // Callbacks
66 ///////////////////////////////////////////////////////////////////////////////
67
operator ()(uint32_t &,Texture * & texture)68 void TextureCache::operator()(uint32_t&, Texture*& texture) {
69 // This will be called already locked
70 if (texture) {
71 mSize -= texture->bitmapSize;
72 TEXTURE_LOGD("TextureCache::callback: name, removed size, mSize = %d, %d, %d",
73 texture->id, texture->bitmapSize, mSize);
74 if (mDebugEnabled) {
75 ALOGD("Texture deleted, size = %d", texture->bitmapSize);
76 }
77 texture->deleteTexture();
78 delete texture;
79 }
80 }
81
82 ///////////////////////////////////////////////////////////////////////////////
83 // Caching
84 ///////////////////////////////////////////////////////////////////////////////
85
resetMarkInUse(void * ownerToken)86 void TextureCache::resetMarkInUse(void* ownerToken) {
87 LruCache<uint32_t, Texture*>::Iterator iter(mCache);
88 while (iter.next()) {
89 if (iter.value()->isInUse == ownerToken) {
90 iter.value()->isInUse = nullptr;
91 }
92 }
93 }
94
canMakeTextureFromBitmap(Bitmap * bitmap)95 bool TextureCache::canMakeTextureFromBitmap(Bitmap* bitmap) {
96 if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) {
97 ALOGW("Bitmap too large to be uploaded into a texture (%dx%d, max=%dx%d)",
98 bitmap->width(), bitmap->height(), mMaxTextureSize, mMaxTextureSize);
99 return false;
100 }
101 return true;
102 }
103
createTexture(Bitmap * bitmap)104 Texture* TextureCache::createTexture(Bitmap* bitmap) {
105 Texture* texture = new Texture(Caches::getInstance());
106 texture->bitmapSize = bitmap->rowBytes() * bitmap->height();
107 texture->generation = bitmap->getGenerationID();
108 texture->upload(*bitmap);
109 return texture;
110 }
111
112 // Returns a prepared Texture* that either is already in the cache or can fit
113 // in the cache (and is thus added to the cache)
getCachedTexture(Bitmap * bitmap)114 Texture* TextureCache::getCachedTexture(Bitmap* bitmap) {
115 if (bitmap->isHardware()) {
116 auto textureIterator = mHardwareTextures.find(bitmap->getStableID());
117 if (textureIterator == mHardwareTextures.end()) {
118 Texture* texture = createTexture(bitmap);
119 mHardwareTextures.insert(std::make_pair(bitmap->getStableID(),
120 std::unique_ptr<Texture>(texture)));
121 if (mDebugEnabled) {
122 ALOGD("Texture created for hw bitmap size = %d", texture->bitmapSize);
123 }
124 return texture;
125 }
126 return textureIterator->second.get();
127 }
128
129 Texture* texture = mCache.get(bitmap->getStableID());
130
131 if (!texture) {
132 if (!canMakeTextureFromBitmap(bitmap)) {
133 return nullptr;
134 }
135
136 const uint32_t size = bitmap->rowBytes() * bitmap->height();
137 bool canCache = size < mMaxSize;
138 // Don't even try to cache a bitmap that's bigger than the cache
139 while (canCache && mSize + size > mMaxSize) {
140 Texture* oldest = mCache.peekOldestValue();
141 if (oldest && !oldest->isInUse) {
142 mCache.removeOldest();
143 } else {
144 canCache = false;
145 }
146 }
147
148 if (canCache) {
149 texture = createTexture(bitmap);
150 mSize += size;
151 TEXTURE_LOGD("TextureCache::get: create texture(%p): name, size, mSize = %d, %d, %d",
152 bitmap, texture->id, size, mSize);
153 if (mDebugEnabled) {
154 ALOGD("Texture created, size = %d", size);
155 }
156 mCache.put(bitmap->getStableID(), texture);
157 }
158 } else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) {
159 // Texture was in the cache but is dirty, re-upload
160 // TODO: Re-adjust the cache size if the bitmap's dimensions have changed
161 texture->upload(*bitmap);
162 texture->generation = bitmap->getGenerationID();
163 }
164
165 return texture;
166 }
167
prefetchAndMarkInUse(void * ownerToken,Bitmap * bitmap)168 bool TextureCache::prefetchAndMarkInUse(void* ownerToken, Bitmap* bitmap) {
169 Texture* texture = getCachedTexture(bitmap);
170 if (texture) {
171 texture->isInUse = ownerToken;
172 }
173 return texture;
174 }
175
prefetch(Bitmap * bitmap)176 bool TextureCache::prefetch(Bitmap* bitmap) {
177 return getCachedTexture(bitmap);
178 }
179
get(Bitmap * bitmap)180 Texture* TextureCache::get(Bitmap* bitmap) {
181 Texture* texture = getCachedTexture(bitmap);
182
183 if (!texture) {
184 if (!canMakeTextureFromBitmap(bitmap)) {
185 return nullptr;
186 }
187 texture = createTexture(bitmap);
188 texture->cleanup = true;
189 }
190
191 return texture;
192 }
193
destroyTexture(uint32_t pixelRefStableID)194 bool TextureCache::destroyTexture(uint32_t pixelRefStableID) {
195 auto hardwareIter = mHardwareTextures.find(pixelRefStableID);
196 if (hardwareIter != mHardwareTextures.end()) {
197 hardwareIter->second->deleteTexture();
198 mHardwareTextures.erase(hardwareIter);
199 return true;
200 }
201 return mCache.remove(pixelRefStableID);
202 }
203
clear()204 void TextureCache::clear() {
205 mCache.clear();
206 for(auto& iter: mHardwareTextures) {
207 iter.second->deleteTexture();
208 }
209 mHardwareTextures.clear();
210 TEXTURE_LOGD("TextureCache:clear(), mSize = %d", mSize);
211 }
212
flush()213 void TextureCache::flush() {
214 if (mFlushRate >= 1.0f || mCache.size() == 0) return;
215 if (mFlushRate <= 0.0f) {
216 clear();
217 return;
218 }
219
220 uint32_t targetSize = uint32_t(mSize * mFlushRate);
221 TEXTURE_LOGD("TextureCache::flush: target size: %d", targetSize);
222
223 while (mSize > targetSize) {
224 mCache.removeOldest();
225 }
226 }
227
228 }; // namespace uirenderer
229 }; // namespace android
230