Home | History | Annotate | Download | only in hwui
      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 "DeviceInfo.h"
     23 #include "Properties.h"
     24 #include "Texture.h"
     25 #include "TextureCache.h"
     26 #include "hwui/Bitmap.h"
     27 #include "utils/TraceUtils.h"
     28 
     29 namespace android {
     30 namespace uirenderer {
     31 
     32 ///////////////////////////////////////////////////////////////////////////////
     33 // Constructors/destructor
     34 ///////////////////////////////////////////////////////////////////////////////
     35 
     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 
     46 TextureCache::~TextureCache() {
     47     this->clear();
     48 }
     49 
     50 ///////////////////////////////////////////////////////////////////////////////
     51 // Size management
     52 ///////////////////////////////////////////////////////////////////////////////
     53 
     54 uint32_t TextureCache::getSize() {
     55     return mSize;
     56 }
     57 
     58 uint32_t TextureCache::getMaxSize() {
     59     return mMaxSize;
     60 }
     61 
     62 ///////////////////////////////////////////////////////////////////////////////
     63 // Callbacks
     64 ///////////////////////////////////////////////////////////////////////////////
     65 
     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", texture->id,
     71                      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 
     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 
     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)", bitmap->width(),
     96               bitmap->height(), mMaxTextureSize, mMaxTextureSize);
     97         return false;
     98     }
     99     return true;
    100 }
    101 
    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)
    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(
    118                     std::make_pair(bitmap->getStableID(), 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 
    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 
    174 bool TextureCache::prefetch(Bitmap* bitmap) {
    175     return getCachedTexture(bitmap);
    176 }
    177 
    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 
    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 
    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 
    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