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