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