• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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