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