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