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 #define LOG_TAG "OpenGLRenderer"
     18 
     19 #include <GLES2/gl2.h>
     20 
     21 #include <SkCanvas.h>
     22 
     23 #include <utils/threads.h>
     24 
     25 #include "TextureCache.h"
     26 #include "Properties.h"
     27 
     28 namespace android {
     29 namespace uirenderer {
     30 
     31 ///////////////////////////////////////////////////////////////////////////////
     32 // Constructors/destructor
     33 ///////////////////////////////////////////////////////////////////////////////
     34 
     35 TextureCache::TextureCache():
     36         mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
     37         mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)),
     38         mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE) {
     39     char property[PROPERTY_VALUE_MAX];
     40     if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) {
     41         INIT_LOGD("  Setting texture cache size to %sMB", property);
     42         setMaxSize(MB(atof(property)));
     43     } else {
     44         INIT_LOGD("  Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE);
     45     }
     46 
     47     if (property_get(PROPERTY_TEXTURE_CACHE_FLUSH_RATE, property, NULL) > 0) {
     48         float flushRate = atof(property);
     49         INIT_LOGD("  Setting texture cache flush rate to %.2f%%", flushRate * 100.0f);
     50         setFlushRate(flushRate);
     51     } else {
     52         INIT_LOGD("  Using default texture cache flush rate of %.2f%%",
     53                 DEFAULT_TEXTURE_CACHE_FLUSH_RATE * 100.0f);
     54     }
     55 
     56     init();
     57 }
     58 
     59 TextureCache::TextureCache(uint32_t maxByteSize):
     60         mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
     61         mSize(0), mMaxSize(maxByteSize) {
     62     init();
     63 }
     64 
     65 TextureCache::~TextureCache() {
     66     mCache.clear();
     67 }
     68 
     69 void TextureCache::init() {
     70     mCache.setOnEntryRemovedListener(this);
     71 
     72     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
     73     INIT_LOGD("    Maximum texture dimension is %d pixels", mMaxTextureSize);
     74 
     75     mDebugEnabled = readDebugLevel() & kDebugCaches;
     76 }
     77 
     78 ///////////////////////////////////////////////////////////////////////////////
     79 // Size management
     80 ///////////////////////////////////////////////////////////////////////////////
     81 
     82 uint32_t TextureCache::getSize() {
     83     return mSize;
     84 }
     85 
     86 uint32_t TextureCache::getMaxSize() {
     87     return mMaxSize;
     88 }
     89 
     90 void TextureCache::setMaxSize(uint32_t maxSize) {
     91     mMaxSize = maxSize;
     92     while (mSize > mMaxSize) {
     93         mCache.removeOldest();
     94     }
     95 }
     96 
     97 void TextureCache::setFlushRate(float flushRate) {
     98     mFlushRate = fmaxf(0.0f, fminf(1.0f, flushRate));
     99 }
    100 
    101 ///////////////////////////////////////////////////////////////////////////////
    102 // Callbacks
    103 ///////////////////////////////////////////////////////////////////////////////
    104 
    105 void TextureCache::operator()(SkBitmap*& bitmap, Texture*& texture) {
    106     // This will be called already locked
    107     if (texture) {
    108         mSize -= texture->bitmapSize;
    109         TEXTURE_LOGD("TextureCache::callback: name, removed size, mSize = %d, %d, %d",
    110                 texture->id, texture->bitmapSize, mSize);
    111         if (mDebugEnabled) {
    112             LOGD("Texture deleted, size = %d", texture->bitmapSize);
    113         }
    114         glDeleteTextures(1, &texture->id);
    115         delete texture;
    116     }
    117 }
    118 
    119 ///////////////////////////////////////////////////////////////////////////////
    120 // Caching
    121 ///////////////////////////////////////////////////////////////////////////////
    122 
    123 Texture* TextureCache::get(SkBitmap* bitmap) {
    124     Texture* texture = mCache.get(bitmap);
    125 
    126     if (!texture) {
    127         if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) {
    128             LOGW("Bitmap too large to be uploaded into a texture");
    129             return NULL;
    130         }
    131 
    132         const uint32_t size = bitmap->rowBytes() * bitmap->height();
    133         // Don't even try to cache a bitmap that's bigger than the cache
    134         if (size < mMaxSize) {
    135             while (mSize + size > mMaxSize) {
    136                 mCache.removeOldest();
    137             }
    138         }
    139 
    140         texture = new Texture;
    141         texture->bitmapSize = size;
    142         generateTexture(bitmap, texture, false);
    143 
    144         if (size < mMaxSize) {
    145             mSize += size;
    146             TEXTURE_LOGD("TextureCache::get: create texture(%p): name, size, mSize = %d, %d, %d",
    147                      bitmap, texture->id, size, mSize);
    148             if (mDebugEnabled) {
    149                 LOGD("Texture created, size = %d", size);
    150             }
    151             mCache.put(bitmap, texture);
    152         } else {
    153             texture->cleanup = true;
    154         }
    155     } else if (bitmap->getGenerationID() != texture->generation) {
    156         generateTexture(bitmap, texture, true);
    157     }
    158 
    159     return texture;
    160 }
    161 
    162 void TextureCache::remove(SkBitmap* bitmap) {
    163     mCache.remove(bitmap);
    164 }
    165 
    166 void TextureCache::removeDeferred(SkBitmap* bitmap) {
    167     Mutex::Autolock _l(mLock);
    168     mGarbage.push(bitmap);
    169 }
    170 
    171 void TextureCache::clearGarbage() {
    172     Mutex::Autolock _l(mLock);
    173     size_t count = mGarbage.size();
    174     for (size_t i = 0; i < count; i++) {
    175         mCache.remove(mGarbage.itemAt(i));
    176     }
    177     mGarbage.clear();
    178 }
    179 
    180 void TextureCache::clear() {
    181     mCache.clear();
    182     TEXTURE_LOGD("TextureCache:clear(), mSize = %d", mSize);
    183 }
    184 
    185 void TextureCache::flush() {
    186     if (mFlushRate >= 1.0f || mCache.size() == 0) return;
    187     if (mFlushRate <= 0.0f) {
    188         clear();
    189         return;
    190     }
    191 
    192     uint32_t targetSize = uint32_t(mSize * mFlushRate);
    193     TEXTURE_LOGD("TextureCache::flush: target size: %d", targetSize);
    194 
    195     while (mSize > targetSize) {
    196         mCache.removeOldest();
    197     }
    198 }
    199 
    200 void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate) {
    201     SkAutoLockPixels alp(*bitmap);
    202 
    203     if (!bitmap->readyToDraw()) {
    204         LOGE("Cannot generate texture from bitmap");
    205         return;
    206     }
    207 
    208     const bool resize = !regenerate || bitmap->width() != int(texture->width) ||
    209             bitmap->height() != int(texture->height);
    210 
    211     if (!regenerate) {
    212         glGenTextures(1, &texture->id);
    213     }
    214 
    215     texture->generation = bitmap->getGenerationID();
    216     texture->width = bitmap->width();
    217     texture->height = bitmap->height();
    218 
    219     glBindTexture(GL_TEXTURE_2D, texture->id);
    220     glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
    221 
    222     switch (bitmap->getConfig()) {
    223     case SkBitmap::kA8_Config:
    224         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    225         uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), texture->height,
    226                 GL_UNSIGNED_BYTE, bitmap->getPixels());
    227         texture->blend = true;
    228         break;
    229     case SkBitmap::kRGB_565_Config:
    230         uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), texture->height,
    231                 GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
    232         texture->blend = false;
    233         break;
    234     case SkBitmap::kARGB_8888_Config:
    235         uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height,
    236                 GL_UNSIGNED_BYTE, bitmap->getPixels());
    237         // Do this after calling getPixels() to make sure Skia's deferred
    238         // decoding happened
    239         texture->blend = !bitmap->isOpaque();
    240         break;
    241     case SkBitmap::kARGB_4444_Config:
    242     case SkBitmap::kIndex8_Config:
    243         uploadLoFiTexture(resize, bitmap, texture->width, texture->height);
    244         texture->blend = !bitmap->isOpaque();
    245         break;
    246     default:
    247         LOGW("Unsupported bitmap config: %d", bitmap->getConfig());
    248         break;
    249     }
    250 
    251     texture->setFilter(GL_LINEAR, GL_LINEAR);
    252     texture->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
    253 }
    254 
    255 void TextureCache::uploadLoFiTexture(bool resize, SkBitmap* bitmap,
    256         uint32_t width, uint32_t height) {
    257     SkBitmap rgbaBitmap;
    258     rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
    259     rgbaBitmap.allocPixels();
    260     rgbaBitmap.eraseColor(0);
    261     rgbaBitmap.setIsOpaque(bitmap->isOpaque());
    262 
    263     SkCanvas canvas(rgbaBitmap);
    264     canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL);
    265 
    266     uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), height,
    267             GL_UNSIGNED_BYTE, rgbaBitmap.getPixels());
    268 }
    269 
    270 void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height,
    271         GLenum type, const GLvoid * data) {
    272     if (resize) {
    273         glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
    274     } else {
    275         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
    276     }
    277 }
    278 
    279 }; // namespace uirenderer
    280 }; // namespace android
    281