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/GLConsumer.h> 46 #include <gui/Surface.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 ((10+TILE_PREFETCH_DISTANCE*2)*(7+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 // Eventually this should be dynamically be determined, and smart scheduling 69 // between the generators should be implemented 70 #define NUM_TEXTURES_GENERATORS 1 71 72 namespace WebCore { 73 74 int TilesManager::getMaxTextureAllocation() 75 { 76 if (m_maxTextureAllocation == -1) { 77 GLint glMaxTextureSize = 0; 78 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glMaxTextureSize); 79 GLUtils::checkGlError("TilesManager::getMaxTextureAllocation"); 80 // Half of glMaxTextureSize can be used for base, the other half for layers. 81 m_maxTextureAllocation = std::min(MAX_TEXTURE_ALLOCATION, glMaxTextureSize / 2); 82 if (!m_highEndGfx) 83 m_maxTextureAllocation = m_maxTextureAllocation / 2; 84 } 85 return m_maxTextureAllocation; 86 } 87 88 TilesManager::TilesManager() 89 : m_layerTexturesRemain(true) 90 , m_highEndGfx(false) 91 , m_currentTextureCount(0) 92 , m_currentLayerTextureCount(0) 93 , m_maxTextureAllocation(-1) 94 , m_generatorReady(false) 95 , m_showVisualIndicator(false) 96 , m_invertedScreen(false) 97 , m_useMinimalMemory(true) 98 , m_useDoubleBuffering(true) 99 , m_contentUpdates(0) 100 , m_webkitContentUpdates(0) 101 , m_queue(0) 102 , m_drawGLCount(1) 103 , m_lastTimeLayersUsed(0) 104 , m_hasLayerTextures(false) 105 , m_eglContext(EGL_NO_CONTEXT) 106 { 107 ALOGV("TilesManager ctor"); 108 m_textures.reserveCapacity(MAX_TEXTURE_ALLOCATION / 2); 109 m_availableTextures.reserveCapacity(MAX_TEXTURE_ALLOCATION / 2); 110 m_tilesTextures.reserveCapacity(MAX_TEXTURE_ALLOCATION / 2); 111 m_availableTilesTextures.reserveCapacity(MAX_TEXTURE_ALLOCATION / 2); 112 113 m_textureGenerators = new sp<TexturesGenerator>[NUM_TEXTURES_GENERATORS]; 114 for (int i = 0; i < NUM_TEXTURES_GENERATORS; i++) { 115 m_textureGenerators[i] = new TexturesGenerator(this); 116 ALOGD("Starting TG #%d, %p", i, m_textureGenerators[i].get()); 117 m_textureGenerators[i]->run("TexturesGenerator"); 118 } 119 } 120 121 TilesManager::~TilesManager() 122 { 123 delete[] m_textureGenerators; 124 } 125 126 127 void TilesManager::allocateTextures() 128 { 129 int nbTexturesToAllocate = m_currentTextureCount - m_textures.size(); 130 ALOGV("%d tiles to allocate (%d textures planned)", nbTexturesToAllocate, m_currentTextureCount); 131 int nbTexturesAllocated = 0; 132 for (int i = 0; i < nbTexturesToAllocate; i++) { 133 TileTexture* texture = new TileTexture( 134 tileWidth(), tileHeight()); 135 // the atomic load ensures that the texture has been fully initialized 136 // before we pass a pointer for other threads to operate on 137 TileTexture* loadedTexture = 138 reinterpret_cast<TileTexture*>( 139 android_atomic_acquire_load(reinterpret_cast<int32_t*>(&texture))); 140 m_textures.append(loadedTexture); 141 nbTexturesAllocated++; 142 } 143 144 int nbLayersTexturesToAllocate = m_currentLayerTextureCount - m_tilesTextures.size(); 145 ALOGV("%d layers tiles to allocate (%d textures planned)", 146 nbLayersTexturesToAllocate, m_currentLayerTextureCount); 147 int nbLayersTexturesAllocated = 0; 148 for (int i = 0; i < nbLayersTexturesToAllocate; i++) { 149 TileTexture* texture = new TileTexture( 150 tileWidth(), tileHeight()); 151 // the atomic load ensures that the texture has been fully initialized 152 // before we pass a pointer for other threads to operate on 153 TileTexture* loadedTexture = 154 reinterpret_cast<TileTexture*>( 155 android_atomic_acquire_load(reinterpret_cast<int32_t*>(&texture))); 156 m_tilesTextures.append(loadedTexture); 157 nbLayersTexturesAllocated++; 158 } 159 ALOGV("allocated %d textures for base (total: %d, %d Mb), %d textures for layers (total: %d, %d Mb)", 160 nbTexturesAllocated, m_textures.size(), 161 m_textures.size() * TILE_WIDTH * TILE_HEIGHT * 4 / 1024 / 1024, 162 nbLayersTexturesAllocated, m_tilesTextures.size(), 163 m_tilesTextures.size() * tileWidth() * tileHeight() * 4 / 1024 / 1024); 164 } 165 166 void TilesManager::discardTextures(bool allTextures, bool glTextures) 167 { 168 const unsigned int max = m_textures.size(); 169 170 unsigned long long sparedDrawCount = ~0; // by default, spare no textures 171 if (!allTextures) { 172 // if we're not deallocating all textures, spare those with max drawcount 173 sparedDrawCount = 0; 174 for (unsigned int i = 0; i < max; i++) { 175 TextureOwner* owner = m_textures[i]->owner(); 176 if (owner) 177 sparedDrawCount = std::max(sparedDrawCount, owner->drawCount()); 178 } 179 } 180 discardTexturesVector(sparedDrawCount, m_textures, glTextures); 181 discardTexturesVector(sparedDrawCount, m_tilesTextures, glTextures); 182 } 183 184 void TilesManager::markAllGLTexturesZero() 185 { 186 for (unsigned int i = 0; i < m_textures.size(); i++) 187 m_textures[i]->m_ownTextureId = 0; 188 for (unsigned int i = 0; i < m_tilesTextures.size(); i++) 189 m_tilesTextures[i]->m_ownTextureId = 0; 190 } 191 192 void TilesManager::discardTexturesVector(unsigned long long sparedDrawCount, 193 WTF::Vector<TileTexture*>& textures, 194 bool deallocateGLTextures) 195 { 196 const unsigned int max = textures.size(); 197 int dealloc = 0; 198 WTF::Vector<int> discardedIndex; 199 for (unsigned int i = 0; i < max; i++) { 200 TextureOwner* owner = textures[i]->owner(); 201 if (!owner || owner->drawCount() < sparedDrawCount) { 202 if (deallocateGLTextures) { 203 // deallocate textures' gl memory 204 textures[i]->discardGLTexture(); 205 discardedIndex.append(i); 206 } else if (owner) { 207 // simply detach textures from owner 208 static_cast<Tile*>(owner)->discardTextures(); 209 } 210 dealloc++; 211 } 212 } 213 214 bool base = textures == m_textures; 215 // Clean up the vector of TileTextures and reset the max texture count. 216 if (discardedIndex.size()) { 217 android::Mutex::Autolock lock(m_texturesLock); 218 for (int i = discardedIndex.size() - 1; i >= 0; i--) 219 textures.remove(discardedIndex[i]); 220 221 int remainedTextureNumber = textures.size(); 222 int* countPtr = base ? &m_currentTextureCount : &m_currentLayerTextureCount; 223 if (remainedTextureNumber < *countPtr) { 224 ALOGV("reset currentTextureCount for %s tiles from %d to %d", 225 base ? "base" : "layer", *countPtr, remainedTextureNumber); 226 *countPtr = remainedTextureNumber; 227 } 228 229 } 230 231 ALOGV("Discarded %d %s textures (out of %d %s tiles)", 232 dealloc, (deallocateGLTextures ? "gl" : ""), 233 max, base ? "base" : "layer"); 234 } 235 236 void TilesManager::gatherTexturesNumbers(int* nbTextures, int* nbAllocatedTextures, 237 int* nbLayerTextures, int* nbAllocatedLayerTextures) 238 { 239 *nbTextures = m_textures.size(); 240 for (unsigned int i = 0; i < m_textures.size(); i++) { 241 TileTexture* texture = m_textures[i]; 242 if (texture->m_ownTextureId) 243 *nbAllocatedTextures += 1; 244 } 245 *nbLayerTextures = m_tilesTextures.size(); 246 for (unsigned int i = 0; i < m_tilesTextures.size(); i++) { 247 TileTexture* texture = m_tilesTextures[i]; 248 if (texture->m_ownTextureId) 249 *nbAllocatedLayerTextures += 1; 250 } 251 } 252 253 void TilesManager::dirtyTexturesVector(WTF::Vector<TileTexture*>& textures) 254 { 255 for (unsigned int i = 0; i < textures.size(); i++) { 256 Tile* currentOwner = static_cast<Tile*>(textures[i]->owner()); 257 if (currentOwner) 258 currentOwner->markAsDirty(); 259 } 260 } 261 262 void TilesManager::dirtyAllTiles() 263 { 264 dirtyTexturesVector(m_textures); 265 dirtyTexturesVector(m_tilesTextures); 266 } 267 268 void TilesManager::printTextures() 269 { 270 #ifdef DEBUG 271 ALOGV("++++++"); 272 for (unsigned int i = 0; i < m_textures.size(); i++) { 273 TileTexture* texture = m_textures[i]; 274 Tile* o = 0; 275 if (texture->owner()) 276 o = (Tile*) texture->owner(); 277 int x = -1; 278 int y = -1; 279 if (o) { 280 x = o->x(); 281 y = o->y(); 282 } 283 ALOGV("[%d] texture %x owner: %x (%d, %d) scale: %.2f", 284 i, texture, o, x, y, o ? o->scale() : 0); 285 } 286 ALOGV("------"); 287 #endif // DEBUG 288 } 289 290 void TilesManager::gatherTextures() 291 { 292 android::Mutex::Autolock lock(m_texturesLock); 293 m_availableTextures = m_textures; 294 m_availableTilesTextures = m_tilesTextures; 295 m_layerTexturesRemain = true; 296 } 297 298 TileTexture* TilesManager::getAvailableTexture(Tile* owner) 299 { 300 android::Mutex::Autolock lock(m_texturesLock); 301 302 WTF::Vector<TileTexture*>* availableTexturePool; 303 if (owner->isLayerTile()) 304 availableTexturePool = &m_availableTilesTextures; 305 else 306 availableTexturePool = &m_availableTextures; 307 308 // Sanity check that the tile does not already own a texture 309 if (owner->backTexture() && owner->backTexture()->owner() == owner) { 310 int removeIndex = availableTexturePool->find(owner->backTexture()); 311 312 // TODO: investigate why texture isn't found 313 if (removeIndex >= 0) 314 availableTexturePool->remove(removeIndex); 315 return owner->backTexture(); 316 } 317 318 // The heuristic for selecting a texture is as follows: 319 // 1. Skip textures currently being painted, they can't be painted while 320 // busy anyway 321 // 2. If a tile isn't owned, break with that one 322 // 3. Don't let tiles acquire their front textures 323 // 4. Otherwise, use the least recently prepared tile, but ignoring tiles 324 // drawn in the last frame to avoid flickering 325 326 TileTexture* farthestTexture = 0; 327 unsigned long long oldestDrawCount = getDrawGLCount() - 1; 328 const unsigned int max = availableTexturePool->size(); 329 for (unsigned int i = 0; i < max; i++) { 330 TileTexture* texture = (*availableTexturePool)[i]; 331 Tile* currentOwner = static_cast<Tile*>(texture->owner()); 332 if (!currentOwner) { 333 // unused texture! take it! 334 farthestTexture = texture; 335 break; 336 } 337 338 if (currentOwner == owner) { 339 // Don't let a tile acquire its own front texture, as the 340 // acquisition logic doesn't handle that 341 continue; 342 } 343 344 unsigned long long textureDrawCount = currentOwner->drawCount(); 345 if (oldestDrawCount > textureDrawCount) { 346 farthestTexture = texture; 347 oldestDrawCount = textureDrawCount; 348 } 349 } 350 351 if (farthestTexture) { 352 Tile* previousOwner = static_cast<Tile*>(farthestTexture->owner()); 353 if (farthestTexture->acquire(owner)) { 354 if (previousOwner) { 355 previousOwner->removeTexture(farthestTexture); 356 357 ALOGV("%s texture %p stolen from tile %d, %d for %d, %d, drawCount was %llu (now %llu)", 358 owner->isLayerTile() ? "LAYER" : "BASE", 359 farthestTexture, previousOwner->x(), previousOwner->y(), 360 owner->x(), owner->y(), 361 oldestDrawCount, getDrawGLCount()); 362 } 363 364 availableTexturePool->remove(availableTexturePool->find(farthestTexture)); 365 return farthestTexture; 366 } 367 } else { 368 if (owner->isLayerTile()) { 369 // couldn't find a tile for a layer, layers shouldn't request redraw 370 // TODO: once we do layer prefetching, don't set this for those 371 // tiles 372 m_layerTexturesRemain = false; 373 } 374 } 375 376 ALOGV("Couldn't find an available texture for %s tile %x (%d, %d) out of %d available", 377 owner->isLayerTile() ? "LAYER" : "BASE", 378 owner, owner->x(), owner->y(), max); 379 #ifdef DEBUG 380 printTextures(); 381 #endif // DEBUG 382 return 0; 383 } 384 385 void TilesManager::setHighEndGfx(bool highEnd) 386 { 387 m_highEndGfx = highEnd; 388 } 389 390 bool TilesManager::highEndGfx() 391 { 392 return m_highEndGfx; 393 } 394 395 int TilesManager::currentTextureCount() 396 { 397 android::Mutex::Autolock lock(m_texturesLock); 398 return m_currentTextureCount; 399 } 400 401 int TilesManager::currentLayerTextureCount() 402 { 403 android::Mutex::Autolock lock(m_texturesLock); 404 return m_currentLayerTextureCount; 405 } 406 407 void TilesManager::setCurrentTextureCount(int newTextureCount) 408 { 409 int maxTextureAllocation = getMaxTextureAllocation(); 410 ALOGV("setCurrentTextureCount: %d (current: %d, max:%d)", 411 newTextureCount, m_currentTextureCount, maxTextureAllocation); 412 if (m_currentTextureCount == maxTextureAllocation || 413 newTextureCount <= m_currentTextureCount) 414 return; 415 416 android::Mutex::Autolock lock(m_texturesLock); 417 m_currentTextureCount = std::min(newTextureCount, maxTextureAllocation); 418 419 allocateTextures(); 420 } 421 422 void TilesManager::setCurrentLayerTextureCount(int newTextureCount) 423 { 424 int maxTextureAllocation = getMaxTextureAllocation(); 425 ALOGV("setCurrentLayerTextureCount: %d (current: %d, max:%d)", 426 newTextureCount, m_currentLayerTextureCount, maxTextureAllocation); 427 if (!newTextureCount && m_hasLayerTextures) { 428 double secondsSinceLayersUsed = WTF::currentTime() - m_lastTimeLayersUsed; 429 if (secondsSinceLayersUsed > LAYER_TEXTURES_DESTROY_TIMEOUT) { 430 unsigned long long sparedDrawCount = ~0; // by default, spare no textures 431 bool deleteGLTextures = true; 432 discardTexturesVector(sparedDrawCount, m_tilesTextures, deleteGLTextures); 433 m_hasLayerTextures = false; 434 } 435 return; 436 } 437 m_lastTimeLayersUsed = WTF::currentTime(); 438 if (m_currentLayerTextureCount == maxTextureAllocation || 439 newTextureCount <= m_currentLayerTextureCount) 440 return; 441 442 android::Mutex::Autolock lock(m_texturesLock); 443 m_currentLayerTextureCount = std::min(newTextureCount, maxTextureAllocation); 444 445 allocateTextures(); 446 m_hasLayerTextures = true; 447 } 448 449 TransferQueue* TilesManager::transferQueue() 450 { 451 // m_queue will be created on the UI thread, although it may 452 // be accessed from the TexturesGenerator. However, that can only happen after 453 // a previous transferQueue() call due to a prepare. 454 if (!m_queue) 455 m_queue = new TransferQueue(m_useMinimalMemory && !m_highEndGfx); 456 return m_queue; 457 } 458 459 // When GL context changed or we get a low memory signal, we want to cleanup all 460 // the GPU memory webview is using. 461 // The recreation will be on the next incoming draw call at the drawGL of 462 // GLWebViewState or the VideoLayerAndroid 463 void TilesManager::cleanupGLResources() 464 { 465 transferQueue()->cleanupGLResourcesAndQueue(); 466 shader()->cleanupGLResources(); 467 videoLayerManager()->cleanupGLResources(); 468 m_eglContext = EGL_NO_CONTEXT; 469 GLUtils::checkGlError("TilesManager::cleanupGLResources"); 470 } 471 472 void TilesManager::updateTilesIfContextVerified() 473 { 474 EGLContext ctx = eglGetCurrentContext(); 475 GLUtils::checkEglError("contextChanged"); 476 if (ctx != m_eglContext) { 477 if (m_eglContext != EGL_NO_CONTEXT) { 478 // A change in EGL context is an unexpected error, but we don't want to 479 // crash or ANR. Therefore, abandon the Surface Texture and GL resources; 480 // they'll be recreated later in setupDrawing. (We can't delete them 481 // since the context is gone) 482 ALOGE("Unexpected : EGLContext changed! current %x , expected %x", 483 ctx, m_eglContext); 484 transferQueue()->resetQueue(); 485 shader()->forceNeedsInit(); 486 videoLayerManager()->forceNeedsInit(); 487 markAllGLTexturesZero(); 488 } else { 489 // This is the first time we went into this new EGL context. 490 // We will have the GL resources to be re-inited and we can't update 491 // dirty tiles yet. 492 ALOGD("new EGLContext from framework: %x ", ctx); 493 } 494 } else { 495 // Here before we draw, update the Tile which has updated content. 496 // Inside this function, just do GPU blits from the transfer queue into 497 // the Tiles' texture. 498 transferQueue()->updateDirtyTiles(); 499 // Clean up GL textures for video layer. 500 videoLayerManager()->deleteUnusedTextures(); 501 } 502 m_eglContext = ctx; 503 return; 504 } 505 506 void TilesManager::removeOperationsForFilter(OperationFilter* filter) 507 { 508 for (int i = 0; i < NUM_TEXTURES_GENERATORS; i++) 509 m_textureGenerators[i]->removeOperationsForFilter(filter); 510 delete filter; 511 } 512 513 bool TilesManager::tryUpdateOperationWithPainter(Tile* tile, TilePainter* painter) 514 { 515 for (int i = 0; i < NUM_TEXTURES_GENERATORS; i++) { 516 if (m_textureGenerators[i]->tryUpdateOperationWithPainter(tile, painter)) 517 return true; 518 } 519 return false; 520 } 521 522 void TilesManager::scheduleOperation(QueuedOperation* operation) 523 { 524 // TODO: painter awareness, store prefer awareness, store preferred thread into painter 525 m_scheduleThread = (m_scheduleThread + 1) % NUM_TEXTURES_GENERATORS; 526 m_textureGenerators[m_scheduleThread]->scheduleOperation(operation); 527 } 528 529 int TilesManager::tileWidth() 530 { 531 return TILE_WIDTH; 532 } 533 534 int TilesManager::tileHeight() 535 { 536 return TILE_HEIGHT; 537 } 538 539 TilesManager* TilesManager::instance() 540 { 541 if (!gInstance) { 542 gInstance = new TilesManager(); 543 ALOGV("instance(), new gInstance is %x", gInstance); 544 } 545 return gInstance; 546 } 547 548 TilesManager* TilesManager::gInstance = 0; 549 550 } // namespace WebCore 551 552 #endif // USE(ACCELERATED_COMPOSITING) 553