TextureCache.cpp revision ec4a4b13eae2241d1613890c1c1c096bed891845
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
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    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
48TextureCache::~TextureCache() {
49    mCache.clear();
50}
51
52///////////////////////////////////////////////////////////////////////////////
53// Size management
54///////////////////////////////////////////////////////////////////////////////
55
56uint32_t TextureCache::getSize() {
57    return mSize;
58}
59
60uint32_t TextureCache::getMaxSize() {
61    return mMaxSize;
62}
63
64///////////////////////////////////////////////////////////////////////////////
65// Callbacks
66///////////////////////////////////////////////////////////////////////////////
67
68void 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
86void 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
95bool 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
104// Returns a prepared Texture* that either is already in the cache or can fit
105// in the cache (and is thus added to the cache)
106Texture* TextureCache::getCachedTexture(Bitmap* bitmap) {
107    Texture* texture = mCache.get(bitmap->getStableID());
108
109    if (!texture) {
110        if (!canMakeTextureFromBitmap(bitmap)) {
111            return nullptr;
112        }
113
114        const uint32_t size = bitmap->rowBytes() * bitmap->height();
115        bool canCache = size < mMaxSize;
116        // Don't even try to cache a bitmap that's bigger than the cache
117        while (canCache && mSize + size > mMaxSize) {
118            Texture* oldest = mCache.peekOldestValue();
119            if (oldest && !oldest->isInUse) {
120                mCache.removeOldest();
121            } else {
122                canCache = false;
123            }
124        }
125
126        if (canCache) {
127            texture = new Texture(Caches::getInstance());
128            texture->bitmapSize = size;
129            texture->generation = bitmap->getGenerationID();
130            SkBitmap skBitmap;
131            bitmap->getSkBitmap(&skBitmap);
132            texture->upload(skBitmap);
133
134            mSize += size;
135            TEXTURE_LOGD("TextureCache::get: create texture(%p): name, size, mSize = %d, %d, %d",
136                     bitmap, texture->id, size, mSize);
137            if (mDebugEnabled) {
138                ALOGD("Texture created, size = %d", size);
139            }
140            mCache.put(bitmap->getStableID(), texture);
141        }
142    } else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) {
143        // Texture was in the cache but is dirty, re-upload
144        // TODO: Re-adjust the cache size if the bitmap's dimensions have changed
145        SkBitmap skBitmap;
146        bitmap->getSkBitmap(&skBitmap);
147        texture->upload(skBitmap);
148        texture->generation = bitmap->getGenerationID();
149    }
150
151    return texture;
152}
153
154bool TextureCache::prefetchAndMarkInUse(void* ownerToken, Bitmap* bitmap) {
155    Texture* texture = getCachedTexture(bitmap);
156    if (texture) {
157        texture->isInUse = ownerToken;
158    }
159    return texture;
160}
161
162bool TextureCache::prefetch(Bitmap* bitmap) {
163    return getCachedTexture(bitmap);
164}
165
166Texture* TextureCache::get(Bitmap* bitmap) {
167    Texture* texture = getCachedTexture(bitmap);
168
169    if (!texture) {
170        if (!canMakeTextureFromBitmap(bitmap)) {
171            return nullptr;
172        }
173
174        const uint32_t size = bitmap->rowBytes() * bitmap->height();
175        texture = new Texture(Caches::getInstance());
176        texture->bitmapSize = size;
177        SkBitmap skBitmap;
178        bitmap->getSkBitmap(&skBitmap);
179        texture->upload(skBitmap);
180        texture->generation = bitmap->getGenerationID();
181        texture->cleanup = true;
182    }
183
184    return texture;
185}
186
187void TextureCache::releaseTexture(uint32_t pixelRefStableID) {
188    Mutex::Autolock _l(mLock);
189    mGarbage.push_back(pixelRefStableID);
190}
191
192void TextureCache::clearGarbage() {
193    Mutex::Autolock _l(mLock);
194    size_t count = mGarbage.size();
195    for (size_t i = 0; i < count; i++) {
196        uint32_t pixelRefId = mGarbage[i];
197        mCache.remove(pixelRefId);
198    }
199    mGarbage.clear();
200}
201
202void TextureCache::clear() {
203    mCache.clear();
204    TEXTURE_LOGD("TextureCache:clear(), mSize = %d", mSize);
205}
206
207void TextureCache::flush() {
208    if (mFlushRate >= 1.0f || mCache.size() == 0) return;
209    if (mFlushRate <= 0.0f) {
210        clear();
211        return;
212    }
213
214    uint32_t targetSize = uint32_t(mSize * mFlushRate);
215    TEXTURE_LOGD("TextureCache::flush: target size: %d", targetSize);
216
217    while (mSize > targetSize) {
218        mCache.removeOldest();
219    }
220}
221
222}; // namespace uirenderer
223}; // namespace android
224