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