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