TextureCache.cpp revision 7adaf3d1aa18c7e521f7154e545fe52d329763c3
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 glDeleteTextures(1, &texture->id); 98 delete texture; 99 } 100} 101 102/////////////////////////////////////////////////////////////////////////////// 103// Caching 104/////////////////////////////////////////////////////////////////////////////// 105 106Texture* TextureCache::get(SkBitmap* bitmap) { 107 mLock.lock(); 108 Texture* texture = mCache.get(bitmap); 109 mLock.unlock(); 110 111 if (!texture) { 112 if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) { 113 LOGW("Bitmap too large to be uploaded into a texture"); 114 return NULL; 115 } 116 117 const uint32_t size = bitmap->rowBytes() * bitmap->height(); 118 // Don't even try to cache a bitmap that's bigger than the cache 119 if (size < mMaxSize) { 120 mLock.lock(); 121 while (mSize + size > mMaxSize) { 122 mCache.removeOldest(); 123 } 124 mLock.unlock(); 125 } 126 127 texture = new Texture; 128 texture->bitmapSize = size; 129 generateTexture(bitmap, texture, false); 130 131 if (size < mMaxSize) { 132 mLock.lock(); 133 mSize += size; 134 mCache.put(bitmap, texture); 135 mLock.unlock(); 136 } else { 137 texture->cleanup = true; 138 } 139 } else if (bitmap->getGenerationID() != texture->generation) { 140 generateTexture(bitmap, texture, true); 141 } 142 143 return texture; 144} 145 146void TextureCache::remove(SkBitmap* bitmap) { 147 Mutex::Autolock _l(mLock); 148 mCache.remove(bitmap); 149} 150 151void TextureCache::clear() { 152 Mutex::Autolock _l(mLock); 153 mCache.clear(); 154} 155 156void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate) { 157 SkAutoLockPixels alp(*bitmap); 158 159 if (!bitmap->readyToDraw()) { 160 LOGE("Cannot generate texture from bitmap"); 161 return; 162 } 163 164 const bool resize = !regenerate || bitmap->width() != int(texture->width) || 165 bitmap->height() != int(texture->height); 166 167 if (!regenerate) { 168 glGenTextures(1, &texture->id); 169 } 170 171 texture->generation = bitmap->getGenerationID(); 172 texture->width = bitmap->width(); 173 texture->height = bitmap->height(); 174 175 glBindTexture(GL_TEXTURE_2D, texture->id); 176 glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); 177 178 switch (bitmap->getConfig()) { 179 case SkBitmap::kA8_Config: 180 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 181 uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), texture->height, 182 GL_UNSIGNED_BYTE, bitmap->getPixels()); 183 texture->blend = true; 184 break; 185 case SkBitmap::kRGB_565_Config: 186 uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), texture->height, 187 GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels()); 188 texture->blend = false; 189 break; 190 case SkBitmap::kARGB_8888_Config: 191 uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height, 192 GL_UNSIGNED_BYTE, bitmap->getPixels()); 193 // Do this after calling getPixels() to make sure Skia's deferred 194 // decoding happened 195 texture->blend = !bitmap->isOpaque(); 196 break; 197 case SkBitmap::kIndex8_Config: 198 uploadPalettedTexture(resize, bitmap, texture->width, texture->height); 199 texture->blend = false; 200 break; 201 default: 202 LOGW("Unsupported bitmap config: %d", bitmap->getConfig()); 203 break; 204 } 205 206 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 207 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 208 209 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 210 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 211} 212 213void TextureCache::uploadPalettedTexture(bool resize, SkBitmap* bitmap, 214 uint32_t width, uint32_t height) { 215 SkBitmap rgbaBitmap; 216 rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); 217 rgbaBitmap.allocPixels(); 218 rgbaBitmap.eraseColor(0); 219 220 SkCanvas canvas(rgbaBitmap); 221 canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL); 222 223 uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), height, 224 GL_UNSIGNED_BYTE, rgbaBitmap.getPixels()); 225} 226 227void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height, 228 GLenum type, const GLvoid * data) { 229 if (resize) { 230 glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data); 231 } else { 232 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); 233 } 234} 235 236}; // namespace uirenderer 237}; // namespace android 238