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