TextureCache.cpp revision 06e7fe5a70409d2050b2b3e1286500f5223099da
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#define ATRACE_TAG ATRACE_TAG_VIEW 19 20#include <GLES2/gl2.h> 21 22#include <SkCanvas.h> 23 24#include <utils/Mutex.h> 25 26#include "Caches.h" 27#include "Texture.h" 28#include "TextureCache.h" 29#include "Properties.h" 30#include "utils/TraceUtils.h" 31 32namespace android { 33namespace uirenderer { 34 35/////////////////////////////////////////////////////////////////////////////// 36// Constructors/destructor 37/////////////////////////////////////////////////////////////////////////////// 38 39TextureCache::TextureCache(): 40 mCache(LruCache<const SkPixelRef*, Texture*>::kUnlimitedCapacity), 41 mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)), 42 mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE) { 43 char property[PROPERTY_VALUE_MAX]; 44 if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) { 45 INIT_LOGD(" Setting texture cache size to %sMB", property); 46 setMaxSize(MB(atof(property))); 47 } else { 48 INIT_LOGD(" Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE); 49 } 50 51 if (property_get(PROPERTY_TEXTURE_CACHE_FLUSH_RATE, property, NULL) > 0) { 52 float flushRate = atof(property); 53 INIT_LOGD(" Setting texture cache flush rate to %.2f%%", flushRate * 100.0f); 54 setFlushRate(flushRate); 55 } else { 56 INIT_LOGD(" Using default texture cache flush rate of %.2f%%", 57 DEFAULT_TEXTURE_CACHE_FLUSH_RATE * 100.0f); 58 } 59 60 init(); 61} 62 63TextureCache::TextureCache(uint32_t maxByteSize): 64 mCache(LruCache<const SkPixelRef*, Texture*>::kUnlimitedCapacity), 65 mSize(0), mMaxSize(maxByteSize) { 66 init(); 67} 68 69TextureCache::~TextureCache() { 70 mCache.clear(); 71} 72 73void TextureCache::init() { 74 mCache.setOnEntryRemovedListener(this); 75 76 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); 77 INIT_LOGD(" Maximum texture dimension is %d pixels", mMaxTextureSize); 78 79 mDebugEnabled = readDebugLevel() & kDebugCaches; 80} 81 82/////////////////////////////////////////////////////////////////////////////// 83// Size management 84/////////////////////////////////////////////////////////////////////////////// 85 86uint32_t TextureCache::getSize() { 87 return mSize; 88} 89 90uint32_t TextureCache::getMaxSize() { 91 return mMaxSize; 92} 93 94void TextureCache::setMaxSize(uint32_t maxSize) { 95 mMaxSize = maxSize; 96 while (mSize > mMaxSize) { 97 mCache.removeOldest(); 98 } 99} 100 101void TextureCache::setFlushRate(float flushRate) { 102 mFlushRate = fmaxf(0.0f, fminf(1.0f, flushRate)); 103} 104 105/////////////////////////////////////////////////////////////////////////////// 106// Callbacks 107/////////////////////////////////////////////////////////////////////////////// 108 109void TextureCache::operator()(const SkPixelRef*&, Texture*& texture) { 110 // This will be called already locked 111 if (texture) { 112 mSize -= texture->bitmapSize; 113 TEXTURE_LOGD("TextureCache::callback: name, removed size, mSize = %d, %d, %d", 114 texture->id, texture->bitmapSize, mSize); 115 if (mDebugEnabled) { 116 ALOGD("Texture deleted, size = %d", texture->bitmapSize); 117 } 118 texture->deleteTexture(); 119 delete texture; 120 } 121} 122 123/////////////////////////////////////////////////////////////////////////////// 124// Caching 125/////////////////////////////////////////////////////////////////////////////// 126 127void TextureCache::resetMarkInUse() { 128 LruCache<const SkPixelRef*, Texture*>::Iterator iter(mCache); 129 while (iter.next()) { 130 iter.value()->isInUse = false; 131 } 132} 133 134bool TextureCache::canMakeTextureFromBitmap(const SkBitmap* bitmap) { 135 if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) { 136 ALOGW("Bitmap too large to be uploaded into a texture (%dx%d, max=%dx%d)", 137 bitmap->width(), bitmap->height(), mMaxTextureSize, mMaxTextureSize); 138 return false; 139 } 140 return true; 141} 142 143// Returns a prepared Texture* that either is already in the cache or can fit 144// in the cache (and is thus added to the cache) 145Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap) { 146 Texture* texture = mCache.get(bitmap->pixelRef()); 147 148 if (!texture) { 149 if (!canMakeTextureFromBitmap(bitmap)) { 150 return NULL; 151 } 152 153 const uint32_t size = bitmap->rowBytes() * bitmap->height(); 154 bool canCache = size < mMaxSize; 155 // Don't even try to cache a bitmap that's bigger than the cache 156 while (canCache && mSize + size > mMaxSize) { 157 Texture* oldest = mCache.peekOldestValue(); 158 if (oldest && !oldest->isInUse) { 159 mCache.removeOldest(); 160 } else { 161 canCache = false; 162 } 163 } 164 165 if (canCache) { 166 texture = new Texture(); 167 texture->bitmapSize = size; 168 generateTexture(bitmap, texture, false); 169 170 mSize += size; 171 TEXTURE_LOGD("TextureCache::get: create texture(%p): name, size, mSize = %d, %d, %d", 172 bitmap, texture->id, size, mSize); 173 if (mDebugEnabled) { 174 ALOGD("Texture created, size = %d", size); 175 } 176 mCache.put(bitmap->pixelRef(), texture); 177 } 178 } else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) { 179 // Texture was in the cache but is dirty, re-upload 180 // TODO: Re-adjust the cache size if the bitmap's dimensions have changed 181 generateTexture(bitmap, texture, true); 182 } 183 184 return texture; 185} 186 187bool TextureCache::prefetchAndMarkInUse(const SkBitmap* bitmap) { 188 Texture* texture = getCachedTexture(bitmap); 189 if (texture) { 190 texture->isInUse = true; 191 } 192 return texture; 193} 194 195Texture* TextureCache::get(const SkBitmap* bitmap) { 196 Texture* texture = getCachedTexture(bitmap); 197 198 if (!texture) { 199 if (!canMakeTextureFromBitmap(bitmap)) { 200 return NULL; 201 } 202 203 const uint32_t size = bitmap->rowBytes() * bitmap->height(); 204 texture = new Texture(); 205 texture->bitmapSize = size; 206 generateTexture(bitmap, texture, false); 207 texture->cleanup = true; 208 } 209 210 return texture; 211} 212 213Texture* TextureCache::getTransient(const SkBitmap* bitmap) { 214 Texture* texture = new Texture(); 215 texture->bitmapSize = bitmap->rowBytes() * bitmap->height(); 216 texture->cleanup = true; 217 218 generateTexture(bitmap, texture, false); 219 220 return texture; 221} 222 223void TextureCache::remove(const SkBitmap* bitmap) { 224 mCache.remove(bitmap->pixelRef()); 225} 226 227void TextureCache::removeDeferred(const SkBitmap* bitmap) { 228 Mutex::Autolock _l(mLock); 229 mGarbage.push(bitmap); 230} 231 232void TextureCache::clearGarbage() { 233 Mutex::Autolock _l(mLock); 234 size_t count = mGarbage.size(); 235 for (size_t i = 0; i < count; i++) { 236 const SkBitmap* bitmap = mGarbage.itemAt(i); 237 mCache.remove(bitmap->pixelRef()); 238 delete bitmap; 239 } 240 mGarbage.clear(); 241} 242 243void TextureCache::clear() { 244 mCache.clear(); 245 TEXTURE_LOGD("TextureCache:clear(), mSize = %d", mSize); 246} 247 248void TextureCache::flush() { 249 if (mFlushRate >= 1.0f || mCache.size() == 0) return; 250 if (mFlushRate <= 0.0f) { 251 clear(); 252 return; 253 } 254 255 uint32_t targetSize = uint32_t(mSize * mFlushRate); 256 TEXTURE_LOGD("TextureCache::flush: target size: %d", targetSize); 257 258 while (mSize > targetSize) { 259 mCache.removeOldest(); 260 } 261} 262 263void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate) { 264 SkAutoLockPixels alp(*bitmap); 265 266 if (!bitmap->readyToDraw()) { 267 ALOGE("Cannot generate texture from bitmap"); 268 return; 269 } 270 271 ATRACE_FORMAT("Upload %ux%u Texture", bitmap->width(), bitmap->height()); 272 273 // We could also enable mipmapping if both bitmap dimensions are powers 274 // of 2 but we'd have to deal with size changes. Let's keep this simple 275 const bool canMipMap = Extensions::getInstance().hasNPot(); 276 277 // If the texture had mipmap enabled but not anymore, 278 // force a glTexImage2D to discard the mipmap levels 279 const bool resize = !regenerate || bitmap->width() != int(texture->width) || 280 bitmap->height() != int(texture->height) || 281 (regenerate && canMipMap && texture->mipMap && !bitmap->hasHardwareMipMap()); 282 283 if (!regenerate) { 284 glGenTextures(1, &texture->id); 285 } 286 287 texture->generation = bitmap->getGenerationID(); 288 texture->width = bitmap->width(); 289 texture->height = bitmap->height(); 290 291 Caches::getInstance().bindTexture(texture->id); 292 293 switch (bitmap->colorType()) { 294 case kAlpha_8_SkColorType: 295 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 296 uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(), 297 texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels()); 298 texture->blend = true; 299 break; 300 case kRGB_565_SkColorType: 301 glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); 302 uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(), 303 texture->width, texture->height, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels()); 304 texture->blend = false; 305 break; 306 case kN32_SkColorType: 307 glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); 308 uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(), 309 texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels()); 310 // Do this after calling getPixels() to make sure Skia's deferred 311 // decoding happened 312 texture->blend = !bitmap->isOpaque(); 313 break; 314 case kARGB_4444_SkColorType: 315 case kIndex_8_SkColorType: 316 glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); 317 uploadLoFiTexture(resize, bitmap, texture->width, texture->height); 318 texture->blend = !bitmap->isOpaque(); 319 break; 320 default: 321 ALOGW("Unsupported bitmap colorType: %d", bitmap->colorType()); 322 break; 323 } 324 325 if (canMipMap) { 326 texture->mipMap = bitmap->hasHardwareMipMap(); 327 if (texture->mipMap) { 328 glGenerateMipmap(GL_TEXTURE_2D); 329 } 330 } 331 332 if (!regenerate) { 333 texture->setFilter(GL_NEAREST); 334 texture->setWrap(GL_CLAMP_TO_EDGE); 335 } 336} 337 338void TextureCache::uploadLoFiTexture(bool resize, const SkBitmap* bitmap, 339 uint32_t width, uint32_t height) { 340 SkBitmap rgbaBitmap; 341 rgbaBitmap.allocPixels(SkImageInfo::MakeN32(width, height, bitmap->alphaType())); 342 rgbaBitmap.eraseColor(0); 343 344 SkCanvas canvas(rgbaBitmap); 345 canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL); 346 347 uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), rgbaBitmap.bytesPerPixel(), 348 width, height, GL_UNSIGNED_BYTE, rgbaBitmap.getPixels()); 349} 350 351void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp, 352 GLsizei width, GLsizei height, GLenum type, const GLvoid * data) { 353 const bool useStride = stride != width && Extensions::getInstance().hasUnpackRowLength(); 354 if ((stride == width) || useStride) { 355 if (useStride) { 356 glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); 357 } 358 359 if (resize) { 360 glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data); 361 } else { 362 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); 363 } 364 365 if (useStride) { 366 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 367 } 368 } else { 369 // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer 370 // if the stride doesn't match the width 371 372 GLvoid * temp = (GLvoid *) malloc(width * height * bpp); 373 if (!temp) return; 374 375 uint8_t * pDst = (uint8_t *)temp; 376 uint8_t * pSrc = (uint8_t *)data; 377 for (GLsizei i = 0; i < height; i++) { 378 memcpy(pDst, pSrc, width * bpp); 379 pDst += width * bpp; 380 pSrc += stride * bpp; 381 } 382 383 if (resize) { 384 glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp); 385 } else { 386 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp); 387 } 388 389 free(temp); 390 } 391} 392 393}; // namespace uirenderer 394}; // namespace android 395