TextureCache.cpp revision d98aa2de9ab18e09c2be1997f41212740f51f6e6
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 28namespace android { 29namespace uirenderer { 30 31/////////////////////////////////////////////////////////////////////////////// 32// Constructors/destructor 33/////////////////////////////////////////////////////////////////////////////// 34 35TextureCache::TextureCache(): 36 mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity), 37 mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)) { 38 char property[PROPERTY_VALUE_MAX]; 39 if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) { 40 LOGD(" Setting texture cache size to %sMB", property); 41 setMaxSize(MB(atof(property))); 42 } else { 43 LOGD(" Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE); 44 } 45 46 init(); 47} 48 49TextureCache::TextureCache(uint32_t maxByteSize): 50 mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity), 51 mSize(0), mMaxSize(maxByteSize) { 52 init(); 53} 54 55TextureCache::~TextureCache() { 56 Mutex::Autolock _l(mLock); 57 mCache.clear(); 58} 59 60void TextureCache::init() { 61 mCache.setOnEntryRemovedListener(this); 62 63 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); 64 LOGD(" Maximum texture dimension is %d pixels", mMaxTextureSize); 65} 66 67/////////////////////////////////////////////////////////////////////////////// 68// Size management 69/////////////////////////////////////////////////////////////////////////////// 70 71uint32_t TextureCache::getSize() { 72 Mutex::Autolock _l(mLock); 73 return mSize; 74} 75 76uint32_t TextureCache::getMaxSize() { 77 Mutex::Autolock _l(mLock); 78 return mMaxSize; 79} 80 81void TextureCache::setMaxSize(uint32_t maxSize) { 82 Mutex::Autolock _l(mLock); 83 mMaxSize = maxSize; 84 while (mSize > mMaxSize) { 85 mCache.removeOldest(); 86 } 87} 88 89/////////////////////////////////////////////////////////////////////////////// 90// Callbacks 91/////////////////////////////////////////////////////////////////////////////// 92 93void TextureCache::operator()(SkBitmap*& bitmap, Texture*& texture) { 94 // This will be called already locked 95 if (texture) { 96 mSize -= texture->bitmapSize; 97 TEXTURE_LOGD("TextureCache::callback: removed size, mSize = %d, %d", 98 texture->bitmapSize, mSize); 99 glDeleteTextures(1, &texture->id); 100 delete texture; 101 } 102} 103 104/////////////////////////////////////////////////////////////////////////////// 105// Caching 106/////////////////////////////////////////////////////////////////////////////// 107 108Texture* TextureCache::get(SkBitmap* bitmap) { 109 mLock.lock(); 110 Texture* texture = mCache.get(bitmap); 111 mLock.unlock(); 112 113 if (!texture) { 114 if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) { 115 LOGW("Bitmap too large to be uploaded into a texture"); 116 return NULL; 117 } 118 119 const uint32_t size = bitmap->rowBytes() * bitmap->height(); 120 // Don't even try to cache a bitmap that's bigger than the cache 121 if (size < mMaxSize) { 122 mLock.lock(); 123 while (mSize + size > mMaxSize) { 124 mCache.removeOldest(); 125 } 126 mLock.unlock(); 127 } 128 129 texture = new Texture; 130 texture->bitmapSize = size; 131 generateTexture(bitmap, texture, false); 132 133 if (size < mMaxSize) { 134 mLock.lock(); 135 mSize += size; 136 TEXTURE_LOGD("TextureCache::get: create texture(0x%p): size, mSize = %d, %d", 137 bitmap, size, mSize); 138 mCache.put(bitmap, texture); 139 mLock.unlock(); 140 } else { 141 texture->cleanup = true; 142 } 143 } else if (bitmap->getGenerationID() != texture->generation) { 144 generateTexture(bitmap, texture, true); 145 } 146 147 return texture; 148} 149 150void TextureCache::remove(SkBitmap* bitmap) { 151 Mutex::Autolock _l(mLock); 152 mCache.remove(bitmap); 153} 154 155void TextureCache::clear() { 156 Mutex::Autolock _l(mLock); 157 mCache.clear(); 158 TEXTURE_LOGD("TextureCache:clear(), miSize = %d", mSize); 159} 160 161void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate) { 162 SkAutoLockPixels alp(*bitmap); 163 164 if (!bitmap->readyToDraw()) { 165 LOGE("Cannot generate texture from bitmap"); 166 return; 167 } 168 169 const bool resize = !regenerate || bitmap->width() != int(texture->width) || 170 bitmap->height() != int(texture->height); 171 172 if (!regenerate) { 173 glGenTextures(1, &texture->id); 174 } 175 176 texture->generation = bitmap->getGenerationID(); 177 texture->width = bitmap->width(); 178 texture->height = bitmap->height(); 179 180 glBindTexture(GL_TEXTURE_2D, texture->id); 181 glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); 182 183 switch (bitmap->getConfig()) { 184 case SkBitmap::kA8_Config: 185 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 186 uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), texture->height, 187 GL_UNSIGNED_BYTE, bitmap->getPixels()); 188 texture->blend = true; 189 break; 190 case SkBitmap::kRGB_565_Config: 191 uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), texture->height, 192 GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels()); 193 texture->blend = false; 194 break; 195 case SkBitmap::kARGB_8888_Config: 196 uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height, 197 GL_UNSIGNED_BYTE, bitmap->getPixels()); 198 // Do this after calling getPixels() to make sure Skia's deferred 199 // decoding happened 200 texture->blend = !bitmap->isOpaque(); 201 break; 202 case SkBitmap::kIndex8_Config: 203 uploadPalettedTexture(resize, bitmap, texture->width, texture->height); 204 texture->blend = false; 205 break; 206 default: 207 LOGW("Unsupported bitmap config: %d", bitmap->getConfig()); 208 break; 209 } 210 211 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 212 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 213 214 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 215 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 216} 217 218void TextureCache::uploadPalettedTexture(bool resize, SkBitmap* bitmap, 219 uint32_t width, uint32_t height) { 220 SkBitmap rgbaBitmap; 221 rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); 222 rgbaBitmap.allocPixels(); 223 rgbaBitmap.eraseColor(0); 224 225 SkCanvas canvas(rgbaBitmap); 226 canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL); 227 228 uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), height, 229 GL_UNSIGNED_BYTE, rgbaBitmap.getPixels()); 230} 231 232void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height, 233 GLenum type, const GLvoid * data) { 234 if (resize) { 235 glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data); 236 } else { 237 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); 238 } 239} 240 241}; // namespace uirenderer 242}; // namespace android 243