1 /* 2 * Copyright 2010, The Android Open Source Project 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #define LOG_TAG "TilesManager" 27 #define LOG_NDEBUG 1 28 29 #include "config.h" 30 #include "TilesManager.h" 31 32 #if USE(ACCELERATED_COMPOSITING) 33 34 #include "AndroidLog.h" 35 #include "GLWebViewState.h" 36 #include "SkCanvas.h" 37 #include "SkDevice.h" 38 #include "SkPaint.h" 39 #include "Tile.h" 40 #include "TileTexture.h" 41 #include "TransferQueue.h" 42 43 #include <android/native_window.h> 44 #include <cutils/atomic.h> 45 #include <gui/SurfaceTexture.h> 46 #include <gui/SurfaceTextureClient.h> 47 #include <wtf/CurrentTime.h> 48 49 // Important: We need at least twice as many textures as is needed to cover 50 // one viewport, otherwise the allocation may stall. 51 // We need n textures for one TiledPage, and another n textures for the 52 // second page used when scaling. 53 // In our case, we use 256*256 textures. Both base and layers can use up to 54 // MAX_TEXTURE_ALLOCATION textures, which is 224MB GPU memory in total. 55 // For low end graphics systems, we cut this upper limit to half. 56 // We've found the viewport dependent value m_currentTextureCount is a reasonable 57 // number to cap the layer tile texturs, it worked on both phones and tablets. 58 // TODO: after merge the pool of base tiles and layer tiles, we should revisit 59 // the logic of allocation management. 60 #define MAX_TEXTURE_ALLOCATION ((6+TILE_PREFETCH_DISTANCE*2)*(5+TILE_PREFETCH_DISTANCE*2)*4) 61 #define TILE_WIDTH 256 62 #define TILE_HEIGHT 256 63 64 #define BYTES_PER_PIXEL 4 // 8888 config 65 66 #define LAYER_TEXTURES_DESTROY_TIMEOUT 60 // If we do not need layers for 60 seconds, free the textures 67 68 namespace WebCore { 69 70 int TilesManager::getMaxTextureAllocation() 71 { 72 if (m_maxTextureAllocation == -1) { 73 GLint glMaxTextureSize = 0; 74 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glMaxTextureSize); 75 GLUtils::checkGlError("TilesManager::getMaxTextureAllocation"); 76 // Half of glMaxTextureSize can be used for base, the other half for layers. 77 m_maxTextureAllocation = std::min(MAX_TEXTURE_ALLOCATION, glMaxTextureSize / 2); 78 if (!m_highEndGfx) 79 m_maxTextureAllocation = m_maxTextureAllocation / 2; 80 } 81 return m_maxTextureAllocation; 82 } 83 84 TilesManager::TilesManager() 85 : m_layerTexturesRemain(true) 86 , m_highEndGfx(false) 87 , m_currentTextureCount(0) 88 , m_currentLayerTextureCount(0) 89 , m_maxTextureAllocation(-1) 90 , m_generatorReady(false) 91 , m_showVisualIndicator(false) 92 , m_invertedScreen(false) 93 , m_useMinimalMemory(true) 94 , m_useDoubleBuffering(true) 95 , m_contentUpdates(0) 96 , m_webkitContentUpdates(0) 97 , m_queue(0) 98 , m_drawGLCount(1) 99 , m_lastTimeLayersUsed(0) 100 , m_hasLayerTextures(false) 101 , m_eglContext(EGL_NO_CONTEXT) 102 { 103 ALOGV("TilesManager ctor"); 104 m_textures.reserveCapacity(MAX_TEXTURE_ALLOCATION); 105 m_availableTextures.reserveCapacity(MAX_TEXTURE_ALLOCATION); 106 m_tilesTextures.reserveCapacity(MAX_TEXTURE_ALLOCATION); 107 m_availableTilesTextures.reserveCapacity(MAX_TEXTURE_ALLOCATION); 108 m_pixmapsGenerationThread = new TexturesGenerator(this); 109 m_pixmapsGenerationThread->run("TexturesGenerator"); 110 } 111 112 void TilesManager::allocateTextures() 113 { 114 int nbTexturesToAllocate = m_currentTextureCount - m_textures.size(); 115 ALOGV("%d tiles to allocate (%d textures planned)", nbTexturesToAllocate, m_currentTextureCount); 116 int nbTexturesAllocated = 0; 117 for (int i = 0; i < nbTexturesToAllocate; i++) { 118 TileTexture* texture = new TileTexture( 119 tileWidth(), tileHeight()); 120 // the atomic load ensures that the texture has been fully initialized 121 // before we pass a pointer for other threads to operate on 122 TileTexture* loadedTexture = 123 reinterpret_cast<TileTexture*>( 124 android_atomic_acquire_load(reinterpret_cast<int32_t*>(&texture))); 125 m_textures.append(loadedTexture); 126 nbTexturesAllocated++; 127 } 128 129 int nbLayersTexturesToAllocate = m_currentLayerTextureCount - m_tilesTextures.size(); 130 ALOGV("%d layers tiles to allocate (%d textures planned)", 131 nbLayersTexturesToAllocate, m_currentLayerTextureCount); 132 int nbLayersTexturesAllocated = 0; 133 for (int i = 0; i < nbLayersTexturesToAllocate; i++) { 134 TileTexture* texture = new TileTexture( 135 tileWidth(), tileHeight()); 136 // the atomic load ensures that the texture has been fully initialized 137 // before we pass a pointer for other threads to operate on 138 TileTexture* loadedTexture = 139 reinterpret_cast<TileTexture*>( 140 android_atomic_acquire_load(reinterpret_cast<int32_t*>(&texture))); 141 m_tilesTextures.append(loadedTexture); 142 nbLayersTexturesAllocated++; 143 } 144 ALOGV("allocated %d textures for base (total: %d, %d Mb), %d textures for layers (total: %d, %d Mb)", 145 nbTexturesAllocated, m_textures.size(), 146 m_textures.size() * TILE_WIDTH * TILE_HEIGHT * 4 / 1024 / 1024, 147 nbLayersTexturesAllocated, m_tilesTextures.size(), 148 m_tilesTextures.size() * tileWidth() * tileHeight() * 4 / 1024 / 1024); 149 } 150 151 void TilesManager::discardTextures(bool allTextures, bool glTextures) 152 { 153 const unsigned int max = m_textures.size(); 154 155 unsigned long long sparedDrawCount = ~0; // by default, spare no textures 156 if (!allTextures) { 157 // if we're not deallocating all textures, spare those with max drawcount 158 sparedDrawCount = 0; 159 for (unsigned int i = 0; i < max; i++) { 160 TextureOwner* owner = m_textures[i]->owner(); 161 if (owner) 162 sparedDrawCount = std::max(sparedDrawCount, owner->drawCount()); 163 } 164 } 165 discardTexturesVector(sparedDrawCount, m_textures, glTextures); 166 discardTexturesVector(sparedDrawCount, m_tilesTextures, glTextures); 167 } 168 169 void TilesManager::markAllGLTexturesZero() 170 { 171 for (unsigned int i = 0; i < m_textures.size(); i++) 172 m_textures[i]->m_ownTextureId = 0; 173 for (unsigned int i = 0; i < m_tilesTextures.size(); i++) 174 m_tilesTextures[i]->m_ownTextureId = 0; 175 } 176 177 void TilesManager::discardTexturesVector(unsigned long long sparedDrawCount, 178 WTF::Vector<TileTexture*>& textures, 179 bool deallocateGLTextures) 180 { 181 const unsigned int max = textures.size(); 182 int dealloc = 0; 183 WTF::Vector<int> discardedIndex; 184 for (unsigned int i = 0; i < max; i++) { 185 TextureOwner* owner = textures[i]->owner(); 186 if (!owner || owner->drawCount() < sparedDrawCount) { 187 if (deallocateGLTextures) { 188 // deallocate textures' gl memory 189 textures[i]->discardGLTexture(); 190 discardedIndex.append(i); 191 } else if (owner) { 192 // simply detach textures from owner 193 static_cast<Tile*>(owner)->discardTextures(); 194 } 195 dealloc++; 196 } 197 } 198 199 bool base = textures == m_textures; 200 // Clean up the vector of TileTextures and reset the max texture count. 201 if (discardedIndex.size()) { 202 android::Mutex::Autolock lock(m_texturesLock); 203 for (int i = discardedIndex.size() - 1; i >= 0; i--) 204 textures.remove(discardedIndex[i]); 205 206 int remainedTextureNumber = textures.size(); 207 int* countPtr = base ? &m_currentTextureCount : &m_currentLayerTextureCount; 208 if (remainedTextureNumber < *countPtr) { 209 ALOGV("reset currentTextureCount for %s tiles from %d to %d", 210 base ? "base" : "layer", *countPtr, remainedTextureNumber); 211 *countPtr = remainedTextureNumber; 212 } 213 214 } 215 216 ALOGV("Discarded %d %s textures (out of %d %s tiles)", 217 dealloc, (deallocateGLTextures ? "gl" : ""), 218 max, base ? "base" : "layer"); 219 } 220 221 void TilesManager::gatherTexturesNumbers(int* nbTextures, int* nbAllocatedTextures, 222 int* nbLayerTextures, int* nbAllocatedLayerTextures) 223 { 224 *nbTextures = m_textures.size(); 225 for (unsigned int i = 0; i < m_textures.size(); i++) { 226 TileTexture* texture = m_textures[i]; 227 if (texture->m_ownTextureId) 228 *nbAllocatedTextures += 1; 229 } 230 *nbLayerTextures = m_tilesTextures.size(); 231 for (unsigned int i = 0; i < m_tilesTextures.size(); i++) { 232 TileTexture* texture = m_tilesTextures[i]; 233 if (texture->m_ownTextureId) 234 *nbAllocatedLayerTextures += 1; 235 } 236 } 237 238 void TilesManager::dirtyTexturesVector(WTF::Vector<TileTexture*>& textures) 239 { 240 for (unsigned int i = 0; i < textures.size(); i++) { 241 Tile* currentOwner = static_cast<Tile*>(textures[i]->owner()); 242 if (currentOwner) 243 currentOwner->markAsDirty(); 244 } 245 } 246 247 void TilesManager::dirtyAllTiles() 248 { 249 dirtyTexturesVector(m_textures); 250 dirtyTexturesVector(m_tilesTextures); 251 } 252 253 void TilesManager::printTextures() 254 { 255 #ifdef DEBUG 256 ALOGV("++++++"); 257 for (unsigned int i = 0; i < m_textures.size(); i++) { 258 TileTexture* texture = m_textures[i]; 259 Tile* o = 0; 260 if (texture->owner()) 261 o = (Tile*) texture->owner(); 262 int x = -1; 263 int y = -1; 264 if (o) { 265 x = o->x(); 266 y = o->y(); 267 } 268 ALOGV("[%d] texture %x owner: %x (%d, %d) scale: %.2f", 269 i, texture, o, x, y, o ? o->scale() : 0); 270 } 271 ALOGV("------"); 272 #endif // DEBUG 273 } 274 275 void TilesManager::gatherTextures() 276 { 277 android::Mutex::Autolock lock(m_texturesLock); 278 m_availableTextures = m_textures; 279 m_availableTilesTextures = m_tilesTextures; 280 m_layerTexturesRemain = true; 281 } 282 283 TileTexture* TilesManager::getAvailableTexture(Tile* owner) 284 { 285 android::Mutex::Autolock lock(m_texturesLock); 286 287 WTF::Vector<TileTexture*>* availableTexturePool; 288 if (owner->isLayerTile()) 289 availableTexturePool = &m_availableTilesTextures; 290 else 291 availableTexturePool = &m_availableTextures; 292 293 // Sanity check that the tile does not already own a texture 294 if (owner->backTexture() && owner->backTexture()->owner() == owner) { 295 int removeIndex = availableTexturePool->find(owner->backTexture()); 296 297 // TODO: investigate why texture isn't found 298 if (removeIndex >= 0) 299 availableTexturePool->remove(removeIndex); 300 return owner->backTexture(); 301 } 302 303 // The heuristic for selecting a texture is as follows: 304 // 1. Skip textures currently being painted, they can't be painted while 305 // busy anyway 306 // 2. If a tile isn't owned, break with that one 307 // 3. Don't let tiles acquire their front textures 308 // 4. Otherwise, use the least recently prepared tile, but ignoring tiles 309 // drawn in the last frame to avoid flickering 310 311 TileTexture* farthestTexture = 0; 312 unsigned long long oldestDrawCount = getDrawGLCount() - 1; 313 const unsigned int max = availableTexturePool->size(); 314 for (unsigned int i = 0; i < max; i++) { 315 TileTexture* texture = (*availableTexturePool)[i]; 316 Tile* currentOwner = static_cast<Tile*>(texture->owner()); 317 if (!currentOwner) { 318 // unused texture! take it! 319 farthestTexture = texture; 320 break; 321 } 322 323 if (currentOwner == owner) { 324 // Don't let a tile acquire its own front texture, as the 325 // acquisition logic doesn't handle that 326 continue; 327 } 328 329 unsigned long long textureDrawCount = currentOwner->drawCount(); 330 if (oldestDrawCount > textureDrawCount) { 331 farthestTexture = texture; 332 oldestDrawCount = textureDrawCount; 333 } 334 } 335 336 if (farthestTexture) { 337 Tile* previousOwner = static_cast<Tile*>(farthestTexture->owner()); 338 if (farthestTexture->acquire(owner)) { 339 if (previousOwner) { 340 previousOwner->removeTexture(farthestTexture); 341 342 ALOGV("%s texture %p stolen from tile %d, %d for %d, %d, drawCount was %llu (now %llu)", 343 owner->isLayerTile() ? "LAYER" : "BASE", 344 farthestTexture, previousOwner->x(), previousOwner->y(), 345 owner->x(), owner->y(), 346 oldestDrawCount, getDrawGLCount()); 347 } 348 349 availableTexturePool->remove(availableTexturePool->find(farthestTexture)); 350 return farthestTexture; 351 } 352 } else { 353 if (owner->isLayerTile()) { 354 // couldn't find a tile for a layer, layers shouldn't request redraw 355 // TODO: once we do layer prefetching, don't set this for those 356 // tiles 357 m_layerTexturesRemain = false; 358 } 359 } 360 361 ALOGV("Couldn't find an available texture for %s tile %x (%d, %d) out of %d available", 362 owner->isLayerTile() ? "LAYER" : "BASE", 363 owner, owner->x(), owner->y(), max); 364 #ifdef DEBUG 365 printTextures(); 366 #endif // DEBUG 367 return 0; 368 } 369 370 void TilesManager::setHighEndGfx(bool highEnd) 371 { 372 m_highEndGfx = highEnd; 373 } 374 375 bool TilesManager::highEndGfx() 376 { 377 return m_highEndGfx; 378 } 379 380 int TilesManager::currentTextureCount() 381 { 382 android::Mutex::Autolock lock(m_texturesLock); 383 return m_currentTextureCount; 384 } 385 386 int TilesManager::currentLayerTextureCount() 387 { 388 android::Mutex::Autolock lock(m_texturesLock); 389 return m_currentLayerTextureCount; 390 } 391 392 void TilesManager::setCurrentTextureCount(int newTextureCount) 393 { 394 int maxTextureAllocation = getMaxTextureAllocation(); 395 ALOGV("setCurrentTextureCount: %d (current: %d, max:%d)", 396 newTextureCount, m_currentTextureCount, maxTextureAllocation); 397 if (m_currentTextureCount == maxTextureAllocation || 398 newTextureCount <= m_currentTextureCount) 399 return; 400 401 android::Mutex::Autolock lock(m_texturesLock); 402 m_currentTextureCount = std::min(newTextureCount, maxTextureAllocation); 403 404 allocateTextures(); 405 } 406 407 void TilesManager::setCurrentLayerTextureCount(int newTextureCount) 408 { 409 int maxTextureAllocation = getMaxTextureAllocation(); 410 ALOGV("setCurrentLayerTextureCount: %d (current: %d, max:%d)", 411 newTextureCount, m_currentLayerTextureCount, maxTextureAllocation); 412 if (!newTextureCount && m_hasLayerTextures) { 413 double secondsSinceLayersUsed = WTF::currentTime() - m_lastTimeLayersUsed; 414 if (secondsSinceLayersUsed > LAYER_TEXTURES_DESTROY_TIMEOUT) { 415 unsigned long long sparedDrawCount = ~0; // by default, spare no textures 416 bool deleteGLTextures = true; 417 discardTexturesVector(sparedDrawCount, m_tilesTextures, deleteGLTextures); 418 m_hasLayerTextures = false; 419 } 420 return; 421 } 422 m_lastTimeLayersUsed = WTF::currentTime(); 423 if (m_currentLayerTextureCount == maxTextureAllocation || 424 newTextureCount <= m_currentLayerTextureCount) 425 return; 426 427 android::Mutex::Autolock lock(m_texturesLock); 428 m_currentLayerTextureCount = std::min(newTextureCount, maxTextureAllocation); 429 430 allocateTextures(); 431 m_hasLayerTextures = true; 432 } 433 434 TransferQueue* TilesManager::transferQueue() 435 { 436 // m_queue will be created on the UI thread, although it may 437 // be accessed from the TexturesGenerator. However, that can only happen after 438 // a previous transferQueue() call due to a prepare. 439 if (!m_queue) 440 m_queue = new TransferQueue(m_useMinimalMemory); 441 return m_queue; 442 } 443 444 // When GL context changed or we get a low memory signal, we want to cleanup all 445 // the GPU memory webview is using. 446 // The recreation will be on the next incoming draw call at the drawGL of 447 // GLWebViewState or the VideoLayerAndroid 448 void TilesManager::cleanupGLResources() 449 { 450 transferQueue()->cleanupGLResourcesAndQueue(); 451 shader()->cleanupGLResources(); 452 videoLayerManager()->cleanupGLResources(); 453 m_eglContext = EGL_NO_CONTEXT; 454 GLUtils::checkGlError("TilesManager::cleanupGLResources"); 455 } 456 457 void TilesManager::updateTilesIfContextVerified() 458 { 459 EGLContext ctx = eglGetCurrentContext(); 460 GLUtils::checkEglError("contextChanged"); 461 if (ctx != m_eglContext) { 462 if (m_eglContext != EGL_NO_CONTEXT) { 463 // A change in EGL context is an unexpected error, but we don't want to 464 // crash or ANR. Therefore, abandon the Surface Texture and GL resources; 465 // they'll be recreated later in setupDrawing. (We can't delete them 466 // since the context is gone) 467 ALOGE("Unexpected : EGLContext changed! current %x , expected %x", 468 ctx, m_eglContext); 469 transferQueue()->resetQueue(); 470 shader()->forceNeedsInit(); 471 videoLayerManager()->forceNeedsInit(); 472 markAllGLTexturesZero(); 473 } else { 474 // This is the first time we went into this new EGL context. 475 // We will have the GL resources to be re-inited and we can't update 476 // dirty tiles yet. 477 ALOGD("new EGLContext from framework: %x ", ctx); 478 } 479 } else { 480 // Here before we draw, update the Tile which has updated content. 481 // Inside this function, just do GPU blits from the transfer queue into 482 // the Tiles' texture. 483 transferQueue()->updateDirtyTiles(); 484 // Clean up GL textures for video layer. 485 videoLayerManager()->deleteUnusedTextures(); 486 } 487 m_eglContext = ctx; 488 return; 489 } 490 491 int TilesManager::tileWidth() 492 { 493 return TILE_WIDTH; 494 } 495 496 int TilesManager::tileHeight() 497 { 498 return TILE_HEIGHT; 499 } 500 501 TilesManager* TilesManager::instance() 502 { 503 if (!gInstance) { 504 gInstance = new TilesManager(); 505 ALOGV("instance(), new gInstance is %x", gInstance); 506 } 507 return gInstance; 508 } 509 510 TilesManager* TilesManager::gInstance = 0; 511 512 } // namespace WebCore 513 514 #endif // USE(ACCELERATED_COMPOSITING) 515