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             ALOGD("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             ALOGW("Bitmap too large to be uploaded into a texture (%dx%d, max=%dx%d)",
    129                     bitmap->width(), bitmap->height(), mMaxTextureSize, mMaxTextureSize);
    130             return NULL;
    131         }
    132 
    133         const uint32_t size = bitmap->rowBytes() * bitmap->height();
    134         // Don't even try to cache a bitmap that's bigger than the cache
    135         if (size < mMaxSize) {
    136             while (mSize + size > mMaxSize) {
    137                 mCache.removeOldest();
    138             }
    139         }
    140 
    141         texture = new Texture;
    142         texture->bitmapSize = size;
    143         generateTexture(bitmap, texture, false);
    144 
    145         if (size < mMaxSize) {
    146             mSize += size;
    147             TEXTURE_LOGD("TextureCache::get: create texture(%p): name, size, mSize = %d, %d, %d",
    148                      bitmap, texture->id, size, mSize);
    149             if (mDebugEnabled) {
    150                 ALOGD("Texture created, size = %d", size);
    151             }
    152             mCache.put(bitmap, texture);
    153         } else {
    154             texture->cleanup = true;
    155         }
    156     } else if (bitmap->getGenerationID() != texture->generation) {
    157         generateTexture(bitmap, texture, true);
    158     }
    159 
    160     return texture;
    161 }
    162 
    163 Texture* TextureCache::getTransient(SkBitmap* bitmap) {
    164     Texture* texture = new Texture;
    165     texture->bitmapSize = bitmap->rowBytes() * bitmap->height();
    166     texture->cleanup = true;
    167 
    168     generateTexture(bitmap, texture, false);
    169 
    170     return texture;
    171 }
    172 
    173 void TextureCache::remove(SkBitmap* bitmap) {
    174     mCache.remove(bitmap);
    175 }
    176 
    177 void TextureCache::removeDeferred(SkBitmap* bitmap) {
    178     Mutex::Autolock _l(mLock);
    179     mGarbage.push(bitmap);
    180 }
    181 
    182 void TextureCache::clearGarbage() {
    183     Mutex::Autolock _l(mLock);
    184     size_t count = mGarbage.size();
    185     for (size_t i = 0; i < count; i++) {
    186         mCache.remove(mGarbage.itemAt(i));
    187     }
    188     mGarbage.clear();
    189 }
    190 
    191 void TextureCache::clear() {
    192     mCache.clear();
    193     TEXTURE_LOGD("TextureCache:clear(), mSize = %d", mSize);
    194 }
    195 
    196 void TextureCache::flush() {
    197     if (mFlushRate >= 1.0f || mCache.size() == 0) return;
    198     if (mFlushRate <= 0.0f) {
    199         clear();
    200         return;
    201     }
    202 
    203     uint32_t targetSize = uint32_t(mSize * mFlushRate);
    204     TEXTURE_LOGD("TextureCache::flush: target size: %d", targetSize);
    205 
    206     while (mSize > targetSize) {
    207         mCache.removeOldest();
    208     }
    209 }
    210 
    211 void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate) {
    212     SkAutoLockPixels alp(*bitmap);
    213 
    214     if (!bitmap->readyToDraw()) {
    215         ALOGE("Cannot generate texture from bitmap");
    216         return;
    217     }
    218 
    219     const bool resize = !regenerate || bitmap->width() != int(texture->width) ||
    220             bitmap->height() != int(texture->height);
    221 
    222     if (!regenerate) {
    223         glGenTextures(1, &texture->id);
    224     }
    225 
    226     texture->generation = bitmap->getGenerationID();
    227     texture->width = bitmap->width();
    228     texture->height = bitmap->height();
    229 
    230     glBindTexture(GL_TEXTURE_2D, texture->id);
    231     if (!regenerate) {
    232         glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
    233     }
    234 
    235     switch (bitmap->getConfig()) {
    236     case SkBitmap::kA8_Config:
    237         if (!regenerate) {
    238             glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    239         }
    240         uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), texture->height,
    241                 GL_UNSIGNED_BYTE, bitmap->getPixels());
    242         texture->blend = true;
    243         break;
    244     case SkBitmap::kRGB_565_Config:
    245         uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), texture->height,
    246                 GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
    247         texture->blend = false;
    248         break;
    249     case SkBitmap::kARGB_8888_Config:
    250         uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height,
    251                 GL_UNSIGNED_BYTE, bitmap->getPixels());
    252         // Do this after calling getPixels() to make sure Skia's deferred
    253         // decoding happened
    254         texture->blend = !bitmap->isOpaque();
    255         break;
    256     case SkBitmap::kARGB_4444_Config:
    257     case SkBitmap::kIndex8_Config:
    258         uploadLoFiTexture(resize, bitmap, texture->width, texture->height);
    259         texture->blend = !bitmap->isOpaque();
    260         break;
    261     default:
    262         ALOGW("Unsupported bitmap config: %d", bitmap->getConfig());
    263         break;
    264     }
    265 
    266     if (!regenerate) {
    267         texture->setFilter(GL_NEAREST);
    268         texture->setWrap(GL_CLAMP_TO_EDGE);
    269     }
    270 }
    271 
    272 void TextureCache::uploadLoFiTexture(bool resize, SkBitmap* bitmap,
    273         uint32_t width, uint32_t height) {
    274     SkBitmap rgbaBitmap;
    275     rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
    276     rgbaBitmap.allocPixels();
    277     rgbaBitmap.eraseColor(0);
    278     rgbaBitmap.setIsOpaque(bitmap->isOpaque());
    279 
    280     SkCanvas canvas(rgbaBitmap);
    281     canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL);
    282 
    283     uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), height,
    284             GL_UNSIGNED_BYTE, rgbaBitmap.getPixels());
    285 }
    286 
    287 void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height,
    288         GLenum type, const GLvoid * data) {
    289     if (resize) {
    290         glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
    291     } else {
    292         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
    293     }
    294 }
    295 
    296 }; // namespace uirenderer
    297 }; // namespace android
    298